Library for metaprogramming
Project description
Pyhandling
Library for advanced continuous handling of anything
Provides tools to extend single call logic on a nearly unlimited scale
You can even integrate the entire program logic into one call
Installation
pip install pyhandling
Usage examples
Composition
Merge your functions into one
from pyhandling import *
complemented_number = str |then>> (lambda line: line + '6') |then>> int
complemented_number(25)
to later get
256
or you can do the same but call the function immediately
25 >= str |then>> (lambda line: line + '6') |then>> int
and get the same result
256
Currying
Add additional arguments to function input arguments
formattly_sum = "{} {}{}".format
post_partial(formattly_sum, "world", '!')("Hello")
using pseudo operators
(formattly_sum |to| "Hello")("world", '!')
(formattly_sum |to* ("Hello", "world"))('!')
(formattly_sum |by| '!')("Hello", "world")
or not necessarily now
container = close(formattly_sum)
opened_container = container("Hello")
opened_container("world", '!')
using all possible ways
post_container = close(formattly_sum, closer=post_partial)
post_container('!')("Hello", "world")
Eventually, they all return
Hello world!
Interface control
Abstract the output value
print(returnly(print)("Some input argument"))
Some input argument
Some input argument
or input values
from functools import partial
eventually(partial(print, 16))(1, 2, 3)
16
Atomic functions
Use synonyms for operators
return_(256)
raise_(Exception("Something is wrong"))
256
Traceback ...
Exception: Something is wrong
for atomic operations
execute_operation(60, '+', 4)
transform(str(), 'not')
64
True
for syntax operations
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
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
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
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
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
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
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
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
that occurred while processing this resource and the resource itself
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
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
take(256)(1, 2, 3)
event_as(execute_operation, 30, '+', 2)(1, 2, 3)
256
32
to create a collection via call
collection_from(1, 2, 3)
(1, 2, 3)
to connect collections
summed_collection_from((1, 2), (3, 4))
(1, 2, 3, 4)
to manage collection nesting
wrap_in_collection(8)
open_collection_items(((1, 2), [3], 4))
(8, )
(1, 2, 3, 4)
to represent something as a collection
as_collection(64)
as_collection([1, 2, 3])
(64, )
(1, 2, 3)
to confirm something multiple times
runner = times(3)
tuple(runner() for _ in range(8))
(True, True, True, False, True, True, True, False)
to raise only a specific error
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
operation_by('*', 4)(64)
callmethod(', ', 'join', ("first", "second"))
256
first, second
to decoratively create action chains
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
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
yes(1, 2, 3)
no(1, 2, 3)
True
False
Immutable classes
Create immutable classes
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
original.some_attr = "some value"
Traceback ...
AttributeError: Type CallingPublisher is immutable
and automatically clone without manual creation
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
other(4)
4
256
Debugging
Display intermediate results
showly(total_sum)([128, [100, 28]])
[128, [100, 28]]
(128, 128)
256
by different ways
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
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file Pyhandling-2.2.0.tar.gz
.
File metadata
- Download URL: Pyhandling-2.2.0.tar.gz
- Upload date:
- Size: 38.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.11.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | bd1c71d7c29b11cdc859407a9d9728b08b3194db12032cfff9e3fc57ac221cfb |
|
MD5 | 6ed0b084e7700b3ce434812617cfc4c6 |
|
BLAKE2b-256 | c5bd1292586b30e0af05d78dbb155652b3d4ec93eef5be3561ca49dc57dfa6c0 |