enumerific 1.0.3__tar.gz → 1.0.4__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.
Files changed (23) hide show
  1. {enumerific-1.0.3/source/enumerific.egg-info → enumerific-1.0.4}/PKG-INFO +1 -1
  2. {enumerific-1.0.3 → enumerific-1.0.4}/source/enumerific/extensible.py +12 -1
  3. enumerific-1.0.4/source/enumerific/version.txt +1 -0
  4. {enumerific-1.0.3 → enumerific-1.0.4/source/enumerific.egg-info}/PKG-INFO +1 -1
  5. {enumerific-1.0.3 → enumerific-1.0.4}/tests/test_extensible_enums.py +62 -8
  6. enumerific-1.0.3/source/enumerific/version.txt +0 -1
  7. {enumerific-1.0.3 → enumerific-1.0.4}/LICENSE.md +0 -0
  8. {enumerific-1.0.3 → enumerific-1.0.4}/README.md +0 -0
  9. {enumerific-1.0.3 → enumerific-1.0.4}/pyproject.toml +0 -0
  10. {enumerific-1.0.3 → enumerific-1.0.4}/requirements.development.txt +0 -0
  11. {enumerific-1.0.3 → enumerific-1.0.4}/requirements.distribution.txt +0 -0
  12. {enumerific-1.0.3 → enumerific-1.0.4}/requirements.txt +0 -0
  13. {enumerific-1.0.3 → enumerific-1.0.4}/setup.cfg +0 -0
  14. {enumerific-1.0.3 → enumerific-1.0.4}/source/enumerific/__init__.py +0 -0
  15. {enumerific-1.0.3 → enumerific-1.0.4}/source/enumerific/exceptions.py +0 -0
  16. {enumerific-1.0.3 → enumerific-1.0.4}/source/enumerific/logging.py +0 -0
  17. {enumerific-1.0.3 → enumerific-1.0.4}/source/enumerific/standard.py +0 -0
  18. {enumerific-1.0.3 → enumerific-1.0.4}/source/enumerific.egg-info/SOURCES.txt +0 -0
  19. {enumerific-1.0.3 → enumerific-1.0.4}/source/enumerific.egg-info/dependency_links.txt +0 -0
  20. {enumerific-1.0.3 → enumerific-1.0.4}/source/enumerific.egg-info/requires.txt +0 -0
  21. {enumerific-1.0.3 → enumerific-1.0.4}/source/enumerific.egg-info/top_level.txt +0 -0
  22. {enumerific-1.0.3 → enumerific-1.0.4}/source/enumerific.egg-info/zip-safe +0 -0
  23. {enumerific-1.0.3 → enumerific-1.0.4}/tests/test_enumerific_library.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: enumerific
3
- Version: 1.0.3
3
+ Version: 1.0.4
4
4
  Summary: Simplifies working with Python enums.
5
5
  Author: Daniel Sissman
6
6
  License-Expression: MIT
@@ -1707,7 +1707,18 @@ class Enumeration(metaclass=EnumerationMetaClass):
1707
1707
  elif self._annotations and name in self._annotations:
1708
1708
  return self._annotations[name]
1709
1709
  elif self._context and name in dir(self._context):
1710
- return object.__getattribute__(self._context, name)
1710
+ # Handle class methods, instance methods and properties here; because we are
1711
+ # performing some special handling for enumerations, we need to reintroduce
1712
+ # the necessary context to the methods here via the descriptor protocol so
1713
+ # the methods and properties work as expected:
1714
+ if callable(attribute := object.__getattribute__(self._context, name)):
1715
+ return attribute.__get__(self)
1716
+ elif isinstance(attribute, property):
1717
+ return attribute.__get__(self)
1718
+ elif isinstance(attribute, classmethod):
1719
+ return attribute.__get__(self)
1720
+ else:
1721
+ return attribute
1711
1722
  else:
1712
1723
  # EnumerationOptionError subclasses AttributeError so we adhere to convention
1713
1724
  raise EnumerationOptionError(
@@ -0,0 +1 @@
1
+ 1.0.4
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: enumerific
3
- Version: 1.0.3
3
+ Version: 1.0.4
4
4
  Summary: Simplifies working with Python enums.
5
5
  Author: Daniel Sissman
6
6
  License-Expression: MIT
@@ -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
- assert len(Colors) == 6
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 did not backfill the new GOLD option
1686
- # which was prevented by setting the backfill keyword argument when creating Colors:
1687
- assert MoreColors.GOLD not in Colors
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