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.
- {enum_properties-2.2.4 → enum_properties-2.3.0}/PKG-INFO +3 -3
- {enum_properties-2.2.4 → enum_properties-2.3.0}/README.md +2 -2
- {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/changelog.rst +11 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/howto.rst +12 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/reference.rst +1 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/justfile +22 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/pyproject.toml +1 -1
- {enum_properties-2.2.4 → enum_properties-2.3.0}/src/enum_properties/__init__.py +47 -1
- enum_properties-2.3.0/tests/annotations/test_aliases.py +211 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_symmetric.py +25 -2
- enum_properties-2.3.0/tests/examples/howto_members_and_aliases.py +22 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/test_examples.py +4 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/uv.lock +154 -136
- {enum_properties-2.2.4 → enum_properties-2.3.0}/.codecov.yml +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/.github/dependabot.yml +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/.github/workflows/lint.yml +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/.github/workflows/release.yml +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/.github/workflows/scorecard.yml +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/.github/workflows/test.yml +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/.gitignore +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/.pre-commit-config.yaml +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/CODE_OF_CONDUCT.md +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/CONTRIBUTING.md +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/LICENSE +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/SECURITY.md +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/.readthedocs.yaml +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/conf.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/index.rst +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/doc/source/tutorial.rst +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/src/enum_properties/py.typed +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/__init__.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_flags.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_interface_eq.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_nestedclass.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_none_coercion.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_perf.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_pickle.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_specialize.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/annotations/test_type_hints.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/big_enum.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/big_enum_annotations.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/__init__.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/address.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/color_example.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/hash_equiv_def.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_add_props.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_dataclass.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_dataclass_integration.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_flag.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_flag_boundaries.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_flags_no_iterable.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_hash_equiv.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_hash_equiv_def.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_legacy.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_metaclass.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_nested_classes.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_nested_classes_313.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_specialized.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_specialized_default.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_specialized_list.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_specialized_missing.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_symmetric_builtins.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_symmetric_decorator.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_symmetric_metaclass.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_symmetric_overload.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_symmetry.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/howto_verify_unique.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/mapbox.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/specialization_example.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/examples/symmetric_example.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/__init__.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_flags.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_interface_eq.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_multi_primitives.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_nestedclass.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_none_coercion.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_perf.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_pickle.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_specialize.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/legacy/test_type_hints.py +0 -0
- {enum_properties-2.2.4 → enum_properties-2.3.0}/tests/pickle_enums.py +0 -0
- {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.
|
|
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
|
[](https://pypi.python.org/pypi/enum-properties)
|
|
40
40
|
[](http://enum-properties.readthedocs.io/?badge=latest/)
|
|
41
41
|
[](https://codecov.io/gh/bckohan/enum-properties)
|
|
42
|
-
[](https://github.com/bckohan/enum-properties/actions/workflows/test.yml?query=branch:main)
|
|
43
|
+
[](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
|
[](https://pypi.python.org/pypi/enum-properties)
|
|
8
8
|
[](http://enum-properties.readthedocs.io/?badge=latest/)
|
|
9
9
|
[](https://codecov.io/gh/bckohan/enum-properties)
|
|
10
|
-
[](https://github.com/bckohan/enum-properties/actions/workflows/test.yml?query=branch:main)
|
|
11
|
+
[](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
|
|
@@ -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.
|
|
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,
|
|
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]
|