Skip to content

Commit a7b4582

Browse files
committed
Add match_none argument on s() - closes #44
1 parent 755b4ac commit a7b4582

File tree

5 files changed

+134
-7
lines changed

5 files changed

+134
-7
lines changed

doc/source/changelog.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22
Change Log
33
==========
44

5+
v1.5.0
6+
======
7+
8+
There is one minimally impactful breaking change in the 1.5.0 release:
9+
10+
* Symmetric properties that are None will not map back to the enumeration value
11+
by default. To replicate the previous behavior, pass True as the `match_none`
12+
argument when instantiating the property with s().
13+
14+
The 1.5.0 release includes two feature improvements:
15+
16+
* Implemented `Configurable behavior for matching none on symmetric fields <https://github.com/bckohan/enum-properties/issues/44>`_
17+
18+
519
v1.4.0
620
======
721

doc/source/usage.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,10 @@ property symmetry. To mark a property as symmetric, use
7676
Color.RED == Color((1, 0, 0)) == Color('0xFF0000') == Color('0xff0000')
7777
7878
Symmetric string properties are by default case sensitive. To mark a property
79-
as case insensitive, use the `case_fold=True` parameter on the
80-
:py:meth:`~enum_properties.s` value.
79+
as case insensitive, use the ``case_fold=True`` parameter on the
80+
:py:meth:`~enum_properties.s` value. By default, none values for symmetric
81+
properties will not be symmetric. To change this behavior pass:
82+
``match_none=True`` to :py:meth:`~enum_properties.s`.
8183

8284
Symmetric property support is added through the
8385
:py:class:`~enum_properties.SymmetricMixin` class which is included in the

enum_properties/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
cached_property = property # pylint: disable=C0103
3232

3333

34-
VERSION = (1, 4, 0)
34+
VERSION = (1, 5, 0)
3535

3636
__title__ = 'Enum Properties'
3737
__version__ = '.'.join(str(i) for i in VERSION)
@@ -77,7 +77,8 @@ class _SProp(_Prop):
7777

7878
def s( # pylint: disable=C0103
7979
prop_name,
80-
case_fold=False
80+
case_fold=False,
81+
match_none=False
8182
):
8283
"""
8384
Add a symmetric property. Enumeration values will be coercible from this
@@ -86,12 +87,15 @@ def s( # pylint: disable=C0103
8687
:param prop_name: The name of the property
8788
:param case_fold: If False, symmetric lookup will be
8889
case sensitive (default)
90+
:param match_none: If True, none values will be symmetric, if False
91+
(default), none values for symmetric properties will not map back to
92+
the enumeration value.
8993
:return: a named symmetric property class
9094
"""
9195
return type(
9296
prop_name,
9397
(_SProp,),
94-
{'symmetric': True, 'case_fold': case_fold}
98+
{'symmetric': True, 'case_fold': case_fold, 'match_none': match_none}
9599
)
96100

97101

@@ -447,6 +451,8 @@ def __new__( # pylint: disable=W0221
447451
)
448452

