forge (python) signatures for fun and profit
Project description
forge (python) signatures for fun and profit
forge is an elegant Python package for crafting function signatures. Its aim is to help you write better, more literate code with less boilerplate.
The power of dynamic signatures is finally within grasp – add, remove, or enhance parameters at will!
forge is a Python-only package hosted on PyPI for Python 3.5+.
The recommended installation method is pip-installing into a virtualenv:
$ pip install python-forge
Re-signing a function
The primary purpose of forge is to alter the public signature of functions:
import forge
@forge.sign(
forge.pos('positional'),
forge.arg('argument'),
*forge.args,
keyword=forge.kwarg(),
**forge.kwargs,
)
def myfunc(*args, **kwargs):
return (args, kwargs)
assert forge.stringify_callable(myfunc) == \
'myfunc(positional, /, argument, *args, keyword, **kwargs)'
args, kwargs = myfunc(1, 2, 3, 4, 5, keyword='abc', extra='xyz')
assert args == (3, 4, 5)
assert kwargs == {
'positional': 1,
'argument': 2,
'keyword': 'abc',
'extra': 'xyz',
}
You can re-map a parameter to a different ParameterKind (e.g. positional-only to positional-or-keyword or keyword-only), and optionally supply a default value:
import forge
@forge.sign(forge.kwarg('color', 'colour', default='blue'))
def myfunc(colour):
return colour
assert forge.stringify_callable(myfunc) == "myfunc(*, color='blue')"
assert myfunc() == 'blue'
You can also supply type annotations for usage with linters like mypy:
import forge
@forge.sign(forge.arg('number', type=int))
@forge.returns(str)
def to_str(number):
return str(number)
assert forge.stringify_callable(to_str) == 'to_str(number:int) -> str'
assert to_str(3) == '3'
Validating a parameter
You can validate arguments by either passing a validator or an iterable (such as a list or tuple) of validators to your FParameter constructor.
import forge
class Present:
pass
def validate_gt5(ctx, name, value):
if value < 5:
raise TypeError("{name} must be >= 5".format(name=name))
@forge.sign(forge.arg('count', validator=validate_gt5))
def send_presents(count):
return [Present() for i in range(count)]
assert forge.stringify_callable(send_presents) == 'send_presents(count)'
try:
send_presents(3)
except TypeError as exc:
assert exc.args[0] == "count must be >= 5"
sent = send_presents(5)
assert len(sent) == 5
for p in sent:
assert isinstance(p, Present)
You can optionally provide a context parameter, such as self, cls, or create your own named parameter with forge.ctx('myparam'), and use that alongside validation:
import forge
def validate_color(ctx, name, value):
if value not in ctx.colors:
raise TypeError(
'expected one of {ctx.colors}, received {value}'.\
format(ctx=ctx, value=value)
)
class ColorSelector:
def __init__(self, *colors):
self.colors = colors
self.selected = None
@forge.sign(
forge.self,
forge.arg('color', validator=validate_color)
)
def select_color(self, color):
self.selected = color
cs = ColorSelector('red', 'green', 'blue')
try:
cs.select_color('orange')
except TypeError as exc:
assert exc.args[0] == \
"expected one of ('red', 'green', 'blue'), received orange"
cs.select_color('red')
assert cs.selected == 'red'
Converting a parameter
You can convert an argument by passing a conversion function to your FParameter constructor.
import forge
def uppercase(ctx, name, value):
return value.upper()
@forge.sign(forge.arg('message', converter=uppercase))
def shout(message):
return message
assert shout('hello over there') == 'HELLO OVER THERE'
You can optionally provide a context parameter, such as self, cls, or create your own named FParameter with forge.ctx('myparam'), and use that alongside conversion:
import forge
def titleize(ctx, name, value):
return '{ctx.title} {value}'.format(ctx=ctx, value=value)
class RoleAnnouncer:
def __init__(self, title):
self.title = title
@forge.sign(forge.self, forge.arg('name', converter=titleize))
def announce(self, name):
return 'Now announcing {name}!'.format(name=name)
doctor_ra = RoleAnnouncer('Doctor')
captain_ra = RoleAnnouncer('Captain')
assert doctor_ra.announce('Strangelove') == \
"Now announcing Doctor Strangelove!"
assert captain_ra.announce('Lionel Mandrake') == \
"Now announcing Captain Lionel Mandrake!"
Project information
forge is released under the MIT license, its documentation lives at Read the Docs, the code on GitHub, and the latest release on PyPI. It’s rigorously tested on Python 3.6+ and PyPy 3.5+.
forge is authored by Devin Fee. Other contributors are listed under https://github.com/dfee/forge/graphs/contributors.
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 Distributions
Built Distribution
Hashes for python_forge-18.5.0-0-py35-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7797d67143be3ebaa12f07d87854b8bd2742a5655d57d0bb7e4af2fb58a1a3aa |
|
MD5 | 7c9880bd3a7caa4b81613cef6ae2e45f |
|
BLAKE2b-256 | 8191bd64e36a1cb66a6543beecb57063e8d3a49c46dd59162a2b80c4382e429e |