Skip to main content

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)

Source distribution for signia 0.1.1
File Size Uploaded Provenance
signia-0.1.1.tar.gz 22.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for signia 0.1.1
File Interpreter ABI Platform
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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page