_simplegeneric.py
109 lines
| 3.0 KiB
| text/x-python
|
PythonLexer
Thomas Kluyver
|
r4756 | """This is version 0.7 of Philip J. Eby's simplegeneric module | ||
Thomas Kluyver
|
r4757 | (http://pypi.python.org/pypi/simplegeneric), patched to work with Python 3, | ||
which doesn't support old-style classes. | ||||
Thomas Kluyver
|
r4756 | """ | ||
vivainio
|
r911 | #Name: simplegeneric | ||
Thomas Kluyver
|
r4756 | #Version: 0.7 | ||
vivainio
|
r911 | #Summary: Simple generic functions (similar to Python's own len(), pickle.dump(), etc.) | ||
Thomas Kluyver
|
r4756 | #Home-page: http://pypi.python.org/pypi/simplegeneric | ||
vivainio
|
r911 | #Author: Phillip J. Eby | ||
#Author-email: peak@eby-sarna.com | ||||
#License: PSF or ZPL | ||||
walter.doerwald
|
r415 | |||
vivainio
|
r911 | __all__ = ["generic"] | ||
Thomas Kluyver
|
r4757 | try: | ||
from types import ClassType, InstanceType | ||||
except ImportError: | ||||
classtypes = type | ||||
else: | ||||
classtypes = type, ClassType | ||||
vivainio
|
r911 | |||
walter.doerwald
|
r415 | def generic(func): | ||
"""Create a simple generic function""" | ||||
_sentinel = object() | ||||
def _by_class(*args, **kw): | ||||
cls = args[0].__class__ | ||||
for t in type(cls.__name__, (cls,object), {}).__mro__: | ||||
f = _gbt(t, _sentinel) | ||||
if f is not _sentinel: | ||||
return f(*args, **kw) | ||||
else: | ||||
return func(*args, **kw) | ||||
Thomas Kluyver
|
r4757 | _by_type = {object: func} | ||
try: | ||||
_by_type[InstanceType] = _by_class | ||||
except NameError: # Python 3 | ||||
pass | ||||
walter.doerwald
|
r415 | _gbt = _by_type.get | ||
Thomas Kluyver
|
r4756 | def when_type(*types): | ||
"""Decorator to add a method that will be called for the given types""" | ||||
for t in types: | ||||
if not isinstance(t, classtypes): | ||||
walter.doerwald
|
r415 | raise TypeError( | ||
Thomas Kluyver
|
r4756 | "%r is not a type or class" % (t,) | ||
walter.doerwald
|
r415 | ) | ||
Thomas Kluyver
|
r4756 | def decorate(f): | ||
for t in types: | ||||
if _by_type.setdefault(t,f) is not f: | ||||
raise TypeError( | ||||
"%r already has method for type %r" % (func, t) | ||||
) | ||||
walter.doerwald
|
r415 | return f | ||
return decorate | ||||
_by_object = {} | ||||
_gbo = _by_object.get | ||||
Thomas Kluyver
|
r4756 | def when_object(*obs): | ||
"""Decorator to add a method to be called for the given object(s)""" | ||||
walter.doerwald
|
r415 | def decorate(f): | ||
Thomas Kluyver
|
r4756 | for o in obs: | ||
if _by_object.setdefault(id(o), (o,f))[1] is not f: | ||||
raise TypeError( | ||||
"%r already has method for object %r" % (func, o) | ||||
) | ||||
walter.doerwald
|
r415 | return f | ||
return decorate | ||||
def dispatch(*args, **kw): | ||||
f = _gbo(id(args[0]), _sentinel) | ||||
if f is _sentinel: | ||||
for t in type(args[0]).__mro__: | ||||
f = _gbt(t, _sentinel) | ||||
if f is not _sentinel: | ||||
return f(*args, **kw) | ||||
else: | ||||
return func(*args, **kw) | ||||
else: | ||||
return f[1](*args, **kw) | ||||
Thomas Kluyver
|
r4756 | dispatch.__name__ = func.__name__ | ||
walter.doerwald
|
r415 | dispatch.__dict__ = func.__dict__.copy() | ||
dispatch.__doc__ = func.__doc__ | ||||
dispatch.__module__ = func.__module__ | ||||
dispatch.when_type = when_type | ||||
dispatch.when_object = when_object | ||||
dispatch.default = func | ||||
dispatch.has_object = lambda o: id(o) in _by_object | ||||
dispatch.has_type = lambda t: t in _by_type | ||||
return dispatch | ||||
def test_suite(): | ||||
import doctest | ||||
return doctest.DocFileSuite( | ||||
'README.txt', | ||||
optionflags=doctest.ELLIPSIS|doctest.REPORT_ONLY_FIRST_FAILURE, | ||||
) | ||||