Via decorators, make complex processing of function or method arguments effortless.
Project description
Art Deco
Description
Art Deco is a library that, via decorators, makes complex processing of function/method arguments effortless.
Use cases for art_deco
include, but are not limited to:
- data validation (e.g., validating the inputs of a function)
- input conversion (e.g., type coercion based on type annotations)
- deserializing / parsing (e.g., parse JSONs or decode MessagePack into Python objects based on type annotations)
- caching
That is, this library shines when one needs to build a decorator that processes inputs based on:
the inspected signature of the function arguments (e.g., needs access to argument names, type annotations,
default values), or runtime information like discerning whether an argument was called positionally or as a keyword,
as part of a variable argument (i.e., *args
, **kwargs
) etc.
Therefore, it works like a charm in conjunction with any data validation library (e.g.,
cerberus
, pandera
) or any library that consumes data from the external world - network, Excel etc.
(e.g., flask
, plotly dash
, pyxll
, xlwings
).
Moreover, art_deco
handles several Python language constructs:
- functions, regular methods, static methods, class methods
- sync and
async
- metaclasses and classes (regular classes,
dataclasses
,attrs
classes,pydantic
models and data classes) - variable arguments (i.e.,
*args
,**kwargs
) - positional only, keyword only arguments
and provides:
art_deco.core
: a fundamental layer based on which one can implement any argument processing logic.art_deco.hack_args
: a convenient, pre-packaged argument processor implemented on top ofart_deco.core
.
Getting Started
Installation
pip install art_deco
Hacking function arguments
To illustrate what can be achieved, let's start with a few toy usages of art_deco.hack_args
.
from __future__ import annotations
from art_deco.hack_args import marks
from art_deco.hack_args.processor import hack_args
@marks.arg_hacker
def hack_x(x: int | float | str) -> str:
"""Function used for data validation and type coercion."""
assert float(x) < 100, f'Validation failed: {x} must be < 100'
return str(x) if isinstance(x, (int, float)) else x
@hack_args({'x': hack_x})
def func(x):
"""Function with a processed argument."""
return x
assert func('1') == func(1) == '1'
# func(200) ----> Errors with AssertionError, thanks to data validation
the same thing can be achieved via the Annotated
type hint:
from typing_extensions import Annotated
@hack_args()
def func(x: Annotated[str, hack_x]) -> str:
"""Function with a processed argument."""
return x
To validate more than one argument at a time:
from art_deco.core.arg_processors.api import Context
@marks.wide_validator
def validate_x_y(_: Context, x: str, y: str) -> None:
"""Validate 2 arguments."""
# Context is an internal concept that allows users to use static information like the inspected signature or
# default values, as well as call runtime information like what arguments were called positionally.
if x.startswith('1'):
assert y.startswith('2'), f'Validation failed: {x} starts with 1, but {y} does not start with 2'
@hack_args({'x': hack_x, 'y': hack_x, ('x', 'y'): validate_x_y})
def func(x, y):
"""Function with 2 processed arguments."""
return f'x={x}, y={y}'
# Similarly we could use `Annotated`
@hack_args()
def func(x: Annotated[str, hack_x, validate_x_y], y: Annotated[str, hack_x, validate_x_y]) -> str:
"""Function with 2 processed arguments."""
return f'x={x}, y={y}'
Implementing a new argument processor
In art_deco.core
, there are 2 types of argument processors:
- static: initialized once, at function definition time, arguments to be processed are determined once as well, while the actual argument processing happens every time the decorated function is called.
- dynamic: initialized every time the decorated function is called, arguments to be processed and the actual argument processing happen every time the decorated functions is called.
Let's look at a static example, since a dynamic processor would have a nearly identical interface.
from __future__ import annotations
from typing import Any, Mapping
from art_deco.core.arg_processors.api import ArgNames, Context, MultiArgValidateFunc
from art_deco.core.arg_processors.static_decorator import static_process_args
from art_deco.core.specs.static_arg_specs import StaticArgSpecs
from art_deco.core.specs.dynamic_arg_specs import DynamicArgSpec
class SimpleCastingProcessor:
def __init__(self, static_args: StaticArgSpecs) -> None:
self.static_args = static_args
self.hacks: dict[str, Any] = {}
for arg in static_args.args:
if arg.has_annotation:
self.hacks[arg.name] = arg.annotation
def should_process_arg(self, arg: str) -> bool:
"""Hook function called to determine whether to process an argument or not, always by name."""
return arg in self.hacks
def process_arg(self, arg: DynamicArgSpec, _: Context) -> Any:
"""This function specifies how to actually process the argument."""
if arg.is_default: # If the value is the default one, do not run any casting
return arg.value
return self.hacks[arg.name](arg.value) # Get the annotation for the argument name and coerce the supplied value
def get_wide_checks(self) -> Mapping[ArgNames, MultiArgValidateFunc]: # No logic for "wide" checks.
return {}
@static_process_args(SimpleCastingProcessor)
def coercion(x: int, y: float) -> float:
return x + y
assert coercion('1', '2') == 3
assert coercion('1', 2) == 3
assert coercion(1, 2) == 3
Development
Read CONTRIBUTING.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file art_deco-0.0.1.tar.gz
.
File metadata
- Download URL: art_deco-0.0.1.tar.gz
- Upload date:
- Size: 15.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.7.15
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0222e3983bd1df0aa59d8d1048b6f2e59ab0e589d6b0172ba94865f691b3fd9e |
|
MD5 | 6f132bdbb29e7d63be6f93fe420527fc |
|
BLAKE2b-256 | e6ca50b242d2a8f9a9862618bcfe89cf6ad9e91721ebaa7764c7be9003a012e1 |
File details
Details for the file art_deco-0.0.1-py3-none-any.whl
.
File metadata
- Download URL: art_deco-0.0.1-py3-none-any.whl
- Upload date:
- Size: 17.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.7.15
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | d78d5c309d95f36bdf4a168637b66afeb5ec7e922f95238db59292e3f869e781 |
|
MD5 | f8eac839f391ee7f9067719abb4839c6 |
|
BLAKE2b-256 | c0f1838375ef818871f3c52172040ac6798f706d1486d5f4ce0cb39d8e9c576c |