<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400..900&display=swap" rel="stylesheet">
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400..900&display=swap');
.orbitron-title {
font-family: "Orbitron", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}
.side-by-side {
display: flex;
}
.image-stuff {
border-radius: 25px;
width: 125px;
height: 125px;
margin-right: 15px;
}
</style>
<div class="side-by-side">
<img src="imgs/mainlogo.png" class="image-stuff"/>
<h1 class="orbitron-title">callsign: simplified introspection</h1>
</div>
<br>
callsign; a python package that abstracts away boilerplate from the standard
library's inspect module. Made for practitioners of DDD
(decorator driven development); minimizing tedium and keeping things
DRY is what we're all about.
Only dependency is python 3 .
## Installation
`pip install callsign`
## Usage
```python
>>> import callsign
>>> def f(x, y: int): return x, y
>>> params = callsign(f, 'hi', y=2)
>>> params # a dict of namedtuples
{
'x': Paramattrs(name='x',
arg='hi',
default=inspect._empty,
kind=ParamKinds.POSITIONAL_OR_KEYWORD,
defaulted=False,
annotation=inspect._empty),
'y': Paramattrs(name='y',
arg=2,
default=inspect._empty,
kind=ParamKinds.POSITIONAL_OR_KEYWORD,
defaulted=False,
annotation=<class 'int'>),
}
>>> params['x'].arg
'hi'
>>> params['x'].annotation
inspect._empty
>>> callsign.isempty(params['x'].annotation)
True
>>> params['y'].annotation
<class 'int'>
```
Any time you do a callsign of a function and its positional + keyword arguments,
you'll get a python dictionary back of each parameter name
mapped to its _Paramattrs_ (the param's attributes).
### Recipes
#### Basic DDD
```python
import callsign
def decorator(fn: Callable) -> Callable:
@functools.wraps(fn)
def newfn(*args, **kwargs) -> Any:
# introspection!
params = callsign(fn, *args, **kwargs)
if params['x'].defaulted:
return fn(*args, **kwargs)
elif params['y'].arg == 'something else':
return fn('doing something with y')
...
return newfn # and bob's your uncle
```
#### Build a rudimentary type checker
```python
import callsign
def type_checker(fn: Callable[..., [Any]]) -> Callable[..., [Any | NoReturn]]:
@functools.wraps(fn)
def typedfn(*args, **kwargs) -> Any:
params = callsign(fn, *args, **kwargs)
for param in params:
if param.annotation and type(param.arg) != param.annotation:
raise TypeError()
return fn(*args, **kwargs) # and bob's your uncle!
return typedfn
```
#### Advanced DDD
```python
import callsign
def decorator(fn: Callable) -> Callable:
@functools.wraps(fn)
def newfn(*args, **kwargs) -> Any:
params = callsign(fn, *args, **kwargs)
mut = {'x': 2, 'y': 3}
positionals, keywords = callsign.arguments(params, mut, safe=True)
return fn(*positionals, **keywords)
return newfn
@decorator
def crazy_signature(x, /, *args, y=10, **kwargs): return x, args, y, kwargs
x, _, y, _ = crazy_signature(10, y=20)
print(x) # 2
print(y) # 3
```
### Paramattrs
The _Paramattrs_ object is a pure python namedtuple, nothing fancy. It defines
the following attributes:
* name: the parameter's name
* arg: the given argument
* default: if a default value was assigned to the function's signature, it's
preserved here, otherwise it's `inspect.empty`
* kind: one of a _ParamKinds_ Enum
* defaulted: this is `True` if no argument is given and a default is being used
* annotation: this will be `inspect._empty` if no type hint is written
in the function signature, otherwise it will be whatever was provided as
a type hint
### ParamKinds
These are taken from the standard library's inspect module, but we've added one:
`ParamKinds.VULNERABLE_DEFAULT`.
VULNERABLE\_DEFAULT is interchangeable with POSITIONAL\_OR\_KEYWORD; but also,
it is a parameter that was given a default value, and that default
has been overridden by a VARARGS parameter, e.g.:
```python
>>> def hello(x=10, *args): return x, args
>>> print( hello() )
(10, ())
>>> print( hello(1, 2, 3) )
(1, (2, 3)) # x = 10 was overridden, x is now 1
```
Raw data
{
"_id": null,
"home_page": null,
"name": "callsign",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "ddd, decorator, decorator driven development, function, introspection, metaprogramming, signature",
"author": "muhamuhamuha",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/1c/0a/fed204b26b1de939b296acecdbd876cc94803faf8a4239d0ec8ee45ea610/callsign-0.1.0.tar.gz",
"platform": null,
"description": "<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n<link href=\"https://fonts.googleapis.com/css2?family=Orbitron:wght@400..900&display=swap\" rel=\"stylesheet\">\n\n<style>\n\n@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400..900&display=swap');\n\n.orbitron-title {\n font-family: \"Orbitron\", sans-serif;\n font-optical-sizing: auto;\n font-weight: 400;\n font-style: normal;\n}\n\n.side-by-side {\n display: flex;\n}\n\n.image-stuff {\n border-radius: 25px;\n width: 125px;\n height: 125px;\n margin-right: 15px;\n}\n\n</style>\n\n<div class=\"side-by-side\">\n <img src=\"imgs/mainlogo.png\" class=\"image-stuff\"/>\n <h1 class=\"orbitron-title\">callsign: simplified introspection</h1>\n</div>\n<br>\n\n\ncallsign; a python package that abstracts away boilerplate from the standard\nlibrary's inspect module. Made for practitioners of DDD \n(decorator driven development); minimizing tedium and keeping things\nDRY is what we're all about.\n\nOnly dependency is python 3 .\n\n\n## Installation\n\n`pip install callsign`\n\n## Usage\n\n```python\n>>> import callsign\n\n>>> def f(x, y: int): return x, y\n\n>>> params = callsign(f, 'hi', y=2)\n>>> params # a dict of namedtuples\n{\n 'x': Paramattrs(name='x',\n arg='hi',\n default=inspect._empty,\n kind=ParamKinds.POSITIONAL_OR_KEYWORD,\n defaulted=False,\n annotation=inspect._empty),\n 'y': Paramattrs(name='y',\n arg=2,\n default=inspect._empty,\n kind=ParamKinds.POSITIONAL_OR_KEYWORD,\n defaulted=False,\n annotation=<class 'int'>),\n}\n\n>>> params['x'].arg\n'hi'\n\n>>> params['x'].annotation\ninspect._empty\n>>> callsign.isempty(params['x'].annotation)\nTrue\n\n>>> params['y'].annotation\n<class 'int'>\n```\n\nAny time you do a callsign of a function and its positional + keyword arguments,\nyou'll get a python dictionary back of each parameter name\nmapped to its _Paramattrs_ (the param's attributes).\n\n### Recipes\n\n#### Basic DDD\n\n```python\nimport callsign\n\ndef decorator(fn: Callable) -> Callable:\n @functools.wraps(fn)\n def newfn(*args, **kwargs) -> Any:\n\n # introspection!\n params = callsign(fn, *args, **kwargs)\n\n if params['x'].defaulted:\n return fn(*args, **kwargs)\n elif params['y'].arg == 'something else':\n return fn('doing something with y')\n\n ...\n return newfn # and bob's your uncle\n```\n\n#### Build a rudimentary type checker\n\n```python\nimport callsign\n\ndef type_checker(fn: Callable[..., [Any]]) -> Callable[..., [Any | NoReturn]]:\n @functools.wraps(fn)\n def typedfn(*args, **kwargs) -> Any:\n \n params = callsign(fn, *args, **kwargs)\n\n for param in params:\n if param.annotation and type(param.arg) != param.annotation:\n raise TypeError()\n\n return fn(*args, **kwargs) # and bob's your uncle!\n return typedfn\n```\n\n#### Advanced DDD\n\n```python\nimport callsign\n\ndef decorator(fn: Callable) -> Callable:\n @functools.wraps(fn)\n def newfn(*args, **kwargs) -> Any:\n params = callsign(fn, *args, **kwargs)\n \n mut = {'x': 2, 'y': 3}\n positionals, keywords = callsign.arguments(params, mut, safe=True)\n return fn(*positionals, **keywords)\n return newfn\n\n@decorator\ndef crazy_signature(x, /, *args, y=10, **kwargs): return x, args, y, kwargs\n\nx, _, y, _ = crazy_signature(10, y=20)\nprint(x) # 2\nprint(y) # 3\n```\n### Paramattrs\n\nThe _Paramattrs_ object is a pure python namedtuple, nothing fancy. It defines\nthe following attributes:\n\n* name: the parameter's name\n* arg: the given argument\n* default: if a default value was assigned to the function's signature, it's\npreserved here, otherwise it's `inspect.empty`\n* kind: one of a _ParamKinds_ Enum\n* defaulted: this is `True` if no argument is given and a default is being used\n* annotation: this will be `inspect._empty` if no type hint is written\nin the function signature, otherwise it will be whatever was provided as\na type hint\n\n### ParamKinds\n\nThese are taken from the standard library's inspect module, but we've added one:\n`ParamKinds.VULNERABLE_DEFAULT`. \n\nVULNERABLE\\_DEFAULT is interchangeable with POSITIONAL\\_OR\\_KEYWORD; but also,\nit is a parameter that was given a default value, and that default\nhas been overridden by a VARARGS parameter, e.g.:\n\n```python\n>>> def hello(x=10, *args): return x, args\n\n>>> print( hello() )\n(10, ())\n\n>>> print( hello(1, 2, 3) )\n(1, (2, 3)) # x = 10 was overridden, x is now 1\n```\n\n",
"bugtrack_url": null,
"license": null,
"summary": "Simplified Introspection",
"version": "0.1.0",
"project_urls": {
"repository": "https://github.com/muhamuhamuha/callsign"
},
"split_keywords": [
"ddd",
" decorator",
" decorator driven development",
" function",
" introspection",
" metaprogramming",
" signature"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "7d8394b30a31488afb94dd632766415ec920f1bd4280a93c1a6542d5d6dfadb4",
"md5": "78926ac544a3d8d85d586ad504be5ce8",
"sha256": "5aacbb9235990e18c4f260623bef15a60be4a341bc34bb1fe16cf2efdec43573"
},
"downloads": -1,
"filename": "callsign-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "78926ac544a3d8d85d586ad504be5ce8",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 5861,
"upload_time": "2024-06-15T19:33:04",
"upload_time_iso_8601": "2024-06-15T19:33:04.609322Z",
"url": "https://files.pythonhosted.org/packages/7d/83/94b30a31488afb94dd632766415ec920f1bd4280a93c1a6542d5d6dfadb4/callsign-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "1c0afed204b26b1de939b296acecdbd876cc94803faf8a4239d0ec8ee45ea610",
"md5": "ba4b817d66e1c4ff3a0cb75b8b6e9a7a",
"sha256": "afa4c594e8aa3228e94edee5256bcf0d01adf294c339118941bc090371eec124"
},
"downloads": -1,
"filename": "callsign-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "ba4b817d66e1c4ff3a0cb75b8b6e9a7a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 1109830,
"upload_time": "2024-06-15T19:33:06",
"upload_time_iso_8601": "2024-06-15T19:33:06.743927Z",
"url": "https://files.pythonhosted.org/packages/1c/0a/fed204b26b1de939b296acecdbd876cc94803faf8a4239d0ec8ee45ea610/callsign-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-06-15 19:33:06",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "muhamuhamuha",
"github_project": "callsign",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "callsign"
}