From 601fab9b154c557dfd848d531a8969f4697d1aa2 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Sun, 1 Apr 2012 15:38:32 +0000 Subject: Imported from /home/lorry/working-area/delta_python-packages_simplegeneric-tarball/simplegeneric_0.8.1.orig.tar.bz2. --- PKG-INFO | 279 ++++++++++++++++++++++++++++ README.txt | 260 ++++++++++++++++++++++++++ classifiers.txt | 14 ++ setup.cfg | 5 + setup.py | 41 ++++ simplegeneric.egg-info/PKG-INFO | 279 ++++++++++++++++++++++++++++ simplegeneric.egg-info/SOURCES.txt | 9 + simplegeneric.egg-info/dependency_links.txt | 1 + simplegeneric.egg-info/top_level.txt | 1 + simplegeneric.py | 123 ++++++++++++ 10 files changed, 1012 insertions(+) create mode 100644 PKG-INFO create mode 100644 README.txt create mode 100644 classifiers.txt create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 simplegeneric.egg-info/PKG-INFO create mode 100644 simplegeneric.egg-info/SOURCES.txt create mode 100644 simplegeneric.egg-info/dependency_links.txt create mode 100644 simplegeneric.egg-info/top_level.txt create mode 100644 simplegeneric.py diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..38b280c --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,279 @@ +Metadata-Version: 1.0 +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 +Description: * 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: already has method for type <...'str'> + + >>> @move.when_object(zig) + ... def this_is_wrong(item, target): pass + Traceback (most recent call last): + ... + TypeError: already has method for 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 + + +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 diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..b11459f --- /dev/null +++ b/README.txt @@ -0,0 +1,260 @@ +========================= +Trivial Generic Functions +========================= + +* 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: already has method for type <...'str'> + + >>> @move.when_object(zig) + ... def this_is_wrong(item, target): pass + Traceback (most recent call last): + ... + TypeError: already has method for 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 + diff --git a/classifiers.txt b/classifiers.txt new file mode 100644 index 0000000..1636f70 --- /dev/null +++ b/classifiers.txt @@ -0,0 +1,14 @@ +Development Status :: 6 - Mature +Development Status :: 7 - Inactive +Intended Audience :: Developers +License :: OSI Approved :: Zope Public License +Programming Language :: Python +Programming Language :: Python :: 2 +Programming Language :: Python :: 2.4 +Programming Language :: Python :: 2.5 +Programming Language :: Python :: 2.6 +Programming Language :: Python :: 2.7 +Programming Language :: Python :: 3 +Operating System :: OS Independent +Topic :: Software Development :: Libraries :: Python Modules + diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..b14b0bc --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0fd9747 --- /dev/null +++ b/setup.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +"""Distutils setup file""" +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + +# Metadata +PACKAGE_NAME = "simplegeneric" +PACKAGE_VERSION = "0.8.1" + +def get_description(): + # Get our long description from the documentation + f = open('README.txt') + lines = [] + for line in f: + if not line.strip(): + break # skip to first blank line + for line in f: + if line.startswith('.. contents::'): + break # read to table of contents + lines.append(line) + f.close() + return ''.join(lines) + +setup( + name=PACKAGE_NAME, + version=PACKAGE_VERSION, + description= "Simple generic functions (similar to Python's own len(), " + "pickle.dump(), etc.)", + long_description = get_description(), + url = "http://cheeseshop.python.org/pypi/simplegeneric", + author="Phillip J. Eby", + author_email="peak@eby-sarna.com", + license="ZPL 2.1", + test_suite = 'simplegeneric.test_suite', + py_modules = ['simplegeneric'], + classifiers = [ + line.strip() for line in open('classifiers.txt') if line.strip() + ], +) diff --git a/simplegeneric.egg-info/PKG-INFO b/simplegeneric.egg-info/PKG-INFO new file mode 100644 index 0000000..38b280c --- /dev/null +++ b/simplegeneric.egg-info/PKG-INFO @@ -0,0 +1,279 @@ +Metadata-Version: 1.0 +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 +Description: * 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: already has method for type <...'str'> + + >>> @move.when_object(zig) + ... def this_is_wrong(item, target): pass + Traceback (most recent call last): + ... + TypeError: already has method for 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 + + +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 diff --git a/simplegeneric.egg-info/SOURCES.txt b/simplegeneric.egg-info/SOURCES.txt new file mode 100644 index 0000000..2b3e36a --- /dev/null +++ b/simplegeneric.egg-info/SOURCES.txt @@ -0,0 +1,9 @@ +README.txt +classifiers.txt +setup.cfg +setup.py +simplegeneric.py +simplegeneric.egg-info/PKG-INFO +simplegeneric.egg-info/SOURCES.txt +simplegeneric.egg-info/dependency_links.txt +simplegeneric.egg-info/top_level.txt \ No newline at end of file diff --git a/simplegeneric.egg-info/dependency_links.txt b/simplegeneric.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/simplegeneric.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/simplegeneric.egg-info/top_level.txt b/simplegeneric.egg-info/top_level.txt new file mode 100644 index 0000000..ec4d598 --- /dev/null +++ b/simplegeneric.egg-info/top_level.txt @@ -0,0 +1 @@ +simplegeneric diff --git a/simplegeneric.py b/simplegeneric.py new file mode 100644 index 0000000..bd12c14 --- /dev/null +++ b/simplegeneric.py @@ -0,0 +1,123 @@ +__all__ = ["generic"] +try: + from types import ClassType, InstanceType + classtypes = type, ClassType +except ImportError: + classtypes = type + InstanceType = None + +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) + + _by_type = {object: func, InstanceType: _by_class} + _gbt = _by_type.get + + 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): + raise TypeError( + "%r is not a type or class" % (t,) + ) + 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) + ) + return f + return decorate + + _by_object = {} + _gbo = _by_object.get + + def when_object(*obs): + """Decorator to add a method to be called for the given object(s)""" + def decorate(f): + 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) + ) + 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) + + dispatch.__name__ = func.__name__ + 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, + ) + +if __name__=='__main__': + import unittest + r = unittest.TextTestRunner() + r.run(test_suite()) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.1