zstdlib 0.1.0__tar.gz → 0.2.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 (33) hide show
  1. {zstdlib-0.1.0 → zstdlib-0.2.0}/PKG-INFO +1 -1
  2. zstdlib-0.2.0/tests/test_enum.py +133 -0
  3. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib/__init__.py +2 -2
  4. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib/ansi.py +1 -1
  5. zstdlib-0.2.0/zstdlib/enum.py +154 -0
  6. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib.egg-info/PKG-INFO +1 -1
  7. zstdlib-0.1.0/tests/test_enum.py +0 -137
  8. zstdlib-0.1.0/zstdlib/enum.py +0 -53
  9. {zstdlib-0.1.0 → zstdlib-0.2.0}/LICENSE +0 -0
  10. {zstdlib-0.1.0 → zstdlib-0.2.0}/README.md +0 -0
  11. {zstdlib-0.1.0 → zstdlib-0.2.0}/pyproject.toml +0 -0
  12. {zstdlib-0.1.0 → zstdlib-0.2.0}/setup.cfg +0 -0
  13. {zstdlib-0.1.0 → zstdlib-0.2.0}/tests/__init__.py +0 -0
  14. {zstdlib-0.1.0 → zstdlib-0.2.0}/tests/log/__init__.py +0 -0
  15. {zstdlib-0.1.0 → zstdlib-0.2.0}/tests/log/base.py +0 -0
  16. {zstdlib-0.1.0 → zstdlib-0.2.0}/tests/log/test_cute.py +0 -0
  17. {zstdlib-0.1.0 → zstdlib-0.2.0}/tests/log/test_trace.py +0 -0
  18. {zstdlib-0.1.0 → zstdlib-0.2.0}/tests/not_set.py +0 -0
  19. {zstdlib-0.1.0 → zstdlib-0.2.0}/tests/test_ansi.py +0 -0
  20. {zstdlib-0.1.0 → zstdlib-0.2.0}/tests/test_frozen.py +0 -0
  21. {zstdlib-0.1.0 → zstdlib-0.2.0}/tests/test_io.py +0 -0
  22. {zstdlib-0.1.0 → zstdlib-0.2.0}/tests/test_singleton.py +0 -0
  23. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib/frozen.py +0 -0
  24. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib/io.py +0 -0
  25. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib/log/__init__.py +0 -0
  26. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib/log/cute.py +0 -0
  27. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib/log/trace.py +0 -0
  28. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib/not_set.py +0 -0
  29. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib/py.typed +0 -0
  30. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib/singleton.py +0 -0
  31. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib.egg-info/SOURCES.txt +0 -0
  32. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib.egg-info/dependency_links.txt +0 -0
  33. {zstdlib-0.1.0 → zstdlib-0.2.0}/zstdlib.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zstdlib
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A set of useful python utilities
5
5
  License: GPLv3
6
6
  Project-URL: Homepage, https://github.com/zwimer/zstdlib
