=================
- **Breaking change**
Reg has undergone another API breaking change. The goals of this
change were:
* Make everything explicit.
* A simpler implementation structure -- dispatch functions maintain
their own registries, which allows for less interacting objects.
* Make the advanced context-dependent dispatch more Pythonic by
using classes with special dispatch methods.
Detailed changes:
* ``reg.Registry`` is gone. Instead you register directly on the
dispatch function::
reg.dispatch('a')
def foo(a):
...
def foo_implementation(a):
...
foo.register(foo_implementation, a=Document)
* Caching is now per dispatch function, not globally per lookup. You
can pass a ``get_key_lookup`` function that wraps
``reg.PredicateRegistry`` instance inside a
``reg.DictCachingKeyLookup`` cache. You can also use a
``reg.LruCachingKeyLookup`` if you expect a dispatch to be called
with a large amount of possible predicate combinations, to
preserve memory.
* The whole concept of a "lookup" is gone:
* ``reg.implicit`` is gone: everything is explicit. There is no more
implicit lookup.
* ``reg.Lookup`` itself is gone -- its now implemented directly in the
dispatch object, but was already how you accessed it.
* The special ``lookup`` argument to pass through the current
``Lookup`` is gone. If you need context-dependent dispatch, you
use dispatch methods.
* If you need context dependent dispatch, where the functions
being dispatched to depend on application context (such as
Morepath's application mounting), you use
``reg.dispatch_method`` to create a dispatch method. A dispatch
method maintains an entirely separate dispatch registry for each
subclass. You use ``reg.methodify`` to register a dispatch
function that takes an optional context first argument.
If you do not use the context-dependent dispatch feature, then to
upgrade your code:
* remove any ``reg.set_implicit`` from your code, setup of
``Lookup`` and the like.
* If you use an explicit ``lookup`` argument you can just remove them.
* You also need to change your registration code: no more
``reg.Registry`` setup.
* Change your registrations to be on the dispatch objects itself
using ``Dispatch.register``.
* To enable caching you need to set up ``get_key_lookup`` on the
dispatch functions. You can create a partially applied version of
``dispatch`` to make this less verbose::
import reg
from functools import partial
def get_caching_key_lookup(r):
return reg.CachingKeyLookup(r, 5000, 5000, 5000)
dispatch = partial(reg.dispatch, get_key_lookup=get_caching_key_lookup)
* ``dispatch_external_predicates`` is gone. Just use ``dispatch``
directly. You can add predicates to an existing Dispatch object
using the ``add_predicates`` method.
If you do use the context-dependent dispatch feature, then you also
need to:
* identify the context class in your application (or create one).
* move the dispatch functions to this class, marking them with
``reg.dispatch_method`` instead of ``reg.dispatch``.
* Registration is now using
``<context_class>.<method>.register``. Functions you register this
way behave as methods to ``context_class``, so get an instance of
this class as the first argument.
* You can also use ``reg.methodify`` to register implementation
functions that do not take the context as the first argument --
this is useful when upgrading existing code.
* Call your context-dependent methods as methods on the context
instance. This way you can indicate what context you are calling
your dispatch methods in, instead of using the `lookup`` argument.
In some cases you want a context-dependent method that actually does
not dispatch on any of its arguments. To support this use case you
can simply set function (that takes an app argument) as a the method
on the context class directly::
Context.my_method = some_function
If you want to set up a function that doesn't take a reference to a
``Context`` instance as its first argument, you can use
``reg.methodify`` to turn it into a method that ignores its first
argument::
Context.my_method = reg.methodify(some_function)
If you want to register a function that might or might not have a
reference to a ``Context`` instance as its first argument, called,
e.g., ``app``, you can use the following::
Context.my_method = reg.methodify(some_function, selfname='app')
- **Breaking change**
Removed the helper function ``mapply`` from the API.
- **Breaking change**
Removed the exception class ``KeyExtractorError`` from the API.
When passing the wrong number of arguments to a dispatch function,
or when using the wrong argument names, you will now get a
TypeError, in conformity with standard Python behaviour.
- **Breaking change**
Removed the ``KeyExtractor`` class from the API. Callables used in
predicate construction now expect the same arguments as the dispatch
function.
- **Breaking change**
Removed the ``argnames`` attribute from ``Predicate`` and its
descendant.
- **Breaking change**
Remove the ``match_argname`` predicate. You can now use
``match_instance`` with no callable instead.
- The second argument for ``match_class`` is now optional; if you
don't supply it ``match_class`` will generate a predicate function
that extracts that name by default.
- The second argument for ``match_instance`` is now optional; if you
don't supply it ``match_instance`` will generate a predicate function
that extracts that name by default.
- Include doctests in Tox and Travis.
- We now use virtualenv and pip instead of buildout to set up the
development environment. The development documentation has been
updated accordingly.
- As we reached 100% code coverage for pytest, coveralls integration
was replaced by the ``--fail-under=100`` argument of ``coverage
report`` in the tox coverage test.