Skip to main content

Lightweight method proxy/wrapper helper with pre, post, and exception hooks.

Project description

AutoWrapper

AutoWrapper is a small Python helper for building wrapper/proxy objects around another object's methods.

It lets a wrapper class expose selected methods from a target object and optionally route method calls through pre/post/exception hooks. This is useful for lightweight instrumentation such as logging, tracing, metrics, access checks, or debugging wrappers.

Current status

This project is still small and intentionally direct: one implementation module, AutoWrapper.py, a lowercase compatibility import module, packaging metadata, and a unittest test suite.

Implemented behavior:

  • Wrap methods from a target object onto a wrapper instance.
  • Call target-bound methods so target instance state is preserved.
  • Support optional hints for selecting which methods to proxy and wrap.
  • Support no-hints mode, which proxies and wraps all public discovered methods.
  • Discover inherited instance methods, static methods, and class methods.
  • Skip private/dunder methods by default, with explicit hint opt-in.
  • Preserve useful method metadata for wrapped methods via functools.wraps().
  • Provide pre, post, and exception hooks with method context.
  • Reject proxied method names that would overwrite wrapper attributes or methods.
  • Provide typed public methods and prefer the snake_case get_methods_to_wrap() discovery API while preserving getMethods2Wrap() compatibility.

Basic usage

from autowrapper import AutoWrapper


class Example:
    def __init__(self):
        self.count = 0

    def increment(self, amount=1):
        self.count += amount
        return self.count


class WrappedExample(AutoWrapper):
    def __init__(self, target):
        self.events = []
        self.build_wrapper(
            target,
            hints={"increment": {"proxy": True, "wrap": True}},
        )

    def _pre_method_hook(self, method_name, method, args, kwargs):
        self.events.append(("pre", method_name, args, kwargs))

    def _post_method_hook(self, method_name, method, args, kwargs, result):
        self.events.append(("post", method_name, result))

    def _exception_method_hook(self, method_name, method, args, kwargs, exc):
        self.events.append(("exception", method_name, exc))


target = Example()
wrapper = WrappedExample(target)

assert wrapper.increment(amount=2) == 2
assert target.count == 2
assert wrapper.events == [
    ("pre", "increment", (), {"amount": 2}),
    ("post", "increment", 2),
]

Scope: object methods, not standalone functions

AutoWrapper is intentionally scoped to wrapping methods discovered from a target object. It supports instance methods, inherited methods, static methods, and class methods on that target object's class.

Standalone/free functions are out of scope for now. If you need to wrap a free function, use a normal decorator directly or place the function behind a small object method before using AutoWrapper.

Method discovery API

The preferred public discovery helper is:

AutoWrapper.get_methods_to_wrap(TargetClass, hints=None)

The original spelling remains available for compatibility:

AutoWrapper.getMethods2Wrap(TargetClass, hints=None)

Hint semantics

build_wrapper(target, hints=...) accepts a dictionary keyed by method name.

Supported keys:

  • proxy: expose this method on the wrapper instance.
  • wrap: route the proxied method through the hook methods.

Behavior:

  • hints is None: proxy and wrap every public discovered instance, static, and class method.
  • proxy: True: expose the method on the wrapper.
  • proxy absent or false: do not expose the method, even if wrap: True.
  • wrap: True: call _pre_method_hook() before the target method and _post_method_hook() after it succeeds.
  • wrap absent or false: expose the target-bound method directly without hooks.
  • Methods absent from a supplied hints dictionary are not proxied.
  • Static methods and class methods are supported and can be proxied/wrapped like instance methods.
  • Private methods whose names begin with _ are skipped by default, but can be explicitly proxied with hints.
  • Proxied method names that collide with existing wrapper attributes or methods raise AttributeError.

Hook signatures

Override these methods in your wrapper subclass when you need behavior around wrapped calls:

def _pre_method_hook(self, method_name, method, args, kwargs):
    pass

def _post_method_hook(self, method_name, method, args, kwargs, result):
    pass

def _exception_method_hook(self, method_name, method, args, kwargs, exc):
    pass

Exceptions raised by wrapped target methods call _exception_method_hook() and then propagate unchanged.

Installation

Once published to PyPI, install with:

python -m pip install blakemere-autowrapper

For local development from a clean checkout:

python -m pip install -e .

The PyPI distribution name is blakemere-autowrapper. The preferred import path is lowercase:

from autowrapper import AutoWrapper

The original import path remains available for compatibility:

from AutoWrapper import AutoWrapper

Running tests

This project currently uses the Python standard-library unittest framework, so no test dependency installation is required.

From a clean checkout, run:

python -m unittest discover -s tests -v

You can also run the inline example in the implementation module:

python AutoWrapper.py

Repository layout

AutoWrapper.py              # implementation and tiny inline example
autowrapper.py              # lowercase import compatibility module
pyproject.toml              # packaging metadata
__init__.py                 # placeholder package marker
tests/test_autowrapper.py   # unittest coverage
BACKLOG.md                  # project improvement backlog

Packaging note

pyproject.toml packages both modules:

  • autowrapper: preferred lowercase import path.
  • AutoWrapper: original compatibility import path.

License

MIT. See LICENSE.

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

blakemere_autowrapper-0.1.1.tar.gz (7.9 kB view details)

Uploaded Source

Built Distribution

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

blakemere_autowrapper-0.1.1-py3-none-any.whl (7.1 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for blakemere_autowrapper-0.1.1.tar.gz
Algorithm Hash digest
SHA256 fa680e3d27610d16291ee2800657b3006ff1deb6da90d22554002e7bcdd65b8d
MD5 bca35197ed1359077ef3b9738519d604
BLAKE2b-256 a646b7287818521c6beb2683e480cd4a2226a4ba65fc5c46cbfb9d320c5b7d5a

See more details on using hashes here.

Provenance

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

Publisher: publish-pypi.yml on RusDavies/blakemere-autowrapper

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

File details

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

File metadata

File hashes

Hashes for blakemere_autowrapper-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 55f515489adf1a802d61c624eb423bd9994b8d86b5835394c53cfccf0ef79735
MD5 a24cf1b0a9aefcc54f56e7514fe8fac3
BLAKE2b-256 2ef8ddeacc5948b2ca354bca10dff4c542cc2b65f6ff261feb3d6963d1e4ab47

See more details on using hashes here.

Provenance

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

Publisher: publish-pypi.yml on RusDavies/blakemere-autowrapper

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