zstdlib 0.2.0__tar.gz → 0.3.2__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 (31) hide show
  1. {zstdlib-0.2.0 → zstdlib-0.3.2}/PKG-INFO +3 -3
  2. {zstdlib-0.2.0 → zstdlib-0.3.2}/pyproject.toml +11 -24
  3. {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/test_frozen.py +8 -2
  4. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/__init__.py +1 -1
  5. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/ansi.py +31 -34
  6. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/enum.py +45 -33
  7. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/log/cute.py +0 -1
  8. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib.egg-info/PKG-INFO +3 -3
  9. {zstdlib-0.2.0 → zstdlib-0.3.2}/LICENSE +0 -0
  10. {zstdlib-0.2.0 → zstdlib-0.3.2}/README.md +0 -0
  11. {zstdlib-0.2.0 → zstdlib-0.3.2}/setup.cfg +0 -0
  12. {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/__init__.py +0 -0
  13. {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/log/__init__.py +0 -0
  14. {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/log/base.py +0 -0
  15. {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/log/test_cute.py +0 -0
  16. {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/log/test_trace.py +0 -0
  17. {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/not_set.py +0 -0
  18. {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/test_ansi.py +0 -0
  19. {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/test_enum.py +0 -0
  20. {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/test_io.py +0 -0
  21. {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/test_singleton.py +0 -0
  22. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/frozen.py +0 -0
  23. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/io.py +0 -0
  24. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/log/__init__.py +0 -0
  25. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/log/trace.py +0 -0
  26. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/not_set.py +0 -0
  27. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/py.typed +0 -0
  28. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/singleton.py +0 -0
  29. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib.egg-info/SOURCES.txt +0 -0
  30. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib.egg-info/dependency_links.txt +0 -0
  31. {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib.egg-info/top_level.txt +0 -0
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zstdlib
3
- Version: 0.2.0
3
+ Version: 0.3.2
4
4
  Summary: A set of useful python utilities
5
5
  License: GPLv3
6
6
  Project-URL: Homepage, https://github.com/zwimer/zstdlib
7
7
  Keywords: remote,pipe
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Programming Language :: Python :: 3.14
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
12
- Requires-Python: >=3.12
12
+ Requires-Python: >=3.14
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
  Dynamic: license-file
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
  name = "zstdlib"
7
7
  classifiers = [
8
8
  "Programming Language :: Python :: 3",
9
- "Programming Language :: Python :: 3.12",
9
+ "Programming Language :: Python :: 3.14",
10
10
  "Development Status :: 5 - Production/Stable",
11
11
  "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
12
12
  ]
@@ -14,20 +14,7 @@ keywords = ["remote", "pipe"]
14
14
  license = {text = "GPLv3"}
15
15
  description = "A set of useful python utilities"
16
16
  urls = {Homepage = "https://github.com/zwimer/zstdlib"}
17
- requires-python = ">= 3.12"
18
- # dependencies = [
19
- # # Client
20
- # "zstandard[cffi]",
21
- # "pycryptodomex",
22
- # "cryptography",
23
- # "requests",
24
- # "tqdm",
25
- # # Server
26
- # "waitress",
27
- # "flask",
28
- # # Both
29
- # "human_readable",
30
- # ]
17
+ requires-python = ">= 3.14"
31
18
  dynamic = ["version"]
32
19
 
33
20
  [project.readme]
@@ -38,14 +25,14 @@ content-type = "text/markdown"
38
25
  include-package-data = true
39
26
  license-files = ["LICENSE"]
40
27
 
41
- [tool.setuptools.packages]
42
- find = {namespaces = false}
28
+ [tool.setuptools.packages.find]
29
+ namespaces = false
43
30
 
44
31
  [tool.setuptools.package-data]
45
32
  zstdlib = ["py.typed"]
46
33
 
47
- [tool.setuptools.dynamic]
48
- version = {attr = "zstdlib.__version__"}
34
+ [tool.setuptools.dynamic.version]
35
+ attr = "zstdlib.__version__"
49
36
 
50
37
  # Tools
51
38
 
@@ -55,15 +42,15 @@ ignore-paths = '^tests/.*$'
55
42
  disable = [
56
43
  "unnecessary-lambda-assignment",
57
44
  "method-cache-max-size-none",
58
- "missing-module-docstring",
59
- "too-few-public-methods",
60
- "line-too-long",
61
- "invalid-name"
45
+ "missing-module-docstring",
46
+ "too-few-public-methods",
47
+ "line-too-long",
48
+ "invalid-name"
62
49
  ]
63
50
 
64
51
  [tool.black]
65
52
  line-length = 110
66
- target-version = ["py312", "py313"]
53
+ target-version = ["py314"]
67
54
 
68
55
  [tool.ruff]
69
56
  line-length = 110
@@ -1,4 +1,5 @@
1
1
  # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,unused-variable,attribute-defined-outside-init
2
+ from sys import version_info
2
3
  import unittest
3
4
 
4
5
  from zstdlib.frozen import Freezable, frozen
@@ -104,10 +105,15 @@ class TestFrozen(unittest.TestCase):
104
105
  f1 = F1()
105
106
  self.assertEqual(f1.__init__.__doc__.strip(), "init doc")
106
107
  self.assertEqual(f1.__init__.__name__, "__init__")
107
- self.assertEqual(f1.__init__.__qualname__, "TestFrozen.test_metadata.<locals>.F2.__init__")
108
+ self.assertEqual(f1.__init__.__qualname__, "TestFrozen.test_metadata.<locals>.F1.__init__")
108
109
  self.assertTupleEqual(f1.__init__.__defaults__, (1,))
109
110
  self.assertDictEqual(f1.__init__.__kwdefaults__, {"b": False})
110
- self.assertDictEqual(f1.__init__.__annotations__, {"a": int, "b": bool, "return": None})
111
+ if version_info <= (3, 13) or hasattr(f1.__init__, "__annotations__"):
112
+ self.assertDictEqual(f1.__init__.__annotations__, {"a": int, "b": bool, "return": None})
113
+ else: # annotations changed in 3.14, .__annotations__ *might* not exist
114
+ from annotationlib import get_annotations
115
+
116
+ self.assertDictEqual(get_annotations(f1.__init__), {"a": int, "b": bool, "return": None})
111
117
 
112
118
  def test_frozen_custom(self):
113
119
  @frozen("custom")
@@ -1,4 +1,4 @@
1
- __version__ = "0.2.0"
1
+ __version__ = "0.3.2"
2
2
 
3
3
  from .not_set import NotSetType, NotSet
4
4
  from .frozen import Freezable, frozen
@@ -39,15 +39,43 @@ class RawColor:
39
39
  self.background = background
40
40
  self.value = self.color.value + (10 if self.background else 0) + (60 if self.bright else 0)
41
41
 
42
+ @classmethod
43
+ @cache
44
+ def _parse_color(cls, item: str) -> str:
45
+ """Construct a color following the rules defined by Color"""
46
+ item = item.lower()
47
+ if "bright_bg" in item:
48
+ raise ValueError("bg_ must precede bright_ in color specification")
49
+ pat = f"(:?bright_)?({'|'.join(list(PureColor.__members__))})"
50
+ shx = re.findall(f"(bg_){pat}", item)
51
+ if len(shx) > 1:
52
+ raise ValueError(f"Multiple background colors specified in {item}")
53
+ bg = None
54
+ if len(shx) == 1:
55
+ item = item.replace("".join(shx[0]), "")
56
+ bg = cls(shx[0][-1], bright="bright_" in shx[0], background=True)
57
+ shx = re.findall(pat, item)
58
+ if len(shx) > 1:
59
+ raise ValueError(f"Multiple foreground colors specified in {item}")
60
+ fg = None
61
+ if len(shx) == 1:
62
+ item = item.replace("".join(shx[0]), "")
63
+ fg = cls(shx[0][-1], bright="bright_" in shx[0])
64
+ # Determine modifiers and construct the color
65
+ attrs = [i for i in item.split("_") if i]
66
+ if any(bad := [i for i in attrs if i not in MODIFIERS]):
67
+ raise ValueError(f"Unknown modifiers(s): {', '.join(bad)}")
68
+ return _generate_code(fg, bg, {i: True for i in MODIFIERS if i in attrs})
69
+
42
70
 
43
71
  class _ColorMeta(type):
44
72
  """
45
73
  A metaclass that implements __getattr__ at the class level for Color
46
74
  """
47
75
 
48
- def __getattr__(cls, item: str) -> "Color":
76
+ def __getattr__(cls, item: str) -> Color:
49
77
  try:
50
- return cls(code=_parse_color(item))
78
+ return cls(code=RawColor._parse_color(item))
51
79
  except ValueError as e:
52
80
  raise AttributeError(str(e)) from e
53
81
 
@@ -91,7 +119,7 @@ class Color(metaclass=_ColorMeta):
91
119
  elif isinstance(fmt, RawColor):
92
120
  self.code = f"{_PREFIX}{fmt.value}m"
93
121
  else:
94
- self.code = _parse_color(fmt) if isinstance(fmt, str) else fmt.code
122
+ self.code = RawColor._parse_color(fmt) if isinstance(fmt, str) else fmt.code
95
123
 
96
124
  def __call__(self, string: str) -> str:
97
125
  """
@@ -145,34 +173,3 @@ def _generate_code(
145
173
  if background:
146
174
  ints.append(background.value)
147
175
  return f"{_PREFIX}{';'.join(map(str, ints))}m"
148
-
149
-
150
- @cache
151
- def _parse_color(item: str) -> str:
152
- """
153
- Construct a color following the rules defined by Color
154
- This method is not a classmethod since python3.13 deprecates mixing classmethod and cache
155
- """
156
- item = item.lower()
157
- if "bright_bg" in item:
158
- raise ValueError("bg_ must precede bright_ in color specification")
159
- pat = f"(:?bright_)?({'|'.join(list(PureColor.__members__))})"
160
- shx = re.findall(f"(bg_){pat}", item)
161
- if len(shx) > 1:
162
- raise ValueError(f"Multiple background colors specified in {item}")
163
- bg: RawColor | None = None
164
- if len(shx) == 1:
165
- item = item.replace("".join(shx[0]), "")
166
- bg = RawColor(shx[0][-1], bright="bright_" in shx[0], background=True)
167
- shx = re.findall(pat, item)
168
- if len(shx) > 1:
169
- raise ValueError(f"Multiple foreground colors specified in {item}")
170
- fg: RawColor | None = None
171
- if len(shx) == 1:
172
- item = item.replace("".join(shx[0]), "")
173
- fg = RawColor(shx[0][-1], bright="bright_" in shx[0])
174
- # Determine modifiers and construct the color
175
- attrs = [i for i in item.split("_") if i]
176
- if any(bad := [i for i in attrs if i not in MODIFIERS]):
177
- raise ValueError(f"Unknown modifiers(s): {', '.join(bad)}")
178
- return _generate_code(fg, bg, {i: True for i in MODIFIERS if i in attrs})
@@ -1,5 +1,6 @@
1
1
  from collections.abc import Iterator
2
2
  from typing import Any
3
+ import annotationlib
3
4
 
4
5
 
5
6
  class _Auto:
@@ -17,6 +18,7 @@ class EnumType(type):
17
18
  These 'Enum' classes may not be modified after creation
18
19
  Items of annotation types in type_check will be type-checked
19
20
  Duplicate values (as determined by set()) are not allowed unless dupes_ok is True
21
+ If dupe-checking is enabled, values must be hashable
20
22
  """
21
23
 
22
24
  _AUTO_PREFIX = "Enum_Auto_"
@@ -36,38 +38,46 @@ class EnumType(type):
36
38
 
37
39
  @classmethod
38
40
  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
+ mcs,
42
+ attrs: dict,
43
+ dupes_ok: bool,
44
+ ans: dict[str, type | None],
45
+ ) -> dict[str, tuple[type | None, Any]]:
41
46
  """
42
47
  Generate enum entries from attrs, checking for duplicates and missing annotations
43
48
  Does not check for empty enum or type correctness
49
+ Updates ans with new annotations
44
50
  """
45
51
  seen = set()
46
52
  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}")
53
+ ret = {}
54
+ for a_name, value in ((i, k) for i, k in attrs.items() if not i.startswith("__")):
55
+ # If auto, ensure not annotated, then annotate
56
+ if value is auto:
57
+ auto_val += 1
58
+ value = mcs._AutoValue(auto_val)
59
+ if a_name in ans:
60
+ raise TypeError("Auto enum entries may not be type annotated")
61
+ ans[a_name] = mcs._AutoValue
62
+ # If annotated as auto, complain
63
+ elif isinstance(value, mcs._AutoValue):
64
+ raise ValueError("Enum entries may not be of type EnumType._AutoValue")
65
+ # Dupe check, requires hash-able values
66
+ if not dupes_ok:
67
+ if value in seen:
68
+ raise ValueError(f"Duplicate enum value: {value}")
69
+ try:
62
70
  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
+ except TypeError:
72
+ raise TypeError(f"Unhashable enum value cannot be dupe checked: {value}") from None
73
+ # Save annotation and value to entries
74
+ try:
75
+ ret[a_name] = (ans[a_name], value)
76
+ except KeyError:
77
+ raise ValueError("Non-auto enum entries must be type annotated") from None
78
+ return ret
79
+
80
+ # pylint: disable=too-many-arguments, dangerous-default-value, too-many-locals
71
81
  def __new__(
72
82
  mcs,
73
83
  name,
@@ -82,24 +92,26 @@ class EnumType(type):
82
92
  # Disallow undesired instance methods
83
93
  if bad := attrs.keys() & {"__init__", "__new__", "__entries__"}:
84
94
  raise AttributeError(f"Illegal methods in Enum class: {', '.join(bad)}")
85
- # Generate entries
86
- _entries, _annotations = mcs._gen_entries_and_annotations(attrs, dupes_ok)
95
+ # Generate entries and annotations
96
+ ans = annotationlib.get_annotations(super().__new__(mcs, name, bases, attrs))
97
+ entries_ = mcs._gen_entries_and_annotations(attrs, dupes_ok, ans)
87
98
  # Validate entries
88
- if no_value := (_annotations.keys() - _entries.keys()):
99
+ if no_value := (ans.keys() - entries_.keys()):
89
100
  raise ValueError(f"Entries must have a value: {' '.join(no_value)}")
90
- if (not empty_ok) and not _entries:
101
+ if (not empty_ok) and not entries_:
91
102
  raise ValueError("Enum type may not be empty")
92
103
  if type_check:
93
- for a_name, (ann, val) in _entries.items():
104
+ for a_name, (ann, val) in entries_.items():
94
105
  if ann in type_check and (
95
106
  (ann is None is not val) or (ann is not None and not isinstance(val, ann))
96
107
  ):
97
108
  raise TypeError(f"Entry {a_name} is not of type {ann}")
98
109
  # Construct class
99
- attrs["__entries__"] = _entries
100
- attrs["__annotations__"] = _annotations
110
+ attrs["__entries__"] = entries_ # For our usage later
101
111
  attrs["__init__"] = attrs["__new__"] = NotImplemented
102
- return type.__new__(mcs, name, bases, attrs, **kwargs)
112
+ ret = type.__new__(mcs, name, bases, attrs, **kwargs)
113
+ super().__setattr__(ret, "__annotations__", ans) # Since we disable this method, use super()
114
+ return ret
103
115
 
104
116
  # Disallow modification
105
117
 
@@ -1,4 +1,3 @@
1
- from __future__ import annotations
2
1
  from logging import CRITICAL, ERROR, WARNING, INFO, DEBUG, Formatter
3
2
  from collections import defaultdict
4
3
  from typing import TYPE_CHECKING
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zstdlib
3
- Version: 0.2.0
3
+ Version: 0.3.2
4
4
  Summary: A set of useful python utilities
5
5
  License: GPLv3
6
6
  Project-URL: Homepage, https://github.com/zwimer/zstdlib
7
7
  Keywords: remote,pipe
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Programming Language :: Python :: 3.14
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
12
- Requires-Python: >=3.12
12
+ Requires-Python: >=3.14
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
  Dynamic: license-file
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