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.
- {zstdlib-0.2.0 → zstdlib-0.3.2}/PKG-INFO +3 -3
- {zstdlib-0.2.0 → zstdlib-0.3.2}/pyproject.toml +11 -24
- {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/test_frozen.py +8 -2
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/__init__.py +1 -1
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/ansi.py +31 -34
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/enum.py +45 -33
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/log/cute.py +0 -1
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib.egg-info/PKG-INFO +3 -3
- {zstdlib-0.2.0 → zstdlib-0.3.2}/LICENSE +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/README.md +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/setup.cfg +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/__init__.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/log/__init__.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/log/base.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/log/test_cute.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/log/test_trace.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/not_set.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/test_ansi.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/test_enum.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/test_io.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/tests/test_singleton.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/frozen.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/io.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/log/__init__.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/log/trace.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/not_set.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/py.typed +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib/singleton.py +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib.egg-info/SOURCES.txt +0 -0
- {zstdlib-0.2.0 → zstdlib-0.3.2}/zstdlib.egg-info/dependency_links.txt +0 -0
- {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
|
|
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.
|
|
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
|
+
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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 = ["
|
|
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>.
|
|
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
|
-
|
|
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")
|
|
@@ -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) ->
|
|
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,
|
|
40
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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 := (
|
|
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
|
|
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
|
|
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__"] =
|
|
100
|
-
attrs["__annotations__"] = _annotations
|
|
110
|
+
attrs["__entries__"] = entries_ # For our usage later
|
|
101
111
|
attrs["__init__"] = attrs["__new__"] = NotImplemented
|
|
102
|
-
|
|
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,15 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zstdlib
|
|
3
|
-
Version: 0.2
|
|
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.
|
|
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
|
+
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|