summaryrefslogtreecommitdiff
path: root/PKG-INFO
blob: 38b280c80976970d90f324bb2ba2a28757b8a665 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
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