@@ -0,0 +1,133 @@
1
+ # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,unused-variable
2
+ import unittest
3
+
4
+ from zstdlib.enum import EnumType, Enum, entries, values, auto
5
+
6
+
7
+ class TestEnumType(unittest.TestCase):
8
+
9
+ def test_valid(self) -> None:
10
+ class ET1(metaclass=EnumType):
11
+ arg1: int = 0
12
+ arg2: int = 1
13
+
14
+ def test_empty(self) -> None:
15
+ class ET1(metaclass=EnumType, empty_ok=True):
16
+ pass
17
+
18
+ with self.assertRaises(ValueError):
19
+
20
+ class ET2(metaclass=EnumType):
21
+ pass
22
+
23
+ def test_dupes(self) -> None:
24
+ class ET1(metaclass=EnumType, dupes_ok=True):
25
+ a: int = 5
26
+ b: int = 5
27
+ c: dict = {}
28
+
29
+ with self.assertRaises(ValueError):
30
+
31
+ class ET2(metaclass=EnumType):
32
+ a: int = 5
33
+ b: int = 5
34
+
35
+ with self.assertRaises(TypeError):
36
+
37
+ class ET3(metaclass=EnumType):
38
+ a: dict = {}
39
+
40
+ def test_type_check(self) -> None:
41
+ class ET1(metaclass=EnumType, type_check=set()):
42
+ a: str = 5 # type: ignore[assignment]
43
+
44
+ with self.assertRaises(TypeError):
45
+
46
+ class ET2(metaclass=EnumType):
47
+ a: str = 5 # type: ignore[assignment]
48
+
49
+ def test_annotations(self) -> None:
50
+ with self.assertRaises(ValueError):
51
+
52
+ class ET1(metaclass=EnumType):
53
+ arg1 = 1
54
+
55
+ with self.assertRaises(ValueError):
56
+
57
+ class ET2(metaclass=EnumType):
58
+ arg1: int
59
+
60
+ def test_instantiation(self) -> None:
61
+ with self.assertRaises(AttributeError):
62
+
63
+ class ET1(metaclass=EnumType):
64
+ arg1: int = 1
65
+
66
+ def __init__(self):
67
+ pass
68
+
69
+ with self.assertRaises(AttributeError):
70
+
71
+ class ET2(metaclass=EnumType):
72
+ arg1: int = 1
73
+
74
+ def __new__(cls):
75
+ pass
76
+
77
+ class ET3(metaclass=EnumType):
78
+ arg1: int = 0
79
+
80
+ with self.assertRaises(TypeError):
81
+ ET3()
82
+
83
+ with self.assertRaises(TypeError):
84
+ ET3.__new__({}) # type: ignore[arg-type]
85
+
86
+ def test_iter(self) -> None:
87
+
88
+ class ET11(metaclass=EnumType):
89
+ arg1: int = 0
90
+ arg2: str = "1"
91
+
92
+ self.assertEqual(tuple(ET11), ("arg1", "arg2"))
93
+
94
+ def test_auto(self) -> None:
95
+
96
+ class ET1(metaclass=EnumType):
97
+ arg1: int = 0
98
+ arg2 = auto
99
+ arg3 = auto
100
+
101
+ self.assertEqual(3, len(set(values(ET1))))
102
+
103
+ with self.assertRaises(TypeError):
104
+
105
+ class ET2(metaclass=EnumType):
106
+ arg1: int = 0
107
+ arg2: str = auto # type: ignore[assignment]
108
+
109
+
110
+ class TestEnum(unittest.TestCase):
111
+
112
+ def test_is(self) -> None:
113
+ self.assertIsInstance(Enum, EnumType)
114
+
115
+
116
+ class TestFunctions(unittest.TestCase):
117
+ def test_entries(self) -> None:
118
+ class E1(Enum):
119
+ arg1: int = 0
120
+ arg2: str = "1"
121
+
122
+ self.assertEqual(entries(E1), {"arg1": (int, 0), "arg2": (str, "1")})
123
+
124
+ def test_values(self) -> None:
125
+ class E1(Enum):
126
+ arg1: int = 0
127
+ arg2: str = "1"
128
+
129
+ self.assertEqual(values(E1), (0, "1"))
130
+
131
+
132
+ if __name__ == "__main__":
133
+ unittest.main()
@@ -1,7 +1,7 @@
1
- __version__ = "0.1.0"
1
+ __version__ = "0.2.0"
2
2
 
3
3
  from .not_set import NotSetType, NotSet
4
4
  from .frozen import Freezable, frozen
5
5
  from .singleton import Singleton
6
- from .enum import Enum
6
+ from .enum import Enum, auto
7
7
  from . import log, io
