Skip to main content

Hassle-free creation of decorators for functions and methods, OO-style.

Project description

Classy Decorators

PyPI version

Hassle-free creation of decorators for functions and methods, OO-style.

Features

  • One decorator to rule them all; it works on functions, methods, classmethods and staticmethods.
  • Easily define parameters with a dataclass-like annotated attribute syntax.
  • Runtime type-checking of decorator paremeters.
  • For decorators with all-default or no parameters, parentheses are optional: @spam() == @spam
  • Inheritance is supported.
  • Any decorator parameters and instance attributes are accessible (as copies) in bound methods as well; no need to worry about those pesky descriptors.
  • MyPy compatible and 100% test coverage.

Dependencies

Python 3.8 or 3.9.

Install

pip install classy-decorators

for better runtime type checking:

pip install classy-decorators[typeguard]

Usage

The following code can also be found in the examples.

Create a decorator by subclassing classy_decorators.Decorator. You can override the the decorated function or method using the __call_inner__ method (__call__ is meant for internal use only and should not be used for this).

Simple decorator

from classy_decorators import Decorator

class Double(Decorator):
    def __call_inner__(self, *args, **kwargs) -> float:
        return super().__call_inner__(*args, **kwargs) * 2

To see it in action, let's decorate a function:

@Double
def add(a, b):
    return a + b

assert add(7, 14) == 42

You can also decorate methods and classmethods:

class AddConstant:
    default_constant = 319

    def __init__(self, constant=default_constant):
        self.constant = constant

    @Double
    def add_to(self, value):
        return value + self.constant

    @Double
    @classmethod
    def add_default(cls, value):
        return value + cls.default_constant

assert AddConstant(7).add_to(14) == 42
assert AddConstant.add_default(14) == 666

Decorator parameters

Our Double decorator is pretty nice, but we can do better! So let's create a decorator that is able to multiply results by any number instead of only by 2:

class Multiply(Decorator):
    factor: int

    def __call_inner__(self, *args, **kwargs) -> float:
        return super().__call_inner__(*args, **kwargs) * self.factor

By simply setting the type-annotated factor attribute, we can use it as decorator parameter. If you are familiar with dataclasses, you can see that this is very similar to defining dataclass fields.

@Multiply(2)
def add_and_double(a, b):
    return a + b

@Multiply(factor=3)
def add_and_triple(a, b):
    return a + b

assert add_and_double(8, 15) == 46
assert add_and_triple(8, 15) == 69

Default parameters and inheritance

It's classy to be DRY, so let's combine our Double and Multiply decorators into one that multiplies by 2, unless specified otherwise:

class DoubleOrMultiply(Multiply):
    factor = 2

@DoubleOrMultiply
def add_and_double(a, b):
    return a + b

@DoubleOrMultiply(factor=3)
def add_and_triple(a, b):
    return a + b

assert add_and_double(7, 14) == 42
assert add_and_triple(8, 15) == 69

Advanced dataclass methods

The Decorator base class provided, aside from __call_inner__, two other interface methods you can override:

  • Decorator.__decorate__(self, **params), which is called just after a function method is decorated, with all decorator parameter values or defaults as keyword arguments, i.e. DoubleOrMultiply.__decorate__(self, factor: int = 2).
  • Decorator.__bind__(self, instance_or_class), which is called when a method (not for functions), is bound to an instance, or when a class/static method is bound to a class.

Additionally, these properties can be used for figuring out what's been decorated:

  • is_function
  • is_method; either an instance, class- or static method
  • is_instancemethod
  • is_classmethod
  • is_staticmethod

And for methods, is_bound and is_unbound are provided.

If you're looking for the original wrapped function, you can find it at __func__.


Classy, eh?

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

classy-decorators-1.2.0.tar.gz (11.1 kB view hashes)

Uploaded Source

Built Distribution

classy_decorators-1.2.0-py3-none-any.whl (22.2 kB view hashes)

Uploaded Python 3

Supported by

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