zstdlib 0.6.0__tar.gz → 0.7.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.7.0}/PKG-INFO +1 -1
- {zstdlib-0.6.0 → zstdlib-0.7.0}/pyproject.toml +39 -12
- {zstdlib-0.6.0 → zstdlib-0.7.0}/tests/log/base.py +13 -11
- {zstdlib-0.6.0 → zstdlib-0.7.0}/tests/log/test_trace.py +1 -2
- {zstdlib-0.6.0 → zstdlib-0.7.0}/tests/test_ansi.py +9 -0
- {zstdlib-0.6.0 → zstdlib-0.7.0}/tests/test_enum.py +4 -3
- {zstdlib-0.6.0 → zstdlib-0.7.0}/tests/test_frozen.py +38 -40
- {zstdlib-0.6.0 → zstdlib-0.7.0}/tests/test_io.py +16 -17
- {zstdlib-0.6.0 → zstdlib-0.7.0}/tests/test_not_set.py +1 -1
- {zstdlib-0.6.0 → zstdlib-0.7.0}/tests/test_singleton.py +10 -10
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/__init__.py +1 -1
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/ansi.py +32 -23
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/enum.py +31 -31
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/frozen.py +18 -15
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/io.py +46 -59
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/log/cute.py +19 -21
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/log/trace.py +14 -14
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/not_set.py +1 -2
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/signal_cm.py +8 -7
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/singleton.py +21 -23
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib.egg-info/PKG-INFO +1 -1
- {zstdlib-0.6.0 → zstdlib-0.7.0}/LICENSE +0 -0
- {zstdlib-0.6.0 → zstdlib-0.7.0}/README.md +0 -0
- {zstdlib-0.6.0 → zstdlib-0.7.0}/setup.cfg +0 -0
- {zstdlib-0.6.0 → zstdlib-0.7.0}/tests/__init__.py +0 -0
- {zstdlib-0.6.0 → zstdlib-0.7.0}/tests/log/__init__.py +0 -0
- {zstdlib-0.6.0 → zstdlib-0.7.0}/tests/log/test_cute.py +0 -0
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/log/__init__.py +0 -0
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib/py.typed +0 -0
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib.egg-info/SOURCES.txt +0 -0
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib.egg-info/dependency_links.txt +0 -0
- {zstdlib-0.6.0 → zstdlib-0.7.0}/zstdlib.egg-info/top_level.txt +0 -0
|
@@ -5,10 +5,10 @@ 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"
|
|
@@ -39,12 +39,12 @@ attr = "zstdlib.__version__"
|
|
|
39
39
|
ignore-paths = '^tests/.*$'
|
|
40
40
|
[tool.pylint."MESSAGES CONTROL"]
|
|
41
41
|
disable = [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
"unnecessary-lambda-assignment",
|
|
43
|
+
"method-cache-max-size-none",
|
|
44
|
+
"missing-module-docstring",
|
|
45
|
+
"too-few-public-methods",
|
|
46
|
+
"line-too-long",
|
|
47
|
+
"invalid-name"
|
|
48
48
|
]
|
|
49
49
|
|
|
50
50
|
[tool.black]
|
|
@@ -54,10 +54,37 @@ target-version = ["py314"]
|
|
|
54
54
|
[tool.ruff]
|
|
55
55
|
line-length = 110
|
|
56
56
|
[tool.ruff.lint]
|
|
57
|
-
ignore=[
|
|
57
|
+
ignore=[
|
|
58
|
+
"ANN401",
|
|
59
|
+
"D10", "D2", "D403", "D415",
|
|
60
|
+
"E731",
|
|
61
|
+
"I001",
|
|
62
|
+
"PLR0913", "PLR2004",
|
|
63
|
+
"S101",
|
|
64
|
+
"TID252",
|
|
65
|
+
]
|
|
66
|
+
extend-select = [
|
|
67
|
+
"ANN",
|
|
68
|
+
"ARG",
|
|
69
|
+
"ASYNC",
|
|
70
|
+
"C4",
|
|
71
|
+
"D",
|
|
72
|
+
"DTZ",
|
|
73
|
+
"PERF",
|
|
74
|
+
"PGH",
|
|
75
|
+
"PIE",
|
|
76
|
+
"PL",
|
|
77
|
+
"PTH",
|
|
78
|
+
"RET",
|
|
79
|
+
"S",
|
|
80
|
+
"SIM",
|
|
81
|
+
"TID"
|
|
82
|
+
]
|
|
83
|
+
[tool.ruff.lint.pydocstyle]
|
|
84
|
+
convention = "google"
|
|
58
85
|
[tool.ruff.lint.per-file-ignores]
|
|
59
86
|
"__init__.py" = ["F401", "F403"]
|
|
60
|
-
"tests/*" = ["DTZ005"]
|
|
87
|
+
"tests/*" = ["DTZ005", "PGH003", "PLW0108"]
|
|
61
88
|
|
|
62
89
|
[tool.bandit]
|
|
63
90
|
skips = ["B101", "B104", "B201"]
|
|
@@ -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,12 @@ class LeftBase:
|
|
|
28
26
|
messages: ClassVar[dict[logging.Logger, list[str]]] = {}
|
|
29
27
|
|
|
30
28
|
@classmethod
|
|
31
|
-
def hijack(
|
|
29
|
+
def hijack(
|
|
30
|
+
cls, name: str, reuse: bool = False, fmt: logging.Formatter | None = None
|
|
31
|
+
) -> AbstractContextManager[logging.Logger]:
|
|
32
32
|
"""
|
|
33
|
-
:
|
|
33
|
+
Returns:
|
|
34
|
+
A logger configured to write to an internal queue
|
|
34
35
|
"""
|
|
35
36
|
log = logging.getLogger(name)
|
|
36
37
|
q: queue.Queue[logging.LogRecord] = queue.Queue()
|
|
@@ -48,9 +49,10 @@ class LeftBase:
|
|
|
48
49
|
|
|
49
50
|
@classmethod
|
|
50
51
|
def _restore(cls, logger: logging.Logger) -> None:
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
:
|
|
52
|
+
"""Restore the logger and read all messages from the queue
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
The messages stored by the logger queue
|
|
54
56
|
"""
|
|
55
57
|
if len(logger.handlers) != 1 or not isinstance(qh := logger.handlers[0], QueueHandler):
|
|
56
58
|
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):
|
|
@@ -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)
|
|
@@ -10,9 +10,7 @@ _PREFIX = "\033["
|
|
|
10
10
|
|
|
11
11
|
@unique
|
|
12
12
|
class PureColor(Enum):
|
|
13
|
-
"""
|
|
14
|
-
An enum of ansi color values
|
|
15
|
-
"""
|
|
13
|
+
"""An enum of ansi color values"""
|
|
16
14
|
|
|
17
15
|
black = 30
|
|
18
16
|
red = auto()
|
|
@@ -26,13 +24,13 @@ class PureColor(Enum):
|
|
|
26
24
|
|
|
27
25
|
|
|
28
26
|
class RawColor:
|
|
29
|
-
"""
|
|
30
|
-
A class that represents a color with all possible modifiers
|
|
31
|
-
"""
|
|
27
|
+
"""A class that represents a color with all possible modifiers"""
|
|
32
28
|
|
|
33
29
|
__slots__ = ("background", "bright", "color", "value")
|
|
34
30
|
|
|
35
|
-
def __init__(
|
|
31
|
+
def __init__(
|
|
32
|
+
self, color: PureColor | str | int, *, bright: bool = False, background: bool = False
|
|
33
|
+
) -> None:
|
|
36
34
|
self.color = PureColor(getattr(PureColor, color) if isinstance(color, str) else color)
|
|
37
35
|
self.bright = bright
|
|
38
36
|
self.background = background
|
|
@@ -68,9 +66,7 @@ class RawColor:
|
|
|
68
66
|
|
|
69
67
|
|
|
70
68
|
class _ColorMeta(type):
|
|
71
|
-
"""
|
|
72
|
-
A metaclass that implements __getattr__ at the class level for Color
|
|
73
|
-
"""
|
|
69
|
+
"""A metaclass that implements __getattr__ at the class level for Color"""
|
|
74
70
|
|
|
75
71
|
def __getattr__(cls, item: str) -> Color:
|
|
76
72
|
try:
|
|
@@ -80,8 +76,7 @@ class _ColorMeta(type):
|
|
|
80
76
|
|
|
81
77
|
|
|
82
78
|
class Color(metaclass=_ColorMeta):
|
|
83
|
-
"""
|
|
84
|
-
Represents an Ansi color code; calling this on a string will apply the code to the string
|
|
79
|
+
"""Represents an Ansi color code; calling this on a string will apply the code to the string
|
|
85
80
|
Can be constructed via .factory; alternatively calling Color.<name> or Color(<name>) will
|
|
86
81
|
automatically construct a color. Colors are constructed with attributes determined by
|
|
87
82
|
splitting <name> on "_". Colors may be made bright by prepending "bright_" to the color name
|
|
@@ -104,10 +99,11 @@ class Color(metaclass=_ColorMeta):
|
|
|
104
99
|
RESET: str = f"{_PREFIX}0m"
|
|
105
100
|
|
|
106
101
|
def __init__(self, fmt: Self | RawColor | str = "", *, code: str = "") -> None:
|
|
107
|
-
"""
|
|
108
|
-
|
|
109
|
-
:
|
|
110
|
-
|
|
102
|
+
"""Create a Color based on either the format or code, exactly one must be passed
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
fmt: A Color, RawColor, or string representation of this desired color
|
|
106
|
+
code: The ansi color code this object represents
|
|
111
107
|
"""
|
|
112
108
|
if fmt != "" and code != "":
|
|
113
109
|
raise ValueError("code and fmt may not both be passed")
|
|
@@ -122,13 +118,15 @@ class Color(metaclass=_ColorMeta):
|
|
|
122
118
|
|
|
123
119
|
def __call__(self, string: str) -> str:
|
|
124
120
|
"""
|
|
125
|
-
:
|
|
121
|
+
Returns:
|
|
122
|
+
The string color codes with the given ansi code
|
|
126
123
|
"""
|
|
127
124
|
return f"{self.code}{string}{self.RESET}"
|
|
128
125
|
|
|
129
126
|
def __add__(self, other: Self) -> Self:
|
|
130
127
|
"""
|
|
131
|
-
:
|
|
128
|
+
Returns:
|
|
129
|
+
A new color made by concatenating these two
|
|
132
130
|
"""
|
|
133
131
|
if not isinstance(other, type(self)):
|
|
134
132
|
raise TypeError("Cannot add non-Color to Color")
|
|
@@ -136,22 +134,32 @@ class Color(metaclass=_ColorMeta):
|
|
|
136
134
|
|
|
137
135
|
def __eq__(self, other: object) -> bool:
|
|
138
136
|
"""
|
|
139
|
-
:
|
|
137
|
+
Returns:
|
|
138
|
+
Whether the two colors are equal
|
|
140
139
|
"""
|
|
141
140
|
return isinstance(other, type(self)) and self.code == other.code
|
|
142
141
|
|
|
142
|
+
def __hash__(self) -> int:
|
|
143
|
+
"""
|
|
144
|
+
Returns:
|
|
145
|
+
A hash of the current color
|
|
146
|
+
"""
|
|
147
|
+
return hash((type(self), self.code))
|
|
148
|
+
|
|
143
149
|
def __repr__(self) -> str:
|
|
144
150
|
"""
|
|
145
|
-
:
|
|
151
|
+
Returns:
|
|
152
|
+
The ansi color code this object represents
|
|
146
153
|
"""
|
|
147
154
|
return f"<Color {self.code!r}>"
|
|
148
155
|
|
|
149
156
|
@classmethod
|
|
150
157
|
def factory(
|
|
151
|
-
cls, *, foreground: RawColor | None = None, background: RawColor | None = None, **modifiers
|
|
158
|
+
cls, *, foreground: RawColor | None = None, background: RawColor | None = None, **modifiers: bool
|
|
152
159
|
) -> Self:
|
|
153
160
|
"""
|
|
154
|
-
:
|
|
161
|
+
Returns:
|
|
162
|
+
The Ansi given the chosen color and various modifiers
|
|
155
163
|
"""
|
|
156
164
|
return cls(code=_generate_code(foreground, background, modifiers))
|
|
157
165
|
|
|
@@ -160,7 +168,8 @@ def _generate_code(
|
|
|
160
168
|
foreground: RawColor | None, background: RawColor | None, modifiers: dict[str, bool]
|
|
161
169
|
) -> str:
|
|
162
170
|
"""
|
|
163
|
-
:
|
|
171
|
+
Returns:
|
|
172
|
+
The Ansi given the chosen color and various modifiers
|
|
164
173
|
"""
|
|
165
174
|
if any(bad := [i for i in modifiers if i not in MODIFIERS]):
|
|
166
175
|
raise ValueError(f"Unknown modifier(s): {bad}")
|