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.
Files changed (36) hide show
  1. {zstdlib-0.6.0 → zstdlib-0.8.0}/PKG-INFO +2 -1
  2. {zstdlib-0.6.0 → zstdlib-0.8.0}/pyproject.toml +43 -13
  3. {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/log/base.py +15 -12
  4. {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/log/test_trace.py +1 -2
  5. {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_ansi.py +9 -0
  6. {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_enum.py +4 -3
  7. {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_frozen.py +38 -40
  8. {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_io.py +16 -17
  9. zstdlib-0.8.0/tests/test_lazy.py +25 -0
  10. {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_not_set.py +1 -1
  11. {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/test_singleton.py +10 -10
  12. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/__init__.py +4 -2
  13. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/ansi.py +46 -30
  14. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/enum.py +31 -33
  15. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/frozen.py +18 -15
  16. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/io.py +47 -60
  17. zstdlib-0.8.0/zstdlib/lazy.py +31 -0
  18. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/log/cute.py +19 -21
  19. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/log/trace.py +14 -14
  20. zstdlib-0.8.0/zstdlib/macos.py +198 -0
  21. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/not_set.py +1 -2
  22. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/signal_cm.py +8 -7
  23. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/singleton.py +21 -23
  24. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib.egg-info/PKG-INFO +2 -1
  25. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib.egg-info/SOURCES.txt +4 -0
  26. zstdlib-0.8.0/zstdlib.egg-info/requires.txt +1 -0
  27. {zstdlib-0.6.0 → zstdlib-0.8.0}/LICENSE +0 -0
  28. {zstdlib-0.6.0 → zstdlib-0.8.0}/README.md +0 -0
  29. {zstdlib-0.6.0 → zstdlib-0.8.0}/setup.cfg +0 -0
  30. {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/__init__.py +0 -0
  31. {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/log/__init__.py +0 -0
  32. {zstdlib-0.6.0 → zstdlib-0.8.0}/tests/log/test_cute.py +0 -0
  33. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/log/__init__.py +0 -0
  34. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib/py.typed +0 -0
  35. {zstdlib-0.6.0 → zstdlib-0.8.0}/zstdlib.egg-info/dependency_links.txt +0 -0
  36. {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.6.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
- "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)",
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
- "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"
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=["E731", "I001"]
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(cls, name: str, reuse: bool = False, fmt: logging.Formatter | None = None):
32
- """
33
- :return: A logger configured to write to an internal queue
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
- Restore the logger and read all messages from the queue
53
- :return: The messages stored by the logger queue
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
- pass
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,unused-variable,attribute-defined-outside-init
2
- from sys import version_info
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
- pass
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
- pass
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
- pass
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
- pass
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
- self.assertEqual(f1.__init__.__doc__.strip(), "init doc")
107
- self.assertEqual(f1.__init__.__name__, "__init__")
108
- self.assertEqual(f1.__init__.__qualname__, "TestFrozen.test_metadata.<locals>.F1.__init__")
109
- self.assertTupleEqual(f1.__init__.__defaults__, (1,))
110
- self.assertDictEqual(f1.__init__.__kwdefaults__, {"b": False})
111
- if version_info < (3, 13, 0.1) 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})
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 = None # type: ignore
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()
@@ -4,7 +4,7 @@ from zstdlib import NotSetType, NotSet
4
4
 
5
5
 
6
6
  class TestNotSet(unittest.TestCase):
7
- def test_not_set(self):
7
+ def test_not_set(self) -> None:
8
8
  self.assertIsInstance(NotSetType, type)
9
9
  self.assertIsInstance(NotSet, NotSetType)
10
10
  self.assertIsNot(None, NotSet)
@@ -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, **kwargs):
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.6.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
  )