signia
Signature tools for Python
Mirror, compare, merge, and compose callables — IDE-friendly and type-aware.
✨ What is Signia?
Signia is a lightweight toolkit for working with Python function signatures.
It makes your wrappers, decorators, and composed functions look and feel right to IDEs, linters, and help().
With Signia you can:
- Mirror another function’s signature (
mirror_signature) - Compare signatures for equality or compatibility (
same_signature) - Merge multiple signatures into a single
inspect.Signature(merge_signatures) - Combine functions with merged signatures and real argument routing (
combine)
Perfect for decorators, adapters, and function composition.
📦 Installation
Signia targets Python 3.9+ and is published to PyPI.
python -m pip install signia
Add it to your project's dependencies (pyproject.toml/requirements.txt)
and you are ready to work with signatures in a type-friendly way.
🚀 Quickstart
Below are concise examples of each public helper exported from
signia. All snippets can be copied into a Python REPL or script.
mirror_signature
from signia import mirror_signature
def greet(name: str, excited: bool = False) -> str:
return f"Hello {name}{'!' if excited else ''}"
@mirror_signature(greet)
def wrapper(*args, **kwargs):
return greet(*args, **kwargs)
assert wrapper.__name__ == "greet"
assert wrapper.__signature__.parameters["name"].annotation is str
The decorator mirrors the wrapped callable's name, documentation, and
inspect.Signature so IDEs and type checkers understand the wrapper.
same_signature
from signia import same_signature
def source(x: int, y: int = 1) -> int:
return x + y
def mirror(x: int, y: int = 1) -> int:
return source(x, y)
assert same_signature(source, mirror)
assert same_signature(source, mirror, strict=False)
def variant(x: int, y: int = 2) -> int:
return x + y
assert not same_signature(source, variant)
assert same_signature(source, variant, strict=False)
assert same_signature(source, variant, strict=False, ignore_annotations=True)
Pass callables (or inspect.Signature instances) to test for strict equality or
structural compatibility, optionally ignoring default mismatches or annotations.
merge_signatures
from signia import merge_signatures
def left(x: int, *, limit: int = 10) -> None:
...
def right(y: str, *, limit: int = 10, verbose: bool = False) -> None:
...
merged = merge_signatures(left, right)
assert str(merged) == "(x: int, y: str, *, limit: int = 10, verbose: bool = False)"
custom_policy = merge_signatures(
left,
right,
policy="prefer-last", # choose metadata from later callables when possible
)
The merger walks parameters in kind order, keeping metadata according to the
selected policy ("prefer-first" by default) and returning a new
inspect.Signature. Return annotations come from the right-most callable with
a non-empty annotation.
combine
from signia import combine
def load(path: str, *, encoding: str = "utf-8") -> str:
return path.upper()
def audit(*, logger: list[str]) -> None:
logger.append("load called")
calls: list[str] = []
wrapped = combine(load, audit)
assert wrapped("demo.txt", logger=calls) == "DEMO.TXT"
assert calls == ["load called"]
# Inspect what each callable received
assert load.vars.args == ("demo.txt",)
assert audit.vars.kwargs == {"logger": calls}
combine uses merge_signatures under the hood so that a single callable can
forward keyword-only arguments to later helpers while keeping the primary
signature intact.
🧩 Handling Signature Conflicts
When merging or combining callables, Signia compares parameter kind, default
values, and annotations. Differing metadata is reported through
SignatureConflictError unless a resolver strategy is supplied.
from inspect import Parameter
from signia import merge_signatures, SignatureConflictError
def alpha(x: int, y: int = 1):
...
def beta(x: int, y: int = 2):
...
try:
merge_signatures(alpha, beta)
except SignatureConflictError as exc:
assert "default 1 vs 2" in str(exc)
merge_signatures(alpha, beta, compare_defaults=False) # tolerates default mismatch
prefer_defaults = merge_signatures(alpha, beta, on_conflict="prefer-defaulted", policy="prefer-last")
assert prefer_defaults.parameters["y"].default == 2
def resolver(name, existing, incoming, conflicts):
# Keep whichever side is annotated, otherwise fall back to the default policy.
if any(kind == "annotation" for kind, *_ in conflicts):
return incoming if incoming.annotation is not Parameter.empty else existing
return incoming
custom = merge_signatures(alpha, beta, on_conflict=resolver)
Custom resolvers receive the conflicting inspect.Parameter objects alongside
their metadata differences and must return a replacement Parameter. This
allows fine-grained reconciliation that aligns perfectly with your project's
needs.
Metadata
Release files for signia 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| signia-0.1.1.tar.gz | 22.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| signia-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 38.4 kB
Release files / signia-0.1.1.tar.gz
| Download URL | signia-0.1.1.tar.gz |
|---|---|
| Size | 22.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3b4b6ef882c2aff4adc9457803e4627912ca91087feb9a622ff901ba65741f98
|
|
BLAKE2b-256 checksum How to use checksums |
9d922d6d8c8ab8388a0f305dd8a89dd6554db55eaac5667ca2f0f0f474704022
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.5
|
Release files / signia-0.1.1-py3-none-any.whl
| Download URL | signia-0.1.1-py3-none-any.whl |
|---|---|
| Size | 15.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ffabebe94efe157bc5a1bccf1963271a457d7d488f32b98f48b89d732d6ccec4
|
|
BLAKE2b-256 checksum How to use checksums |
4c3d72e3c78fb3cffc7069508ebf87d1fc3c4c00017f185c5fa1f122039109d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.5
|
Security for signia 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.