zstdlib 0.0.2__tar.gz → 0.0.4__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.0.2/zstdlib.egg-info → zstdlib-0.0.4}/PKG-INFO +1 -1
- {zstdlib-0.0.2 → zstdlib-0.0.4}/tests/log/test_cute.py +2 -2
- {zstdlib-0.0.2 → zstdlib-0.0.4}/tests/test_enum.py +1 -1
- zstdlib-0.0.4/tests/test_frozen.py +109 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/tests/test_singleton.py +1 -1
- zstdlib-0.0.4/zstdlib/__init__.py +6 -0
- zstdlib-0.0.4/zstdlib/frozen.py +87 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/zstdlib/singleton.py +4 -6
- {zstdlib-0.0.2 → zstdlib-0.0.4/zstdlib.egg-info}/PKG-INFO +1 -1
- {zstdlib-0.0.2 → zstdlib-0.0.4}/zstdlib.egg-info/SOURCES.txt +2 -0
- zstdlib-0.0.2/zstdlib/__init__.py +0 -5
- {zstdlib-0.0.2 → zstdlib-0.0.4}/LICENSE +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/README.md +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/pyproject.toml +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/setup.cfg +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/tests/__init__.py +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/tests/log/__init__.py +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/tests/log/base.py +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/tests/log/test_trace.py +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/tests/test_ansi.py +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/zstdlib/ansi.py +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/zstdlib/enum.py +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/zstdlib/log/__init__.py +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/zstdlib/log/cute.py +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/zstdlib/log/trace.py +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/zstdlib/py.typed +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/zstdlib.egg-info/dependency_links.txt +0 -0
- {zstdlib-0.0.2 → zstdlib-0.0.4}/zstdlib.egg-info/top_level.txt +0 -0
|
@@ -77,7 +77,7 @@ class TestCuteFormatter(LeftBase, unittest.TestCase):
|
|
|
77
77
|
except ValueError:
|
|
78
78
|
log.error("test", exc_info=True)
|
|
79
79
|
spt = self.messages[log][0].split("\n")
|
|
80
|
-
self.
|
|
80
|
+
self.assertGreater(len(spt), 2)
|
|
81
81
|
self.assertEqual("Traceback (most recent call last):", spt[1].strip())
|
|
82
82
|
self.assertEqual("raise ValueError(name)", spt[-2].strip())
|
|
83
83
|
self.assertEqual(f"ValueError: {name}", spt[-1].strip())
|
|
@@ -91,7 +91,7 @@ class TestCuteFormatter(LeftBase, unittest.TestCase):
|
|
|
91
91
|
log.info(base)
|
|
92
92
|
messages.add(self.messages[log][0].rsplit("|", 1)[-1].strip())
|
|
93
93
|
cols = ("red", "green", "yellow", "blue", "magenta", "cyan", "default")
|
|
94
|
-
self.
|
|
94
|
+
self.assertSetEqual({getattr(Color, i)(base) for i in cols}, messages)
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,unused-variable,attribute-defined-outside-init
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
from zstdlib.frozen import Freezable, frozen
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestFreezable(unittest.TestCase):
|
|
8
|
+
|
|
9
|
+
def test_unfrozen(self):
|
|
10
|
+
class F1(Freezable):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
f1 = F1()
|
|
14
|
+
f1.a = 1
|
|
15
|
+
self.assertEqual(f1.a, 1)
|
|
16
|
+
del f1.a
|
|
17
|
+
self.assertIs(getattr(f1, "a", None), None)
|
|
18
|
+
|
|
19
|
+
def test_frozen(self):
|
|
20
|
+
class F1(Freezable):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
f1 = F1()
|
|
24
|
+
f1.a = 1
|
|
25
|
+
f1.freeze()
|
|
26
|
+
self.assertEqual(f1.a, 1)
|
|
27
|
+
with self.assertRaises(AttributeError):
|
|
28
|
+
f1.a = 2
|
|
29
|
+
with self.assertRaises(AttributeError):
|
|
30
|
+
del f1.a
|
|
31
|
+
|
|
32
|
+
def test_thaw(self):
|
|
33
|
+
class F1(Freezable):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
f1 = F1()
|
|
37
|
+
f1.a = 1
|
|
38
|
+
f1.freeze()
|
|
39
|
+
self.assertEqual(f1.a, 1)
|
|
40
|
+
f1.thaw()
|
|
41
|
+
f1.a = 2
|
|
42
|
+
self.assertEqual(f1.a, 2)
|
|
43
|
+
del f1.a
|
|
44
|
+
self.assertIs(getattr(f1, "a", None), None)
|
|
45
|
+
|
|
46
|
+
def test_permanent_freeze(self):
|
|
47
|
+
class F1(Freezable):
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
f1 = F1()
|
|
51
|
+
f1.a = 1
|
|
52
|
+
f1.freeze(permanent=True)
|
|
53
|
+
self.assertEqual(f1.a, 1)
|
|
54
|
+
with self.assertRaises(RuntimeError):
|
|
55
|
+
f1.thaw()
|
|
56
|
+
with self.assertRaises(AttributeError):
|
|
57
|
+
f1.a = 1
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestFrozen(unittest.TestCase):
|
|
61
|
+
|
|
62
|
+
def test_frozen(self):
|
|
63
|
+
@frozen
|
|
64
|
+
class F1:
|
|
65
|
+
def __init__(self):
|
|
66
|
+
self.a = 1
|
|
67
|
+
self.b = 1
|
|
68
|
+
del self.b
|
|
69
|
+
|
|
70
|
+
f1 = F1()
|
|
71
|
+
self.assertEqual(f1.a, 1)
|
|
72
|
+
self.assertIs(getattr(f1, "b", None), None)
|
|
73
|
+
with self.assertRaises(AttributeError):
|
|
74
|
+
f1.a = 1
|
|
75
|
+
with self.assertRaises(AttributeError):
|
|
76
|
+
del f1.a
|
|
77
|
+
|
|
78
|
+
def test_metadata(self):
|
|
79
|
+
@frozen
|
|
80
|
+
class F2:
|
|
81
|
+
def __init__(self):
|
|
82
|
+
"""
|
|
83
|
+
init doc
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
f2 = F2()
|
|
87
|
+
self.assertEqual(f2.__init__.__doc__.strip(), "init doc")
|
|
88
|
+
self.assertEqual(f2.__init__.__name__, "__init__")
|
|
89
|
+
self.assertEqual(f2.__init__.__qualname__, "TestFrozen.test_metadata.<locals>.F2.__init__")
|
|
90
|
+
|
|
91
|
+
def test_frozen_custom(self):
|
|
92
|
+
@frozen("custom")
|
|
93
|
+
class F3:
|
|
94
|
+
def __init__(self):
|
|
95
|
+
self.a = 1
|
|
96
|
+
|
|
97
|
+
def custom(self):
|
|
98
|
+
self.a = 2
|
|
99
|
+
|
|
100
|
+
f3 = F3()
|
|
101
|
+
self.assertEqual(f3.a, 1)
|
|
102
|
+
f3.custom()
|
|
103
|
+
self.assertEqual(f3.a, 2)
|
|
104
|
+
with self.assertRaises(AttributeError):
|
|
105
|
+
f3.a = 1
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
unittest.main()
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import TypeVar
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Freezable:
|
|
6
|
+
"""
|
|
7
|
+
A base class for objects that can be frozen
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
11
|
+
super().__init__(*args, **kwargs) # For MRO super classes
|
|
12
|
+
if hasattr(self, "_frozen") or hasattr(self, "_can_thaw"):
|
|
13
|
+
raise ValueError("Base class defines _frozen or _can_thaw")
|
|
14
|
+
self._can_thaw: bool = True
|
|
15
|
+
self._frozen: bool = False
|
|
16
|
+
|
|
17
|
+
def freeze(self, *, permanent: bool = False):
|
|
18
|
+
"""
|
|
19
|
+
Prevent further modifications to this object
|
|
20
|
+
"""
|
|
21
|
+
if permanent:
|
|
22
|
+
self._can_thaw = False
|
|
23
|
+
self._frozen = True
|
|
24
|
+
|
|
25
|
+
def thaw(self):
|
|
26
|
+
"""
|
|
27
|
+
Allow modifications to this object
|
|
28
|
+
"""
|
|
29
|
+
if not self._can_thaw:
|
|
30
|
+
raise RuntimeError("Cannot thaw permanently frozen object")
|
|
31
|
+
object.__setattr__(self, "_frozen", False)
|
|
32
|
+
|
|
33
|
+
def __setattr__(self, key: str, value) -> None:
|
|
34
|
+
if getattr(self, "_frozen", False):
|
|
35
|
+
raise AttributeError("Cannot modify frozen object")
|
|
36
|
+
super().__setattr__(key, value)
|
|
37
|
+
|
|
38
|
+
def __delattr__(self, item: str) -> None:
|
|
39
|
+
if getattr(self, "_frozen", False):
|
|
40
|
+
raise AttributeError("Cannot modify frozen object")
|
|
41
|
+
super().__delattr__(item)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_SELF = TypeVar("_SELF")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def frozen(arg: str | type[_SELF]):
|
|
48
|
+
"""
|
|
49
|
+
A class decorator to permanently freeze a class after some method, __init__ by default
|
|
50
|
+
If passed a string, will freeze after the method with that name
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def shim_method(cls: type[_SELF], name, new) -> None:
|
|
54
|
+
original = getattr(cls, name)
|
|
55
|
+
new.__qualname__ = original.__qualname__
|
|
56
|
+
new.__name__ = original.__name__
|
|
57
|
+
new.__doc__ = original.__doc__
|
|
58
|
+
setattr(cls, name, new)
|
|
59
|
+
|
|
60
|
+
def mk_frozen(cls: type[_SELF], method: str) -> type:
|
|
61
|
+
original_method: Callable = getattr(cls, method)
|
|
62
|
+
|
|
63
|
+
def new_method(self: _SELF, *args, **kwargs):
|
|
64
|
+
ret = original_method(self, *args, **kwargs)
|
|
65
|
+
# pylint: disable=protected-access
|
|
66
|
+
self._frozen = True # type: ignore[attr-defined]
|
|
67
|
+
return ret
|
|
68
|
+
|
|
69
|
+
def __setattr__(self: _SELF, key: str, value) -> None:
|
|
70
|
+
if getattr(self, "_frozen", False):
|
|
71
|
+
raise AttributeError("Cannot modify frozen object")
|
|
72
|
+
super(cls, self).__setattr__(key, value) # type: ignore[misc]
|
|
73
|
+
|
|
74
|
+
def __delattr__(self: _SELF, item: str) -> None:
|
|
75
|
+
if getattr(self, "_frozen", False):
|
|
76
|
+
raise AttributeError("Cannot modify frozen object")
|
|
77
|
+
super(cls, self).__delattr__(item) # type: ignore[misc]
|
|
78
|
+
|
|
79
|
+
# Update cls with the new methods
|
|
80
|
+
shim_method(cls, method, new_method)
|
|
81
|
+
shim_method(cls, "__delattr__", __delattr__)
|
|
82
|
+
shim_method(cls, "__setattr__", __setattr__)
|
|
83
|
+
return cls
|
|
84
|
+
|
|
85
|
+
if isinstance(arg, str):
|
|
86
|
+
return lambda cls: mk_frozen(cls, arg)
|
|
87
|
+
return mk_frozen(arg, "__init__")
|
|
@@ -7,10 +7,8 @@ class SingletonType(type):
|
|
|
7
7
|
A thread-safe singleton metaclass
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
_seen: set[type] = (
|
|
11
|
-
|
|
12
|
-
) # Objects in this have been seen by singleton before and should not be constructed again
|
|
13
|
-
_instances: dict[type, Any] = {} # Fully constructed singleton objects
|
|
10
|
+
_seen: set[type] = set() # Types that should not be constructed again
|
|
11
|
+
_instances: dict[type, Any] = {} # Fully constructed and initialized singleton types
|
|
14
12
|
_lock = Condition()
|
|
15
13
|
# For preventing subclassing of Singletons
|
|
16
14
|
_types: list[type] = []
|
|
@@ -21,7 +19,7 @@ class SingletonType(type):
|
|
|
21
19
|
Intercept all class definitions to prevent subclassing singleton types except for Singleton
|
|
22
20
|
"""
|
|
23
21
|
if "__init_subclass__" in attrs:
|
|
24
|
-
raise NotImplementedError("Singleton's should not be subclassed")
|
|
22
|
+
raise NotImplementedError("Singleton's should not be subclassed or implement __init_subclass__")
|
|
25
23
|
|
|
26
24
|
def _init_subclass(cls):
|
|
27
25
|
with mcs._types_lock:
|
|
@@ -46,7 +44,7 @@ class SingletonType(type):
|
|
|
46
44
|
cls._lock.wait_for(lambda: cls in cls._instances)
|
|
47
45
|
return cls._instances[cls]
|
|
48
46
|
cls._seen.add(cls)
|
|
49
|
-
# The object has not been constructed before, create it outside
|
|
47
|
+
# The object has not been constructed before, create it outside any lock to avoid delays
|
|
50
48
|
obj = super().__call__(*args, **kwargs)
|
|
51
49
|
with cls._lock:
|
|
52
50
|
cls._instances[cls] = obj
|
|
@@ -4,6 +4,7 @@ pyproject.toml
|
|
|
4
4
|
tests/__init__.py
|
|
5
5
|
tests/test_ansi.py
|
|
6
6
|
tests/test_enum.py
|
|
7
|
+
tests/test_frozen.py
|
|
7
8
|
tests/test_singleton.py
|
|
8
9
|
tests/log/__init__.py
|
|
9
10
|
tests/log/base.py
|
|
@@ -12,6 +13,7 @@ tests/log/test_trace.py
|
|
|
12
13
|
zstdlib/__init__.py
|
|
13
14
|
zstdlib/ansi.py
|
|
14
15
|
zstdlib/enum.py
|
|
16
|
+
zstdlib/frozen.py
|
|
15
17
|
zstdlib/py.typed
|
|
16
18
|
zstdlib/singleton.py
|
|
17
19
|
zstdlib.egg-info/PKG-INFO
|
|
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
|