This decorator can be used to specify injection rules for decorated
    function arguments. Each argument to this decorator should be
    a :term:`specification` for injecting into related argument of decorated
    function.  `None` can be given instead of a specification to prevent
    argument from being injected. This is handy for positional arguments.

    Example::

        @inject(None, IDBConnection, logger=(ILogger, 'system'))
        def foo(noninjectable_argument, db_connection, logger=None):
            pass

    This decorator can be used multiple times and also with
    :py:class:`UnrealizedInjection` class. Specified dependencies are collected
    and when conflicting the outermost :term:`specification` is used::

        @inject(db=(IDBConnection, 'archive2'))
        @inject(db=IDBConnection)
        def foo(db=injected(IDBConnection, 'archive')):
            # In this example 'archive2' database connection will be injected.
            pass

def inject(*positional_dependencies, **keyword_dependencies):
    """
    This decorator can be used to specify injection rules for decorated
    function arguments. Each argument to this decorator should be
    a :term:`specification` for injecting into related argument of decorated
    function.  `None` can be given instead of a specification to prevent
    argument from being injected. This is handy for positional arguments.

    Example::

        @inject(None, IDBConnection, logger=(ILogger, 'system'))
        def foo(noninjectable_argument, db_connection, logger=None):
            pass

    This decorator can be used multiple times and also with
    :py:class:`UnrealizedInjection` class. Specified dependencies are collected
    and when conflicting the outermost :term:`specification` is used::

        @inject(db=(IDBConnection, 'archive2'))
        @inject(db=IDBConnection)
        def foo(db=injected(IDBConnection, 'archive')):
            # In this example 'archive2' database connection will be injected.
            pass
    """
    def decorator(function):
        dependencies = get_dependencies(function)

        def process_dependency_tuples(tuples):
            for key, dependency_description in tuples:
                if dependency_description is None:
                    specification = None
                elif isinstance(dependency_description, UnrealizedInjection):
                    specification = dependency_description.specification
                else:
                    specification = dependency_description
                if specification is None:
                    try:
                        del dependencies[key]
                    except KeyError:
                        pass
                else:
                    dependencies[key] = specification

        process_dependency_tuples(enumerate(positional_dependencies))
        process_dependency_tuples(six.iteritems(keyword_dependencies))
        function.__injection__ = dependencies
        return function
    return decorator