Skip to main content

epypen is a lightweight translation decorator that bridges the gap between raw Python objects and type-annotated data.

Project description

epypen

A Lightweight Translation Decorator for Python Objects

PyPI version
License: GPL-3.0 Build Status Coverage Downloads


Table of Contents


Overview

epypen is a lightweight translation decorator that bridges the gap between raw Python objects and type-annotated data. It allows automatic conversion of function arguments and return values based on type annotations. This is especially useful in projects that use Pydantic models (for example, FastAPI applications) to perform validation and transformation without writing extra boilerplate code.


Features

  • Automatic Parameter Conversion: Converts function arguments to their expected types based on annotations.
  • Return Type Translation: Adjusts function outputs to match annotated return types.
  • Signature Preservation: Updates function signatures according to annotations—e.g., using via() on a parameter will modify its annotation in the final signature.
  • Supports Multiple Function Types: Works with synchronous, asynchronous, instance, class, and static methods.
  • Custom Conversion Logic: Users can supply custom conversion functions via parameter_conversions and return_value_conversions.
  • Built on Modern Python Tools: Leverages Pydantic, typing-extensions, and Python’s introspection capabilities.

Installation

Install epypen directly from PyPI:

pip install epypen

Usage

Basic Conversion Example

This simple example demonstrates how epypen converts parameter types and the return value based on annotations:

from epypen import converted

@converted
def _sum(a: int, b: int) -> int:
    return a + b

# Although passed as strings, the arguments are converted to integers.
assert _sum("2", "3") == 5

Main Usage: Converting to Pydantic Models

A common use case for epypen is to seamlessly convert dictionaries into Pydantic models. For example:

from pydantic import BaseModel
from epypen import converted, to

# Define Pydantic models for input and output.
class X(BaseModel):
    x: int

class Y(BaseModel):
    y: int

class Result(BaseModel):
    z: int

@converted
def add(x: X, y: Y) -> Result:
    return Result(z=x.x + y.y)

# You can pass dictionaries and they will be automatically converted:
result = add({"x": "2"}, {"y": 3})
assert result == Result(z=5)

Advanced Usage

Understanding to() and via()

  • to():
    This helper is used in return type annotations to specify the target type. For example, if your function returns a string that should be converted to an integer, wrap the return annotation with to(int).

    from epypen import converted, to
    from typing_extensions import Annotated
    
    @converted
    def _cast(x: int, y: int) -> Annotated[str, to(int)]:
        return str(x + y)
    
    # The returned string '5' is automatically converted to integer 5.
    assert _cast("2", "3") == 5
    
  • via():
    Use this helper in parameter annotations to indicate the original type that should be used before conversion. It also modifies the function’s signature, so the parameter type in the final signature becomes the type provided to via().

    from epypen import converted, via, to
    from typing_extensions import Annotated
    import inspect
    
    @converted
    def foo(x: Annotated[int, via(str)], y: int = 3) -> Annotated[int, to(bool)]:
        return x + y
    
    # After decoration, the signature of `foo` changes:
    print(inspect.signature(foo))
    # Expected output: (x: str, y: int = 3) -> bool
    

Note: Mixing these directives incorrectly (e.g., using to() in parameters or via() in return types) will raise a TypeError.

Customizing Conversions

epypen allows you to customize how conversion is performed by providing your own lists of conversion functions via the parameter_conversions and return_value_conversions parameters. By default, epypen uses predefined conversion functions (e.g., for converting strings to integers, dictionaries to Pydantic models, etc.). However, if you need specialized conversion logic, you can supply your own functions.

For example:

from epypen import converted, ConversionsError

# Custom conversion function that deliberately fails.
def always_fail(typ, obj):
    raise ValueError("Custom conversion failed!")

@converted(parameter_conversions=[always_fail])
def foo(x):
    return x

try:
    foo("test")
except ConversionsError as e:
    print("Custom conversion failed as expected:", e)

You can similarly override the conversion logic for return values using return_value_conversions. This feature lets you extend epypen to handle any custom types or non-standard conversion requirements in your projects.

Handling Conversion Errors

If no conversion function successfully converts a parameter or a return value, epypen raises a ConversionsError. This error aggregates all the conversion exceptions, allowing you to see which conversion functions were attempted and why they failed.

from epypen import converted, ConversionsError

def always_fail(typ, obj):
    raise ValueError("Conversion always fails!")

@converted(parameter_conversions=[always_fail])
def foo(x):
    return x

try:
    foo("test")
except ConversionsError as e:
    print("Conversion failed:", e)

In this snippet, since every conversion attempt raises an error, a ConversionsError is raised containing the list of exceptions from each failed conversion.


Gotchas

  • Annotation Consistency:
    Use via() for parameters and to() for return types. Incorrect usage will result in a TypeError.

  • Single Directive Limitation:
    Only one metadata conversion directive (either to() or via()) should be provided per parameter or return type to prevent ambiguity.

  • Signature Update:
    The function’s signature is modified according to the annotations. If you rely on introspection, verify that the updated signature meets your needs.


Contributing

Contributions are very welcome! If you encounter issues, have suggestions, or want to submit bug fixes, please open an issue or a pull request.


License

This project is licensed under the GPL-3.0 license.


Acknowledgments

  • Inspired by auto-conversion features in FastAPI and Pydantic.

Feel free to expand this README with additional examples or troubleshooting tips as epypen evolves. Happy converting!

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

epypen-1.0.3.tar.gz (8.2 kB view details)

Uploaded Source

Built Distribution

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

epypen-1.0.3-py3-none-any.whl (8.8 kB view details)

Uploaded Python 3

File details

Details for the file epypen-1.0.3.tar.gz.

File metadata

  • Download URL: epypen-1.0.3.tar.gz
  • Upload date:
  • Size: 8.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.6.3

File hashes

Hashes for epypen-1.0.3.tar.gz
Algorithm Hash digest
SHA256 ee67a2b1b128ca064a70612bb15faf5a09926442ff8787b76f86d4687e2b494b
MD5 ffa711c1d6b37ca90190ab1d43edb48f
BLAKE2b-256 964c82b1fa6cb69e821b53d29ede9199923df740cbbbab77ca359d9ee42ed864

See more details on using hashes here.

File details

Details for the file epypen-1.0.3-py3-none-any.whl.

File metadata

  • Download URL: epypen-1.0.3-py3-none-any.whl
  • Upload date:
  • Size: 8.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.6.3

File hashes

Hashes for epypen-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 e64354c3902180023d67fd3f1576063426413657a2372991d85ab2ef8482951b
MD5 c00d2838557e2cdc9d06b451b6a6364e
BLAKE2b-256 cfbce7436725dfa3a94e33714f1ee0642595dc34e2dfacc919372d8062899bb6

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