Skip to main content

Function Signature Tools

Project description

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.

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

signia-0.1.1.tar.gz (22.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

signia-0.1.1-py3-none-any.whl (15.5 kB view details)

Uploaded Python 3

File details

Details for the file signia-0.1.1.tar.gz.

File metadata

  • Download URL: signia-0.1.1.tar.gz
  • Upload date:
  • Size: 22.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.5

File hashes

Hashes for signia-0.1.1.tar.gz
Algorithm Hash digest
SHA256 3b4b6ef882c2aff4adc9457803e4627912ca91087feb9a622ff901ba65741f98
MD5 72cdf30c95f0f1d6f35f1a9eb6477eef
BLAKE2b-256 9d922d6d8c8ab8388a0f305dd8a89dd6554db55eaac5667ca2f0f0f474704022

See more details on using hashes here.

File details

Details for the file signia-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: signia-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 15.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.5

File hashes

Hashes for signia-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ffabebe94efe157bc5a1bccf1963271a457d7d488f32b98f48b89d732d6ccec4
MD5 4d27d290b1dd5691a0e677ccc1c20a50
BLAKE2b-256 4c3d72e3c78fb3cffc7069508ebf87d1fc3c4c00017f185c5fa1f122039109d3

See more details on using hashes here.

Supported by

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