Pyhandling


NamePyhandling JSON
Version 2.2.0 PyPI version JSON
download
home_pagehttps://github.com/TheArtur128/Pyhandling
SummaryLibrary for metaprogramming
upload_time2023-01-29 15:30:11
maintainer
docs_urlNone
authorArthur
requires_python>=3.11
licenseGNU General Public License v3.0
keywords syntax library utils logging metaprogramming annotations immutability data-structures error-handling handling checking utils-library pseudo-operators
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Pyhandling
Library for advanced continuous handling of anything

Provides tools to extend single call logic on a nearly unlimited scale</br>
You can even integrate the entire program logic into one call

## Installation
`pip install pyhandling`

## Usage examples
### Composition
Merge your functions into one

```python
from pyhandling import *


complemented_number = str |then>> (lambda line: line + '6') |then>> int
complemented_number(25)
```

to later get
```python
256
```

or you can do the same but call the function immediately
```python
25 >= str |then>> (lambda line: line + '6') |then>> int
```

and get the same result
```python
256
```

### Currying
Add additional arguments to function input arguments
```python
formattly_sum = "{} {}{}".format

post_partial(formattly_sum, "world", '!')("Hello") 
```

using pseudo operators
```python
(formattly_sum |to| "Hello")("world", '!')
(formattly_sum |to* ("Hello", "world"))('!')

(formattly_sum |by| '!')("Hello", "world")
```

or not necessarily now
```python
container = close(formattly_sum)
opened_container = container("Hello")

opened_container("world", '!')
```

using all possible ways
```python
post_container = close(formattly_sum, closer=post_partial)

post_container('!')("Hello", "world")
```

Eventually, they all return
```
Hello world!
```

### Interface control
Abstract the output value
```python
print(returnly(print)("Some input argument"))
```
```
Some input argument
Some input argument
```

or input values
```python
from functools import partial


eventually(partial(print, 16))(1, 2, 3)
```
```
16
```

### Atomic functions
Use synonyms for operators
```python
return_(256)
raise_(Exception("Something is wrong"))
```
```
256

Traceback ...
Exception: Something is wrong
```

for atomic operations
```python
execute_operation(60, '+', 4)
transform(str(), 'not')
```
```
64
True
```

for syntax operations
```python
call(range, 16)

getitem_of({"some-key": "some-value"}, "some-key")
```
```
range(16)
some-value
```

### Annotating
Use standart annotation templates from `annotations` package for routine cases
```python
from pyhandling.annotations import checker_of, reformer_of, merger_of

from pyannotating import number


is_number_even: checker_of[number] = lambda number: number % 2 == 0

add_hundert_to: reformer_of[number] = lambda number: number + 100

format_lines: merger_of[str] = "{} {}!".format
```

or annotations themselves
```python
from pyannotating import many_or_one

from pyhandling.annotations import handler, decorator


executing_of: Callable[[many_or_one[handler]], decorator] = ...
```

### Function building
Create functions by describing them
```python
total_sum: Callable[[Iterable[many_or_one[number]]], number] = documenting_by(
    """
    Function of summing numbers from the input collection or the sum of its
    subcollection.
    """
)(
    close(map |then>> tuple)(
        on_condition(isinstance |by| Iterable, sum, else_=return_)
    )
    |then>> sum
)
```

in several processing processes
```python
ratio_of_square_to_full: reformer_of[number] = documenting_by(
    """
    Function of getting the ratio of the square of the input number to the
    input number to the power of its value.
    """
)(
    mergely(
        take(execute_operation),
        mergely(
            take(execute_operation),
            return_,
            take('*'),
            return_
        ),
        take('/'),
        mergely(
            take(execute_operation),
            return_,
            take('**'),
            return_
        )
    )
)
```

or in an indefinite number of iterative executions
```python
from pyhandling.annotations import dirty


increase_up_to_ten: dirty[reformer_of[number]] = documenting_by(
    """
    Function that prints numbers between the input number and 10 inclusive and
    returns 10.
    """
)(
    recursively(
        returnly(print) |then>> post_partial(execute_operation, '+', 1),
        post_partial(execute_operation, '<', 10)
    )
    |then>> returnly(print)
)


increase_up_to_ten(8)
```
```
8
9
10
```

