Merge lp:~barry/flufl.enum/lp1162375 into lp:~barry/flufl.enum/trunk
- lp1162375
- Merge into trunk
Proposed by
Barry Warsaw
Status: | Merged |
---|---|
Merged at revision: | 76 |
Proposed branch: | lp:~barry/flufl.enum/lp1162375 |
Merge into: | lp:~barry/flufl.enum/trunk |
Diff against target: |
423 lines (+91/-109) 7 files modified
README.rst (+3/-3) flufl/enum/NEWS.rst (+2/-1) flufl/enum/README.rst (+12/-8) flufl/enum/__init__.py (+1/-2) flufl/enum/_enum.py (+12/-13) flufl/enum/docs/using.rst (+33/-50) flufl/enum/tests/test_enum.py (+28/-32) |
To merge this branch: | bzr merge lp:~barry/flufl.enum/lp1162375 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Barry Warsaw | Pending | ||
Review via email:
|
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'README.rst' |
2 | --- README.rst 2012-04-19 21:55:28 +0000 |
3 | +++ README.rst 2013-03-31 04:42:19 +0000 |
4 | @@ -4,9 +4,9 @@ |
5 | |
6 | A Python enumeration package. |
7 | |
8 | -The ``flufl.enum`` library is yet another Python enumeration package. Its |
9 | -goal is to provide simple, specific, concise semantics in an easy to read and |
10 | -write syntax. ``flufl.enum`` has just enough of the features needed to make |
11 | +The ``flufl.enum`` library is a Python enumeration package. Its goal is to |
12 | +provide simple, specific, concise semantics in an easy to read and write |
13 | +syntax. ``flufl.enum`` has just enough of the features needed to make |
14 | enumerations useful, but without a lot of extra baggage to weigh them down. |
15 | This work grew out of the Mailman 3.0 project. |
16 | |
17 | |
18 | === modified file 'flufl/enum/NEWS.rst' |
19 | --- flufl/enum/NEWS.rst 2013-03-30 00:04:40 +0000 |
20 | +++ flufl/enum/NEWS.rst 2013-03-31 04:42:19 +0000 |
21 | @@ -10,7 +10,8 @@ |
22 | * Add ``IntEnum`` class which returns int-subclass enum values. (LP: #1132976) |
23 | - Add ``__index__()`` method to support slicing. (LP: #1132972) |
24 | - Add non-deprecated ``__int__()`` method. |
25 | - - Add `make_int()` convenience function. |
26 | + * Deprecate ``make()``; use ``Enum()`` instead. Also, allow for calling |
27 | + ``IntEnum()`` to create integer valued enums. (LP: #1162375) |
28 | * Add ``.value`` attribute to enum values. (LP: #1132859) |
29 | * For ``__getitem__()`` and ``__call__()``, fall back to using the ``.value`` |
30 | attribute if the argument has one. (LP: #1124596) |
31 | |
32 | === modified file 'flufl/enum/README.rst' |
33 | --- flufl/enum/README.rst 2013-03-01 23:31:57 +0000 |
34 | +++ flufl/enum/README.rst 2013-03-31 04:42:19 +0000 |
35 | @@ -2,30 +2,32 @@ |
36 | flufl.enum - A Python enumeration package |
37 | ========================================= |
38 | |
39 | -This package is called ``flufl.enum``. It is yet another Python enumeration |
40 | -package, but with a slightly different take on syntax and semantics than |
41 | -earlier such packages. |
42 | +This package is called ``flufl.enum``, a Python enumeration package. |
43 | |
44 | The goals of ``flufl.enum`` are to produce simple, specific, concise semantics |
45 | in an easy to read and write syntax. ``flufl.enum`` has just enough of the |
46 | features needed to make enumerations useful, but without a lot of extra |
47 | baggage to weigh them down. This work grew out of the Mailman 3.0 project and |
48 | -it is the enum package used there. Until version 3.0, this package was called |
49 | +it is the enum package used there. This package was previously called |
50 | ``munepy``. |
51 | |
52 | +``flufl.enum`` is an implementation of the standard library enumeration |
53 | +package described in `PEP 435`_ for `Python 3.4`_. It is available as a |
54 | +separate package for use in older Python versions. |
55 | + |
56 | |
57 | Requirements |
58 | ============ |
59 | |
60 | -``flufl.enum`` requires Python 2.6 or newer, and is compatible with Python 3. |
61 | +``flufl.enum`` requires Python 2.7 or newer, and is compatible with Python 3.2 |
62 | +and later. |
63 | |
64 | |
65 | Documentation |
66 | ============= |
67 | |
68 | -A `simple guide`_ to using the library is available within this package, in |
69 | -the form of doctests. The manual is also available online in the Cheeseshop |
70 | -at: |
71 | +A `simple guide`_ to using the library is available within this package. The |
72 | +manual is also available online at: |
73 | |
74 | http://package.python.org/flufl.enum |
75 | |
76 | @@ -95,3 +97,5 @@ |
77 | |
78 | .. _`simple guide`: docs/using.html |
79 | .. _`virtualenv`: http://www.virtualenv.org/en/latest/index.html |
80 | +.. _`PEP 435`: http://www.python.org/dev/peps/pep-0435/ |
81 | +.. _`Python 3.4`: http://www.python.org/dev/peps/pep-0429/ |
82 | |
83 | === modified file 'flufl/enum/__init__.py' |
84 | --- flufl/enum/__init__.py 2013-03-30 00:13:26 +0000 |
85 | +++ flufl/enum/__init__.py 2013-03-31 04:42:19 +0000 |
86 | @@ -24,10 +24,9 @@ |
87 | 'IntEnum', |
88 | '__version__', |
89 | 'make', |
90 | - 'make_int', |
91 | ] |
92 | |
93 | |
94 | __version__ = '3.4' |
95 | |
96 | -from ._enum import Enum, IntEnum, make, make_int |
97 | +from ._enum import Enum, IntEnum, make |
98 | |
99 | === modified file 'flufl/enum/_enum.py' |
100 | --- flufl/enum/_enum.py 2013-03-30 00:13:26 +0000 |
101 | +++ flufl/enum/_enum.py 2013-03-31 04:42:19 +0000 |
102 | @@ -23,7 +23,6 @@ |
103 | 'Enum', |
104 | 'IntEnum', |
105 | 'make', |
106 | - 'make_int', |
107 | ] |
108 | |
109 | |
110 | @@ -113,8 +112,14 @@ |
111 | raise ValueError(item) |
112 | return getattr(cls, attr) |
113 | |
114 | - # Support both MyEnum[i] and MyEnum(i) |
115 | - __call__ = __getitem__ |
116 | + def __call__(cls, *args): |
117 | + # One-argument calling is a deprecated synonym for getitem. |
118 | + if len(args) == 1: |
119 | + warnings.warn('MyEnum(arg) is deprecated; use MyEnum[arg]', |
120 | + DeprecationWarning, 2) |
121 | + return cls.__getitem__(args[0]) |
122 | + name, source = args |
123 | + return _make(cls, name, source) |
124 | |
125 | |
126 | |
127 | |
128 | @@ -176,7 +181,6 @@ |
129 | __hash__ = object.__hash__ |
130 | |
131 | |
132 | - |
133 | |
134 | # Define the Enum class using metaclass syntax compatible with both Python 2 |
135 | # and Python 3. |
136 | Enum = EnumMetaclass(str('Enum'), (), { |
137 | @@ -184,6 +188,7 @@ |
138 | }) |
139 | |
140 | |
141 | + |
142 | |
143 | class IntEnumValue(int, EnumValue): |
144 | """An EnumValue that is also an integer.""" |
145 | |
146 | @@ -217,7 +222,7 @@ |
147 | |
148 | |
149 | |
150 | def _make(enum_class, name, source): |
151 | - """The common implementation for `make()` and `make_int()`.""" |
152 | + """The common implementation for `Enum()` and `IntEnum()`.""" |
153 | namespace = {} |
154 | illegals = [] |
155 | have_strings = None |
156 | @@ -272,12 +277,6 @@ |
157 | :raises ValueError: when a heterogeneous source is given, or when |
158 | non-identifiers are used as enumeration value names. |
159 | """ |
160 | + warnings.warn('make() is deprecated; use Enum(name, source)', |
161 | + DeprecationWarning, 2) |
162 | return _make(Enum, name, source) |
163 | - |
164 | -def make_int(name, source): |
165 | - """Return an Enum class from a name and a source. |
166 | - |
167 | - This is exactly like the `make()` function, except that the enum values of |
168 | - the returned enum are integer subclasses. |
169 | - """ |
170 | - return _make(IntEnum, name, source) |
171 | |
172 | === modified file 'flufl/enum/docs/using.rst' |
173 | --- flufl/enum/docs/using.rst 2013-03-29 23:55:51 +0000 |
174 | +++ flufl/enum/docs/using.rst 2013-03-31 04:42:19 +0000 |
175 | @@ -11,8 +11,8 @@ |
176 | Within an enumeration, the values can be compared by identity, and the |
177 | enumeration itself can be iterated over. The underlying values can be |
178 | retrieved from the enumeration items. An integer based variant is provided |
179 | -which allows items to be used as slices, interoperability with |
180 | -integer-accepting C-based APIs, and for logical operations. |
181 | +which allows items to be used as slices, to interoperate with C-based APIs, |
182 | +and for logical operations. |
183 | |
184 | |
185 | Motivation |
186 | @@ -92,16 +92,13 @@ |
187 | Convenience API |
188 | --------------- |
189 | |
190 | -Alternatively, you can create an enumeration using the `make()` convenience |
191 | -function, which takes an iterable object or dictionary providing the item |
192 | -names and values. The first argument to `make()` is the name of the |
193 | -enumeration. |
194 | - |
195 | -If a sequence of identifiers is given, the values are auto-assigned integers |
196 | +Alternatively, you can create an enumeration functionally using the calling |
197 | +syntax on the `Enum` class. The first argument is the name of the new |
198 | +enumeration, and the second argument is a sequence of strings, giving the |
199 | +names of the enumeration values. The values are auto-assigned integers |
200 | starting from 1. |
201 | |
202 | - >>> from flufl.enum import make |
203 | - >>> Rush = make('Rush', 'geddy alex neil'.split()) |
204 | + >>> Rush = Enum('Rush', 'geddy alex neil'.split()) |
205 | |
206 | The ``str`` and ``repr`` provide details. |
207 | |
208 | @@ -110,7 +107,7 @@ |
209 | >>> print(repr(Rush.geddy)) |
210 | <EnumValue: Rush.geddy [value=1]> |
211 | |
212 | -See the section on the `Functional API`_ for more information. |
213 | +See the section on the `Functional API`_ for more options and information. |
214 | |
215 | |
216 | Values |
217 | @@ -316,39 +313,27 @@ |
218 | Conversions |
219 | =========== |
220 | |
221 | -You can convert back to the enumeration item by calling the ``Enum`` class, |
222 | -passing in the value for the item you want. |
223 | +You can convert back to the enumeration item by using the ``Enum`` class's |
224 | +``getitem`` syntax, passing in the value for the item you want. |
225 | |
226 | - >>> Colors(2) |
227 | + >>> Colors[2] |
228 | <EnumValue: Colors.green [value=2]> |
229 | - >>> Rush('bass') |
230 | + >>> Rush['bass'] |
231 | <EnumValue: Rush.geddy [value=bass]> |
232 | - >>> Colors(1) is Colors.red |
233 | + >>> Colors[1] is Colors.red |
234 | True |
235 | |
236 | The ``Enum`` class also accepts the string name of the enumeration value. |
237 | |
238 | - >>> Colors('red') |
239 | - <EnumValue: Colors.red [value=1]> |
240 | - >>> Rush('alex') |
241 | - <EnumValue: Rush.alex [value=guitar]> |
242 | - >>> Colors('blue') is Colors.blue |
243 | - True |
244 | - |
245 | -The ``Enum`` base class also supports ``getitem`` syntax, exactly equivalent |
246 | -to the class's call semantics. |
247 | - |
248 | - >>> Colors[1] |
249 | - <EnumValue: Colors.red [value=1]> |
250 | - >>> Colors[1] is Colors.red |
251 | - True |
252 | >>> Colors['red'] |
253 | <EnumValue: Colors.red [value=1]> |
254 | - |
255 | -For consistency, both the call and getitem syntax accept an enum value. |
256 | - |
257 | - >>> Colors(Colors.green) |
258 | - <EnumValue: Colors.green [value=2]> |
259 | + >>> Rush['alex'] |
260 | + <EnumValue: Rush.alex [value=guitar]> |
261 | + >>> Colors['blue'] is Colors.blue |
262 | + True |
263 | + |
264 | +For consistency, ``getitem`` syntax accept an enum value. |
265 | + |
266 | >>> Colors[Colors.green] |
267 | <EnumValue: Colors.green [value=2]> |
268 | |
269 | @@ -417,18 +402,17 @@ |
270 | Functional API |
271 | ============== |
272 | |
273 | -As described above, you can create enumerations using the convenience function |
274 | -`make()`, which takes an iterable object or dictionary to provide the item |
275 | -names and values. |
276 | +As described above, you can create enumerations functionally using the calling |
277 | +syntax for ``Enum`` and ``IntEnum``. |
278 | |
279 | -The first argument to `make()` is the name of the enumeration, and it returns |
280 | -the so-named ``Enum`` subclass. The second argument is a `source` which can |
281 | -be either an iterable or a dictionary. In the most basic usage, `source` |
282 | -returns a sequence of strings which name the enumeration items. In this case, |
283 | -the values are automatically assigned starting from 1. |
284 | +The first argument is always the name of the enumeration, and it returns the |
285 | +so-named ``Enum`` (or ``IntEnum``) subclass. The second argument is a |
286 | +`source` which can be either a sequence of strings or 2-tuples. In the most |
287 | +basic usage, `source` is a sequence of strings which name the enumeration |
288 | +items. In this case, the values are automatically assigned starting from 1. |
289 | :: |
290 | |
291 | - >>> make('Animals', 'ant bee cat dog'.split()) |
292 | + >>> Enum('Animals', 'ant bee cat dog'.split()) |
293 | <Animals {ant: 1, bee: 2, cat: 3, dog: 4}> |
294 | |
295 | The items in source can also be 2-tuples, where the first item is the |
296 | @@ -440,15 +424,14 @@ |
297 | ... while True: |
298 | ... yield start |
299 | ... start <<= 1 |
300 | - >>> make('Flags', zip(list('abcdefg'), enumiter())) |
301 | + >>> Enum('Flags', zip(list('abcdefg'), enumiter())) |
302 | <Flags {a: 1, b: 2, c: 4, d: 8, e: 16, f: 32, g: 64}> |
303 | |
304 | -If you want to create ``IntEnum``s, use the `make_int()` function. It has the |
305 | -same signature as `make()` but the items of the returned enum are int |
306 | -subclasses. |
307 | +If you want to create an ``IntEnum`` functionally, call that class instead. |
308 | +This has the same signature as calling `Enum` but the items of the returned |
309 | +enumeration are int subclasses. |
310 | |
311 | - >>> from flufl.enum import make_int |
312 | - >>> Numbers = make_int('Numbers', 'one two three four'.split()) |
313 | + >>> Numbers = IntEnum('Numbers', 'one two three four'.split()) |
314 | >>> Numbers.three == 3 |
315 | True |
316 | |
317 | |
318 | === modified file 'flufl/enum/tests/test_enum.py' |
319 | --- flufl/enum/tests/test_enum.py 2013-03-30 00:13:26 +0000 |
320 | +++ flufl/enum/tests/test_enum.py 2013-03-31 04:42:19 +0000 |
321 | @@ -26,9 +26,10 @@ |
322 | ] |
323 | |
324 | |
325 | +import warnings |
326 | import unittest |
327 | |
328 | -from flufl.enum import Enum, IntEnum, make, make_int |
329 | +from flufl.enum import Enum, IntEnum, make |
330 | from operator import index |
331 | |
332 | |
333 | @@ -36,16 +37,6 @@ |
334 | class TestEnum(unittest.TestCase): |
335 | """Additional unit tests.""" |
336 | |
337 | - def test_invalid_call_arguments(self): |
338 | - # Calling an Enum with invalid value raises an exception. |
339 | - class Colors(Enum): |
340 | - red = 1 |
341 | - green = 2 |
342 | - blue = 3 |
343 | - with self.assertRaises(ValueError) as cm: |
344 | - Colors('magenta') |
345 | - self.assertEqual(cm.exception.args, ('magenta',)) |
346 | - |
347 | def test_invalid_getitem_arguments(self): |
348 | # getitem on an Enum with invalid value raises an exception. |
349 | class Colors(Enum): |
350 | @@ -77,41 +68,46 @@ |
351 | magenta = 2 # Oops! |
352 | self.assertEqual(str(cm.exception), 'Multiple enum values: 2') |
353 | |
354 | - def test_enum_make_not_all_2_tuples(self): |
355 | + def test_functional_api_not_all_2_tuples(self): |
356 | # If 2-tuples are used, all items must be 2-tuples. |
357 | - self.assertRaises(ValueError, make, 'Animals', ( |
358 | + self.assertRaises(ValueError, Enum, 'Animals', ( |
359 | ('ant', 1), |
360 | ('bee', 2), |
361 | 'cat', |
362 | ('dog', 4), |
363 | )) |
364 | - self.assertRaises(ValueError, make, 'Animals', ( |
365 | + self.assertRaises(ValueError, Enum, 'Animals', ( |
366 | ('ant', 1), |
367 | ('bee', 2), |
368 | ('cat',), |
369 | ('dog', 4), |
370 | )) |
371 | - self.assertRaises(ValueError, make, 'Animals', ( |
372 | + self.assertRaises(ValueError, Enum, 'Animals', ( |
373 | ('ant', 1), |
374 | ('bee', 2), |
375 | ('cat', 3, 'oops'), |
376 | ('dog', 4), |
377 | )) |
378 | |
379 | - def test_make_identifiers(self): |
380 | - # Ensure that the make() interface also enforces identifiers. |
381 | - try: |
382 | - make('Foo', ('1', '2', '3')) |
383 | - except ValueError as exc: |
384 | - self.assertEqual(exc.args[0], 'non-identifiers: 1 2 3') |
385 | - else: |
386 | - raise AssertionError('Expected a ValueError') |
387 | - try: |
388 | - make('Foo', (('ant', 1), ('bee', 2), ('3', 'cat'))) |
389 | - except ValueError as exc: |
390 | - self.assertEqual(exc.args[0], 'non-identifiers: 3') |
391 | - else: |
392 | - raise AssertionError('Expected a ValueError') |
393 | + def test_functional_api_identifiers(self): |
394 | + # Ensure that the functional API enforces identifiers. |
395 | + with self.assertRaises(ValueError) as cm: |
396 | + Enum('Foo', ('1', '2', '3')) |
397 | + self.assertEqual(cm.exception.args[0], 'non-identifiers: 1 2 3') |
398 | + with self.assertRaises(ValueError) as cm: |
399 | + Enum('Foo', (('ant', 1), ('bee', 2), ('3', 'cat'))) |
400 | + self.assertEqual(cm.exception.args[0], 'non-identifiers: 3') |
401 | + |
402 | + def test_deprecate_make(self): |
403 | + # LP: #1162375 -- use Enum() calling syntax instead. |
404 | + with warnings.catch_warnings(record=True) as seen: |
405 | + # In Python 3.3+ we can use self.assertWarns() |
406 | + warnings.simplefilter('always') |
407 | + Animals = make('Animals', 'ant bee cat'.split()) |
408 | + self.assertEqual(len(seen), 1) |
409 | + self.assertEqual(seen[0].category, DeprecationWarning) |
410 | + # We don't need to assert the deprecation message. |
411 | + self.assertEqual(Animals.ant.value, 1) |
412 | |
413 | |
414 | |
415 | |
416 | @@ -134,9 +130,9 @@ |
417 | # are equal. |
418 | self.assertEqual(index(Animals.bee), Animals.bee.__index__()) |
419 | |
420 | - def test_make_int_type(self): |
421 | - # make_int() enum values are ints. |
422 | - Toppings = make_int( |
423 | + def test_int_enums_type(self): |
424 | + # IntEnum() enum values are ints. |
425 | + Toppings = IntEnum( |
426 | 'Toppings', |
427 | dict(olives=1, onions=2, mushrooms=4, cheese=8, garlic=16).items()) |
428 | self.assertEqual(Toppings.garlic, 16) |