toradh


Nametoradh JSON
Version 0.2.2 PyPI version JSON
download
home_pagehttps://github.com/levensworth/toradh
SummaryMinimalistic library intended to bring better flow control to Python.
upload_time2024-09-03 11:27:13
maintainerNone
docs_urlNone
authorSantiago Bassani
requires_python<4.0,>=3.8
licenseMIT
keywords toradh rust result optional control-flow
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # TORADH
[![codecov](https://codecov.io/github/levensworth/toradh/graph/badge.svg?token=hsojulL0hJ)](https://codecov.io/github/levensworth/toradh)
[![pypi](https://img.shields.io/pypi/v/toradh.svg)](https://pypi.python.org/pypi/toradh/)
[![version](https://img.shields.io/pypi/pyversions/toradh.svg)](https://pypi.python.org/pypi/toradh/)
[![license](https://img.shields.io/pypi/l/toradh.svg)](https://en.wikipedia.org/wiki/GNU_General_Public_License)


Minimalistic library intended to bring better flow control to Python.

## Motivation:
Have you ever install a new sdk or lib that you really wanted to use for a particular project only to find out, mid development
that if you try to `.get()` something that is not present in the instance it will `raise` an `ValueError` which was not mentioned
in any docstring. Does it sounds familiar? well, this kind of frustration is what `toradh` (pronounced "taru") comes to ease.
By bringing some of the key fundamental structures of languages such as `Rust` to Python, we aim to make exception handling a little
less chaotic and more predictable when needed. 

## Install:
```
pip install toradh
```


## Usage:
We support structural pattern matching with the `match` operator as well as build in methods for control flow management.

```python
from typing import Literal
from toradh import Result, Ok, Err

DB = {
    1: 'john', 
    2: 'jane',
}

def create_user(name: str) -> Result[int, ValueError | KeyError]:
    if name in DB:
        return Err(KeyError(f'A user by {name} already exists'))
    if len(name) > 10:
        return Err(ValueError('names can not be too long'))
    user_id = len(DB)+1
    DB[user_id] = name
    return Ok(user_id)

def basic_handling():
    # In this example, we don't want to interrupt the execution
    # but we don't really want to handle specific errors
    res = create_user('janet')
    match res:
        case Ok(user_id):
            print(f'successfully persisted under {user_id}')
        case Err(err):
            print(f'There was an error => {err}')

def concrete_error_handling():
    # In this case, we are handling each possible scenario and 
    # taking some sort of action based on the type of error
    res = create_user('janet')
    
    # If all cases aren't handle mypy will alert about this.
    match res.kind():
        case int():
            print(f'successfully persisted under {res.unwrap()}')
        case ValueError():
            print(f'There was a problem with the name\'s length')
        case KeyError():
            print(f'Name already exists')
            #include possible measure to recover from this error
            # ...

def no_handling():
    # in this case, we do not want to handle the possible errors
    # if any are give, the .unwrap() call simply raise them as normal python code
    res = create_user('janet')
    print(f'successfully persisted under {res.unwrap()}')

if __name__ == '__main__':
    basic_handling()

    concrete_error_handling()
    
    no_handling()

```


## why use Toradh?
First let's go over some simple examples:

Let's go over an example not using the framework
```python

DB = {
    1: 'john', 
    2: 'jane',
}

# instead of this
def get_user_by_id_old(user_id: int) -> str | None:
    return DB.get(user_id)    

def main():
    user = get_user_by_id_old(1)
    
    if user is not None:
        print(f'Hello {user}')
    else:
        print('id not found')
```

and how it would look like if using it
```python
from toradh import Optional, Option, Nothing, Some

DB = {
    1: 'john', 
    2: 'jane',
}

def get_user_by_id_new(user_id: int) -> Optional[str]:
    if user_id not in DB:
        return Nothing()
    
    return Some(DB.get(user_id))
    
def main():
    user = get_user_by_id_new(1)
    
    if user.is_some():
        print(f'Hello {user.unwrap()}')
    else:
        print('id not found')
        
```


Now, at this point it really doesn't add too much. But if you allow the following state to 
exist in your `DB`.

```python
D  = {
    1: 'john',
    2: 'jane',
    3: None
}
```
Then things, get tricky for the first implementation. 
***How do you distinguish between the DB value `None` and the state of element not found?***

A possible solution would be:

```python

DB = {
    1: 'john', 
    2: 'jane',
}

def get_user_by_id_old(user_id: int) -> str | None:
    return DB[user_id] # this will raise a KeyError if user_id is not part of DB

def main():
    try:
        user = get_user_by_id_old(1)
    except KeyError:
        print('user not found') 
        return # cut control flow here
    
    if user is not None:
        print(f'Hello {user}')
    else:
        print(f'User gone')
```

Which is not ideal as the KeyError exception is not visible throw the type hint system, which puts the 
pleasure of correctly handling this behavior on the invoker.

As opposed to this implementation:

```python
from toradh import Optional, Option, Nothing, Some

DB = {
    1: 'john', 
    2: 'jane',
    3: None
}

def get_user_by_id_new(user_id: int) -> Optional[str | None]:
    if user_id not in DB:
        return Nothing()
    
    return Some(DB.get(user_id))
    
def main():
    user = get_user_by_id_new(1)
    
    match user:
        case Nothing():
            print('id not found')    
        case Some(None):
            print('User is gone')
        case Some(name):
            print(f'Hello {name}')
        
```

In this example (although not really a good use of `None`) we can see that there is a clear distinction between the absence of what 
we want and an actual product of calling the function.


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/levensworth/toradh",
    "name": "toradh",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.8",
    "maintainer_email": null,
    "keywords": "toradh, rust, result, optional, control-flow",
    "author": "Santiago Bassani",
    "author_email": "santiago.bassani96@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/d9/2f/d745d6da846c0a45fdb86d136f1c7e4dba9ea84f8ae96bcf446afbc1e5d4/toradh-0.2.2.tar.gz",
    "platform": null,
    "description": "# TORADH\n[![codecov](https://codecov.io/github/levensworth/toradh/graph/badge.svg?token=hsojulL0hJ)](https://codecov.io/github/levensworth/toradh)\n[![pypi](https://img.shields.io/pypi/v/toradh.svg)](https://pypi.python.org/pypi/toradh/)\n[![version](https://img.shields.io/pypi/pyversions/toradh.svg)](https://pypi.python.org/pypi/toradh/)\n[![license](https://img.shields.io/pypi/l/toradh.svg)](https://en.wikipedia.org/wiki/GNU_General_Public_License)\n\n\nMinimalistic library intended to bring better flow control to Python.\n\n## Motivation:\nHave you ever install a new sdk or lib that you really wanted to use for a particular project only to find out, mid development\nthat if you try to `.get()` something that is not present in the instance it will `raise` an `ValueError` which was not mentioned\nin any docstring. Does it sounds familiar? well, this kind of frustration is what `toradh` (pronounced \"taru\") comes to ease.\nBy bringing some of the key fundamental structures of languages such as `Rust` to Python, we aim to make exception handling a little\nless chaotic and more predictable when needed. \n\n## Install:\n```\npip install toradh\n```\n\n\n## Usage:\nWe support structural pattern matching with the `match` operator as well as build in methods for control flow management.\n\n```python\nfrom typing import Literal\nfrom toradh import Result, Ok, Err\n\nDB = {\n    1: 'john', \n    2: 'jane',\n}\n\ndef create_user(name: str) -> Result[int, ValueError | KeyError]:\n    if name in DB:\n        return Err(KeyError(f'A user by {name} already exists'))\n    if len(name) > 10:\n        return Err(ValueError('names can not be too long'))\n    user_id = len(DB)+1\n    DB[user_id] = name\n    return Ok(user_id)\n\ndef basic_handling():\n    # In this example, we don't want to interrupt the execution\n    # but we don't really want to handle specific errors\n    res = create_user('janet')\n    match res:\n        case Ok(user_id):\n            print(f'successfully persisted under {user_id}')\n        case Err(err):\n            print(f'There was an error => {err}')\n\ndef concrete_error_handling():\n    # In this case, we are handling each possible scenario and \n    # taking some sort of action based on the type of error\n    res = create_user('janet')\n    \n    # If all cases aren't handle mypy will alert about this.\n    match res.kind():\n        case int():\n            print(f'successfully persisted under {res.unwrap()}')\n        case ValueError():\n            print(f'There was a problem with the name\\'s length')\n        case KeyError():\n            print(f'Name already exists')\n            #include possible measure to recover from this error\n            # ...\n\ndef no_handling():\n    # in this case, we do not want to handle the possible errors\n    # if any are give, the .unwrap() call simply raise them as normal python code\n    res = create_user('janet')\n    print(f'successfully persisted under {res.unwrap()}')\n\nif __name__ == '__main__':\n    basic_handling()\n\n    concrete_error_handling()\n    \n    no_handling()\n\n```\n\n\n## why use Toradh?\nFirst let's go over some simple examples:\n\nLet's go over an example not using the framework\n```python\n\nDB = {\n    1: 'john', \n    2: 'jane',\n}\n\n# instead of this\ndef get_user_by_id_old(user_id: int) -> str | None:\n    return DB.get(user_id)    \n\ndef main():\n    user = get_user_by_id_old(1)\n    \n    if user is not None:\n        print(f'Hello {user}')\n    else:\n        print('id not found')\n```\n\nand how it would look like if using it\n```python\nfrom toradh import Optional, Option, Nothing, Some\n\nDB = {\n    1: 'john', \n    2: 'jane',\n}\n\ndef get_user_by_id_new(user_id: int) -> Optional[str]:\n    if user_id not in DB:\n        return Nothing()\n    \n    return Some(DB.get(user_id))\n    \ndef main():\n    user = get_user_by_id_new(1)\n    \n    if user.is_some():\n        print(f'Hello {user.unwrap()}')\n    else:\n        print('id not found')\n        \n```\n\n\nNow, at this point it really doesn't add too much. But if you allow the following state to \nexist in your `DB`.\n\n```python\nD  = {\n    1: 'john',\n    2: 'jane',\n    3: None\n}\n```\nThen things, get tricky for the first implementation. \n***How do you distinguish between the DB value `None` and the state of element not found?***\n\nA possible solution would be:\n\n```python\n\nDB = {\n    1: 'john', \n    2: 'jane',\n}\n\ndef get_user_by_id_old(user_id: int) -> str | None:\n    return DB[user_id] # this will raise a KeyError if user_id is not part of DB\n\ndef main():\n    try:\n        user = get_user_by_id_old(1)\n    except KeyError:\n        print('user not found') \n        return # cut control flow here\n    \n    if user is not None:\n        print(f'Hello {user}')\n    else:\n        print(f'User gone')\n```\n\nWhich is not ideal as the KeyError exception is not visible throw the type hint system, which puts the \npleasure of correctly handling this behavior on the invoker.\n\nAs opposed to this implementation:\n\n```python\nfrom toradh import Optional, Option, Nothing, Some\n\nDB = {\n    1: 'john', \n    2: 'jane',\n    3: None\n}\n\ndef get_user_by_id_new(user_id: int) -> Optional[str | None]:\n    if user_id not in DB:\n        return Nothing()\n    \n    return Some(DB.get(user_id))\n    \ndef main():\n    user = get_user_by_id_new(1)\n    \n    match user:\n        case Nothing():\n            print('id not found')    \n        case Some(None):\n            print('User is gone')\n        case Some(name):\n            print(f'Hello {name}')\n        \n```\n\nIn this example (although not really a good use of `None`) we can see that there is a clear distinction between the absence of what \nwe want and an actual product of calling the function.\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Minimalistic library intended to bring better flow control to Python.",
    "version": "0.2.2",
    "project_urls": {
        "Homepage": "https://github.com/levensworth/toradh",
        "Repository": "https://github.com/levensworth/toradh"
    },
    "split_keywords": [
        "toradh",
        " rust",
        " result",
        " optional",
        " control-flow"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "f306f490ff7c2e4796c0861bed973ef1246be3f141a215f9b756bc264d88d62a",
                "md5": "6c446f43c0d272fb6016ea0860b22c94",
                "sha256": "ccf3f42935ad5ebba0fc53ce81a498ee3f28b350d5f01396b5e4b79852f52ad5"
            },
            "downloads": -1,
            "filename": "toradh-0.2.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "6c446f43c0d272fb6016ea0860b22c94",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.8",
            "size": 7473,
            "upload_time": "2024-09-03T11:27:12",
            "upload_time_iso_8601": "2024-09-03T11:27:12.938366Z",
            "url": "https://files.pythonhosted.org/packages/f3/06/f490ff7c2e4796c0861bed973ef1246be3f141a215f9b756bc264d88d62a/toradh-0.2.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d92fd745d6da846c0a45fdb86d136f1c7e4dba9ea84f8ae96bcf446afbc1e5d4",
                "md5": "2b71f7483134ceb1219c92acea8c8bed",
                "sha256": "9d7d0c93d23ee7b85688ed8236188535cf048fc036c897e35ddfafba71e7a93c"
            },
            "downloads": -1,
            "filename": "toradh-0.2.2.tar.gz",
            "has_sig": false,
            "md5_digest": "2b71f7483134ceb1219c92acea8c8bed",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.8",
            "size": 6975,
            "upload_time": "2024-09-03T11:27:13",
            "upload_time_iso_8601": "2024-09-03T11:27:13.935605Z",
            "url": "https://files.pythonhosted.org/packages/d9/2f/d745d6da846c0a45fdb86d136f1c7e4dba9ea84f8ae96bcf446afbc1e5d4/toradh-0.2.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-09-03 11:27:13",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "levensworth",
    "github_project": "toradh",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "tox": true,
    "lcname": "toradh"
}
        
Elapsed time: 0.44620s