|
@@ -0,0 +1,281 @@
|
|
|
+Metadata-Version: 2.1
|
|
|
+Name: simplegeneric
|
|
|
+Version: 0.8.1
|
|
|
+Summary: Simple generic functions (similar to Python's own len(), pickle.dump(), etc.)
|
|
|
+Home-page: http://cheeseshop.python.org/pypi/simplegeneric
|
|
|
+Author: Phillip J. Eby
|
|
|
+Author-email: peak@eby-sarna.com
|
|
|
+License: ZPL 2.1
|
|
|
+Platform: UNKNOWN
|
|
|
+Classifier: Development Status :: 6 - Mature
|
|
|
+Classifier: Development Status :: 7 - Inactive
|
|
|
+Classifier: Intended Audience :: Developers
|
|
|
+Classifier: License :: OSI Approved :: Zope Public License
|
|
|
+Classifier: Programming Language :: Python
|
|
|
+Classifier: Programming Language :: Python :: 2
|
|
|
+Classifier: Programming Language :: Python :: 2.4
|
|
|
+Classifier: Programming Language :: Python :: 2.5
|
|
|
+Classifier: Programming Language :: Python :: 2.6
|
|
|
+Classifier: Programming Language :: Python :: 2.7
|
|
|
+Classifier: Programming Language :: Python :: 3
|
|
|
+Classifier: Operating System :: OS Independent
|
|
|
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
+
|
|
|
+* New in 0.8: Source and tests are compatible with Python 3 (w/o ``setup.py``)
|
|
|
+* 0.8.1: setup.py is now compatible with Python 3 as well
|
|
|
+* New in 0.7: `Multiple Types or Objects`_
|
|
|
+* New in 0.6: `Inspection and Extension`_, and thread-safe method registration
|
|
|
+
|
|
|
+The ``simplegeneric`` module lets you define simple single-dispatch
|
|
|
+generic functions, akin to Python's built-in generic functions like
|
|
|
+``len()``, ``iter()`` and so on. However, instead of using
|
|
|
+specially-named methods, these generic functions use simple lookup
|
|
|
+tables, akin to those used by e.g. ``pickle.dump()`` and other
|
|
|
+generic functions found in the Python standard library.
|
|
|
+
|
|
|
+As you can see from the above examples, generic functions are actually
|
|
|
+quite common in Python already, but there is no standard way to create
|
|
|
+simple ones. This library attempts to fill that gap, as generic
|
|
|
+functions are an `excellent alternative to the Visitor pattern`_, as
|
|
|
+well as being a great substitute for most common uses of adaptation.
|
|
|
+
|
|
|
+This library tries to be the simplest possible implementation of generic
|
|
|
+functions, and it therefore eschews the use of multiple or predicate
|
|
|
+dispatch, as well as avoiding speedup techniques such as C dispatching
|
|
|
+or code generation. But it has absolutely no dependencies, other than
|
|
|
+Python 2.4, and the implementation is just a single Python module of
|
|
|
+less than 100 lines.
|
|
|
+
|
|
|
+
|
|
|
+Usage
|
|
|
+-----
|
|
|
+
|
|
|
+Defining and using a generic function is straightforward::
|
|
|
+
|
|
|
+>>> from simplegeneric import generic
|
|
|
+>>> @generic
|
|
|
+... def move(item, target):
|
|
|
+... """Default implementation goes here"""
|
|
|
+... print("what you say?!")
|
|
|
+
|
|
|
+>>> @move.when_type(int)
|
|
|
+... def move_int(item, target):
|
|
|
+... print("In AD %d, %s was beginning." % (item, target))
|
|
|
+
|
|
|
+>>> @move.when_type(str)
|
|
|
+... def move_str(item, target):
|
|
|
+... print("How are you %s!!" % item)
|
|
|
+... print("All your %s are belong to us." % (target,))
|
|
|
+
|
|
|
+>>> zig = object()
|
|
|
+>>> @move.when_object(zig)
|
|
|
+... def move_zig(item, target):
|
|
|
+... print("You know what you %s." % (target,))
|
|
|
+... print("For great justice!")
|
|
|
+
|
|
|
+>>> move(2101, "war")
|
|
|
+In AD 2101, war was beginning.
|
|
|
+
|
|
|
+>>> move("gentlemen", "base")
|
|
|
+How are you gentlemen!!
|
|
|
+All your base are belong to us.
|
|
|
+
|
|
|
+>>> move(zig, "doing")
|
|
|
+You know what you doing.
|
|
|
+For great justice!
|
|
|
+
|
|
|
+>>> move(27.0, 56.2)
|
|
|
+what you say?!
|
|
|
+
|
|
|
+
|
|
|
+Inheritance and Allowed Types
|
|
|
+-----------------------------
|
|
|
+
|
|
|
+Defining multiple methods for the same type or object is an error::
|
|
|
+
|
|
|
+>>> @move.when_type(str)
|
|
|
+... def this_is_wrong(item, target):
|
|
|
+... pass
|
|
|
+Traceback (most recent call last):
|
|
|
+...
|
|
|
+TypeError: <function move...> already has method for type <...'str'>
|
|
|
+
|
|
|
+>>> @move.when_object(zig)
|
|
|
+... def this_is_wrong(item, target): pass
|
|
|
+Traceback (most recent call last):
|
|
|
+...
|
|
|
+TypeError: <function move...> already has method for object <object ...>
|
|
|
+
|
|
|
+And the ``when_type()`` decorator only accepts classes or types::
|
|
|
+
|
|
|
+>>> @move.when_type(23)
|
|
|
+... def move_23(item, target):
|
|
|
+... print("You have no chance to survive!")
|
|
|
+Traceback (most recent call last):
|
|
|
+...
|
|
|
+TypeError: 23 is not a type or class
|
|
|
+
|
|
|
+Methods defined for supertypes are inherited following MRO order::
|
|
|
+
|
|
|
+>>> class MyString(str):
|
|
|
+... """String subclass"""
|
|
|
+
|
|
|
+>>> move(MyString("ladies"), "drinks")
|
|
|
+How are you ladies!!
|
|
|
+All your drinks are belong to us.
|
|
|
+
|
|
|
+Classic class instances are also supported (although the lookup process
|
|
|
+is slower than for new-style instances)::
|
|
|
+
|
|
|
+>>> class X: pass
|
|
|
+>>> class Y(X): pass
|
|
|
+
|
|
|
+>>> @move.when_type(X)
|
|
|
+... def move_x(item, target):
|
|
|
+... print("Someone set us up the %s!!!" % (target,))
|
|
|
+
|
|
|
+>>> move(X(), "bomb")
|
|
|
+Someone set us up the bomb!!!
|
|
|
+
|
|
|
+>>> move(Y(), "dance")
|
|
|
+Someone set us up the dance!!!
|
|
|
+
|
|
|
+
|
|
|
+Multiple Types or Objects
|
|
|
+-------------------------
|
|
|
+
|
|
|
+As a convenience, you can now pass more than one type or object to the
|
|
|
+registration methods::
|
|
|
+
|
|
|
+>>> @generic
|
|
|
+... def isbuiltin(ob):
|
|
|
+... return False
|
|
|
+>>> @isbuiltin.when_type(int, str, float, complex, type)
|
|
|
+... @isbuiltin.when_object(None, Ellipsis)
|
|
|
+... def yes(ob):
|
|
|
+... return True
|
|
|
+
|
|
|
+>>> isbuiltin(1)
|
|
|
+True
|
|
|
+>>> isbuiltin(object)
|
|
|
+True
|
|
|
+>>> isbuiltin(object())
|
|
|
+False
|
|
|
+>>> isbuiltin(X())
|
|
|
+False
|
|
|
+>>> isbuiltin(None)
|
|
|
+True
|
|
|
+>>> isbuiltin(Ellipsis)
|
|
|
+True
|
|
|
+
|
|
|
+
|
|
|
+Defaults and Docs
|
|
|
+-----------------
|
|
|
+
|
|
|
+You can obtain a function's default implementation using its ``default``
|
|
|
+attribute::
|
|
|
+
|
|
|
+>>> @move.when_type(Y)
|
|
|
+... def move_y(item, target):
|
|
|
+... print("Someone set us up the %s!!!" % (target,))
|
|
|
+... move.default(item, target)
|
|
|
+
|
|
|
+>>> move(Y(), "dance")
|
|
|
+Someone set us up the dance!!!
|
|
|
+what you say?!
|
|
|
+
|
|
|
+
|
|
|
+``help()`` and other documentation tools see generic functions as normal
|
|
|
+function objects, with the same name, attributes, docstring, and module as
|
|
|
+the prototype/default function::
|
|
|
+
|
|
|
+>>> help(move)
|
|
|
+Help on function move:
|
|
|
+...
|
|
|
+move(*args, **kw)
|
|
|
+Default implementation goes here
|
|
|
+...
|
|
|
+
|
|
|
+
|
|
|
+Inspection and Extension
|
|
|
+------------------------
|
|
|
+
|
|
|
+You can find out if a generic function has a method for a type or object using
|
|
|
+the ``has_object()`` and ``has_type()`` methods::
|
|
|
+
|
|
|
+>>> move.has_object(zig)
|
|
|
+True
|
|
|
+>>> move.has_object(42)
|
|
|
+False
|
|
|
+
|
|
|
+>>> move.has_type(X)
|
|
|
+True
|
|
|
+>>> move.has_type(float)
|
|
|
+False
|
|
|
+
|
|
|
+Note that ``has_type()`` only queries whether there is a method registered for
|
|
|
+the *exact* type, not subtypes or supertypes::
|
|
|
+
|
|
|
+>>> class Z(X): pass
|
|
|
+>>> move.has_type(Z)
|
|
|
+False
|
|
|
+
|
|
|
+You can create a generic function that "inherits" from an existing generic
|
|
|
+function by calling ``generic()`` on the existing function::
|
|
|
+
|
|
|
+>>> move2 = generic(move)
|
|
|
+>>> move(2101, "war")
|
|
|
+In AD 2101, war was beginning.
|
|
|
+
|
|
|
+Any methods added to the new generic function override *all* methods in the
|
|
|
+"base" function::
|
|
|
+
|
|
|
+>>> @move2.when_type(X)
|
|
|
+... def move2_X(item, target):
|
|
|
+... print("You have no chance to survive make your %s!" % (target,))
|
|
|
+
|
|
|
+>>> move2(X(), "time")
|
|
|
+You have no chance to survive make your time!
|
|
|
+
|
|
|
+>>> move2(Y(), "time")
|
|
|
+You have no chance to survive make your time!
|
|
|
+
|
|
|
+Notice that even though ``move()`` has a method for type ``Y``, the method
|
|
|
+defined for ``X`` in ``move2()`` takes precedence. This is because the
|
|
|
+``move`` function is used as the ``default`` method of ``move2``, and ``move2``
|
|
|
+has no method for type ``Y``::
|
|
|
+
|
|
|
+>>> move2.default is move
|
|
|
+True
|
|
|
+>>> move.has_type(Y)
|
|
|
+True
|
|
|
+>>> move2.has_type(Y)
|
|
|
+False
|
|
|
+
|
|
|
+
|
|
|
+Limitations
|
|
|
+-----------
|
|
|
+
|
|
|
+* The first argument is always used for dispatching, and it must always be
|
|
|
+passed *positionally* when the function is called.
|
|
|
+
|
|
|
+* Documentation tools don't see the function's original argument signature, so
|
|
|
+you have to describe it in the docstring.
|
|
|
+
|
|
|
+* If you have optional arguments, you must duplicate them on every method in
|
|
|
+order for them to work correctly. (On the plus side, it means you can have
|
|
|
+different defaults or required arguments for each method, although relying on
|
|
|
+that quirk probably isn't a good idea.)
|
|
|
+
|
|
|
+These restrictions may be lifted in later releases, if I feel the need. They
|
|
|
+would require runtime code generation the way I do it in ``RuleDispatch``,
|
|
|
+however, which is somewhat of a pain. (Alternately I could use the
|
|
|
+``BytecodeAssembler`` package to do the code generation, as that's a lot easier
|
|
|
+to use than string-based code generation, but that would introduce more
|
|
|
+dependencies, and I'm trying to keep this simple so I can just
|
|
|
+toss it into Chandler without a big footprint increase.)
|
|
|
+
|
|
|
+.. _excellent alternative to the Visitor pattern: http://peak.telecommunity.com/DevCenter/VisitorRevisited
|
|
|
+
|
|
|
+
|
|
|
+
|