zstdlib 0.3.0__tar.gz → 0.4.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 (31) hide show
  1. {zstdlib-0.3.0 → zstdlib-0.4.0}/PKG-INFO +1 -1
  2. {zstdlib-0.3.0 → zstdlib-0.4.0}/pyproject.toml +3 -2
  3. {zstdlib-0.3.0 → zstdlib-0.4.0}/tests/log/base.py +5 -5
  4. {zstdlib-0.3.0 → zstdlib-0.4.0}/tests/log/test_cute.py +1 -1
  5. {zstdlib-0.3.0 → zstdlib-0.4.0}/tests/test_enum.py +2 -2
  6. {zstdlib-0.3.0 → zstdlib-0.4.0}/tests/test_frozen.py +8 -2
  7. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib/__init__.py +1 -1
  8. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib/ansi.py +33 -37
  9. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib/enum.py +4 -4
  10. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib/frozen.py +0 -1
  11. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib/io.py +7 -16
  12. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib/log/cute.py +10 -8
  13. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib/log/trace.py +1 -1
  14. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib/singleton.py +3 -4
  15. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib.egg-info/PKG-INFO +1 -1
  16. {zstdlib-0.3.0 → zstdlib-0.4.0}/LICENSE +0 -0
  17. {zstdlib-0.3.0 → zstdlib-0.4.0}/README.md +0 -0
  18. {zstdlib-0.3.0 → zstdlib-0.4.0}/setup.cfg +0 -0
  19. {zstdlib-0.3.0 → zstdlib-0.4.0}/tests/__init__.py +0 -0
  20. {zstdlib-0.3.0 → zstdlib-0.4.0}/tests/log/__init__.py +0 -0
  21. {zstdlib-0.3.0 → zstdlib-0.4.0}/tests/log/test_trace.py +0 -0
  22. {zstdlib-0.3.0 → zstdlib-0.4.0}/tests/not_set.py +0 -0
  23. {zstdlib-0.3.0 → zstdlib-0.4.0}/tests/test_ansi.py +0 -0
  24. {zstdlib-0.3.0 → zstdlib-0.4.0}/tests/test_io.py +0 -0
  25. {zstdlib-0.3.0 → zstdlib-0.4.0}/tests/test_singleton.py +0 -0
  26. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib/log/__init__.py +0 -0
  27. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib/not_set.py +0 -0
  28. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib/py.typed +0 -0
  29. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib.egg-info/SOURCES.txt +0 -0
  30. {zstdlib-0.3.0 → zstdlib-0.4.0}/zstdlib.egg-info/dependency_links.txt +0 -0
  31. {zstdlib-0.3.0 → zstdlib-0.4.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.0
3
+ Version: 0.4.0
4
4
  Summary: A set of useful python utilities
5
5
  License: GPLv3
6
6
  Project-URL: Homepage, https://github.com/zwimer/zstdlib
@@ -50,14 +50,15 @@ disable = [
50
50
 
51
51
  [tool.black]
52
52
  line-length = 110
53
- target-version = ["py313"] # py314 when available
53
+ target-version = ["py314"]
54
54
 
55
55
  [tool.ruff]
56
56
  line-length = 110
57
57
  [tool.ruff.lint]
58
- ignore=["E731"]
58
+ ignore=["E731", "I001"]
59
59
  [tool.ruff.lint.per-file-ignores]
60
60
  "__init__.py" = ["F401", "F403"]
61
+ "tests/*" = ["DTZ005"]
61
62
 
62
63
  [tool.bandit]
63
64
  skips = ["B101", "B104", "B201"]
@@ -1,8 +1,8 @@
1
1
  from logging.handlers import QueueHandler
2
2
  from contextlib import contextmanager
3
3
  from collections.abc import Callable
4
+ from typing import ClassVar, Any
4
5
  from threading import Lock
5
- from typing import Any
6
6
  import logging
7
7
  import queue
8
8
 
@@ -20,12 +20,12 @@ class LeftBase:
20
20
  A base class for tests that hijack loggers
21
21
  """
22
22
 
23
- _hijacked: set[logging.Logger] = set() # Set of all loggers that have ever been hijacked
24
- _qmap: dict[logging.Logger, queue.Queue[logging.LogRecord]] = {}
25
- _lb_old: dict[logging.Logger, list[logging.Handler]] = {}
23
+ _hijacked: ClassVar[set[logging.Logger]] = set() # Set of all loggers that have ever been hijacked
24
+ _qmap: ClassVar[dict[logging.Logger, queue.Queue[logging.LogRecord]]] = {}
25
+ _lb_old: ClassVar[dict[logging.Logger, list[logging.Handler]]] = {}
26
26
  _lb_lock = Lock()
27
27
 
28
- messages: dict[logging.Logger, list[str]] = {}
28
+ messages: ClassVar[dict[logging.Logger, list[str]]] = {}
29
29
 
30
30
  @classmethod
31
31
  def hijack(cls, name: str, reuse: bool = False, fmt: logging.Formatter | None = None):
@@ -106,7 +106,7 @@ class TestCuteFormatter(LeftBase, unittest.TestCase):
106
106
  try:
107
107
  raise ValueError(name)
108
108
  except ValueError:
109
- log.error("test", exc_info=True)
109
+ log.exception("test")
110
110
  spt = self.messages[log][0].split("\n")
111
111
  self.assertGreater(len(spt), 2)
112
112
  self.assertEqual("Traceback (most recent call last):", spt[1].strip())
@@ -24,7 +24,7 @@ class TestEnumType(unittest.TestCase):
24
24
  class ET1(metaclass=EnumType, dupes_ok=True):
25
25
  a: int = 5
26
26
  b: int = 5
27
- c: dict = {}
27
+ c: dict = {} # noqa: RUF012
28
28
 
29
29
  with self.assertRaises(ValueError):
30
30
 
@@ -35,7 +35,7 @@ class TestEnumType(unittest.TestCase):
35
35
  with self.assertRaises(TypeError):
36
36
 
37
37
  class ET3(metaclass=EnumType):
38
- a: dict = {}
38
+ a: dict = {} # noqa: RUF012
39
39
 
40
40
  def test_type_check(self) -> None:
41
41
  class ET1(metaclass=EnumType, type_check=set()):
@@ -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>.F2.__init__")
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
- self.assertDictEqual(f1.__init__.__annotations__, {"a": int, "b": bool, "return": None})
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})
111
117
 
112
118
  def test_frozen_custom(self):
113
119
  @frozen("custom")
@@ -1,4 +1,4 @@
1
- __version__ = "0.3.0"
1
+ __version__ = "0.4.0"
2
2
 
3
3
  from .not_set import NotSetType, NotSet
4
4
  from .frozen import Freezable, frozen
@@ -3,7 +3,6 @@ from functools import cache
3
3
  from typing import Self
4
4
  import re
5
5
 
6
-
7
6
  MODIFIERS = ("bold", "dim", "italic", "underline", "blinking", "inverse", "hidden", "strikethrough")
8
7
 
9
8
  _PREFIX = "\033["
@@ -31,7 +30,7 @@ class RawColor:
31
30
  A class that represents a color with all possible modifiers
32
31
  """
33
32
 
34
- __slots__ = ("color", "bright", "background", "value")
33
+ __slots__ = ("background", "bright", "color", "value")
35
34
 
36
35
  def __init__(self, color: PureColor | str | int, *, bright: bool = False, background: bool = False):
37
36
  self.color = PureColor(getattr(PureColor, color) if isinstance(color, str) else color)
@@ -39,15 +38,43 @@ class RawColor:
39
38
  self.background = background
40
39
  self.value = self.color.value + (10 if self.background else 0) + (60 if self.bright else 0)
41
40
 
41
+ @classmethod
42
+ @cache
43
+ def _parse_color(cls, item: str) -> str:
44
+ """Construct a color following the rules defined by Color"""
45
+ item = item.lower()
46
+ if "bright_bg" in item:
47
+ raise ValueError("bg_ must precede bright_ in color specification")
48
+ pat = f"(:?bright_)?({'|'.join(list(PureColor.__members__))})"
49
+ shx = re.findall(f"(bg_){pat}", item)
50
+ if len(shx) > 1:
51
+ raise ValueError(f"Multiple background colors specified in {item}")
52
+ bg = None
53
+ if len(shx) == 1:
54
+ item = item.replace("".join(shx[0]), "")
55
+ bg = cls(shx[0][-1], bright="bright_" in shx[0], background=True)
56
+ shx = re.findall(pat, item)
57
+ if len(shx) > 1:
58
+ raise ValueError(f"Multiple foreground colors specified in {item}")
59
+ fg = None
60
+ if len(shx) == 1:
61
+ item = item.replace("".join(shx[0]), "")
62
+ fg = cls(shx[0][-1], bright="bright_" in shx[0])
63
+ # Determine modifiers and construct the color
64
+ attrs = [i for i in item.split("_") if i]
65
+ if any(bad := [i for i in attrs if i not in MODIFIERS]):
66
+ raise ValueError(f"Unknown modifiers(s): {', '.join(bad)}")
67
+ return _generate_code(fg, bg, {i: True for i in MODIFIERS if i in attrs})
68
+
42
69
 
43
70
  class _ColorMeta(type):
44
71
  """
45
72
  A metaclass that implements __getattr__ at the class level for Color
46
73
  """
47
74
 
48
- def __getattr__(cls, item: str) -> "Color":
75
+ def __getattr__(cls, item: str) -> Color:
49
76
  try:
50
- return cls(code=_parse_color(item))
77
+ return cls(code=RawColor._parse_color(item))
51
78
  except ValueError as e:
52
79
  raise AttributeError(str(e)) from e
53
80
 
@@ -91,7 +118,7 @@ class Color(metaclass=_ColorMeta):
91
118
  elif isinstance(fmt, RawColor):
92
119
  self.code = f"{_PREFIX}{fmt.value}m"
93
120
  else:
94
- self.code = _parse_color(fmt) if isinstance(fmt, str) else fmt.code
121
+ self.code = RawColor._parse_color(fmt) if isinstance(fmt, str) else fmt.code
95
122
 
96
123
  def __call__(self, string: str) -> str:
97
124
  """
@@ -117,7 +144,7 @@ class Color(metaclass=_ColorMeta):
117
144
  """
118
145
  :return: The ansi color code this object represents
119
146
  """
120
- return f"<Color {repr(self.code)}>"
147
+ return f"<Color {self.code!r}>"
121
148
 
122
149
  @classmethod
123
150
  def factory(
@@ -145,34 +172,3 @@ def _generate_code(
145
172
  if background:
146
173
  ints.append(background.value)
147
174
  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,5 @@
1
1
  from collections.abc import Iterator
2
- from typing import ForwardRef, Any
2
+ from typing import ClassVar, Any
3
3
  import annotationlib
4
4
 
5
5
 
@@ -22,7 +22,7 @@ class EnumType(type):
22
22
  """
23
23
 
24
24
  _AUTO_PREFIX = "Enum_Auto_"
25
- _TC_DEFAULT: set[type | None] = {int, str, float, bool, complex, bytes, None, type(None)}
25
+ _TC_DEFAULT: ClassVar[set[type | None]] = {int, str, float, bool, complex, bytes, None, type(None)}
26
26
 
27
27
  class _AutoValue:
28
28
  """A class representing an automatically assigned enum value"""
@@ -41,7 +41,7 @@ class EnumType(type):
41
41
  mcs,
42
42
  attrs: dict,
43
43
  dupes_ok: bool,
44
- ans: dict[str, type | ForwardRef],
44
+ ans: dict[str, type | None],
45
45
  ) -> dict[str, tuple[type | None, Any]]:
46
46
  """
47
47
  Generate enum entries from attrs, checking for duplicates and missing annotations
@@ -163,4 +163,4 @@ def values(enum: Any) -> tuple[Any, ...]:
163
163
  return tuple(k[1] for k in enum.__entries__.values()) # type: ignore[attr-defined]
164
164
 
165
165
 
166
- __all__ = ("Enum", "EnumType", "entries", "values", "auto")
166
+ __all__ = ("Enum", "EnumType", "auto", "entries", "values")
@@ -1,6 +1,5 @@
1
1
  from collections.abc import Callable
2
2
 
3
-
4
3
  _FN_ATTRS = (
5
4
  "__annotations__",
6
5
  "__type_params__",
@@ -1,19 +1,10 @@
1
- from io import (
2
- IOBase,
3
- TextIOBase as _PyRawTextIOBase,
4
- RawIOBase as _PyRawIOBase,
5
- BufferedIOBase as _PyBufferedIOBase,
6
- )
1
+ from io import BufferedIOBase, TextIOBase, RawIOBase, IOBase
2
+ from weakref import WeakKeyDictionary
7
3
  from threading import RLock
8
4
  from itertools import chain
9
5
  from typing import Self
10
- import weakref
11
6
 
12
-
13
- __all__ = ("TextIO", "BinaryIO", "io")
14
-
15
- type _BinaryBase = _PyRawIOBase | _PyBufferedIOBase
16
- type _TextBase = _PyRawTextIOBase
7
+ __all__ = ("BinaryIO", "TextIO", "io")
17
8
 
18
9
 
19
10
  class ProtectedFile:
@@ -52,8 +43,8 @@ class _IOWrapperBase[T: (str, bytes)]:
52
43
  Note: This class takes ownership of the input object, do not use it elsewhere
53
44
  """
54
45
 
55
- __slots__ = ("lock", "f", "_buffer", "_eof")
56
- _instances: weakref.WeakKeyDictionary[IOBase, Self] = weakref.WeakKeyDictionary()
46
+ __slots__ = ("_buffer", "_eof", "f", "lock")
47
+ _instances: WeakKeyDictionary[IOBase, Self] = WeakKeyDictionary()
57
48
  _wr_lock = RLock()
58
49
 
59
50
  def __new__(cls, f: IOBase, binary: bool) -> Self:
@@ -189,8 +180,8 @@ class _IOWrapperBase[T: (str, bytes)]:
189
180
 
190
181
  def _mode(f: IOBase, binary: bool | None) -> bool:
191
182
  mode = getattr(f, "mode", "")
192
- binary = binary or "b" in mode or isinstance(f, (_PyRawIOBase, _PyBufferedIOBase))
193
- text = (binary is False) or (mode and "b" not in mode) or isinstance(f, _PyRawTextIOBase)
183
+ binary = binary or "b" in mode or isinstance(f, (RawIOBase, BufferedIOBase))
184
+ text = (binary is False) or (mode and "b" not in mode) or isinstance(f, TextIOBase)
194
185
  if not text and not binary:
195
186
  raise TypeError("Cannot determine if IO object is text or binary")
196
187
  if text and binary:
@@ -1,7 +1,6 @@
1
- from __future__ import annotations
2
1
  from logging import CRITICAL, ERROR, WARNING, INFO, DEBUG, Formatter
2
+ from typing import TYPE_CHECKING, ClassVar
3
3
  from collections import defaultdict
4
- from typing import TYPE_CHECKING
5
4
  from zlib import adler32
6
5
  from copy import copy
7
6
 
@@ -16,9 +15,9 @@ class CuteFormatter(Formatter):
16
15
  A log formatter that can print log messages with colors.
17
16
  """
18
17
 
19
- __slots__ = ("colored", "_color", "_cmap", "_lvl_cmap", "_dim_level", "_name_width")
20
- DEFAULT_CUTE_WIDTHS: dict[str, int] = {"cute_levelname": 8, "cute_time": 23, "cute_name": 12}
21
- DEFAULT_LEVEL_COLORS: dict[int, Color] = {
18
+ __slots__ = ("_cmap", "_color", "_dim_level", "_lvl_cmap", "_name_width", "colored")
19
+ DEFAULT_CUTE_WIDTHS: ClassVar[dict[str, int]] = {"cute_levelname": 8, "cute_time": 23, "cute_name": 12}
20
+ DEFAULT_LEVEL_COLORS: ClassVar[dict[int, Color]] = {
22
21
  INFO: Color.blue,
23
22
  WARNING: Color.yellow,
24
23
  ERROR: Color.red,
@@ -32,9 +31,9 @@ class CuteFormatter(Formatter):
32
31
  *args,
33
32
  colored: bool = True,
34
33
  dim_level: int = DEBUG,
35
- colors: dict[str, Color] = {},
36
- level_colors: dict[int, Color] = {},
37
- cute_widths: dict[str, int] = {},
34
+ colors: dict[str, Color] | None = None,
35
+ level_colors: dict[int, Color] | None = None,
36
+ cute_widths: dict[str, int] | None = None,
38
37
  **kwargs,
39
38
  ):
40
39
  """
@@ -52,6 +51,9 @@ class CuteFormatter(Formatter):
52
51
  :param cute_widths: The widths of the cute_ columns in the log message
53
52
  :param kwargs: Passed to logging.Formatter
54
53
  """
54
+ colors = {} if colors is None else colors
55
+ level_colors = {} if level_colors is None else level_colors
56
+ cute_widths = {} if cute_widths is None else cute_widths
55
57
  super().__init__(fmt, *args, **kwargs)
56
58
  self._dim_level: int = dim_level
57
59
  self._cmap: dict[str, Color] = dict(colors)
@@ -46,7 +46,7 @@ def _define_logging_trace(method: str, value: int) -> None:
46
46
  has no handlers, call basicConfig() to add a console handler with a
47
47
  pre-defined format.
48
48
  """
49
- logging.log(value, msg, *args, **kwargs)
49
+ logging.log(value, msg, *args, **kwargs) # noqa: LOG015
50
50
 
51
51
  setattr(logging, method, trace)
52
52
 
@@ -1,9 +1,8 @@
1
- from typing import TypeVar, Self, Any, cast
1
+ from typing import ClassVar, TypeVar, Self, Any, cast
2
2
  from collections import defaultdict
3
3
  from threading import RLock
4
4
  from functools import cache
5
5
 
6
-
7
6
  T = TypeVar("T")
8
7
 
9
8
 
@@ -26,9 +25,9 @@ class _SingletonType(type):
26
25
  """
27
26
 
28
27
  _disallow_init_subclass = False # If True, disallow __init_subclass__ in __new__'s attrs
29
- _instances: dict[type, Any] = {} # Fully constructed and initialized singleton types
28
+ _instances: ClassVar[dict[type, Any]] = {} # Fully constructed and initialized singleton types
30
29
  # Lock is preferred to RLock, but could deadlock if user write a constructor that invokes itself
31
- _cls_locks: dict[type, RLock] = defaultdict(RLock)
30
+ _cls_locks: ClassVar[dict[type, RLock]] = defaultdict(RLock)
32
31
  _lock = RLock()
33
32
 
34
33
  def __new__(mcs, name, bases, attrs, **kwargs):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zstdlib
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: A set of useful python utilities
5
5
  License: GPLv3
6
6
  Project-URL: Homepage, https://github.com/zwimer/zstdlib
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