FunChain


NameFunChain JSON
Version 0.1.0 PyPI version JSON
download
home_page
SummaryChain functions easily and safely
upload_time2023-12-17 17:15:40
maintainer
docs_urlNone
authorMARSO Adnan
requires_python>=3.9
licenseMIT License Copyright (c) 2022 MARSO Adnan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords function functional chain chaining pipe compose composition processing safe handle report failure
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <div align="center" id="heading">
  <h1><img src="./docs/_static/favicon/favicon.svg" alt="logo" width="24" height="24" /> FunChain</h1>
  <strong style="">chain functions easily and safely</strong>
  <div>
    <br/>
    <a href="https://github.com/mediadnan/funchain/actions/workflows/tests.yml"><img src="https://github.com/mediadnan/funchain/actions/workflows/tests.yml/badge.svg" alt="Tests" /></a>
    <a href="https://codecov.io/gh/mediadnan/FunChain" ><img src="https://codecov.io/gh/mediadnan/FunChain/graph/badge.svg?token=HZWUDTLC3O"/></a>
    <img src="https://img.shields.io/github/license/mediadnan/funchain" alt="License MIT" />
    <a href="https://funchain.readthedocs.io/en/latest/"><img src="https://img.shields.io/readthedocs/funchain" alt="Read the Docs"></a>
    <img src="https://img.shields.io/pypi/pyversions/funchain" alt="Python versions" />
    <img src="https://img.shields.io/pypi/v/funchain" alt="PyPI version" />
    <img src="https://img.shields.io/pypi/dm/funchain" alt="PyPI - Downloads"/>
  </div>
</div> 


## Introduction
**FunChain** is a package that provides tools and utilities to easily compose functions sequentially
to create processing pipelines with minimum code and simple syntax,
all while tracking, reporting and containing any crash that could occur in any step.  

The library was part of another API client project and was developed to reduce boilerplate code when dealing
with networking and unstructured responses, after that and due to its general purpose usage, it was separated 
into an independent library that could be integrated in other python projects.

