enum-properties 2.2.4__tar.gz → 2.3.0__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 (84) hide show
  1. {enum_properties-2.2.4 → enum_properties-2.3.0}/PKG-INFO +3 -3
  2. {enum_properties-2.2.4 → enum_properties-2.3.0}/README.md +2 -2
  3. {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/changelog.rst +11 -0
  4. {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/howto.rst +12 -0
  5. {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/reference.rst +1 -0
  6. {enum_properties-2.2.4 → enum_properties-2.3.0}/justfile +22 -0
  7. {enum_properties-2.2.4 → enum_properties-2.3.0}/pyproject.toml +1 -1
  8. {enum_properties-2.2.4 → enum_properties-2.3.0}/src/enum_properties/__init__.py +47 -1
  9. enum_properties-2.3.0/tests/annotations/test_aliases.py +211 -0
  10. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_symmetric.py +25 -2
  11. enum_properties-2.3.0/tests/examples/howto_members_and_aliases.py +22 -0
  12. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/test_examples.py +4 -0
  13. {enum_properties-2.2.4 → enum_properties-2.3.0}/uv.lock +154 -136
  14. {enum_properties-2.2.4 → enum_properties-2.3.0}/.codecov.yml +0 -0
  15. {enum_properties-2.2.4 → enum_properties-2.3.0}/.github/dependabot.yml +0 -0
  16. {enum_properties-2.2.4 → enum_properties-2.3.0}/.github/workflows/lint.yml +0 -0
  17. {enum_properties-2.2.4 → enum_properties-2.3.0}/.github/workflows/release.yml +0 -0
  18. {enum_properties-2.2.4 → enum_properties-2.3.0}/.github/workflows/scorecard.yml +0 -0
  19. {enum_properties-2.2.4 → enum_properties-2.3.0}/.github/workflows/test.yml +0 -0
  20. {enum_properties-2.2.4 → enum_properties-2.3.0}/.gitignore +0 -0
  21. {enum_properties-2.2.4 → enum_properties-2.3.0}/.pre-commit-config.yaml +0 -0
  22. {enum_properties-2.2.4 → enum_properties-2.3.0}/CODE_OF_CONDUCT.md +0 -0
  23. {enum_properties-2.2.4 → enum_properties-2.3.0}/CONTRIBUTING.md +0 -0
  24. {enum_properties-2.2.4 → enum_properties-2.3.0}/LICENSE +0 -0
  25. {enum_properties-2.2.4 → enum_properties-2.3.0}/SECURITY.md +0 -0
  26. {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/.readthedocs.yaml +0 -0
  27. {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/conf.py +0 -0
  28. {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/index.rst +0 -0
  29. {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/tutorial.rst +0 -0
  30. {enum_properties-2.2.4 → enum_properties-2.3.0}/src/enum_properties/py.typed +0 -0
  31. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/__init__.py +0 -0
  32. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test.py +0 -0
  33. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_flags.py +0 -0
  34. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_interface_eq.py +0 -0
  35. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_nestedclass.py +0 -0
  36. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_none_coercion.py +0 -0
  37. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_perf.py +0 -0
  38. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_pickle.py +0 -0
  39. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_specialize.py +0 -0
  40. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_type_hints.py +0 -0
  41. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/big_enum.py +0 -0
  42. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/big_enum_annotations.py +0 -0
  43. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/__init__.py +0 -0
  44. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/address.py +0 -0
  45. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/color_example.py +0 -0
  46. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/hash_equiv_def.py +0 -0
  47. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_add_props.py +0 -0
  48. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_dataclass.py +0 -0
  49. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_dataclass_integration.py +0 -0
  50. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_flag.py +0 -0
  51. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_flag_boundaries.py +0 -0
  52. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_flags_no_iterable.py +0 -0
  53. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_hash_equiv.py +0 -0
  54. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_hash_equiv_def.py +0 -0
  55. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_legacy.py +0 -0
  56. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_metaclass.py +0 -0
  57. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_nested_classes.py +0 -0
  58. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_nested_classes_313.py +0 -0
  59. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_specialized.py +0 -0
  60. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_specialized_default.py +0 -0
  61. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_specialized_list.py +0 -0
  62. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_specialized_missing.py +0 -0
  63. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_symmetric_builtins.py +0 -0
  64. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_symmetric_decorator.py +0 -0
  65. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_symmetric_metaclass.py +0 -0
  66. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_symmetric_overload.py +0 -0
  67. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_symmetry.py +0 -0
  68. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_verify_unique.py +0 -0
  69. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/mapbox.py +0 -0
  70. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/specialization_example.py +0 -0
  71. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/symmetric_example.py +0 -0
  72. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/__init__.py +0 -0
  73. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test.py +0 -0
  74. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_flags.py +0 -0
  75. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_interface_eq.py +0 -0
  76. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_multi_primitives.py +0 -0
  77. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_nestedclass.py +0 -0
  78. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_none_coercion.py +0 -0
  79. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_perf.py +0 -0
  80. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_pickle.py +0 -0
  81. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_specialize.py +0 -0
  82. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_type_hints.py +0 -0
  83. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/pickle_enums.py +0 -0
  84. {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/pickle_enums_annotations.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: enum-properties
3
- Version: 2.2.4
3
+ Version: 2.3.0
4
4
  Summary: Add properties and method specializations to Python enumeration values with a simple declarative syntax.
5
5
  Project-URL: Homepage, https://github.com/bckohan/enum-properties
6
6
  Project-URL: Documentation, https://enum-properties.readthedocs.io
@@ -39,8 +39,8 @@ Description-Content-Type: text/markdown
39
39
  [![PyPI status](https://img.shields.io/pypi/status/enum-properties.svg)](https://pypi.python.org/pypi/enum-properties)
40
40
  [![Documentation Status](https://readthedocs.org/projects/enum-properties/badge/?version=latest)](http://enum-properties.readthedocs.io/?badge=latest/)
41
41
  [![Code Cov](https://codecov.io/gh/bckohan/enum-properties/branch/main/graph/badge.svg?token=0IZOKN2DYL)](https://codecov.io/gh/bckohan/enum-properties)
42
- [![Test Status](https://github.com/bckohan/django-enum/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/bckohan/enum-properties/actions/workflows/test.yml?query=branch:main)
43
- [![Lint Status](https://github.com/bckohan/django-enum/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/bckohan/enum-properties/actions/workflows/lint.yml?query=branch:main)
42
+ [![Test Status](https://github.com/bckohan/enum-properties/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/bckohan/enum-properties/actions/workflows/test.yml?query=branch:main)
43
+ [![Lint Status](https://github.com/bckohan/enum-properties/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/bckohan/enum-properties/actions/workflows/lint.yml?query=branch:main)
44
44
 
45
45
  Add properties to Python enumeration values with a simple declarative syntax. [Enum Properties](https://enum-properties.readthedocs.io/en/latest) is a lightweight extension to [Python's Enum class](https://docs.python.org/3/library/enum.html). Example:
46
46
 
@@ -7,8 +7,8 @@
7
7
  [![PyPI status](https://img.shields.io/pypi/status/enum-properties.svg)](https://pypi.python.org/pypi/enum-properties)
8
8
  [![Documentation Status](https://readthedocs.org/projects/enum-properties/badge/?version=latest)](http://enum-properties.readthedocs.io/?badge=latest/)
9
9
  [![Code Cov](https://codecov.io/gh/bckohan/enum-properties/branch/main/graph/badge.svg?token=0IZOKN2DYL)](https://codecov.io/gh/bckohan/enum-properties)
10
- [![Test Status](https://github.com/bckohan/django-enum/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/bckohan/enum-properties/actions/workflows/test.yml?query=branch:main)
11
- [![Lint Status](https://github.com/bckohan/django-enum/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/bckohan/enum-properties/actions/workflows/lint.yml?query=branch:main)
10
+ [![Test Status](https://github.com/bckohan/enum-properties/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/bckohan/enum-properties/actions/workflows/test.yml?query=branch:main)
11
+ [![Lint Status](https://github.com/bckohan/enum-properties/actions/workflows/lint.yml/badge.svg?branch=main)](https://github.com/bckohan/enum-properties/actions/workflows/lint.yml?query=branch:main)
12
12
 
13
13
  Add properties to Python enumeration values with a simple declarative syntax. [Enum Properties](https://enum-properties.readthedocs.io/en/latest) is a lightweight extension to [Python's Enum class](https://docs.python.org/3/library/enum.html). Example:
14
14
 
@@ -2,6 +2,17 @@
2
2
  Change Log
3
3
  ==========
4
4
 
5
+ v2.3.0 (2025-03-29)
6
+ ===================
7
+
8
+ * Implemented `Need a way to distinguish symmetric properties from first class aliases. <https://github.com/bckohan/enum-properties/issues/86>`_
9
+
10
+ v2.2.5 (2025-03-22)
11
+ ===================
12
+
13
+ * Fix readme badges.
14
+
15
+
5
16
  v2.2.4 (2025-03-17)
6
17
  ===================
7
18
 
@@ -282,6 +282,18 @@ enumeration for the following reasons:**
282
282
  .. literalinclude:: ../../tests/examples/howto_dataclass_integration.py
283
283
 
284
284
 
285
+ .. _howto_members_and_aliases:
286
+ Get members and aliases
287
+ -----------------------
288
+
289
+ Symmetric properties are added to the :attr:`~enum.EnumType.__members__` attribute,
290
+ and alias members do not appear in :attr:`~enum.Enum._member_names_`. To get a list
291
+ of first class members and aliases use
292
+ :attr:`~enum_properties.EnumProperties.__first_class_members__`. This class member may
293
+ also be overridden if you wish to customize this behavior for users.
294
+
295
+ .. literalinclude:: ../../tests/examples/howto_members_and_aliases.py
296
+
285
297
  .. _howto_hash_equivalency:
286
298
 
287
299
  Define hash equivalent enums
@@ -14,3 +14,4 @@ Module
14
14
  :undoc-members:
15
15
  :show-inheritance:
16
16
  :private-members:
17
+ :special-members: __first_class_members__
@@ -130,6 +130,28 @@ check-docs-links: _link_check
130
130
  check-docs:
131
131
  @just run doc8 --ignore-path ./doc/build --max-line-length 100 -q ./doc
132
132
 
133
+ # fetch the intersphinx references for the given package
134
+ [script]
135
+ fetch-refs LIB: install-docs
136
+ import os
137
+ from pathlib import Path
138
+ import logging as _logging
139
+ import sys
140
+ import runpy
141
+ from sphinx.ext.intersphinx import inspect_main
142
+ _logging.basicConfig()
143
+
144
+ libs = runpy.run_path(Path(os.getcwd()) / "doc/source/conf.py").get("intersphinx_mapping")
145
+ url = libs.get("{{ LIB }}", None)
146
+ if not url:
147
+ sys.exit(f"Unrecognized {{ LIB }}, must be one of: {', '.join(libs.keys())}")
148
+ if url[1] is None:
149
+ url = f"{url[0].rstrip('/')}/objects.inv"
150
+ else:
151
+ url = url[1]
152
+
153
+ raise SystemExit(inspect_main([url]))
154
+
133
155
  # lint the code
134
156
  check-lint:
135
157
  @just run ruff check --select I
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "enum-properties"
7
- version = "2.2.4"
7
+ version = "2.3.0"
8
8
  description = "Add properties and method specializations to Python enumeration values with a simple declarative syntax."
9
9
  requires-python = ">=3.8,<4.0"
10
10
  authors = [
@@ -19,7 +19,7 @@ from collections.abc import Generator, Hashable, Iterable
19
19
  from dataclasses import dataclass
20
20
  from functools import cached_property
21
21
 
22
- VERSION = (2, 2, 4)
22
+ VERSION = (2, 3, 0)
23
23
 
24
24
  __title__ = "Enum Properties"
25
25
  __version__ = ".".join(str(i) for i in VERSION)
@@ -230,6 +230,38 @@ class SymmetricMixin(with_typehint("EnumProperties")): # type: ignore
230
230
  property will be a case sensitive symmetric property.
231
231
  """
232
232
 
233
+ _ep_symmetric_map_: t.Dict[t.Any, enum.Enum]
234
+ """
235
+ The case sensitive mapping of symmetric values to enumeration values.
236
+ """
237
+
238
+ _ep_isymmetric_map_: t.Dict[str, enum.Enum]
239
+ """
240
+ The case insensitive mapping of symmetric values to enumeration values.
241
+ """
242
+
243
+ _ep_coerce_types_: t.List[t.Type[t.Any]]
244
+ """
245
+ On instantiation, if _missing_ is invoked a coercion attempt will be made to each
246
+ of these types before failure.
247
+ """
248
+
249
+ _num_sym_props_: int
250
+ """
251
+ The number of symmetric properties on this enumeration.
252
+ """
253
+
254
+ _properties_: t.List[_Prop]
255
+ """
256
+ List of properties defined on the enumeration class.
257
+ """
258
+
259
+ __first_class_members__: t.List[str]
260
+ """
261
+ The list of first class members - this includes all members and aliases. May be
262
+ overridden.
263
+ """
264
+
233
265
  def __eq__(self, value: t.Any) -> bool:
234
266
  """Symmetric equality - try to coerce value before failure"""
235
267
  if isinstance(value, self.__class__):
@@ -346,6 +378,7 @@ class EnumPropertiesMeta(enum.EnumMeta):
346
378
  # members reserved for use by EnumProperties
347
379
  RESERVED = [
348
380
  "_properties_",
381
+ "_num_sym_props_",
349
382
  "_ep_coerce_types_",
350
383
  "_ep_symmetric_map_",
351
384
  "_ep_isymmetric_map_",
@@ -354,7 +387,9 @@ class EnumPropertiesMeta(enum.EnumMeta):
354
387
  _ep_symmetric_map_: t.Dict[t.Any, enum.Enum]
355
388
  _ep_isymmetric_map_: t.Dict[str, enum.Enum]
356
389
  _ep_coerce_types_: t.List[t.Type[t.Any]]
390
+ _num_sym_props_: int
357
391
  _properties_: t.List[_Prop]
392
+ __first_class_members__: t.List[str]
358
393
 
359
394
  @classmethod
360
395
  def __prepare__(mcs, cls, bases, **kwargs):
@@ -396,6 +431,7 @@ class EnumPropertiesMeta(enum.EnumMeta):
396
431
  _ids_: t.Dict[int, str] = {}
397
432
  _member_names: t.Union[t.List[str], t.Dict[str, t.Any]]
398
433
  _create_properties_: bool = False
434
+ __first_class_members__: t.List[str] = []
399
435
 
400
436
  class AnnotationPropertyRecorder(dict):
401
437
  class_dict: "_PropertyEnumDict"
@@ -479,6 +515,7 @@ class EnumPropertiesMeta(enum.EnumMeta):
479
515
  # todo remove below when minimum python >= 3.13
480
516
  not isinstance(value, type)
481
517
  ):
518
+ self.__first_class_members__.append(key)
482
519
  try:
483
520
  num_vals = len(value) - len(self._ep_properties_)
484
521
  if num_vals < 1 or len(self._ep_properties_) != len(
@@ -553,10 +590,16 @@ class EnumPropertiesMeta(enum.EnumMeta):
553
590
  **kwargs,
554
591
  )
555
592
  cls._ep_coerce_types_ = []
593
+ cls._num_sym_props_ = 0
556
594
  cls._ep_symmetric_map_ = cls._member_map_
557
595
  cls._ep_isymmetric_map_ = {}
558
596
  cls._properties_ = list(classdict._ep_properties_.keys())
559
597
 
598
+ # we allow users to override this
599
+ cls.__first_class_members__ = classdict.get(
600
+ "__first_class_members__", classdict.__first_class_members__
601
+ )
602
+
560
603
  if classdict._specialized_:
561
604
  for val in cls: # type: ignore[var-annotated]
562
605
  val = t.cast(enum.Enum, val)
@@ -598,11 +641,13 @@ class EnumPropertiesMeta(enum.EnumMeta):
598
641
  setattr(member, prop, values[idx])
599
642
 
600
643
  # we reverse to maintain precedence order for symmetric lookups
644
+ cls._num_sym_props_ = 0
601
645
  member_values = t.cast(
602
646
  t.List[enum.Enum],
603
647
  list(cls._value2member_map_.values() or cls.__members__.values()),
604
648
  )
605
649
  for prop in reversed([prop for prop in cls._properties_ if prop.symmetric]):
650
+ cls._num_sym_props_ += 1
606
651
  prop = t.cast(_SProp, prop)
607
652
  for idx, val2 in enumerate(reversed(classdict._ep_properties_[prop])):
608
653
  enum_cls = member_values[len(member_values) - 1 - idx]
@@ -615,6 +660,7 @@ class EnumPropertiesMeta(enum.EnumMeta):
615
660
  add_coerce_type(type(val2))
616
661
 
617
662
  # add builtin symmetries
663
+ cls._num_sym_props_ += len(getattr(cls, "_symmetric_builtins_", []))
618
664
  for sym_builtin in reversed(getattr(cls, "_symmetric_builtins_", [])):
619
665
  # allow simple strings for the default case
620
666
  if isinstance(sym_builtin, str):
@@ -0,0 +1,211 @@
1
+ import sys
2
+ import enum
3
+ from unittest import TestCase
4
+ from typing_extensions import Annotated
5
+
6
+ from enum_properties import (
7
+ EnumProperties,
8
+ FlagProperties,
9
+ Symmetric,
10
+ specialize,
11
+ symmetric,
12
+ )
13
+
14
+
15
+ class TestAliases(TestCase):
16
+ def test_enum_alias(self):
17
+ class EnumWithAliases(EnumProperties):
18
+ label: Annotated[str, Symmetric(case_fold=True)]
19
+
20
+ A = 1, "a"
21
+ B = 2, "b"
22
+ C = 3, "c"
23
+ X = C, "x"
24
+ Y = B, "y"
25
+ Z = A, "z"
26
+
27
+ self.assertEqual(
28
+ EnumWithAliases.__first_class_members__, ["A", "B", "C", "X", "Y", "Z"]
29
+ )
30
+
31
+ class EnumWithAliasesComplex(EnumProperties):
32
+ label: Annotated[str, Symmetric(case_fold=True)]
33
+
34
+ A = 1, "a"
35
+ B = 2, "b"
36
+ C = 3, "c"
37
+ X = C, "x"
38
+ Y = B, "y"
39
+ Z = A, "z"
40
+
41
+ @symmetric(case_fold=True)
42
+ def x3(self) -> str:
43
+ return self.label * 3
44
+
45
+ @property
46
+ def prop(self) -> str:
47
+ return self.label * 5
48
+
49
+ def method(self) -> str:
50
+ return self.label * 7
51
+
52
+ @specialize(A)
53
+ def method(self) -> str:
54
+ return self.label * 8
55
+
56
+ if sys.version_info[:2] >= (3, 11):
57
+
58
+ @enum.nonmember
59
+ class Nested:
60
+ pass
61
+ else:
62
+
63
+ class Nested:
64
+ pass
65
+
66
+ self.assertEqual(
67
+ EnumWithAliasesComplex.__first_class_members__,
68
+ ["A", "B", "C", "X", "Y", "Z"],
69
+ )
70
+
71
+ class EnumWithAliasesOverride1(EnumProperties):
72
+ label: Annotated[str, Symmetric(case_fold=True)]
73
+
74
+ A = 1, "a"
75
+ B = 2, "b"
76
+ C = 3, "c"
77
+ X = C, "x"
78
+ Y = B, "y"
79
+ Z = A, "z"
80
+
81
+ __first_class_members__ = ["A", "B", "C", "X", "Y"]
82
+
83
+ self.assertEqual(
84
+ EnumWithAliasesOverride1.__first_class_members__, ["A", "B", "C", "X", "Y"]
85
+ )
86
+
87
+ class EnumWithAliasesOverride2(EnumProperties):
88
+ label: Annotated[str, Symmetric(case_fold=True)]
89
+
90
+ __first_class_members__ = ["A", "B", "C", "X"]
91
+
92
+ A = 1, "a"
93
+ B = 2, "b"
94
+ C = 3, "c"
95
+ X = C, "x"
96
+ Y = B, "y"
97
+ Z = A, "z"
98
+
99
+ self.assertEqual(
100
+ EnumWithAliasesOverride2.__first_class_members__, ["A", "B", "C", "X"]
101
+ )
102
+
103
+ def test_flag_alias(self):
104
+ class FlagWithAliases(FlagProperties):
105
+ label: Annotated[str, Symmetric(case_fold=True)]
106
+
107
+ A = 1 << 0, "a"
108
+ B = 1 << 1, "b"
109
+ C = 1 << 2, "c"
110
+ X = C, "x"
111
+ Y = B, "y"
112
+ Z = A, "z"
113
+
114
+ AB = A | B, "ab"
115
+ AC = A | C, "ac"
116
+ BC = B | C, "bc"
117
+ ABC = A | B | C, "abc"
118
+
119
+ self.assertEqual(
120
+ FlagWithAliases.__first_class_members__,
121
+ ["A", "B", "C", "X", "Y", "Z", "AB", "AC", "BC", "ABC"],
122
+ )
123
+
124
+ class FlagWithAliasesComplex(FlagProperties):
125
+ label: Annotated[str, Symmetric(case_fold=True)]
126
+
127
+ A = 1 << 0, "a"
128
+ B = 1 << 1, "b"
129
+ C = 1 << 2, "c"
130
+ X = C, "x"
131
+ Y = B, "y"
132
+ Z = A, "z"
133
+
134
+ AB = A | B, "ab"
135
+ AC = A | C, "ac"
136
+ BC = B | C, "bc"
137
+ ABC = A | B | C, "abc"
138
+
139
+ @symmetric(case_fold=True)
140
+ def x3(self) -> str:
141
+ return self.label * 3
142
+
143
+ @property
144
+ def prop(self) -> str:
145
+ return self.label * 5
146
+
147
+ def method(self) -> str:
148
+ return self.label * 7
149
+
150
+ @specialize(A)
151
+ def method(self) -> str:
152
+ return self.label * 8
153
+
154
+ if sys.version_info[:2] >= (3, 11):
155
+
156
+ @enum.nonmember
157
+ class Nested:
158
+ pass
159
+ else:
160
+
161
+ class Nested:
162
+ pass
163
+
164
+ self.assertEqual(
165
+ FlagWithAliasesComplex.__first_class_members__,
166
+ ["A", "B", "C", "X", "Y", "Z", "AB", "AC", "BC", "ABC"],
167
+ )
168
+
169
+ class FlagWithAliasesOverride1(FlagProperties):
170
+ label: Annotated[str, Symmetric(case_fold=True)]
171
+
172
+ __first_class_members__ = ["A", "B", "C", "X", "Y", "ABC"]
173
+
174
+ A = 1 << 0, "a"
175
+ B = 1 << 1, "b"
176
+ C = 1 << 2, "c"
177
+ X = C, "x"
178
+ Y = B, "y"
179
+ Z = A, "z"
180
+
181
+ AB = A | B, "ab"
182
+ AC = A | C, "ac"
183
+ BC = B | C, "bc"
184
+ ABC = A | B | C, "abc"
185
+
186
+ self.assertEqual(
187
+ FlagWithAliasesOverride1.__first_class_members__,
188
+ ["A", "B", "C", "X", "Y", "ABC"],
189
+ )
190
+
191
+ class FlagWithAliasesOverride2(FlagProperties):
192
+ label: Annotated[str, Symmetric(case_fold=True)]
193
+
194
+ A = 1 << 0, "a"
195
+ B = 1 << 1, "b"
196
+ C = 1 << 2, "c"
197
+ X = C, "x"
198
+ Y = B, "y"
199
+ Z = A, "z"
200
+
201
+ AB = A | B, "ab"
202
+ AC = A | C, "ac"
203
+ BC = B | C, "bc"
204
+ ABC = A | B | C, "abc"
205
+
206
+ __first_class_members__ = ["A", "B", "C", "X", "Y", "ABC"]
207
+
208
+ self.assertEqual(
209
+ FlagWithAliasesOverride2.__first_class_members__,
210
+ ["A", "B", "C", "X", "Y", "ABC"],
211
+ )
@@ -1,7 +1,11 @@
1
1
  from unittest import TestCase
2
- from typing_extensions import Annotated
3
-
4
2
  from enum_properties import EnumProperties, symmetric
3
+ import sys
4
+
5
+ if sys.version_info[0:2] >= (3, 11):
6
+ from enum import property as enum_property
7
+ else:
8
+ from types import DynamicClassAttribute as enum_property
5
9
 
6
10
 
7
11
  class TestSymmetricDecorator(TestCase):
@@ -82,3 +86,22 @@ class TestSymmetricDecorator(TestCase):
82
86
  self.assertTrue(SymEnum(SymEnum.ONE.label) is SymEnum.ONE)
83
87
  self.assertTrue(SymEnum(SymEnum.TWO.label) is SymEnum.TWO)
84
88
  self.assertTrue(SymEnum(SymEnum.THREE.label) is SymEnum.THREE)
89
+
90
+ def test_make_enum_properties_symmetric(self):
91
+ class SymEnum(EnumProperties):
92
+ ONE = 1
93
+ TWO = 2
94
+ THREE = 3
95
+
96
+ @symmetric(case_fold=True)
97
+ @enum_property
98
+ def label(self):
99
+ return self.name
100
+
101
+ self.assertEqual(SymEnum.ONE.label, "ONE")
102
+ self.assertEqual(SymEnum.TWO.label, "TWO")
103
+ self.assertEqual(SymEnum.THREE.label, "THREE")
104
+
105
+ self.assertTrue(SymEnum("one") is SymEnum.ONE)
106
+ self.assertTrue(SymEnum("tWo") is SymEnum.TWO)
107
+ self.assertTrue(SymEnum("THRee") is SymEnum.THREE)
@@ -0,0 +1,22 @@
1
+ import typing as t
2
+ from enum_properties import EnumProperties, Symmetric
3
+
4
+
5
+ class MyEnum(EnumProperties):
6
+
7
+ label: t.Annotated[str, Symmetric()]
8
+
9
+ A = 1, "a"
10
+ B = 2, "b"
11
+ C = 3, "c"
12
+ ALIAS_TO_A = A, "a"
13
+
14
+
15
+ # __first_class_members__ contains members and aliases
16
+ assert MyEnum.__first_class_members__ == ["A", "B", "C", "ALIAS_TO_A"]
17
+
18
+ # __members__ contains all members, including aliases and symmetric aliases
19
+ assert set(MyEnum.__members__.keys()) == {"A", "B", "C", "ALIAS_TO_A", "a", "b", "c"}
20
+
21
+ # iterating contains only non-alias members
22
+ assert list(MyEnum) == [MyEnum.A, MyEnum.B, MyEnum.C]
@@ -123,3 +123,7 @@ def test_howto_hash_equiv_def():
123
123
 
124
124
  def test_howto_legacy():
125
125
  from tests.examples import howto_legacy
126
+
127
+
128
+ def test_howto_members_and_aliases():
129
+ from tests.examples import howto_members_and_aliases