### Chain breaking
Forcibly break the chain of actions
```python
optionally_exponentiate: Callable[[number], number | BadResourceWrapper] = documenting_by(
    """Function of exponentiation of the input number if it is > 0."""
)(
    maybe(
        on_condition(
            post_partial(execute_operation, '<', 0),
            BadResourceWrapper,
            else_=return_
        )
        |then>> post_partial(execute_operation, '**', 2)
    )
)


optionally_exponentiate(-16)
```
```
<Wrapper of bad -16>
```

with the possibility of returning a "bad" resource
```python
main: dirty[reformer_of[number]] = optionally_exponentiate |then>> optionally_get_bad_resource_from

main(8)
main(-16)
```
```
64
-16
```

You can also interrupt by returning an error proxy that stores the error </br>that occurred while processing this resource and the resource itself
```python
from pyhandling.annotations import reformer_of


div_by_zero: reformer_of[number] = documenting_by(
    """Function for dividing an input number by zero."""
)(
    post_partial(execute_operation, '/', 0)
)


main: Callable[[number], number | BadResourceError] = (
    returnly_rollbackable(div_by_zero, take(True))
)


main(256)
```
```
BadResourceError('Resource "256" could not be handled due to ZeroDivisionError: division by zero')
```

with corresponding possibilities
```python
main: reformer_of[number] = (
    partial(map |then>> maybe, returnly_rollbackable |by| take(True))(
        post_partial(execute_operation, '*', 2)
        |then>> div_by_zero
    )
    |then>> optionally_get_bad_resource_from
)


main(16)
```
```
32
```

### Batteries
Use out-of-the-box functions to abstract from input arguments
```python
take(256)(1, 2, 3)
event_as(execute_operation, 30, '+', 2)(1, 2, 3)
```
```
256
32
```

to create a collection via call
```python
collection_from(1, 2, 3)
```
```
(1, 2, 3)
```

to connect collections
```python
summed_collection_from((1, 2), (3, 4))
```
```
(1, 2, 3, 4)
```

to manage collection nesting
```python
wrap_in_collection(8)
open_collection_items(((1, 2), [3], 4))
```
```
(8, )
(1, 2, 3, 4)
```

to represent something as a collection
```python
as_collection(64)
as_collection([1, 2, 3])
```
```
(64, )
(1, 2, 3)
```

to confirm something multiple times
```python
runner = times(3)
tuple(runner() for _ in range(8))
```
```
(True, True, True, False, True, True, True, False)
```

to raise only a specific error
```python
optional_raise = optional_raising_of(ZeroDivisionError)

optional_raise(TypeError())
optional_raise(ZeroDivisionError("can't divide by zero"))
```
```
TypeError()

Traceback ...
ZeroDivisionError: can't divide by zero
```

to execute operations
```python
operation_by('*', 4)(64)
callmethod(', ', 'join', ("first", "second"))
```
```
256
first, second
```

to decoratively create action chains
```python
next_action_decorator_of(operation_by('**', 4))(operation_by('+', 1))(3)
previous_action_decorator_of(operation_by('+', 2))(operation_by('**', 2))(6)
```
```
256
64
```

to stop the chain when an error occurs
```python
breakable_chain = chain_breaking_on_error_that(isinstance |by| ZeroDivisionError)(
    (execute_operation |by* ('+', 4)) |then>> div_by_zero
)

breakable_chain(12)
```
```
BadResourceError('Resource "16" could not be handled due to ZeroDivisionError: division by zero')
```

to use shortcuts of routine options
```python
yes(1, 2, 3)
no(1, 2, 3)
```
```
True
False
```

### Immutable classes
Create immutable classes
```python
from typing import Iterable, Callable


@publicly_immutable
class CallingPublisher:
    name = DelegatingProperty('_name')
    followers = DelegatingProperty('_followers', getting_converter=tuple)

    def __init__(self, name: int, followers: Iterable[Callable] = tuple()):
        self._name = name
        self._followers = list(followers)

    def __repr__(self) -> str:
        return f"Publisher {self._name} with followers {self._followers}"

    def __call__(self, *args, **kwargs) -> None:
        for follower in self._followers:
            follower(*args, **kwargs)

    @to_clone
    def with_follower(self, follower: Callable) -> None:
        self._followers.append(follower)


original = CallingPublisher("Some publisher", [print])
```

