summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2012-04-01 15:38:32 +0000
committer <>2014-10-07 10:28:45 +0000
commit601fab9b154c557dfd848d531a8969f4697d1aa2 (patch)
tree542bc5a53bf8a907fbf1c656807c0f788f56a940
downloadsimplegeneric-tarball-601fab9b154c557dfd848d531a8969f4697d1aa2.tar.gz
Imported from /home/lorry/working-area/delta_python-packages_simplegeneric-tarball/simplegeneric_0.8.1.orig.tar.bz2.HEADsimplegeneric_0.8.1.origmaster
-rw-r--r--PKG-INFO279
-rw-r--r--README.txt260
-rw-r--r--classifiers.txt14
-rw-r--r--setup.cfg5
-rw-r--r--setup.py41
-rw-r--r--simplegeneric.egg-info/PKG-INFO279
-rw-r--r--simplegeneric.egg-info/SOURCES.txt9
-rw-r--r--simplegeneric.egg-info/dependency_links.txt1
-rw-r--r--simplegeneric.egg-info/top_level.txt1
-rw-r--r--simplegeneric.py123
10 files changed, 1012 insertions, 0 deletions
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: <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
+
+
+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: <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
+
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: <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
+
+
+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())
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+