449453
def add_sym_lookup(prop, p_val, enum_inst):
454+
if p_val is None and not prop.match_none:
455+
return
450456
if not isinstance(p_val, Hashable):
451457
raise ValueError(
452458
f'{cls}.{prop}:{p_val} is not hashable. Symmetrical '

enum_properties/tests/tests.py

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,72 @@ class Color(
259259
GREEN = 2, 'Verde', (0, 1, 0), '00ff00'
260260
BLUE = 3, 'Azul', (0, 0, 1), '0000ff'
261261

262+
def test_symmetric_match_none_parameter(self):
263+
264+
# test default behavior
265+
class ColorDefault(
266+
EnumProperties,
267+
p('spanish'),
268+
s('rgb'),
269+
s('hex')
270+
):
271+
272+
RED = 1, 'Roja', (1, 0, 0), 'ff0000'
273+
GREEN = 2, 'Verde', (0, 1, 0), None
274+
BLUE = 3, 'Azul', (0, 0, 1), None
275+
276+
self.assertEqual(ColorDefault.RED, 'ff0000')
277+
self.assertNotEqual(ColorDefault.GREEN, None)
278+
self.assertNotEqual(ColorDefault.BLUE, None)
279+
self.assertRaises(ValueError, ColorDefault, None)
280+
self.assertRaises(ValueError, ColorDefault, 'FF0000')
281+
self.assertEqual(ColorDefault('ff0000'), ColorDefault.RED)
282+
self.assertEqual(ColorDefault((1, 0, 0)), ColorDefault.RED)
283+
self.assertEqual(ColorDefault((0, 1, 0)), ColorDefault.GREEN)
284+
self.assertEqual(ColorDefault((0, 0, 1)), ColorDefault.BLUE)
285+
286+
class ColorNoMatchNone(
287+
EnumProperties,
288+
p('spanish'),
289+
s('rgb'),
290+
s('hex', case_fold=True, match_none=False)
291+
):
292+
293+
RED = 1, 'Roja', (1, 0, 0), 'ff0000'
294+
GREEN = 2, 'Verde', (0, 1, 0), None
295+
BLUE = 3, 'Azul', (0, 0, 1), None
296+
297+
self.assertEqual(ColorNoMatchNone.RED, 'fF0000')
298+
self.assertNotEqual(ColorNoMatchNone.GREEN, None)
299+
self.assertNotEqual(ColorNoMatchNone.BLUE, None)
300+
self.assertRaises(ValueError, ColorNoMatchNone, None)
301+
self.assertEqual(ColorNoMatchNone('Ff0000'), ColorNoMatchNone.RED)
302+
self.assertEqual(ColorNoMatchNone((1, 0, 0)), ColorNoMatchNone.RED)
303+
self.assertEqual(ColorNoMatchNone((0, 1, 0)), ColorNoMatchNone.GREEN)
304+
self.assertEqual(ColorNoMatchNone((0, 0, 1)), ColorNoMatchNone.BLUE)
305+
306+
class ColorMatchNone(
307+
EnumProperties,
308+
p('spanish'),
309+
s('rgb'),
310+
s('hex', match_none=True)
311+
):
312+
313+
RED = 1, 'Roja', (1, 0, 0), 'ff0000'
314+
GREEN = 2, 'Verde', (0, 1, 0), None
315+
BLUE = 3, 'Azul', (0, 0, 1), None
316+
317+
self.assertNotEqual(ColorMatchNone.RED, 'FF0000')
318+
self.assertEqual(ColorMatchNone.RED, 'ff0000')
319+
self.assertEqual(ColorMatchNone.GREEN, None)
320+
self.assertNotEqual(ColorMatchNone.BLUE, None)
321+
self.assertEqual(ColorMatchNone(None), ColorMatchNone.GREEN)
322+
self.assertEqual(ColorMatchNone('ff0000'), ColorMatchNone.RED)
323+
self.assertRaises(ValueError, ColorMatchNone, 'FF0000')
324+
self.assertEqual(ColorMatchNone((1, 0, 0)), ColorMatchNone.RED)
325+
self.assertEqual(ColorMatchNone((0, 1, 0)), ColorMatchNone.GREEN)
326+
self.assertEqual(ColorMatchNone((0, 0, 1)), ColorMatchNone.BLUE)
327+
262328
def test_properties_no_symmetry(self):
263329
"""
264330
Tests that absence of SymmetricMixin works but w/o symmetric
@@ -546,7 +612,7 @@ class Color(
546612
EnumProperties,
547613
p('spanish'),
548614
s('rgb'),
549-
s('hex', case_fold=True)
615+
s('hex', case_fold=True, match_none=True)
550616
):
551617
RED = 1, 'Roja', (1, 0, 0), 'ff0000'
552618
GREEN = 2, None, (0, 1, 0), '00ff00'
@@ -1676,6 +1742,26 @@ class Type3:
16761742
self.assertTrue(MyEnum.Type3.value().__class__ is MyEnum.Type3.value)
16771743

16781744

1745+
class TestGiantFlags(TestCase):
1746+
1747+
def test_over64_flags(self):
1748+
1749+
class BigFlags(IntFlagProperties, p('label')):
1750+
1751+
ONE = 2**0, 'one'
1752+
MIDDLE = 2**64, 'middle'
1753+
MIXED = ONE | MIDDLE, 'mixed'
1754+
LAST = 2**128, 'last'
1755+
1756+
self.assertEqual((BigFlags.ONE | BigFlags.LAST).value, 2**128 + 1)
1757+
self.assertEqual(
1758+
(BigFlags.MIDDLE | BigFlags.LAST).value, 2**128 + 2**64
1759+
)
1760+
self.assertEqual(
1761+
(BigFlags.MIDDLE | BigFlags.ONE).label, 'mixed'
1762+
)
1763+
1764+
16791765
class TestSpecialize(TestCase):
16801766
"""
16811767
Test the specialize decorator
@@ -1872,3 +1958,22 @@ def test_property_access_time(self):
18721958

18731959
for_loop_time = perf_counter() - for_loop_time
18741960
print('for loop time: {}'.format(for_loop_time))
1961+
1962+
def test_symmetric_mapping(self):
1963+
"""
1964+
Symmetric mapping benchmarks
1965+
1966+
v1.4.0 ISOCountry: ~3 seconds (macbook M1 Pro)
1967+
v1.4.1 ISOCountry: ~ seconds (macbook M1 Pro) (x faster)
1968+
"""
1969+
self.assertEqual(
1970+
self.ISOCountry(self.ISOCountry.US.full_name.lower()),
1971+
self.ISOCountry.US
1972+
)
1973+
from time import perf_counter
1974+
for_loop_time = perf_counter()
1975+
for i in range(1000000):
1976+
self.ISOCountry(self.ISOCountry.US.full_name.lower())
1977+
1978+
for_loop_time = perf_counter() - for_loop_time
1979+
print('for loop time: {}'.format(for_loop_time))

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "enum-properties"
3-
version = "1.4.0"
3+
version = "1.5.0"
44
description = "Add properties and method specializations to Python enumeration values with a simple declarative syntax."
55
authors = ["Brian Kohan <[email protected]>"]
66
license = "MIT"

0 commit comments

Comments
 (0)