Metadata-Version: 1.1
Name: Decorum
Version: 1.0.3
Summary: Tool for writing simple decorators.
Home-page: https://pypi.python.org/pypi/Decorum
Author: Zach Kelling
Author-email: zk@monoid.io
License: MIT
Description: =======
        Decorum
        =======
        
        .. image:: https://travis-ci.org/zeekay/decorum.png?branch=master
            :target: https://travis-ci.org/zeekay/decorum
        
        Decorum is a library which aims to make it easier to write flexible and simple
        decorators:
        
        * use decorators just like you need: don't bother with parentheses, pass
          options when you need them.
        
        * write decorators just like you think: they get initialized with or without
          options, they wrap a function, they are callables.
        
        * test decorators: it's really easy, you have no excuse!
        
        
        Inherit from Decorum
        ====================
        
        In order to write your own decorators, just subclass ``decorum.Decorum``:
        
        .. code:: pycon
        
           >>> from decorum import Decorum
        
           >>> class empty_decorator(Decorum):
           ...     """Just inherit from Decorum's builtins."""
        
        Of course you will want to customize decorator's behaviour! There are only
        three methods to be aware of:
        
        * ``init()`` setups the decorator itself;
        
        * ``wraps()`` decorates the function;
        
        * ``call()`` runs the wrapped function and returns result.
        
        Here is a simple decorator to illustrate Decorum usage:
        
        .. code:: pycon
        
           >>> from __future__ import print_function
        
           >>> class verbose_decorator(Decorum):
           ...    """Print info about decoration process."""
           ...    def init(self, *args, **kwargs):
           ...        print('Initializing decorator with args={} and kwargs={}'
           ...              .format(args, kwargs))
           ...        return super(verbose_decorator, self).init(*args, **kwargs)
           ...
           ...    def wraps(self, f):
           ...        print('Wrapping function "{}"'.format(f.__name__))
           ...        return super(verbose_decorator, self).wraps(f)
           ...
           ...    def call(self, *args, **kwargs):
           ...        print('Running wrapped function with args={} and kwargs={}'
           ...              .format(args, kwargs))
           ...        return super(verbose_decorator, self).call(*args, **kwargs)
        
        Then you can use it as any decorator:
        
        .. code:: pycon
        
           >>> @verbose_decorator
           ... def foo(x):
           ...     print(x)
           Initializing decorator with args=() and kwargs={}
           Wrapping function "foo"
        
           >>> foo('bar')
           Running wrapped function with args=('bar',) and kwargs={}
           bar
        
        .. note::
        
           You can also use ``decorum.decorator`` to turn classes into decorators.
        
           .. code:: pycon
        
              >>> from decorum import decorator
        
              >>> @decorator
              ... class noop:
              ...     """Override init(), wraps() or call() if you like."""
        
              >>> @noop
              ... def foo():
              ...     """Do nothing."""
        
           The result is a class that inherits from the original class and Decorum:
        
           .. code:: pycon
        
              >>> isinstance(foo, noop)
              True
              >>> isinstance(foo, Decorum)
              True
        
        
        Don't bother with parentheses
        =============================
        
        Decorum lets you write decorators with and without arguments in a unified way.
        Then your decorator can be used with or without arguments, called or not, and
        it will work the same way:
        
        .. code:: pycon
        
           >>> @verbose_decorator
           ... def foo(x):
           ...     print(x)
           Initializing decorator with args=() and kwargs={}
           Wrapping function "foo"
        
        Is identical to:
        
        .. code:: pycon
        
           >>> @verbose_decorator()
           ... def foo(x):
           ...     print(x)
           Initializing decorator with args=() and kwargs={}
           Wrapping function "foo"
        
        
        Initialize decorator with options
        =================================
        
        To implement a decorator that accepts a custom options, just change ``init()``:
        
        .. code:: pycon
        
           >>> class configurable_decorator(Decorum):
           ...    def init(self, custom_option='default', *args, **kwargs):
           ...        print('Initializing decorator with custom_option="{}"'
           ...              .format(custom_option))
           ...        # Remember the option, typically for use in wraps() or call().
           ...        self.custom_option = custom_option
           ...        # Call super().init() with remaining arguments.
           ...        return super(configurable_decorator, self).init(*args, **kwargs)
        
        As we used a keyword argument, it is optional:
        
        .. code:: pycon
        
           >>> @configurable_decorator
           ... def foo(x):
           ...     print(x)
           Initializing decorator with custom_option="default"
        
           >>> foo.custom_option
           'default'
        
        And we can pass this option when decorating a function. Either as positional
        argument...
        
        .. code:: pycon
        
           >>> @configurable_decorator('positional')
           ... def foo(x):
           ...     print(x)
           Initializing decorator with custom_option="positional"
        
           >>> foo.custom_option
           'positional'
        
        ... or keyword argument:
        
        .. code:: pycon
        
           >>> @configurable_decorator(custom_option='keyword')
           ... def foo(x):
           ...     print(x)
           Initializing decorator with custom_option="keyword"
        
           >>> foo.custom_option
           'keyword'
        
        Of course, you cannot pass arguments that are not declared as ``init()``
        options:
        
        .. code:: pycon
        
           >>> @configurable_decorator(wrong_option=True)  # doctest: +ELLIPSIS
           ... def foo(x):
           ...     print(x)
           Traceback (most recent call last):
             ...
           TypeError: init() got an unexpected keyword argument 'wrong_option'
        
        .. note::
        
           In most cases, ``init()`` should accept additional arguments and and proxy
           them to parent via ``super(...).init(*args, **kwargs)``. This way, options
           of ancestor classes are supported.
        
           As an example, ``Decorum`` base class declares ``assigned`` keyword argument
           in ``init()`` (see section `wrap function <#wrap-function>`_ below).
        
        
        Wrap function
        =============
        
        The ``wraps()`` method allows you to handle the decorated function. It receives
        the function to decorate as single positional argument, and returns a callable
        (typically ``self``).
        
        In most cases, ``wraps()`` function will ``return super(..., self).wraps(f)``.
        
        By default, the base ``Decorum.wraps()`` will try to keep assign certain
        attributes to the wrapped function for you, namely ``__doc__`` and
        ``__name__``. This feature uses ``functools.wraps``.
        
        .. code:: pycon
        
           >>> class identity(Decorum):
           ...     """Noop decorator: does nothing!"""
        
           >>> @identity
           ... def my_function():
           ...     """My function's docstring."""
        
           >>> print(my_function.__name__)
           my_function
           >>> print(my_function.__doc__)
           My function's docstring.
        
        The optional ``assigned`` keyword argument can be used to specify which
        attributes of the original function are assigned directly to the matching
        attributes on the wrapper function. This defaults to
        ``functools.WRAPPER_ASSIGNMENTS``. You can specify ``False`` or ``None`` to
        disable this.
        
        .. code:: pycon
        
           >>> @identity(assigned=None)
           ... def my_function():
           ...     """My function's docstring."""
        
           >>> print(my_function.__name__)
           identity
           >>> print(my_function.__doc__)
           Noop decorator: does nothing!
        
        
        Run decorated function
        ======================
        
        ``Decorum.call()`` method receives ``*args`` and ``**kwargs`` as input. It runs
        the wrapped function, and returns the result.
        
        Here is a simple decorator that repeats the result of decorated function:
        
        .. code:: pycon
        
           >>> class repeat(Decorum):
           ...     def call(self, *args, **kwargs):
           ...         result = super(repeat, self).call(*args, **kwargs)
           ...         return ' '.join([result] * 2)
        
           >>> @repeat
           ... def parrot(word):
           ...     return word
        
           >>> parrot('hello')
           'hello hello'
        
        
        Test decorators
        ===============
        
        Decorum makes it easy to test custom decorators.
        
        Assert a decorator has expected behaviour
        -----------------------------------------
        
        Decorators are defined as classes, so you have fine-grained control over what
        you test. And your tests can focus on what you customized.
        
        Let's check ``repeat`` decorator from the section before. Since we just
        overrode ``call()``, let's focus on it:
        
        .. code:: pycon
        
           >>> decorator = repeat(lambda x: x.upper())
        
           >>> result = decorator.call('input')
           >>> assert result == 'INPUT INPUT'
        
        That's quite useful to unit test decorators.
        
        
        Assert a function has been decorated
        ------------------------------------
        
        Decorators are instances of ``Decorum`` or subclasses. So you can inspect
        decorated functions.
        
        Let's inspect ``my_function`` from the examples above:
        
        .. code:: pycon
        
           >>> assert isinstance(my_function, Decorum)
           >>> assert isinstance(my_function, identity)
        
        
        Known limitations
        =================
        
        Decorum has little known limitations:
        
        * your decorator's ``init()`` cannot receive a single positional argument that
          is a callable. Decorum will try trigger ``wraps()`` instead of ``init()`` in
          such a case.
        
        
        About
        =====
        
        Decorum project is free software, published under MIT license.
        
        * PyPI: https://pypi.python.org/pypi/Decorum
        * Code repository: https://github.com/zeekay/decorum
        * Bugtracker: https://github.com/zeekay/decorum/issues
        * Continuous integration: https://travis-ci.org/zeekay/decorum
        
Keywords: decorator decorators
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: License :: OSI Approved :: MIT License
Classifier: Intended Audience :: Developers