that can't change any public attribute
```python
original.some_attr = "some value"
```
```
Traceback ...
AttributeError: Type CallingPublisher is immutable
```

and automatically clone without manual creation
```python
other = original.with_follower(operation_by('**', 4) |then>> print)

original.followers
other.followers
```
```
(<built-in function print>,)
(<built-in function print>, ActionChain(...))
```

what would eventually
```python
other(4)
```
```
4
256
```

### Debugging
Display intermediate results
```python
showly(total_sum)([128, [100, 28]])
```
```
[128, [100, 28]]
(128, 128)
256
```

by different ways
```python
logger = Logger(is_date_logging=True)

showly(total_sum, writer=logger)([[2, 10], [15, 15]])

print(*logger.logs, sep='\n')
```
```
[2023-01-24 21:38:28.791516] [[2, 10], [15, 15]]
[2023-01-24 21:38:28.791516] (12, 30)
[2023-01-24 21:38:28.791516] 42
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/TheArtur128/Pyhandling",
    "name": "Pyhandling",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": "",
    "keywords": "syntax,library,utils,logging,metaprogramming,annotations,immutability,data-structures,error-handling,handling,checking,utils-library,pseudo-operators",
    "author": "Arthur",
    "author_email": "s9339307190@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/c5/bd/1292586b30e0af05d78dbb155652b3d4ec93eef5be3561ca49dc57dfa6c0/Pyhandling-2.2.0.tar.gz",
    "platform": null,
    "description": "# Pyhandling\r\nLibrary for advanced continuous handling of anything\r\n\r\nProvides tools to extend single call logic on a nearly unlimited scale</br>\r\nYou can even integrate the entire program logic into one call\r\n\r\n## Installation\r\n`pip install pyhandling`\r\n\r\n## Usage examples\r\n### Composition\r\nMerge your functions into one\r\n\r\n```python\r\nfrom pyhandling import *\r\n\r\n\r\ncomplemented_number = str |then>> (lambda line: line + '6') |then>> int\r\ncomplemented_number(25)\r\n```\r\n\r\nto later get\r\n```python\r\n256\r\n```\r\n\r\nor you can do the same but call the function immediately\r\n```python\r\n25 >= str |then>> (lambda line: line + '6') |then>> int\r\n```\r\n\r\nand get the same result\r\n```python\r\n256\r\n```\r\n\r\n### Currying\r\nAdd additional arguments to function input arguments\r\n```python\r\nformattly_sum = \"{} {}{}\".format\r\n\r\npost_partial(formattly_sum, \"world\", '!')(\"Hello\") \r\n```\r\n\r\nusing pseudo operators\r\n```python\r\n(formattly_sum |to| \"Hello\")(\"world\", '!')\r\n(formattly_sum |to* (\"Hello\", \"world\"))('!')\r\n\r\n(formattly_sum |by| '!')(\"Hello\", \"world\")\r\n```\r\n\r\nor not necessarily now\r\n```python\r\ncontainer = close(formattly_sum)\r\nopened_container = container(\"Hello\")\r\n\r\nopened_container(\"world\", '!')\r\n```\r\n\r\nusing all possible ways\r\n```python\r\npost_container = close(formattly_sum, closer=post_partial)\r\n\r\npost_container('!')(\"Hello\", \"world\")\r\n```\r\n\r\nEventually, they all return\r\n```\r\nHello world!\r\n```\r\n\r\n### Interface control\r\nAbstract the output value\r\n```python\r\nprint(returnly(print)(\"Some input argument\"))\r\n```\r\n```\r\nSome input argument\r\nSome input argument\r\n```\r\n\r\nor input values\r\n```python\r\nfrom functools import partial\r\n\r\n\r\neventually(partial(print, 16))(1, 2, 3)\r\n```\r\n```\r\n16\r\n```\r\n\r\n### Atomic functions\r\nUse synonyms for operators\r\n```python\r\nreturn_(256)\r\nraise_(Exception(\"Something is wrong\"))\r\n```\r\n```\r\n256\r\n\r\nTraceback ...\r\nException: Something is wrong\r\n```\r\n\r\nfor atomic operations\r\n```python\r\nexecute_operation(60, '+', 4)\r\ntransform(str(), 'not')\r\n```\r\n```\r\n64\r\nTrue\r\n```\r\n\r\nfor syntax operations\r\n```python\r\ncall(range, 16)\r\n\r\ngetitem_of({\"some-key\": \"some-value\"}, \"some-key\")\r\n```\r\n```\r\nrange(16)\r\nsome-value\r\n```\r\n\r\n### Annotating\r\nUse standart annotation templates from `annotations` package for routine cases\r\n```python\r\nfrom pyhandling.annotations import checker_of, reformer_of, merger_of\r\n\r\nfrom pyannotating import number\r\n\r\n\r\nis_number_even: checker_of[number] = lambda number: number % 2 == 0\r\n\r\nadd_hundert_to: reformer_of[number] = lambda number: number + 100\r\n\r\nformat_lines: merger_of[str] = \"{} {}!\".format\r\n```\r\n\r\nor annotations themselves\r\n```python\r\nfrom pyannotating import many_or_one\r\n\r\nfrom pyhandling.annotations import handler, decorator\r\n\r\n\r\nexecuting_of: Callable[[many_or_one[handler]], decorator] = ...\r\n```\r\n\r\n### Function building\r\nCreate functions by describing them\r\n```python\r\ntotal_sum: Callable[[Iterable[many_or_one[number]]], number] = documenting_by(\r\n    \"\"\"\r\n    Function of summing numbers from the input collection or the sum of its\r\n    subcollection.\r\n    \"\"\"\r\n)(\r\n    close(map |then>> tuple)(\r\n        on_condition(isinstance |by| Iterable, sum, else_=return_)\r\n    )\r\n    |then>> sum\r\n)\r\n```\r\n\r\nin several processing processes\r\n```python\r\nratio_of_square_to_full: reformer_of[number] = documenting_by(\r\n    \"\"\"\r\n    Function of getting the ratio of the square of the input number to the\r\n    input number to the power of its value.\r\n    \"\"\"\r\n)(\r\n    mergely(\r\n        take(execute_operation),\r\n        mergely(\r\n            take(execute_operation),\r\n            return_,\r\n            take('*'),\r\n            return_\r\n        ),\r\n        take('/'),\r\n        mergely(\r\n            take(execute_operation),\r\n            return_,\r\n            take('**'),\r\n            return_\r\n        )\r\n    )\r\n)\r\n```\r\n\r\nor in an indefinite number of iterative executions\r\n```python\r\nfrom pyhandling.annotations import dirty\r\n\r\n\r\nincrease_up_to_ten: dirty[reformer_of[number]] = documenting_by(\r\n    \"\"\"\r\n    Function that prints numbers between the input number and 10 inclusive and\r\n    returns 10.\r\n    \"\"\"\r\n)(\r\n    recursively(\r\n        returnly(print) |then>> post_partial(execute_operation, '+', 1),\r\n        post_partial(execute_operation, '<', 10)\r\n    )\r\n    |then>> returnly(print)\r\n)\r\n\r\n\r\nincrease_up_to_ten(8)\r\n```\r\n```\r\n8\r\n9\r\n10\r\n```\r\n\r\n### Chain breaking\r\nForcibly break the chain of actions\r\n```python\r\noptionally_exponentiate: Callable[[number], number | BadResourceWrapper] = documenting_by(\r\n    \"\"\"Function of exponentiation of the input number if it is > 0.\"\"\"\r\n)(\r\n    maybe(\r\n        on_condition(\r\n            post_partial(execute_operation, '<', 0),\r\n            BadResourceWrapper,\r\n            else_=return_\r\n        )\r\n        |then>> post_partial(execute_operation, '**', 2)\r\n    )\r\n)\r\n\r\n\r\noptionally_exponentiate(-16)\r\n```\r\n```\r\n<Wrapper of bad -16>\r\n```\r\n\r\nwith the possibility of returning a \"bad\" resource\r\n```python\r\nmain: dirty[reformer_of[number]] = optionally_exponentiate |then>> optionally_get_bad_resource_from\r\n\r\nmain(8)\r\nmain(-16)\r\n```\r\n```\r\n64\r\n-16\r\n```\r\n\r\nYou can also interrupt by returning an error proxy that stores the error </br>that occurred while processing this resource and the resource itself\r\n```python\r\nfrom pyhandling.annotations import reformer_of\r\n\r\n\r\ndiv_by_zero: reformer_of[number] = documenting_by(\r\n    \"\"\"Function for dividing an input number by zero.\"\"\"\r\n)(\r\n    post_partial(execute_operation, '/', 0)\r\n)\r\n\r\n\r\nmain: Callable[[number], number | BadResourceError] = (\r\n    returnly_rollbackable(div_by_zero, take(True))\r\n)\r\n\r\n\r\nmain(256)\r\n```\r\n```\r\nBadResourceError('Resource \"256\" could not be handled due to ZeroDivisionError: division by zero')\r\n```\r\n\r\nwith corresponding possibilities\r\n```python\r\nmain: reformer_of[number] = (\r\n    partial(map |then>> maybe, returnly_rollbackable |by| take(True))(\r\n        post_partial(execute_operation, '*', 2)\r\n        |then>> div_by_zero\r\n    )\r\n    |then>> optionally_get_bad_resource_from\r\n)\r\n\r\n\r\nmain(16)\r\n```\r\n```\r\n32\r\n```\r\n\r\n### Batteries\r\nUse out-of-the-box functions to abstract from input arguments\r\n```python\r\ntake(256)(1, 2, 3)\r\nevent_as(execute_operation, 30, '+', 2)(1, 2, 3)\r\n```\r\n```\r\n256\r\n32\r\n```\r\n\r\nto create a collection via call\r\n```python\r\ncollection_from(1, 2, 3)\r\n```\r\n```\r\n(1, 2, 3)\r\n```\r\n\r\nto connect collections\r\n```python\r\nsummed_collection_from((1, 2), (3, 4))\r\n```\r\n```\r\n(1, 2, 3, 4)\r\n```\r\n\r\nto manage collection nesting\r\n```python\r\nwrap_in_collection(8)\r\nopen_collection_items(((1, 2), [3], 4))\r\n```\r\n```\r\n(8, )\r\n(1, 2, 3, 4)\r\n```\r\n\r\nto represent something as a collection\r\n```python\r\nas_collection(64)\r\nas_collection([1, 2, 3])\r\n```\r\n```\r\n(64, )\r\n(1, 2, 3)\r\n```\r\n\r\nto confirm something multiple times\r\n```python\r\nrunner = times(3)\r\ntuple(runner() for _ in range(8))\r\n```\r\n```\r\n(True, True, True, False, True, True, True, False)\r\n```\r\n\r\nto raise only a specific error\r\n```python\r\noptional_raise = optional_raising_of(ZeroDivisionError)\r\n\r\noptional_raise(TypeError())\r\noptional_raise(ZeroDivisionError(\"can't divide by zero\"))\r\n```\r\n```\r\nTypeError()\r\n\r\nTraceback ...\r\nZeroDivisionError: can't divide by zero\r\n```\r\n\r\nto execute operations\r\n```python\r\noperation_by('*', 4)(64)\r\ncallmethod(', ', 'join', (\"first\", \"second\"))\r\n```\r\n```\r\n256\r\nfirst, second\r\n```\r\n\r\nto decoratively create action chains\r\n```python\r\nnext_action_decorator_of(operation_by('**', 4))(operation_by('+', 1))(3)\r\nprevious_action_decorator_of(operation_by('+', 2))(operation_by('**', 2))(6)\r\n```\r\n```\r\n256\r\n64\r\n```\r\n\r\nto stop the chain when an error occurs\r\n```python\r\nbreakable_chain = chain_breaking_on_error_that(isinstance |by| ZeroDivisionError)(\r\n    (execute_operation |by* ('+', 4)) |then>> div_by_zero\r\n)\r\n\r\nbreakable_chain(12)\r\n```\r\n```\r\nBadResourceError('Resource \"16\" could not be handled due to ZeroDivisionError: division by zero')\r\n```\r\n\r\nto use shortcuts of routine options\r\n```python\r\nyes(1, 2, 3)\r\nno(1, 2, 3)\r\n```\r\n```\r\nTrue\r\nFalse\r\n```\r\n\r\n### Immutable classes\r\nCreate immutable classes\r\n```python\r\nfrom typing import Iterable, Callable\r\n\r\n\r\n@publicly_immutable\r\nclass CallingPublisher:\r\n    name = DelegatingProperty('_name')\r\n    followers = DelegatingProperty('_followers', getting_converter=tuple)\r\n\r\n    def __init__(self, name: int, followers: Iterable[Callable] = tuple()):\r\n        self._name = name\r\n        self._followers = list(followers)\r\n\r\n    def __repr__(self) -> str:\r\n        return f\"Publisher {self._name} with followers {self._followers}\"\r\n\r\n    def __call__(self, *args, **kwargs) -> None:\r\n        for follower in self._followers:\r\n            follower(*args, **kwargs)\r\n\r\n    @to_clone\r\n    def with_follower(self, follower: Callable) -> None:\r\n        self._followers.append(follower)\r\n\r\n\r\noriginal = CallingPublisher(\"Some publisher\", [print])\r\n```\r\n\r\nthat can't change any public attribute\r\n```python\r\noriginal.some_attr = \"some value\"\r\n```\r\n```\r\nTraceback ...\r\nAttributeError: Type CallingPublisher is immutable\r\n```\r\n\r\nand automatically clone without manual creation\r\n```python\r\nother = original.with_follower(operation_by('**', 4) |then>> print)\r\n\r\noriginal.followers\r\nother.followers\r\n```\r\n```\r\n(<built-in function print>,)\r\n(<built-in function print>, ActionChain(...))\r\n```\r\n\r\nwhat would eventually\r\n```python\r\nother(4)\r\n```\r\n```\r\n4\r\n256\r\n```\r\n\r\n### Debugging\r\nDisplay intermediate results\r\n```python\r\nshowly(total_sum)([128, [100, 28]])\r\n```\r\n```\r\n[128, [100, 28]]\r\n(128, 128)\r\n256\r\n```\r\n\r\nby different ways\r\n```python\r\nlogger = Logger(is_date_logging=True)\r\n\r\nshowly(total_sum, writer=logger)([[2, 10], [15, 15]])\r\n\r\nprint(*logger.logs, sep='\\n')\r\n```\r\n```\r\n[2023-01-24 21:38:28.791516] [[2, 10], [15, 15]]\r\n[2023-01-24 21:38:28.791516] (12, 30)\r\n[2023-01-24 21:38:28.791516] 42\r\n```\r\n",
    "bugtrack_url": null,
    "license": "GNU General Public License v3.0",
    "summary": "Library for metaprogramming",
    "version": "2.2.0",
    "split_keywords": [
        "syntax",
        "library",
        "utils",
        "logging",
        "metaprogramming",
        "annotations",
        "immutability",
        "data-structures",
        "error-handling",
        "handling",
        "checking",
        "utils-library",
        "pseudo-operators"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c5bd1292586b30e0af05d78dbb155652b3d4ec93eef5be3561ca49dc57dfa6c0",
                "md5": "6ed0b084e7700b3ce434812617cfc4c6",
                "sha256": "bd1c71d7c29b11cdc859407a9d9728b08b3194db12032cfff9e3fc57ac221cfb"
            },
            "downloads": -1,
            "filename": "Pyhandling-2.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "6ed0b084e7700b3ce434812617cfc4c6",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 38500,
            "upload_time": "2023-01-29T15:30:11",
            "upload_time_iso_8601": "2023-01-29T15:30:11.902102Z",
            "url": "https://files.pythonhosted.org/packages/c5/bd/1292586b30e0af05d78dbb155652b3d4ec93eef5be3561ca49dc57dfa6c0/Pyhandling-2.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-01-29 15:30:11",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "TheArtur128",
    "github_project": "Pyhandling",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [],
    "lcname": "pyhandling"
}
        
Elapsed time: 0.03364s