@@ -144,7 +144,7 @@ def _generate_code(
144
144
  ints.append(foreground.value)
145
145
  if background:
146
146
  ints.append(background.value)
147
- return f"{_PREFIX}{';'.join(str(i) for i in ints)}m"
147
+ return f"{_PREFIX}{';'.join(map(str, ints))}m"
148
148
 
149
149
 
150
150
  @cache
@@ -0,0 +1,154 @@
1
+ from collections.abc import Iterator
2
+ from typing import Any
3
+
4
+
5
+ class _Auto:
6
+ """A sentinel value for automatic enum values"""
7
+
8
+
9
+ # Pass this as a value to have it automatically assigned
10
+ auto = _Auto()
11
+
12
+
13
+ class EnumType(type):
14
+ """
15
+ Metaclass for uninstantiable Enum classes with required annotations and unique values
16
+ Enum entry names may not be prefixed with __ or be an EnumType._AutoValue
17
+ These 'Enum' classes may not be modified after creation
18
+ Items of annotation types in type_check will be type-checked
19
+ Duplicate values (as determined by set()) are not allowed unless dupes_ok is True
20
+ """
21
+
22
+ _AUTO_PREFIX = "Enum_Auto_"
23
+ _TC_DEFAULT: set[type | None] = {int, str, float, bool, complex, bytes, None, type(None)}
24
+
25
+ class _AutoValue:
26
+ """A class representing an automatically assigned enum value"""
27
+
28
+ def __init__(self, value: int) -> None:
29
+ self._value = value
30
+
31
+ def __str__(self) -> str:
32
+ return f"<Auto: {self._value}>"
33
+
34
+ def __repr__(self) -> str:
35
+ return str(self)
36
+
37
+ @classmethod
38
+ def _gen_entries_and_annotations(
39
+ mcs, attrs: dict, dupes_ok: bool
40
+ ) -> tuple[dict[str, tuple[type | None, Any]], dict[str, type | None]]:
41
+ """
42
+ Generate enum entries from attrs, checking for duplicates and missing annotations
43
+ Does not check for empty enum or type correctness
44
+ """
45
+ seen = set()
46
+ auto_val = 0
47
+ _entries = {}
48
+ _annotations = dict(attrs.get("__annotations__", {}))
49
+ try:
50
+ for a_name, value in ((i, k) for i, k in attrs.items() if not i.startswith("__")):
51
+ if value is auto:
52
+ auto_val += 1
53
+ value = mcs._AutoValue(auto_val)
54
+ if a_name in _annotations:
55
+ raise TypeError("Auto enum entries may not be type annotated")
56
+ _annotations[a_name] = mcs._AutoValue
57
+ elif isinstance(value, mcs._AutoValue):
58
+ raise ValueError("Enum entries may not be of type EnumType._AutoValue")
59
+ if not dupes_ok:
60
+ if value in seen:
61
+ raise ValueError(f"Duplicate enum value: {value}")
62
+ seen.add(value)
63
+ _entries[a_name] = (_annotations[a_name], value)
64
+ except TypeError:
65
+ raise TypeError(f"Unhashable enum value: {value}") from None
66
+ except KeyError:
67
+ raise ValueError("Non-auto enum entries must be type annotated") from None
68
+ return _entries, _annotations
69
+
70
+ # pylint: disable=too-many-arguments, dangerous-default-value
71
+ def __new__(
72
+ mcs,
73
+ name,
74
+ bases,
75
+ attrs,
76
+ *,
77
+ empty_ok: bool = False,
78
+ dupes_ok: bool = False,
79
+ type_check: set[type | None] = _TC_DEFAULT,
80
+ **kwargs,
81
+ ):
82
+ # Disallow undesired instance methods
83
+ if bad := attrs.keys() & {"__init__", "__new__", "__entries__"}:
84
+ raise AttributeError(f"Illegal methods in Enum class: {', '.join(bad)}")
85
+ # Generate entries
86
+ _entries, _annotations = mcs._gen_entries_and_annotations(attrs, dupes_ok)
87
+ # Validate entries
88
+ if no_value := (_annotations.keys() - _entries.keys()):
89
+ raise ValueError(f"Entries must have a value: {' '.join(no_value)}")
90
+ if (not empty_ok) and not _entries:
91
+ raise ValueError("Enum type may not be empty")
92
+ if type_check:
93
+ for a_name, (ann, val) in _entries.items():
94
+ if ann in type_check and (
95
+ (ann is None is not val) or (ann is not None and not isinstance(val, ann))
96
+ ):
97
+ raise TypeError(f"Entry {a_name} is not of type {ann}")
98
+ # Construct class
99
+ attrs["__entries__"] = _entries
100
+ attrs["__annotations__"] = _annotations
101
+ attrs["__init__"] = attrs["__new__"] = NotImplemented
102
+ return type.__new__(mcs, name, bases, attrs, **kwargs)
103
+
104
+ # Disallow modification
105
+
106
+ def __delattr__(cls, *_):
107
+ raise AttributeError("This class cannot be modified")
108
+
109
+ def __setattr__(cls, *_):
110
+ raise AttributeError("This class cannot be modified")
111
+
112
+ # Class methods for the derived types
113
+
114
+ def __iter__(cls) -> Iterator[str]:
115
+ """Iterator over enum keys"""
116
+ yield from cls.__entries__ # type: ignore
117
+
118
+ def _disallow(cls):
119
+ """Disallow instantiation"""
120
+ raise AttributeError("This class may not be instantiated")
121
+
122
+
123
+ class Enum(metaclass=EnumType, empty_ok=True):
124
+ """
125
+ An uninstantiable Enum base type
126
+ Subclasses must provide type-annotated fields with unique values
127
+ Fields may not be prefixed with "__"
128
+ Derived classes will not be modifiable
129
+ """
130
+
131
+
132
+ def entries(enum: Any) -> dict[str, tuple[type | None, Any]]:
133
+ """
134
+ :param enum: A class whose metaclass is EnumType
135
+ :return: A dict mapping names to (type annotation, value) pairs
136
+ If enum's type is not an EnumType, raises TypeError
137
+ """
138
+ if not isinstance(enum, EnumType):
139
+ raise TypeError("enum must have a metaclass of EnumType")
140
+ return dict(enum.__entries__) # type: ignore[attr-defined]
141
+
142
+
143
+ def values(enum: Any) -> tuple[Any, ...]:
144
+ """
145
+ :param enum: A class whose metaclass is EnumType
146
+ :return: A tuple of all values of enum
147
+ If enum's type is not an EnumType, raises TypeError
148
+ """
149
+ if not isinstance(enum, EnumType):
150
+ raise TypeError("enum must have a metaclass of EnumType")
151
+ return tuple(k[1] for k in enum.__entries__.values()) # type: ignore[attr-defined]
152
+
153
+
154
+ __all__ = ("Enum", "EnumType", "entries", "values", "auto")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zstdlib
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A set of useful python utilities
5
5
  License: GPLv3
6
6
  Project-URL: Homepage, https://github.com/zwimer/zstdlib
@@ -1,137 +0,0 @@
1
- # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,unused-variable
2
- import unittest
3
-
4
- from zstdlib.enum import EnumType, Enum
5
-
6
-
7
- class TestEnumType(unittest.TestCase):
8
-
9
- def test_valid(self) -> None:
10
- class ET1(metaclass=EnumType):
11
- arg1: int = 0
12
- arg2: int = 1
13
-
14
- def test_empty(self) -> None:
15
- class ET2(metaclass=EnumType, empty_ok=True):
16
- pass
17
-
18
- with self.assertRaises(ValueError):
19
-
20
- class ET3(metaclass=EnumType):
21
- pass
22
-
23
- def test_dupe(self) -> None:
24
- with self.assertRaises(ValueError):
25
-
26
- class ET4(metaclass=EnumType):
27
- arg1: int = 0
28
- arg2: int = 0
29
-
30
- def test_annotations(self) -> None:
31
- with self.assertRaises(ValueError):
32
-
33
- class ET5(metaclass=EnumType):
34
- arg1 = 1
35
-
36
- with self.assertRaises(ValueError):
37
-
38
- class ET6(metaclass=EnumType):
39
- arg1: int
40
-
41
- with self.assertRaises(TypeError):
42
-
43
- class ET7(metaclass=EnumType):
44
- arg1: str = 0 # type: ignore[assignment]
45
-
46
- def test_instantiation(self) -> None:
47
- with self.assertRaises(AttributeError):
48
-
49
- class ET8(metaclass=EnumType):
50
- arg1: int = 1
51
-
52
- def __init__(self):
53
- pass
54
-
55
- with self.assertRaises(AttributeError):
56
-
57
- class ET9(metaclass=EnumType):
58
- arg1: int = 1
59
-
60
- def __new__(cls):
61
- pass
62
-
63
- class ET10(metaclass=EnumType):
64
- arg1: int = 0
65
-
66
- with self.assertRaises(TypeError):
67
- ET10()
68
- with self.assertRaises(TypeError):
69
- ET10.__init__({})
70
-
71
-
72
- class TestEnum(unittest.TestCase):
73
- def test_valid(self) -> None:
74
- class E1(Enum):
75
- arg1: int = 0
76
- arg2: int = 1
77
-
78
- def test_empty(self) -> None:
79
- class E2(Enum, empty_ok=True):
80
- pass
81
-
82
- with self.assertRaises(ValueError):
83
-
84
- class E3(Enum):
85
- pass
86
-
87
- def test_dupe(self) -> None:
88
- with self.assertRaises(ValueError):
89
-
90
- class E4(Enum):
91
- arg1: int = 0
92
- arg2: int = 0
93
-
94
- def test_annotations(self) -> None:
95
- with self.assertRaises(ValueError):
96
-
97
- class E5(Enum):
98
- arg1 = 1
99
-
100
- with self.assertRaises(ValueError):
101
-
102
- class E6(Enum):
103
- arg1: int
104
-
105
- with self.assertRaises(TypeError):
106
-
107
- class E7(Enum):
108
- arg1: str = 0 # type: ignore[assignment]
109
-
110
- def test_instantiation(self) -> None:
111
- with self.assertRaises(AttributeError):
112
-
113
- class E8(Enum):
114
- arg1: int = 1
115
-
116
- def __init__(self):
117
- pass
118
-
119
- with self.assertRaises(AttributeError):
120
-
121
- class E9(Enum):
122
- arg1: int = 1
123
-
124
- def __new__(cls):
125
- pass
126
-
127
- class E10(Enum):
128
- arg1: int = 0
129
-
130
- with self.assertRaises(TypeError):
131
- E10()
132
- with self.assertRaises(TypeError):
133
- E10.__init__({})
134
-
135
-
136
- if __name__ == "__main__":
137
- unittest.main()
@@ -1,53 +0,0 @@
1
- import collections
2
-
3
-
4
- class EnumType(type):
5
- """
6
- Metaclass for uninstantiable Enum classes with required annotations and unique values
7
- Enum values may not be prefixed with _
8
- These 'Enum' classes may not be modified after creation
9
- """
10
-
11
- def __new__(mcs, name, bases, attrs, **kwargs):
12
- # Disallow instantiation
13
- for bad in ("__init__", "__new__"):
14
- if bad in attrs:
15
- raise AttributeError("Cannot define __init__ or __new__")
16
- attrs[bad] = None
17
- # Check annotations
18
- public = {i: k for i, k in attrs.items() if not i.startswith("__")}
19
- annotations = attrs.get("__annotations__", {})
20
- eok = kwargs.pop("empty_ok", False)
21
- if not public and not annotations:
22
- if not eok:
23
- raise ValueError("Enum type is empty")
24
- if bad := (pub_set := set(public)) - (an_set := set(annotations)):
25
- raise ValueError(f"All enum entries must be type annotated: {bad}")
26
- if bad := an_set - pub_set:
27
- raise ValueError(f"All type annotated entries must have a value: {bad}")
28
- for i, typ in annotations.items():
29
- if not isinstance(attrs[i], typ):
30
- raise TypeError(f"{i} is not of type {typ}")
31
- # Disallow duplicate values
32
- counts = collections.Counter(public.values())
33
- if dups := {i: k for i, k in public.items() if counts[k] > 1}:
34
- raise ValueError(f"Duplicate values: {dups}")
35
- # Construct class
36
- return type.__new__(mcs, name, bases, attrs, **kwargs)
37
-
38
- # Disallow modification
39
-
40
- def __delattr__(cls, *_):
41
- raise AttributeError("This class cannot be modified")
42
-
43
- def __setattr__(cls, *_):
44
- raise AttributeError("This class cannot be modified")
45
-
46
-
47
- class Enum(metaclass=EnumType, empty_ok=True):
48
- """
49
- A uninstantiable Enum base type
50
- Subclasses must provide type-annotated fields with unique values
51
- Fields may not be prefixed with "_"
52
- Derived classes will not be modifiable
53
- """
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
File without changes
File without changes
File without changes
File without changes
File without changes