zstdlib 0.6.0__tar.gz → 0.8.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.
- {zstdlib-0.6.0 → zstdlib-0.8.0}/PKG-INFO +2 -1
- {zstdlib-0.6.0 → zstdlib-0.8.0}/pyproject.toml +43 -13
- {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/log/base.py +15 -12
- {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/log/test_trace.py +1 -2
- {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_ansi.py +9 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_enum.py +4 -3
- {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_frozen.py +38 -40
- {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_io.py +16 -17
- zstdlib-0.8.0/tests/test_lazy.py +25 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_not_set.py +1 -1
- {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_singleton.py +10 -10
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/__init__.py +4 -2
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/ansi.py +46 -30
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/enum.py +31 -33
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/frozen.py +18 -15
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/io.py +47 -60
- zstdlib-0.8.0/zstdlib/lazy.py +31 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/log/cute.py +19 -21
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/log/trace.py +14 -14
- zstdlib-0.8.0/zstdlib/macos.py +198 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/not_set.py +1 -2
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/signal_cm.py +8 -7
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/singleton.py +21 -23
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib.egg-info/PKG-INFO +2 -1
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib.egg-info/SOURCES.txt +4 -0
- zstdlib-0.8.0/zstdlib.egg-info/requires.txt +1 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/LICENSE +0 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/README.md +0 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/setup.cfg +0 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/__init__.py +0 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/log/__init__.py +0 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/log/test_cute.py +0 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/log/__init__.py +0 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/py.typed +0 -0
- {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib.egg-info/dependency_links.txt +0 -0
- {zstdlib-0.6.0 → zstdlib-0.8.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.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: A set of useful python utilities
|
|
5
5
|
License: GPLv3
|
|
6
6
|
Project-URL: Homepage, https://github.com/zwimer/zstdlib
|
|
@@ -11,6 +11,7 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
|
11
11
|
Requires-Python: >=3.14
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
+
Requires-Dist: osascript2
|
|
14
15
|
Dynamic: license-file
|
|
15
16
|
|
|
16
17
|
# zstdlib
|
|
@@ -5,16 +5,19 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "zstdlib"
|
|
7
7
|
classifiers = [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
"Programming Language :: Python :: 3",
|
|
9
|
+
"Programming Language :: Python :: 3.14",
|
|
10
|
+
"Development Status :: 5 - Production/Stable",
|
|
11
|
+
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
|
12
12
|
]
|
|
13
13
|
license = {text = "GPLv3"}
|
|
14
14
|
description = "A set of useful python utilities"
|
|
15
15
|
urls = {Homepage = "https://github.com/zwimer/zstdlib"}
|
|
16
16
|
requires-python = ">= 3.14"
|
|
17
17
|
dynamic = ["version"]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"osascript2" # For macOS
|
|
20
|
+
]
|
|
18
21
|
|
|
19
22
|
[project.readme]
|
|
20
23
|
file = "README.md"
|
|
@@ -39,12 +42,12 @@ attr = "zstdlib.__version__"
|
|
|
39
42
|
ignore-paths = '^tests/.*$'
|
|
40
43
|
[tool.pylint."MESSAGES CONTROL"]
|
|
41
44
|
disable = [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
"unnecessary-lambda-assignment",
|
|
46
|
+
"method-cache-max-size-none",
|
|
47
|
+
"missing-module-docstring",
|
|
48
|
+
"too-few-public-methods",
|
|
49
|
+
"line-too-long",
|
|
50
|
+
"invalid-name"
|
|
48
51
|
]
|
|
49
52
|
|
|
50
53
|
[tool.black]
|
|
@@ -54,13 +57,40 @@ target-version = ["py314"]
|
|
|
54
57
|
[tool.ruff]
|
|
55
58
|
line-length = 110
|
|
56
59
|
[tool.ruff.lint]
|
|
57
|
-
ignore=[
|
|
60
|
+
ignore=[
|
|
61
|
+
"ANN401",
|
|
62
|
+
"D10", "D2", "D403", "D415",
|
|
63
|
+
"E731",
|
|
64
|
+
"I001",
|
|
65
|
+
"PLR0913", "PLR2004",
|
|
66
|
+
"S101", "S603",
|
|
67
|
+
"TID252",
|
|
68
|
+
]
|
|
69
|
+
extend-select = [
|
|
70
|
+
"ANN",
|
|
71
|
+
"ARG",
|
|
72
|
+
"ASYNC",
|
|
73
|
+
"C4",
|
|
74
|
+
"D",
|
|
75
|
+
"DTZ",
|
|
76
|
+
"PERF",
|
|
77
|
+
"PGH",
|
|
78
|
+
"PIE",
|
|
79
|
+
"PL",
|
|
80
|
+
"PTH",
|
|
81
|
+
"RET",
|
|
82
|
+
"S",
|
|
83
|
+
"SIM",
|
|
84
|
+
"TID"
|
|
85
|
+
]
|
|
86
|
+
[tool.ruff.lint.pydocstyle]
|
|
87
|
+
convention = "google"
|
|
58
88
|
[tool.ruff.lint.per-file-ignores]
|
|
59
89
|
"__init__.py" = ["F401", "F403"]
|
|
60
|
-
"tests/*" = ["DTZ005"]
|
|
90
|
+
"tests/*" = ["DTZ005", "PGH003", "PLW0108"]
|
|
61
91
|
|
|
62
92
|
[tool.bandit]
|
|
63
|
-
skips = ["B101", "B104", "B201"]
|
|
93
|
+
skips = ["B101", "B104", "B201", "B404", "B603"]
|
|
64
94
|
|
|
65
95
|
[tool.vulture]
|
|
66
96
|
ignore_names = []
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
from contextlib import AbstractContextManager, contextmanager
|
|
2
|
+
from collections.abc import Callable, Iterator
|
|
1
3
|
from logging.handlers import QueueHandler
|
|
2
|
-
from contextlib import contextmanager
|
|
3
|
-
from collections.abc import Callable
|
|
4
4
|
from typing import ClassVar, Any
|
|
5
5
|
from threading import Lock
|
|
6
6
|
import logging
|
|
@@ -8,7 +8,7 @@ import queue
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@contextmanager
|
|
11
|
-
def _cml(logger: logging.Logger, exit_func: Callable[[], Any]):
|
|
11
|
+
def _cml(logger: logging.Logger, exit_func: Callable[[], Any]) -> Iterator[logging.Logger]:
|
|
12
12
|
try:
|
|
13
13
|
yield logger
|
|
14
14
|
finally:
|
|
@@ -16,9 +16,7 @@ def _cml(logger: logging.Logger, exit_func: Callable[[], Any]):
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class LeftBase:
|
|
19
|
-
"""
|
|
20
|
-
A base class for tests that hijack loggers
|
|
21
|
-
"""
|
|
19
|
+
"""A base class for tests that hijack loggers"""
|
|
22
20
|
|
|
23
21
|
_hijacked: ClassVar[set[logging.Logger]] = set() # Set of all loggers that have ever been hijacked
|
|
24
22
|
_qmap: ClassVar[dict[logging.Logger, queue.Queue[logging.LogRecord]]] = {}
|
|
@@ -28,9 +26,13 @@ class LeftBase:
|
|
|
28
26
|
messages: ClassVar[dict[logging.Logger, list[str]]] = {}
|
|
29
27
|
|
|
30
28
|
@classmethod
|
|
31
|
-
def hijack(
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
def hijack(
|
|
30
|
+
cls, name: str, reuse: bool = False, fmt: logging.Formatter | None = None
|
|
31
|
+
) -> AbstractContextManager[logging.Logger]:
|
|
32
|
+
"""Create a hijacked logger
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
A logger configured to write to an internal queue
|
|
34
36
|
"""
|
|
35
37
|
log = logging.getLogger(name)
|
|
36
38
|
q: queue.Queue[logging.LogRecord] = queue.Queue()
|
|
@@ -48,9 +50,10 @@ class LeftBase:
|
|
|
48
50
|
|
|
49
51
|
@classmethod
|
|
50
52
|
def _restore(cls, logger: logging.Logger) -> None:
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
:
|
|
53
|
+
"""Restore the logger and read all messages from the queue
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
The messages stored by the logger queue
|
|
54
57
|
"""
|
|
55
58
|
if len(logger.handlers) != 1 or not isinstance(qh := logger.handlers[0], QueueHandler):
|
|
56
59
|
raise RuntimeError("Logger not hijacked")
|
|
@@ -11,8 +11,7 @@ from .base import LeftBase
|
|
|
11
11
|
class TestTrace(LeftBase, unittest.TestCase):
|
|
12
12
|
|
|
13
13
|
def test_trace(self) -> None:
|
|
14
|
-
"""
|
|
15
|
-
Avoid splitting up into multiple functions since trace.install() affects global state
|
|
14
|
+
"""Avoid splitting up into multiple functions since trace.install() affects global state
|
|
16
15
|
Keeping this as one function ensures that the tests are run in order
|
|
17
16
|
"""
|
|
18
17
|
# Test bad installs
|
|
@@ -51,6 +51,15 @@ class TestAnsiColor(unittest.TestCase):
|
|
|
51
51
|
self.assertEqual(f"\033[31;42m{msg}\033[0m", Color.red_bg_green(msg))
|
|
52
52
|
self.assertEqual(f"\033[1;3;32;101m{msg}\033[0m", Color.bg_bright_red_bold_italic_green(msg))
|
|
53
53
|
|
|
54
|
+
def test_hash(self) -> None:
|
|
55
|
+
c1 = Color("black")
|
|
56
|
+
c2 = Color("black")
|
|
57
|
+
other = Color("blue")
|
|
58
|
+
self.assertEqual(c1, c2)
|
|
59
|
+
self.assertNotEqual(c1, other)
|
|
60
|
+
self.assertEqual(hash(c1), hash(c2))
|
|
61
|
+
self.assertNotEqual(hash(c1), hash(other))
|
|
62
|
+
|
|
54
63
|
|
|
55
64
|
if __name__ == "__main__":
|
|
56
65
|
unittest.main()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,unused-variable
|
|
2
|
+
from typing import Self
|
|
2
3
|
import unittest
|
|
3
4
|
|
|
4
5
|
from zstdlib.enum import EnumType, Enum, entries, values, auto
|
|
@@ -63,7 +64,7 @@ class TestEnumType(unittest.TestCase):
|
|
|
63
64
|
class ET1(metaclass=EnumType):
|
|
64
65
|
arg1: int = 1
|
|
65
66
|
|
|
66
|
-
def __init__(self):
|
|
67
|
+
def __init__(self) -> None:
|
|
67
68
|
pass
|
|
68
69
|
|
|
69
70
|
with self.assertRaises(AttributeError):
|
|
@@ -71,8 +72,8 @@ class TestEnumType(unittest.TestCase):
|
|
|
71
72
|
class ET2(metaclass=EnumType):
|
|
72
73
|
arg1: int = 1
|
|
73
74
|
|
|
74
|
-
def __new__(cls):
|
|
75
|
-
|
|
75
|
+
def __new__(cls) -> Self:
|
|
76
|
+
return super().__new__(cls)
|
|
76
77
|
|
|
77
78
|
class ET3(metaclass=EnumType):
|
|
78
79
|
arg1: int = 0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
|
|
2
|
-
from
|
|
1
|
+
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
|
|
2
|
+
from annotationlib import get_annotations
|
|
3
3
|
import unittest
|
|
4
4
|
|
|
5
5
|
from zstdlib.frozen import Freezable, frozen
|
|
@@ -7,22 +7,24 @@ from zstdlib.frozen import Freezable, frozen
|
|
|
7
7
|
|
|
8
8
|
class TestFreezable(unittest.TestCase):
|
|
9
9
|
|
|
10
|
-
def test_unfrozen(self):
|
|
10
|
+
def test_unfrozen(self) -> None:
|
|
11
11
|
class F1(Freezable):
|
|
12
|
-
|
|
12
|
+
def __init__(self, a: int) -> None:
|
|
13
|
+
super().__init__()
|
|
14
|
+
self.a = a
|
|
13
15
|
|
|
14
|
-
f1 = F1()
|
|
15
|
-
f1.a = 1
|
|
16
|
+
f1 = F1(1)
|
|
16
17
|
self.assertEqual(f1.a, 1)
|
|
17
18
|
del f1.a
|
|
18
19
|
self.assertIs(getattr(f1, "a", None), None)
|
|
19
20
|
|
|
20
|
-
def test_frozen(self):
|
|
21
|
+
def test_frozen(self) -> None:
|
|
21
22
|
class F1(Freezable):
|
|
22
|
-
|
|
23
|
+
def __init__(self, a: int) -> None:
|
|
24
|
+
super().__init__()
|
|
25
|
+
self.a = a
|
|
23
26
|
|
|
24
|
-
f1 = F1()
|
|
25
|
-
f1.a = 1
|
|
27
|
+
f1 = F1(1)
|
|
26
28
|
f1.freeze()
|
|
27
29
|
self.assertEqual(f1.a, 1)
|
|
28
30
|
with self.assertRaises(AttributeError):
|
|
@@ -31,12 +33,13 @@ class TestFreezable(unittest.TestCase):
|
|
|
31
33
|
del f1.a
|
|
32
34
|
f1.freeze() # Should not raise an error
|
|
33
35
|
|
|
34
|
-
def test_thaw(self):
|
|
36
|
+
def test_thaw(self) -> None:
|
|
35
37
|
class F1(Freezable):
|
|
36
|
-
|
|
38
|
+
def __init__(self, a: int) -> None:
|
|
39
|
+
super().__init__()
|
|
40
|
+
self.a = a
|
|
37
41
|
|
|
38
|
-
f1 = F1()
|
|
39
|
-
f1.a = 1
|
|
42
|
+
f1 = F1(1)
|
|
40
43
|
f1.freeze()
|
|
41
44
|
self.assertEqual(f1.a, 1)
|
|
42
45
|
f1.thaw()
|
|
@@ -45,12 +48,13 @@ class TestFreezable(unittest.TestCase):
|
|
|
45
48
|
del f1.a
|
|
46
49
|
self.assertIs(getattr(f1, "a", None), None)
|
|
47
50
|
|
|
48
|
-
def test_permanent_freeze(self):
|
|
51
|
+
def test_permanent_freeze(self) -> None:
|
|
49
52
|
class F1(Freezable):
|
|
50
|
-
|
|
53
|
+
def __init__(self, a: int) -> None:
|
|
54
|
+
super().__init__()
|
|
55
|
+
self.a = a
|
|
51
56
|
|
|
52
|
-
f1 = F1()
|
|
53
|
-
f1.a = 1
|
|
57
|
+
f1 = F1(1)
|
|
54
58
|
f1.freeze() # To ensure that .freeze(permanent=True) works when already frozen too
|
|
55
59
|
f1.freeze(permanent=True)
|
|
56
60
|
f1.freeze(permanent=True) # Should not raise an error
|
|
@@ -60,7 +64,7 @@ class TestFreezable(unittest.TestCase):
|
|
|
60
64
|
with self.assertRaises(AttributeError):
|
|
61
65
|
f1.a = 1
|
|
62
66
|
|
|
63
|
-
def test_properties(self):
|
|
67
|
+
def test_properties(self) -> None:
|
|
64
68
|
|
|
65
69
|
class F1(Freezable):
|
|
66
70
|
pass
|
|
@@ -78,10 +82,10 @@ class TestFreezable(unittest.TestCase):
|
|
|
78
82
|
|
|
79
83
|
class TestFrozen(unittest.TestCase):
|
|
80
84
|
|
|
81
|
-
def test_frozen(self):
|
|
85
|
+
def test_frozen(self) -> None:
|
|
82
86
|
@frozen
|
|
83
87
|
class F1:
|
|
84
|
-
def __init__(self):
|
|
88
|
+
def __init__(self) -> None:
|
|
85
89
|
self.a = 1
|
|
86
90
|
self.b = 1
|
|
87
91
|
del self.b
|
|
@@ -94,34 +98,28 @@ class TestFrozen(unittest.TestCase):
|
|
|
94
98
|
with self.assertRaises(AttributeError):
|
|
95
99
|
del f1.a
|
|
96
100
|
|
|
97
|
-
def test_metadata(self):
|
|
101
|
+
def test_metadata(self) -> None:
|
|
98
102
|
@frozen
|
|
99
103
|
class F1:
|
|
100
104
|
def __init__(self, a: int = 1, *, b: bool = False) -> None:
|
|
101
|
-
"""
|
|
102
|
-
init doc
|
|
103
|
-
"""
|
|
105
|
+
"""init doc"""
|
|
104
106
|
|
|
105
107
|
f1 = F1()
|
|
106
|
-
|
|
107
|
-
self.assertEqual(
|
|
108
|
-
self.assertEqual(
|
|
109
|
-
self.
|
|
110
|
-
self.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
self.assertDictEqual(get_annotations(f1.__init__), {"a": int, "b": bool, "return": None})
|
|
117
|
-
|
|
118
|
-
def test_frozen_custom(self):
|
|
108
|
+
f1_init = f1.__init__ # type: ignore[misc]
|
|
109
|
+
self.assertEqual(f1_init.__doc__.strip(), "init doc")
|
|
110
|
+
self.assertEqual(f1_init.__name__, "__init__")
|
|
111
|
+
self.assertEqual(f1_init.__qualname__, "TestFrozen.test_metadata.<locals>.F1.__init__")
|
|
112
|
+
self.assertTupleEqual(f1_init.__defaults__, (1,))
|
|
113
|
+
self.assertDictEqual(f1_init.__kwdefaults__, {"b": False})
|
|
114
|
+
self.assertDictEqual(get_annotations(f1_init), {"a": int, "b": bool, "return": None})
|
|
115
|
+
|
|
116
|
+
def test_frozen_custom(self) -> None:
|
|
119
117
|
@frozen("custom")
|
|
120
118
|
class F1:
|
|
121
|
-
def __init__(self):
|
|
119
|
+
def __init__(self) -> None:
|
|
122
120
|
self.a = 1
|
|
123
121
|
|
|
124
|
-
def custom(self):
|
|
122
|
+
def custom(self) -> None:
|
|
125
123
|
self.a = 2
|
|
126
124
|
|
|
127
125
|
f1 = F1()
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from typing import Any
|
|
1
2
|
import tempfile
|
|
2
3
|
import unittest
|
|
3
4
|
|
|
@@ -5,16 +6,14 @@ from zstdlib.io import ProtectedFile, BinaryIO, TextIO, io
|
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class RamFile(tempfile.SpooledTemporaryFile):
|
|
8
|
-
"""
|
|
9
|
-
A file-like object that stores its contents in memory.
|
|
10
|
-
"""
|
|
9
|
+
"""A file-like object that stores its contents in memory."""
|
|
11
10
|
|
|
12
|
-
rollover =
|
|
11
|
+
rollover = NotImplemented # Disallow rollover
|
|
13
12
|
|
|
14
|
-
def __init__(self, *args, mode: str = "w+", **kwargs):
|
|
13
|
+
def __init__(self, *args: Any, mode: str = "w+", **kwargs: Any) -> None:
|
|
15
14
|
super().__init__(10**9, *args, mode=mode, **kwargs)
|
|
16
15
|
|
|
17
|
-
def load(self, x):
|
|
16
|
+
def load(self, x: str) -> None:
|
|
18
17
|
self.write(x)
|
|
19
18
|
self.flush()
|
|
20
19
|
self.seek(self.tell() - len(x))
|
|
@@ -22,7 +21,7 @@ class RamFile(tempfile.SpooledTemporaryFile):
|
|
|
22
21
|
|
|
23
22
|
class TestIO(unittest.TestCase):
|
|
24
23
|
|
|
25
|
-
def test_init(self):
|
|
24
|
+
def test_init(self) -> None:
|
|
26
25
|
with RamFile(mode="wb+") as fb, RamFile() as fs:
|
|
27
26
|
ts = TextIO(fs)
|
|
28
27
|
tb = BinaryIO(fb)
|
|
@@ -33,7 +32,7 @@ class TestIO(unittest.TestCase):
|
|
|
33
32
|
self.assertIs(ts, io(fs))
|
|
34
33
|
self.assertIs(tb, io(fb))
|
|
35
34
|
|
|
36
|
-
def test_protected_file(self):
|
|
35
|
+
def test_protected_file(self) -> None:
|
|
37
36
|
with RamFile() as f:
|
|
38
37
|
p = ProtectedFile(f)
|
|
39
38
|
self.assertIs(f, p.raw)
|
|
@@ -42,13 +41,13 @@ class TestIO(unittest.TestCase):
|
|
|
42
41
|
with self.assertRaises(AttributeError):
|
|
43
42
|
getattr(p, i)
|
|
44
43
|
|
|
45
|
-
def test_unread(self):
|
|
44
|
+
def test_unread(self) -> None:
|
|
46
45
|
with RamFile() as raw:
|
|
47
46
|
f = io(raw)
|
|
48
|
-
f.unread("hello")
|
|
47
|
+
f.unread("hello") # type: ignore[arg-type] # STF types as bytes regardless of mode :/
|
|
49
48
|
self.assertEqual("hello", f._buffer)
|
|
50
49
|
|
|
51
|
-
def test_read(self):
|
|
50
|
+
def test_read(self) -> None:
|
|
52
51
|
with RamFile() as raw:
|
|
53
52
|
raw.load("foobaz")
|
|
54
53
|
f = io(raw)
|
|
@@ -56,18 +55,18 @@ class TestIO(unittest.TestCase):
|
|
|
56
55
|
self.assertEqual("fo", f.read(2))
|
|
57
56
|
self.assertEqual("o", f.read(1))
|
|
58
57
|
# Test read of buffer
|
|
59
|
-
f.unread("bar")
|
|
58
|
+
f.unread("bar") # type: ignore[arg-type] # STF types as bytes regardless of mode :/
|
|
60
59
|
self.assertEqual("b", f.read(1))
|
|
61
60
|
# Test read of file + buffer
|
|
62
|
-
f.unread("foob")
|
|
61
|
+
f.unread("foob") # type: ignore[arg-type] # STF types as bytes regardless of mode :/
|
|
63
62
|
self.assertEqual("foobar", f.read(6))
|
|
64
|
-
f.unread("foobar")
|
|
63
|
+
f.unread("foobar") # type: ignore[arg-type] # STF types as bytes regardless of mode :/
|
|
65
64
|
self.assertEqual("foobarbaz", f.read())
|
|
66
65
|
|
|
67
|
-
def test_peek(self):
|
|
66
|
+
def test_peek(self) -> None:
|
|
68
67
|
with RamFile() as raw:
|
|
69
68
|
f = io(raw)
|
|
70
|
-
f.unread("foo")
|
|
69
|
+
f.unread("foo") # type: ignore[arg-type] # STF types as bytes regardless of mode :/
|
|
71
70
|
# Test peek of buffer
|
|
72
71
|
for i in range(3):
|
|
73
72
|
self.assertEqual("fo", f.peek(2))
|
|
@@ -78,7 +77,7 @@ class TestIO(unittest.TestCase):
|
|
|
78
77
|
self.assertEqual("ba", f.peek(2))
|
|
79
78
|
self.assertEqual("bar", f.read())
|
|
80
79
|
# Test peek of both
|
|
81
|
-
f.unread("baz")
|
|
80
|
+
f.unread("baz") # type: ignore[arg-type] # STF types as bytes regardless of mode :/
|
|
82
81
|
raw.load("qux")
|
|
83
82
|
for i in range(3):
|
|
84
83
|
for k in range(5):
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
from zstdlib.lazy import Str
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _append(buf: list[str], *, obj: str) -> str:
|
|
8
|
+
buf.append(obj)
|
|
9
|
+
return str(len(buf))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestLazy(unittest.TestCase):
|
|
13
|
+
|
|
14
|
+
def test_str(self) -> None:
|
|
15
|
+
buf: list[str] = []
|
|
16
|
+
obj = "apples"
|
|
17
|
+
x = Str(_append, buf, obj=obj)
|
|
18
|
+
self.assertEqual(buf, [])
|
|
19
|
+
one = str(x) # Invoke Lazy Str
|
|
20
|
+
self.assertEqual(one, "1")
|
|
21
|
+
self.assertEqual(buf, [obj])
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
unittest.main()
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
from threading import Thread, Lock
|
|
4
4
|
from datetime import datetime
|
|
5
|
+
from typing import Any
|
|
5
6
|
from time import sleep
|
|
6
7
|
import unittest
|
|
7
8
|
|
|
@@ -32,12 +33,12 @@ class TestSingleton(unittest.TestCase):
|
|
|
32
33
|
with self.assertRaises(TypeError): # Not ok to define __init_subclass__ in Singleton
|
|
33
34
|
|
|
34
35
|
class ST3(Singleton):
|
|
35
|
-
def __init_subclass__(cls, **
|
|
36
|
+
def __init_subclass__(cls, **_: Any) -> None:
|
|
36
37
|
pass
|
|
37
38
|
|
|
38
|
-
def test_instance(self):
|
|
39
|
+
def test_instance(self) -> None:
|
|
39
40
|
class ST(Singleton):
|
|
40
|
-
def __init__(self, _):
|
|
41
|
+
def __init__(self, _: Any) -> None:
|
|
41
42
|
pass
|
|
42
43
|
|
|
43
44
|
self.assertTrue(issubclass(NoInstanceError, TypeError))
|
|
@@ -47,11 +48,10 @@ class TestSingleton(unittest.TestCase):
|
|
|
47
48
|
self.assertIs(ST.instance(), obj)
|
|
48
49
|
|
|
49
50
|
with self.assertRaises(TypeError):
|
|
50
|
-
ST.instance(4)
|
|
51
|
+
ST.instance(4) # type: ignore[call-arg]
|
|
51
52
|
|
|
52
|
-
def test_multi_thread(self):
|
|
53
|
-
"""
|
|
54
|
-
Ensure that Singleton is thread safe and that constructing an object doesn't delay other threads
|
|
53
|
+
def test_multi_thread(self) -> None:
|
|
54
|
+
"""Ensure that Singleton is thread safe and that constructing an object doesn't delay other threads
|
|
55
55
|
Technically this is more of a heuristic, but failing is extremely unlikely if this is thread safe
|
|
56
56
|
Verifies that classes can construct concurrently, thread-safely, and only one instance is created
|
|
57
57
|
"""
|
|
@@ -96,11 +96,11 @@ class TestSingleton(unittest.TestCase):
|
|
|
96
96
|
|
|
97
97
|
# Verify concurrent construction of ST1 and ST2
|
|
98
98
|
class Fast(Singleton):
|
|
99
|
-
def __init__(self):
|
|
99
|
+
def __init__(self) -> None:
|
|
100
100
|
sleep(0.3)
|
|
101
101
|
|
|
102
102
|
class Slow(Singleton):
|
|
103
|
-
def __init__(self):
|
|
103
|
+
def __init__(self) -> None:
|
|
104
104
|
sleep(0.8)
|
|
105
105
|
|
|
106
106
|
run_all(simple("f1", Slow, 0), simple("f2", Fast, 0.2))
|
|
@@ -120,7 +120,7 @@ class TestSingleton(unittest.TestCase):
|
|
|
120
120
|
class ST(Singleton):
|
|
121
121
|
first = True
|
|
122
122
|
|
|
123
|
-
def __init__(self):
|
|
123
|
+
def __init__(self) -> None:
|
|
124
124
|
if self.first:
|
|
125
125
|
self.first = False
|
|
126
126
|
sleep(0.4)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.8.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
6
|
from .signal_cm import signal_cm
|
|
7
7
|
from .enum import Enum, auto
|
|
8
|
-
from . import log, io
|
|
8
|
+
from . import macos, lazy, log, io
|
|
9
9
|
|
|
10
10
|
__all__ = (
|
|
11
11
|
"Enum",
|
|
@@ -16,6 +16,8 @@ __all__ = (
|
|
|
16
16
|
"auto",
|
|
17
17
|
"frozen",
|
|
18
18
|
"io",
|
|
19
|
+
"lazy",
|
|
19
20
|
"log",
|
|
21
|
+
"macos",
|
|
20
22
|
"signal_cm",
|
|
21
23
|
)
|