Skip to main content

Simplified Introspection

Project description

<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>

callsign: simplified introspection


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

>>> 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

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

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

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.:

>>> 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

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

callsign-0.1.0.tar.gz (1.1 MB view hashes)

Uploaded Source

Built Distribution

callsign-0.1.0-py3-none-any.whl (5.9 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page