**FunChain** is now based on another standalone library called [Failures](https://pypi.org/project/failures/)
that only specialises in labeling and reporting nested errors.

> **_NOTE_** This library is still in **experimentation** phase, 
> if you plan to include it in your production app, make sure
> to test that use case to avoid any unexpected bugs.


## Installation
You can include ``FunChain`` into your environment using this command

````shell
pip install funchain
````

## Audience
Anyone working on a python project that requires processing data through multiple functions and needs to isolate
each step and report errors with labels and details **can** benefit from the tooling offered by this library,
using it may reduce boilerplate code and code repetition.

Continue reading the documentation to find out if ``FunChain`` offers tools that you need 
or aligns with your design pattern.

## Features
### Composing functions sequentially 🔗
Composing functions in a sequence to create a pipeline
is the **main functionality** of this library, the sequence
of functions *(or so-called nodes)* results in a single
function-like objects that passes the result of one function
to the next as input.

This means `fun1 ➜ fun2 ➜ fun3`, or programmatically speaking
`fun3(fun2(fun1(input)))`

### Iterating multiple items ⛓️
While composing functions, we might want to apply a function
to each item of an iterable instead of applying it to the whole
iterable, for example if ``fun1`` returns a list like ``[1, 2, 3]`` 
the next one will be called like ``[fun2(1), fun2(2), fun2(3)]`` 
instead of ``fun2([1, 2, 3])``

This means `fun1 ⭄ fun2`, or programmatically speaking 
`[fun2(item) for item in fun1(input)]`

### Branching 🦑
When a returned value needs to take multiple routes at some points,
we can use branching methods, and that by defining the structure
of the result and _(either a ``dict`` and ``list``)_ filling it
with the sequence of functions, and the result will have the same 
structure.

So for dictionaries, the model `{'a': fun1, 'b': fun2}` will return
`{'a': fun1(input), 'b': fun2(input)}`

And the model `[fun1, fun2]` will return `[fun1(input), fun2(input)]`.

### Debug friendly 🪲
Composing multiple functions makes it hard
to traceback errors and get which input caused it,
especially in production where it's hard to reproduce 
the same crash or debug it.
But thanks to the ``failures`` library, tracking and pinpointing 
nested errors becomes way easier; each exception gets wrapped
and labeled with a qualified name indicating its location, and
the exact input that caused it.

### Good and minimal syntax 🎈
The syntax of this library was intentionally made easy and minimal users to compose functions,
to achieve complex pipeline structures with the least amount of code, and make it more readable and intuitive
to be easily maintained. It also makes it a beginner-friendly tool with a gentle learning curve.

### Async support 🪄
All the previously mentioned features are available for asynchronous
operations; coroutines can be composed together the same way to produce
an asynchronous chain of functions,
the same if a normal function is piped to an async one.

Normal and asynchronous functions can be mixed together and
`funchain` will know what to do, and if any async function is chained,
the result will automatically be an asynchronous callable.

This makes this library a good choice for processing IO intensive
operations, such as performing network requests or reading files from
the hard drive in a non-blocking way.

### Flexibility
The structure of a function chain can be as deep and complex as needed,
every component is considered a chain node, so a chain can contain a
dict or list model and each can contain another chain or model and so.

## Usage
### Function chaining

This example illustrates how to compose functions in a sequence
using `chain()`

```python
>>> from funchain import chain

>>> def add_two(num: int) -> int:
...   return num + 2

>>> def double(num: int) -> int:
...   return num * 2

>>> fun = chain(add_two, double, add_two)

>>> fun(5)
16
```
In this example we created two simple functions `add_two` and `double`
and we chained them `chain(add_two, double, add_two)`.

This basically means that any input given to `fun` _which is a chain_,
will be incremented by 2, then doubled, then incremented again by 2;

This mean: `add_two(5) = 7 ⮕ double(7) = 14 ⮕ add_two(14) = 16`

### Predefined nodes
We can compile nodes in advance using `node()` and chain them later
by concatenation _(using_ `+` _operator)_

```python
>>> from funchain import node

>>> add_two = node(lambda x: x + 2, name="add_two")

>>> double = node(lambda x: x * 2, name="double")

>>> fun = add_two | double | add_two

>>> fun(5)
16
```

This works the same as the first example and it's more convenient for functions that are meant to be used
as components.

If we had a functions like the previous example,
we can integrate compile it to a node like this

```python
>>> from funchain import node

>>> def double(num: int) -> int:
...   return num * 2

>>> nd = node(double)

>>> fun = nd | nd

>>> fun(5)
20
```


_... TODO_

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "FunChain",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": "",
    "keywords": "function,functional,chain,chaining,pipe,compose,composition,processing,safe,handle,report,failure",
    "author": "MARSO Adnan",
    "author_email": "",
    "download_url": "https://files.pythonhosted.org/packages/02/ac/0070dd2b92f6230c7b9e7baa40b2f5edfffd3b2327381e2d4e7f55887983/FunChain-0.1.0.tar.gz",
    "platform": null,
    "description": "<div align=\"center\" id=\"heading\">\n  <h1><img src=\"./docs/_static/favicon/favicon.svg\" alt=\"logo\" width=\"24\" height=\"24\" /> FunChain</h1>\n  <strong style=\"\">chain functions easily and safely</strong>\n  <div>\n    <br/>\n    <a href=\"https://github.com/mediadnan/funchain/actions/workflows/tests.yml\"><img src=\"https://github.com/mediadnan/funchain/actions/workflows/tests.yml/badge.svg\" alt=\"Tests\" /></a>\n    <a href=\"https://codecov.io/gh/mediadnan/FunChain\" ><img src=\"https://codecov.io/gh/mediadnan/FunChain/graph/badge.svg?token=HZWUDTLC3O\"/></a>\n    <img src=\"https://img.shields.io/github/license/mediadnan/funchain\" alt=\"License MIT\" />\n    <a href=\"https://funchain.readthedocs.io/en/latest/\"><img src=\"https://img.shields.io/readthedocs/funchain\" alt=\"Read the Docs\"></a>\n    <img src=\"https://img.shields.io/pypi/pyversions/funchain\" alt=\"Python versions\" />\n    <img src=\"https://img.shields.io/pypi/v/funchain\" alt=\"PyPI version\" />\n    <img src=\"https://img.shields.io/pypi/dm/funchain\" alt=\"PyPI - Downloads\"/>\n  </div>\n</div> \n\n\n## Introduction\n**FunChain** is a package that provides tools and utilities to easily compose functions sequentially\nto create processing pipelines with minimum code and simple syntax,\nall while tracking, reporting and containing any crash that could occur in any step.  \n\nThe library was part of another API client project and was developed to reduce boilerplate code when dealing\nwith networking and unstructured responses, after that and due to its general purpose usage, it was separated \ninto an independent library that could be integrated in other python projects.\n\n**FunChain** is now based on another standalone library called [Failures](https://pypi.org/project/failures/)\nthat only specialises in labeling and reporting nested errors.\n\n> **_NOTE_** This library is still in **experimentation** phase, \n> if you plan to include it in your production app, make sure\n> to test that use case to avoid any unexpected bugs.\n\n\n## Installation\nYou can include ``FunChain`` into your environment using this command\n\n````shell\npip install funchain\n````\n\n## Audience\nAnyone working on a python project that requires processing data through multiple functions and needs to isolate\neach step and report errors with labels and details **can** benefit from the tooling offered by this library,\nusing it may reduce boilerplate code and code repetition.\n\nContinue reading the documentation to find out if ``FunChain`` offers tools that you need \nor aligns with your design pattern.\n\n## Features\n### Composing functions sequentially \ud83d\udd17\nComposing functions in a sequence to create a pipeline\nis the **main functionality** of this library, the sequence\nof functions *(or so-called nodes)* results in a single\nfunction-like objects that passes the result of one function\nto the next as input.\n\nThis means `fun1 \u279c fun2 \u279c fun3`, or programmatically speaking\n`fun3(fun2(fun1(input)))`\n\n### Iterating multiple items \u26d3\ufe0f\nWhile composing functions, we might want to apply a function\nto each item of an iterable instead of applying it to the whole\niterable, for example if ``fun1`` returns a list like ``[1, 2, 3]`` \nthe next one will be called like ``[fun2(1), fun2(2), fun2(3)]`` \ninstead of ``fun2([1, 2, 3])``\n\nThis means `fun1 \u2b44 fun2`, or programmatically speaking \n`[fun2(item) for item in fun1(input)]`\n\n### Branching \ud83e\udd91\nWhen a returned value needs to take multiple routes at some points,\nwe can use branching methods, and that by defining the structure\nof the result and _(either a ``dict`` and ``list``)_ filling it\nwith the sequence of functions, and the result will have the same \nstructure.\n\nSo for dictionaries, the model `{'a': fun1, 'b': fun2}` will return\n`{'a': fun1(input), 'b': fun2(input)}`\n\nAnd the model `[fun1, fun2]` will return `[fun1(input), fun2(input)]`.\n\n### Debug friendly \ud83e\udeb2\nComposing multiple functions makes it hard\nto traceback errors and get which input caused it,\nespecially in production where it's hard to reproduce \nthe same crash or debug it.\nBut thanks to the ``failures`` library, tracking and pinpointing \nnested errors becomes way easier; each exception gets wrapped\nand labeled with a qualified name indicating its location, and\nthe exact input that caused it.\n\n### Good and minimal syntax \ud83c\udf88\nThe syntax of this library was intentionally made easy and minimal users to compose functions,\nto achieve complex pipeline structures with the least amount of code, and make it more readable and intuitive\nto be easily maintained. It also makes it a beginner-friendly tool with a gentle learning curve.\n\n### Async support \ud83e\ude84\nAll the previously mentioned features are available for asynchronous\noperations; coroutines can be composed together the same way to produce\nan asynchronous chain of functions,\nthe same if a normal function is piped to an async one.\n\nNormal and asynchronous functions can be mixed together and\n`funchain` will know what to do, and if any async function is chained,\nthe result will automatically be an asynchronous callable.\n\nThis makes this library a good choice for processing IO intensive\noperations, such as performing network requests or reading files from\nthe hard drive in a non-blocking way.\n\n### Flexibility\nThe structure of a function chain can be as deep and complex as needed,\nevery component is considered a chain node, so a chain can contain a\ndict or list model and each can contain another chain or model and so.\n\n## Usage\n### Function chaining\n\nThis example illustrates how to compose functions in a sequence\nusing `chain()`\n\n```python\n>>> from funchain import chain\n\n>>> def add_two(num: int) -> int:\n...   return num + 2\n\n>>> def double(num: int) -> int:\n...   return num * 2\n\n>>> fun = chain(add_two, double, add_two)\n\n>>> fun(5)\n16\n```\nIn this example we created two simple functions `add_two` and `double`\nand we chained them `chain(add_two, double, add_two)`.\n\nThis basically means that any input given to `fun` _which is a chain_,\nwill be incremented by 2, then doubled, then incremented again by 2;\n\nThis mean: `add_two(5) = 7 \u2b95 double(7) = 14 \u2b95 add_two(14) = 16`\n\n### Predefined nodes\nWe can compile nodes in advance using `node()` and chain them later\nby concatenation _(using_ `+` _operator)_\n\n```python\n>>> from funchain import node\n\n>>> add_two = node(lambda x: x + 2, name=\"add_two\")\n\n>>> double = node(lambda x: x * 2, name=\"double\")\n\n>>> fun = add_two | double | add_two\n\n>>> fun(5)\n16\n```\n\nThis works the same as the first example and it's more convenient for functions that are meant to be used\nas components.\n\nIf we had a functions like the previous example,\nwe can integrate compile it to a node like this\n\n```python\n>>> from funchain import node\n\n>>> def double(num: int) -> int:\n...   return num * 2\n\n>>> nd = node(double)\n\n>>> fun = nd | nd\n\n>>> fun(5)\n20\n```\n\n\n_... TODO_\n",
    "bugtrack_url": null,
    "license": "MIT License  Copyright (c) 2022 MARSO Adnan  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ",
    "summary": "Chain functions easily and safely",
    "version": "0.1.0",
    "project_urls": {
        "bugs": "https://github.com/mediadnan/funchain/issues",
        "documentation": "https://funchain.readthedocs.com",
        "repository": "https://github.com/mediadnan/funchain"
    },
    "split_keywords": [
        "function",
        "functional",
        "chain",
        "chaining",
        "pipe",
        "compose",
        "composition",
        "processing",
        "safe",
        "handle",
        "report",
        "failure"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ce753ed1336d40ed2d2c3912fe6242810bf6eafcbfcbb17c837d54b33a43d704",
                "md5": "60a610aed7d8902866691ce757a93fee",
                "sha256": "b903225fc022517824456d5889ab0c4a1134705bb567df3067e8ed436a1b176c"
            },
            "downloads": -1,
            "filename": "FunChain-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "60a610aed7d8902866691ce757a93fee",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 11634,
            "upload_time": "2023-12-17T17:15:38",
            "upload_time_iso_8601": "2023-12-17T17:15:38.254117Z",
            "url": "https://files.pythonhosted.org/packages/ce/75/3ed1336d40ed2d2c3912fe6242810bf6eafcbfcbb17c837d54b33a43d704/FunChain-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "02ac0070dd2b92f6230c7b9e7baa40b2f5edfffd3b2327381e2d4e7f55887983",
                "md5": "14f036cb4df68df285fa48564f967e1a",
                "sha256": "e35dae1364c83ddb4b48b50695a98a33ac92a5b0525c488edb09befc215e086c"
            },
            "downloads": -1,
            "filename": "FunChain-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "14f036cb4df68df285fa48564f967e1a",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 12540,
            "upload_time": "2023-12-17T17:15:40",
            "upload_time_iso_8601": "2023-12-17T17:15:40.068655Z",
            "url": "https://files.pythonhosted.org/packages/02/ac/0070dd2b92f6230c7b9e7baa40b2f5edfffd3b2327381e2d4e7f55887983/FunChain-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-12-17 17:15:40",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "mediadnan",
    "github_project": "funchain",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "tox": true,
    "lcname": "funchain"
}
        
Elapsed time: 4.45270s