enumerific 1.0.3__tar.gz → 1.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {enumerific-1.0.3/source/enumerific.egg-info → enumerific-1.0.5}/PKG-INFO +1 -1
- {enumerific-1.0.3 → enumerific-1.0.5}/source/enumerific/extensible.py +19 -4
- enumerific-1.0.5/source/enumerific/version.txt +1 -0
- {enumerific-1.0.3 → enumerific-1.0.5/source/enumerific.egg-info}/PKG-INFO +1 -1
- {enumerific-1.0.3 → enumerific-1.0.5}/tests/test_extensible_enums.py +62 -8
- enumerific-1.0.3/source/enumerific/version.txt +0 -1
- {enumerific-1.0.3 → enumerific-1.0.5}/LICENSE.md +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/README.md +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/pyproject.toml +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/requirements.development.txt +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/requirements.distribution.txt +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/requirements.txt +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/setup.cfg +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/source/enumerific/__init__.py +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/source/enumerific/exceptions.py +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/source/enumerific/logging.py +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/source/enumerific/standard.py +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/source/enumerific.egg-info/SOURCES.txt +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/source/enumerific.egg-info/dependency_links.txt +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/source/enumerific.egg-info/requires.txt +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/source/enumerific.egg-info/top_level.txt +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/source/enumerific.egg-info/zip-safe +0 -0
- {enumerific-1.0.3 → enumerific-1.0.5}/tests/test_enumerific_library.py +0 -0
|
@@ -1195,7 +1195,7 @@ class EnumerationMetaClass(type):
|
|
|
1195
1195
|
|
|
1196
1196
|
logger.debug("+" * 100)
|
|
1197
1197
|
|
|
1198
|
-
def __getattr__(self, name) -> object:
|
|
1198
|
+
def __getattr__(self, name: str) -> object:
|
|
1199
1199
|
# logger.debug("%s.__getattr__(name: %s)", self.__class__.__name__, name)
|
|
1200
1200
|
|
|
1201
1201
|
if name.startswith("_") or name in self._special:
|
|
@@ -1651,7 +1651,7 @@ class Enumeration(metaclass=EnumerationMetaClass):
|
|
|
1651
1651
|
)
|
|
1652
1652
|
|
|
1653
1653
|
if annotations is None:
|
|
1654
|
-
|
|
1654
|
+
self._annotations = anno(value=self.value)
|
|
1655
1655
|
elif isinstance(annotations, anno):
|
|
1656
1656
|
self._annotations = annotations
|
|
1657
1657
|
else:
|
|
@@ -1704,10 +1704,21 @@ class Enumeration(metaclass=EnumerationMetaClass):
|
|
|
1704
1704
|
return object.__getattribute__(self, name)
|
|
1705
1705
|
elif self._enumerations and name in self._enumerations:
|
|
1706
1706
|
return self._enumerations[name]
|
|
1707
|
+
elif self._context and name in dir(self._context):
|
|
1708
|
+
# Handle class methods, instance methods and properties here; because we are
|
|
1709
|
+
# performing some special handling for enumerations, we need to reintroduce
|
|
1710
|
+
# the necessary context to the methods here via the descriptor protocol so
|
|
1711
|
+
# the methods and properties work as expected:
|
|
1712
|
+
if callable(attribute := object.__getattribute__(self._context, name)):
|
|
1713
|
+
return attribute.__get__(self)
|
|
1714
|
+
elif isinstance(attribute, property):
|
|
1715
|
+
return attribute.__get__(self)
|
|
1716
|
+
elif isinstance(attribute, classmethod):
|
|
1717
|
+
return attribute.__get__(self)
|
|
1718
|
+
else:
|
|
1719
|
+
return attribute
|
|
1707
1720
|
elif self._annotations and name in self._annotations:
|
|
1708
1721
|
return self._annotations[name]
|
|
1709
|
-
elif self._context and name in dir(self._context):
|
|
1710
|
-
return object.__getattribute__(self._context, name)
|
|
1711
1722
|
else:
|
|
1712
1723
|
# EnumerationOptionError subclasses AttributeError so we adhere to convention
|
|
1713
1724
|
raise EnumerationOptionError(
|
|
@@ -1742,6 +1753,10 @@ class Enumeration(metaclass=EnumerationMetaClass):
|
|
|
1742
1753
|
def value(self) -> object:
|
|
1743
1754
|
return self._value
|
|
1744
1755
|
|
|
1756
|
+
@property
|
|
1757
|
+
def annotations(self) -> anno:
|
|
1758
|
+
return self._annotations
|
|
1759
|
+
|
|
1745
1760
|
@property
|
|
1746
1761
|
def aliased(self) -> bool:
|
|
1747
1762
|
logger.debug(
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.5
|
|
@@ -1583,7 +1583,7 @@ def test_membership_in_tuple():
|
|
|
1583
1583
|
def test_attribute_access():
|
|
1584
1584
|
"""Test access to attributes (methods, properties, etc) on an Enumeration subclass"""
|
|
1585
1585
|
|
|
1586
|
-
class Colors(Enumeration):
|
|
1586
|
+
class Colors(Enumeration, backfill=True):
|
|
1587
1587
|
"""Create a test Color enumeration based on the Enumeration class"""
|
|
1588
1588
|
|
|
1589
1589
|
RED = auto(RGB=(255, 0, 0))
|
|
@@ -1661,7 +1661,7 @@ def test_attribute_access():
|
|
|
1661
1661
|
# Create an enumeration subclass of the Colors enumeration, inheriting its options
|
|
1662
1662
|
# and attributes, and adding a new GOLD option for testing:
|
|
1663
1663
|
class MoreColors(Colors):
|
|
1664
|
-
GOLD = auto(RGB=(255, 215, 0))
|
|
1664
|
+
GOLD = auto(RGB=(255, 215, 0), metallic=True)
|
|
1665
1665
|
|
|
1666
1666
|
# Ensure that the MoreColors enumeration subclass is of the expected type
|
|
1667
1667
|
assert issubclass(MoreColors, Enumeration)
|
|
@@ -1670,8 +1670,10 @@ def test_attribute_access():
|
|
|
1670
1670
|
# Ensure that the MoreColors enumeration subclass has the expected number of options
|
|
1671
1671
|
assert len(MoreColors) == 7
|
|
1672
1672
|
|
|
1673
|
-
# Ensure that the Colors enumeration superclass has the expected number of options
|
|
1674
|
-
|
|
1673
|
+
# Ensure that the Colors enumeration superclass has the expected number of options,
|
|
1674
|
+
# which because we enabled backfilling, so that it gains any enumeration options
|
|
1675
|
+
# added to any of its subclasses, should be 7 at this point:
|
|
1676
|
+
assert len(Colors) == 7
|
|
1675
1677
|
|
|
1676
1678
|
# Ensure that the MoreColors enumeration subclass has the expected options
|
|
1677
1679
|
assert MoreColors.RED in Colors
|
|
@@ -1682,9 +1684,9 @@ def test_attribute_access():
|
|
|
1682
1684
|
assert MoreColors.VIOLET in Colors
|
|
1683
1685
|
assert MoreColors.GOLD in MoreColors
|
|
1684
1686
|
|
|
1685
|
-
# Ensure that the Colors enumeration superclass
|
|
1686
|
-
#
|
|
1687
|
-
assert MoreColors.GOLD
|
|
1687
|
+
# Ensure that the Colors enumeration superclass backfilled the new GOLD option which
|
|
1688
|
+
# was enabled by setting the backfill keyword argument to True when creating Colors:
|
|
1689
|
+
assert MoreColors.GOLD in Colors
|
|
1688
1690
|
|
|
1689
1691
|
# Ensure that the MoreColors enumeration subclass as the expected methods
|
|
1690
1692
|
assert hasattr(MoreColors, "isWarm")
|
|
@@ -1694,12 +1696,35 @@ def test_attribute_access():
|
|
|
1694
1696
|
assert MoreColors.GOLD.isWarm() is True
|
|
1695
1697
|
assert MoreColors.GOLD.isCool() is False
|
|
1696
1698
|
|
|
1699
|
+
# Keep a reference to the original instance of Colors for identity checking, etc.
|
|
1700
|
+
OriginalColors = Colors
|
|
1701
|
+
|
|
1697
1702
|
# Create an enumeration subclass of the Colors enumeration, inheriting its options
|
|
1698
1703
|
# and attributes, and adding a new SILVER option for testing; note when subclassing,
|
|
1699
1704
|
# the subclass can be given the same name as the class it inherits from, so in this
|
|
1700
1705
|
# scope it effectively replaces the superclass, at least by its direct name:
|
|
1701
1706
|
class Colors(MoreColors):
|
|
1702
|
-
SILVER = auto(RGB=(192, 192, 192))
|
|
1707
|
+
SILVER = auto(RGB=(192, 192, 192), metallic=True)
|
|
1708
|
+
|
|
1709
|
+
def isMetallic(self) -> bool:
|
|
1710
|
+
"""Return `True` for metallic Colors options, and False otherwise; this is
|
|
1711
|
+
determined by checking if the Colors option has a 'metallic' annotation on
|
|
1712
|
+
its value, and if so, if 'metallic' has a value of `True`; note that we can
|
|
1713
|
+
use the `.get(name, default)` method to check if an annotation exists and
|
|
1714
|
+
to return an optional default or `None` if it does not. Accessing attributes
|
|
1715
|
+
that do not exist results in an exception being raised, so for attributes
|
|
1716
|
+
that are not consistently defined for all options on a given enumeration,
|
|
1717
|
+
using the `.get()` method offers a safe way to attempt access or default."""
|
|
1718
|
+
return self.get("metallic", default=False) is True
|
|
1719
|
+
|
|
1720
|
+
@property
|
|
1721
|
+
def HEX(self) -> str:
|
|
1722
|
+
"""Return the hexademimal string representation of the RGB color value."""
|
|
1723
|
+
return f"{self.RGB[0]:02X}{self.RGB[1]:02X}{self.RGB[2]:02X}"
|
|
1724
|
+
|
|
1725
|
+
@classmethod
|
|
1726
|
+
def count(cls) -> int:
|
|
1727
|
+
return len(cls)
|
|
1703
1728
|
|
|
1704
1729
|
# Ensure that the Colors enumeration subclass is of the expected type
|
|
1705
1730
|
assert issubclass(Colors, Enumeration)
|
|
@@ -1725,3 +1750,32 @@ def test_attribute_access():
|
|
|
1725
1750
|
# Ensure that the Colors enumeration subclass methods return the expected values
|
|
1726
1751
|
assert Colors.SILVER.isWarm() is False
|
|
1727
1752
|
assert Colors.SILVER.isCool() is True
|
|
1753
|
+
|
|
1754
|
+
# Attempt to reconcile a Colors option, in this case via its name, "RED"
|
|
1755
|
+
color: Colors = Colors.reconcile("RED")
|
|
1756
|
+
assert isinstance(color, Enumeration)
|
|
1757
|
+
|
|
1758
|
+
# Assert that the reconciled option is the one we expected
|
|
1759
|
+
assert color is Colors.RED
|
|
1760
|
+
assert color.name == "RED"
|
|
1761
|
+
assert color.value == 1
|
|
1762
|
+
assert color == 1
|
|
1763
|
+
|
|
1764
|
+
# Note: As the second version of the "Colors" class defined above shadows the first,
|
|
1765
|
+
# we cannot perform identity checking on options associated with the first version
|
|
1766
|
+
# via the second class; they can only be performed if we have a reference to the
|
|
1767
|
+
# original class object, because while these two "Colors" classes share the same
|
|
1768
|
+
# name, they are distinct objects with different identities, and enumeration options
|
|
1769
|
+
# are always created as instances of the class they are defined within or registered
|
|
1770
|
+
# on; if giving a subclass of an enumeration class the same name, be aware that any
|
|
1771
|
+
# identity checking must be performed against the object that the option is tied to:
|
|
1772
|
+
assert isinstance(Colors.RED, OriginalColors)
|
|
1773
|
+
assert isinstance(color, OriginalColors)
|
|
1774
|
+
|
|
1775
|
+
# Assert that attribute access to annotations, properties and methods works:
|
|
1776
|
+
assert color.RGB == (255, 0, 0) # RGB is an annotation added to the option's value
|
|
1777
|
+
assert color.HLS == (0.0, 50.0, 100.0) # HLS is a property on to the Colors class
|
|
1778
|
+
assert color.isWarm() is True # isWarm() is a method on the first Colors class
|
|
1779
|
+
assert color.isMetallic() is False # isMetallic() is defined on the Colors subclass
|
|
1780
|
+
assert color.HEX == "FF0000" # HEX is a property defined on the Colors subclass
|
|
1781
|
+
assert color.count() == 8 # count() is a classmethod defined on the Colors subclass
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.0.3
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|