Skip to main content

Decoripy helps you making better decorators.

Project description

Decoripy

decoripy provides a well-structured template class for creating Python decorators. It uses inheritance to be efficient and simple.

Python 2.7, 3.4, 3.5, 3.6, 3.7, 3.8 PyPI version Build Status codecov

Table of contents

  1. Motivation
  2. Usage

1. Motivation

With decoripy, writing a decorator becomes very easy. It aims to improve the Python language expressiveness by enhancing a very powerful Python mechanism.

Decoripy provides a template built upon the basic wrapping of a function, hiding the implementation details, and providing some useful advantages:

  • no distinction between decorator with or without arguments has to be done;
  • a temporal based execution is provided.

Decorator arguments

With decoripy you could create decorator with or without arguments with no pain. In standard Python you should handle the arguments passed to the decorator, because, in this case, the wrapper function does not take the function as a the first argument. So you could do something like this:

@MyDecorator
def function_to_decorate(var):
    pass

or

@MyDecorator(True)
def function_to_decorate(var):
    pass

or

@MyDecorator(timeout=3000, num_retries=3)
def function_to_decorate(var):
    pass

or

@MyDecorator(True, timeout=3000, num_retries=3)
def function_to_decorate(var):
    pass

and you have not to change your code. The unnamed arguments (*args) passed to the decorator can be accessed by using the positional order (For example, the first parameters could be taken in this way: first_arg = args[0], see Usage). The named arguments (**kwargs) passed to the decorator are parsed and can be accessed by their name (For example, timeout could be used in the implementation code in this way: self.timeout, see Usage).

Temporal based execution

The decoripy template is built to provide temporal based execution:

  • you could execute a pre-operation before the decorated function is executed;
  • you could do some operation while the decorated function is executed;
  • you could execute a post-operation after the decorated function is executed.

In this way you can control the execution flow of the decorated function.

Nested decorator

You could nest more decorator. The order respects the writing order, so:

from decoripy import AbstractDecorator


class First(AbstractDecorator):

   def do_before(self, *args, **kwargs):
       print("Executing: First do_before")

   def do(self, *args, **kwargs):
       print("Executing: First do")
       return self.function(*args, **kwargs)

   def do_after(self, *args, **kwargs):
       print("Executing: First do_after")


class Second(AbstractDecorator):

   def do_before(self, *args, **kwargs):
       print("Executing: Second do_before")

   def do(self, *args, **kwargs):
       print("Executing: Second do")
       return self.function(*args, **kwargs)

   def do_after(self, *args, **kwargs):
       print("Executing: Second do_after")


@First(timeout=3000)
@Second
def function_to_decorate(var):
   print("Executing: function -", var)

Result:

Executing: First do_before
Executing: First do
Executing: Second do_before
Executing: Second do
Executing: function - True
Executing: Second do_after
Executing: First do_after

@First.do_before() is executed before, then @First.do(); while executing it, @Second.do_before() is triggered, then @Second.do(). The last functions to be executed are @Second.do_after() and @First.do_after(), as you could expected.

2. Usage

In order to create a new decorator, you have only to write a new class inheriting from the abstract class AbstractDecorator, and overriding the following (optional) methods:

  • do_before: if you want to make some operation before the function execution. You can also return the result of the operation and it is stored in the self.before_result variable. It can be used in the other self.do() and self.do_after() functions.
  • do: if you want to add some operation while executing the function. It is mandatory doing the self.function(*args, **kwargs) call here to trigger the decorated function execution. You can also return the result of the operation and it is stored in the self.execution_result variable. It can be used in the other and self.do_after() functions.
  • do_after: if you want to make some operation after the function execution. You can also return the result of the operation and it is stored in the self.after_result variable.

The overriding of the three functions is optional. Clearly, no overriding means no operations done upon the decorated function. Summarizing, you have only to handle the temporal phases you are interested on.

You can access to the function handler through the self.function variable. The decorator unnamed parameters can be accessed through the self.args variable. The decorator named parameters can be accessed through the self.kwargs varaible or


Example 1 - No decorator arguments:

from decoripy import AbstractDecorator


class DecoratorWithoutArguments(AbstractDecorator):

    def do_before(self, *args, **kwargs):
        print("Executing: do_before")
        return "Executed: do_before"

    def do(self, *args, **kwargs):
        print(self.before_result, ", Executing: do")
        function_result = self.function(*args, **kwargs)
        return function_result + ", Executed: do"

    def do_after(self, *args, **kwargs):
        print(self.execution_result, ", Executing: do_after")
        return "Executed: do_after"


@DecoratorWithoutArguments
def function_to_decorate(var1, var2, dict_var1, dict_var2):
    print("Executing: function: ", var1, var2, dict_var1, dict_var2)
    return "Executed: function"


function_to_decorate(1, "var2", dict_var1=[1, 2, 3], dict_var2={"key": "value"})

Example 2 - Decorator arguments

from decoripy import AbstractDecorator


class DecoratorWithParameters(AbstractDecorator):

    def do_before(self, *args, **kwargs):
        if self.execute_before:
            print("Executing: do_before")
            return "Executed: do_before"

    def do(self, *args, **kwargs):
        # Non-existing params -> error!
        try:
            if self.execute_do:
                print(self.before_result, ", Executing: do")
                function_result = self.function(*args, **kwargs)
                return function_result + ", Executed: do"
        except AttributeError:
            self.function(*args, **kwargs)

    def do_after(self, *args, **kwargs):
        if self.execute_after:
            print(self.execution_result, ", Executing: do_after")
            return "Executed: do_after"


@DecoratorWithParameters(3, execute_before=False, execute_after=False)
def function_to_decorate(var1, var2, dict_var1, dict_var2):
    print("Executing: function: ", var1, var2, dict_var1, dict_var2)
    return "Executed: function"

function_to_decorate(1, "var2", dict_var1=[1, 2, 3], dict_var2={"key": "value"})

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

decoripy-0.0.4.tar.gz (6.2 kB view details)

Uploaded Source

Built Distribution

decoripy-0.0.4-py3-none-any.whl (5.6 kB view details)

Uploaded Python 3

File details

Details for the file decoripy-0.0.4.tar.gz.

File metadata

  • Download URL: decoripy-0.0.4.tar.gz
  • Upload date:
  • Size: 6.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.2.0 requests-toolbelt/0.9.1 tqdm/4.36.1 CPython/3.8.0

File hashes

Hashes for decoripy-0.0.4.tar.gz
Algorithm Hash digest
SHA256 80b079193786384a2fd5ce9b492a3b40ae83ecb37b86b2196040e0d40ca430c4
MD5 42de5a44b28ae5896cbe5574ad6a641a
BLAKE2b-256 620a7b20d63f98efdcc4d01359160cea0fa1c8289b1e7e1c1e60151fc409d479

See more details on using hashes here.

File details

Details for the file decoripy-0.0.4-py3-none-any.whl.

File metadata

  • Download URL: decoripy-0.0.4-py3-none-any.whl
  • Upload date:
  • Size: 5.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/41.2.0 requests-toolbelt/0.9.1 tqdm/4.36.1 CPython/3.8.0

File hashes

Hashes for decoripy-0.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 0b2a0a634f105f00b8f3991f82f09d921818b6c2118065a335b96c302f963d0a
MD5 73bb9bd1240545b7adbefda414eb745f
BLAKE2b-256 c9b36e15f53d1d72b9705f5b8e857419055e5f82701fafd40c45c97a95920515

See more details on using hashes here.

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