# 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"
}