CHANGES¶
0.11 (unreleased)¶
Breaking change
The
key_predicatefunction is gone. You can now usePredicate(..., index=KeyIndex)ormatch_keyinstead.Breaking change
The
class_predicatefunction is gone. You can now usePredicate(..., index=ClassIndex),match_instanceormatch_classinstead.Breaking change
The undocumented
Sentinelclass andNOT_FOUNDobject are gone.Breaking change
The class
PredicateRegistryis not longer part of the API. Internally, the classesMultiPredicate,MultiIndex,SingleValueRegistryhave all been merged intoPredicateRegistry, which should now considered an implementation detail.The second argument for
match_keyis now optional; if you don’t supply itmatch_keywill generate a predicate function that extracts that name by default.The documentation now includes a section describing the internals of Reg.
Upload universal wheels to pypi during release.
0.10 (2016-10-04)¶
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.Registryis 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_lookupfunction that wrapsreg.PredicateRegistryinstance inside areg.DictCachingKeyLookupcache. You can also use areg.LruCachingKeyLookupif 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.implicitis gone: everything is explicit. There is no more implicit lookup.reg.Lookupitself is gone – its now implemented directly in the dispatch object, but was already how you accessed it.- The special
lookupargument to pass through the currentLookupis 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_methodto create a dispatch method. A dispatch method maintains an entirely separate dispatch registry for each subclass. You usereg.methodifyto 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_implicitfrom your code, setup ofLookupand the like.If you use an explicit
lookupargument you can just remove them.You also need to change your registration code: no more
reg.Registrysetup.Change your registrations to be on the dispatch objects itself using
Dispatch.register.To enable caching you need to set up
get_key_lookupon the dispatch functions. You can create a partially applied version ofdispatchto 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_predicatesis gone. Just usedispatchdirectly. You can add predicates to an existing Dispatch object using theadd_predicatesmethod.
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_methodinstead of@reg.dispatch. - Registration is now using
<context_class>.<method>.register. Functions you register this way behave as methods tocontext_class, so get an instance of this class as the first argument. - You can also use
reg.methodifyto 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
Contextinstance as its first argument, you can usereg.methodifyto 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
Contextinstance 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
mapplyfrom the API.Breaking change
Removed the exception class
KeyExtractorErrorfrom 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
KeyExtractorclass from the API. Callables used in predicate construction now expect the same arguments as the dispatch function.Breaking change
Removed the
argnamesattribute fromPredicateand its descendant.Breaking change
Remove the
match_argnamepredicate. You can now usematch_instancewith no callable instead.The second argument for
match_classis now optional; if you don’t supply itmatch_classwill generate a predicate function that extracts that name by default.The second argument for
match_instanceis now optional; if you don’t supply itmatch_instancewill 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=100argument ofcoverage reportin the tox coverage test.
0.9.3 (2016-07-18)¶
- Minor fixes to documentation.
- Add tox test environments for Python 3.4 and 3.5, PyPy 3 and PEP 8.
- Make Python 3.5 the default Python environment.
- Changed location
NoImplicitLookupErrorwas imported from in__init__.py.
0.9.2 (2014-11-13)¶
- Reg was a bit too strict; when you had multiple (but not single) predicates, Reg would raise KeyError when you put in an unknown key. Now they’re just being silently ignored, as they don’t do any harm.
- Eliminated a check in
ArgExtractorthat could never take place. - Bring test coverage back up to 100%.
- Add converage configuration to ignore test files in coverage reporting.
0.9.1 (2014-11-11)¶
- A bugfix in the behavior of the fallback logic. In situations with multiple predicates of which one is a class predicate it was possible for a fallback not to be found even though a fallback was available.
0.9 (2014-11-11)¶
Total rewrite of Reg! This includes a range of changes that can break code. The primary motivations for this rewrite:
- unify predicate system with class-based lookup system.
- extract dispatch information from specific arguments instead of all arguments.
Some specific changes:
Replaced
@reg.genericdecorator with@reg.dispatch()decorator. This decorator can be configured with predicates that extract information from the arguments. Rewrite this:@reg.generic def foo(obj): pass
to this:
@reg.dispatch('obj') def foo(obj): pass
And this:
@reg.generic def bar(a, b): pass
To this:
@reg.dispatch('a', 'b') def bar(a, b): pass
This is to get dispatch on the classes of these instance arguments. If you want to match on the class of an attribute of an argument (for instance) you can use
match_instancewith a function:@reg.dispatch(match_instance('a', lambda a: a.attr))The first argument to
match_instanceis the name of the predicate by which you refer to it inregister_function.You can also use
match_classto have direct dispatch on classes (useful for replicating classmethods), andmatch_keyto have dispatch on the (immutable) value of the argument (useful for a view predicate system). Like formatch_instance, you supply functions to these match functions that extract the exact information to dispatch on from the argument.The
register_functionAPI replaces theregisterAPI to register a function. Replace this:r.register(foo, (SomeClass,), dispatched_to)
with:
r.register_function(foo, dispatched_to, obj=SomeClass)
You now use keyword parameters to indicate exactly those arguments specified by
reg.dispatch()are actually predicate arguments. You don’t need to worry about the order of predicates anymore when you register a function for it.The new
classgenericfunctionality is part of the predicate system now; you can usereg.match_classinstead. Replace:@reg.classgeneric def foo(cls): pass
with:
@reg.dispatch(reg.match_class('cls', lambda cls: cls)) def foo(cls): pass
You can do this with any argument now, not just the first one.
pep443 support is gone. Reg is focused on its own dispatch system.
Compose functionality is gone – it turns out Morepath doesn’t use lookup composition to support App inheritance. The cached lookup functionality has moved into
registry.pyand now also supports caching of predicate-based lookups.Dependency on the future module is gone in favor of a small amount of compatibility code.
0.8 (2014-08-28)¶
- Added a
@reg.classgeneric. This is like@reg.generic, but the first argument is treated as a class, not as an instance. This makes it possible to replace@classmethodwith a generic function too. - Fix documentation on running documentation tests. For some reason this did not work properly anymore without running sphinxpython explicitly.
- Optimization: improve performance of generic function calls by
employing
lookup_mapplyinstead of generalmapply, as we only care about passing in the lookup argument when it’s defined, and any other arguments should work as before. Also added aperf.pywhich is a simple generic function timing script.
0.7 (2014-06-17)¶
- Python 2.6 compatibility. (Ivo van der Wijk)
- Class maps (and thus generic function lookup) now works with old style classes as well.
- Marked as production/stable now in
setup.py.
0.6 (2014-04-08)¶
- Removed unused code from mapply.py.
- Typo fix in API docs.
0.5 (2014-01-21)¶
- Make
reg.ANYpublic. Used for predicates that match any value.
0.4 (2014-01-14)¶
- arginfo has been totally rewritten and is now part of the public API of reg.
0.3 (2014-01-06)¶
- Experimental Python 3.3 support thanks to the future module.
0.2 (2013-12-19)¶
- If a generic function implementation defines a
lookupargument that argument will be the lookup used to call it. - Added
reg.mapply(). This allows you to call things with more keyword arguments than it accepts, ignoring those extra keyword args. - A function that returns
Noneis not assumed to fail, so no fallback to the original generic function is triggered anymore. - An optional
precalcfacility is made available onMatcherto avoid some recalculation. - Implement a specific
PredicateMatcherthat matches a value on predicate.
0.1 (2013-10-28)¶
- Initial public release.