Skip to main content

Add your description here

Project description

extensionmethods

Mimics C#-style extension methods in Python.

extensionmethods is a tiny package that lets you “attach” functions to existing types without modifying their source code, enabling method-like syntax, better chaining, and cleaner separation of optional dependencies.

Example usage

In C#, you can add methods to existing types:

public static class IntExtensions
{
    public static int Double(this int x)
    {
        return x * 2;
    }
}

int result = 7.Double();

You get method syntax without modifying int.

With the extensionmethods package you can achieve similiar functionality like so:

from extensionmethods import extension

@extension(to=int)
def double(x: int) -> int:
    return x * 2

result = 7 | double()
print(result)  # 14

With parameters:

@extension(to=int)
def add_then_multiply(x: int, to_add: int, to_multiply: int) -> int:
    return (x + to_add) * to_multiply

result = 7 | add_then_multiply(11, 3)
print(result)  # 54

The value on the left side becomes the first argument of the function.

Type safety and type checking

The extension methods are type-aware.

When you declare an extension, you bind it to a specific type:

@extension(to=int)
def double(x: int) -> int:
    return x * 2

This gives you safety at two levels:

  • IDE / static type checking Type checkers and editors can detect incorrect usage:

    "hello" | double()  # type error
    

    Your IDE (e.g. VS Code) can flag this because the extension is declared for int, not str.

  • Runtime enforcement

    Even if type checking is bypassed, the library validates the type at runtime:

    >>> "hello" | double()
    TypeError: Extension 'double' can only be used on 'int', not 'str'
    

Installation

Using pip:

pip install extensionmethods

Using uv:

uv pip install extensionmethods

Why use extension methods?

Readability through chaining

Instead of nested calls:

result = normalize(scale(center(data)))

You can express the same flow step-by-step:

result = data | center() | scale() | normalize()

This reads left-to-right and mirrors how data is conceptually transformed.

Modularity and dependency isolation

Suppose you maintain a core class:

class Dataset:
    ...

You want export helpers:

  • to_pandas()
  • to_numpy()
  • to_torch()

If you put these methods directly on Dataset, your core package must depend on pandas, numpy, and torch.

Instead, keep the core dependency-free:

# core package
class Dataset:
    ...

Then provide optional extensions:

# dataset_pandas package
import pandas as pd
from extensionmethods import extension
from core import Dataset

@extension(to=Dataset)
def to_pandas(ds: Dataset) -> pd.DataFrame:
    ...

Usage:

import dataset_pandas  # registers the extension

df = dataset | to_pandas()

Now:

  • The core package has zero heavy dependencies
  • Users only install what they need
  • Functionality stays logically grouped

Known caveats

IDE type hints may be misleading

Editors like VS Code may show hover/type information for the decorator wrapper, not the original function.

@extension(to=int)
def double(x: int) -> int:
    return x * 2

Hovering double may not show the expected signature (x: int) -> int, but instead (function) double: ExtensionDecoratorFactory[int].

Uses the | operator (__ror__)

The system works by overriding the right-side bitwise OR operator.

result = value | extension_call()

This only works if the left-hand type does not fully consume the | operator itself.

For example, sets already use |:

{1, 2} | {3}   # set union

If a type defines its own __or__ in a way that prevents fallback to __ror__, the extension method will not run.

License

This project is licensed under the MIT License. See the LICENSE file for details.

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

extensionmethods-0.1.1.tar.gz (14.6 kB view details)

Uploaded Source

Built Distribution

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

extensionmethods-0.1.1-py3-none-any.whl (4.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: extensionmethods-0.1.1.tar.gz
  • Upload date:
  • Size: 14.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for extensionmethods-0.1.1.tar.gz
Algorithm Hash digest
SHA256 b2d4c53b398d829c03c209d02995a3eecd5706d6e55a69d6d0690c7452e67d33
MD5 945da62546a51b163973f9cfe996ec41
BLAKE2b-256 4993f5b5b456f91da5bd75c80e06e58b9af91874e762e266148324aa047553d9

See more details on using hashes here.

Provenance

The following attestation bundles were made for extensionmethods-0.1.1.tar.gz:

Publisher: build-test-publish.yml on Pim-Mostert/extensionmethods

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

File hashes

Hashes for extensionmethods-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b9ed7f0ba696e22c027eab17fc158bda14dfc795ff72ac2fa2a2a9961667eb19
MD5 5351a413fba6df9fea699f0cc4797c93
BLAKE2b-256 f0bcc68ec79a7ac5dd00e9b056dcb899589d8a6f91efb16f1c6190e0036f75e1

See more details on using hashes here.

Provenance

The following attestation bundles were made for extensionmethods-0.1.1-py3-none-any.whl:

Publisher: build-test-publish.yml on Pim-Mostert/extensionmethods

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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