Merge lp:~barry/flufl.enum/lp1162375 into lp:~barry/flufl.enum/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
Reviewer Review Type Date Requested Status
Barry Warsaw Pending
Review via email: mp+156290@code.launchpad.net
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)

Subscribers

People subscribed via source and target branches

to all changes: