errortools 1.2.0__tar.gz → 1.3.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 (63) hide show
  1. {errortools-1.2.0/errortools.egg-info → errortools-1.3.0}/PKG-INFO +24 -9
  2. {errortools-1.2.0 → errortools-1.3.0}/README.md +23 -8
  3. {errortools-1.2.0 → errortools-1.3.0}/_errortools/cache.py +82 -81
  4. {errortools-1.2.0 → errortools-1.3.0}/_errortools/classes/abc.py +351 -351
  5. {errortools-1.2.0 → errortools-1.3.0}/_errortools/classes/group.py +118 -118
  6. {errortools-1.2.0 → errortools-1.3.0}/_errortools/cli.py +94 -85
  7. errortools-1.3.0/_errortools/const.py +10 -0
  8. errortools-1.3.0/_errortools/decorator/__init__.py +1 -0
  9. errortools-1.3.0/_errortools/decorator/deprecated.py +33 -0
  10. {errortools-1.2.0 → errortools-1.3.0}/_errortools/ignore.py +89 -0
  11. {errortools-1.2.0 → errortools-1.3.0}/_errortools/logging/base.py +2 -2
  12. errortools-1.3.0/_errortools/partial.py +110 -0
  13. {errortools-1.2.0 → errortools-1.3.0}/_errortools/raises.py +183 -166
  14. {errortools-1.2.0 → errortools-1.3.0}/_errortools/version.py +7 -7
  15. {errortools-1.2.0 → errortools-1.3.0}/errortools/__init__.py +158 -152
  16. {errortools-1.2.0 → errortools-1.3.0/errortools.egg-info}/PKG-INFO +24 -9
  17. {errortools-1.2.0 → errortools-1.3.0}/errortools.egg-info/SOURCES.txt +6 -2
  18. {errortools-1.2.0 → errortools-1.3.0}/setup.py +34 -34
  19. errortools-1.3.0/tests/__init__.py +12 -0
  20. errortools-1.3.0/tests/run_tests.py +19 -0
  21. {errortools-1.2.0 → errortools-1.3.0}/tests/test_abc.py +303 -314
  22. {errortools-1.2.0 → errortools-1.3.0}/tests/test_cache.py +4 -0
  23. errortools-1.3.0/tests/test_decorator.py +84 -0
  24. {errortools-1.2.0 → errortools-1.3.0}/tests/test_descriptor.py +3 -12
  25. {errortools-1.2.0 → errortools-1.3.0}/tests/test_errorcodes.py +4 -1
  26. {errortools-1.2.0 → errortools-1.3.0}/tests/test_groups.py +3 -3
  27. {errortools-1.2.0 → errortools-1.3.0}/tests/test_ignore.py +102 -1
  28. {errortools-1.2.0 → errortools-1.3.0}/tests/test_logging.py +3 -4
  29. {errortools-1.2.0 → errortools-1.3.0}/tests/test_mixins.py +3 -5
  30. errortools-1.3.0/tests/test_partials.py +232 -0
  31. {errortools-1.2.0 → errortools-1.3.0}/tests/test_raises.py +3 -20
  32. {errortools-1.2.0 → errortools-1.3.0}/tests/test_typing.py +5 -8
  33. {errortools-1.2.0 → errortools-1.3.0}/tests/test_warnings.py +3 -0
  34. errortools-1.2.0/_errortools/tools/__init__.py +0 -1
  35. errortools-1.2.0/_errortools/tools/_warps.py +0 -27
  36. errortools-1.2.0/tests/__init__.py +0 -3
  37. errortools-1.2.0/tests/run_tests.py +0 -13
  38. {errortools-1.2.0 → errortools-1.3.0}/AUTHORS.txt +0 -0
  39. {errortools-1.2.0 → errortools-1.3.0}/LICENSE.txt +0 -0
  40. {errortools-1.2.0 → errortools-1.3.0}/_errortools/__init__.py +0 -0
  41. {errortools-1.2.0 → errortools-1.3.0}/_errortools/classes/__init__.py +0 -0
  42. {errortools-1.2.0 → errortools-1.3.0}/_errortools/classes/errorcodes.py +0 -0
  43. {errortools-1.2.0 → errortools-1.3.0}/_errortools/classes/warn.py +0 -0
  44. {errortools-1.2.0 → errortools-1.3.0}/_errortools/future.py +0 -0
  45. {errortools-1.2.0 → errortools-1.3.0}/_errortools/logging/__init__.py +0 -0
  46. {errortools-1.2.0 → errortools-1.3.0}/_errortools/logging/level.py +0 -0
  47. {errortools-1.2.0 → errortools-1.3.0}/_errortools/logging/logger.py +0 -0
  48. {errortools-1.2.0 → errortools-1.3.0}/_errortools/logging/record.py +0 -0
  49. {errortools-1.2.0 → errortools-1.3.0}/_errortools/logging/sink.py +0 -0
  50. {errortools-1.2.0 → errortools-1.3.0}/_errortools/metadata.py +0 -0
  51. {errortools-1.2.0 → errortools-1.3.0}/_errortools/methods/__init__.py +0 -0
  52. {errortools-1.2.0 → errortools-1.3.0}/_errortools/methods/errorattr.py +0 -0
  53. {errortools-1.2.0 → errortools-1.3.0}/_errortools/methods/errordelattr.py +0 -0
  54. {errortools-1.2.0 → errortools-1.3.0}/_errortools/methods/errorhasattr.py +0 -0
  55. {errortools-1.2.0 → errortools-1.3.0}/_errortools/methods/errorsetattr.py +0 -0
  56. {errortools-1.2.0 → errortools-1.3.0}/_errortools/py.typed +0 -0
  57. {errortools-1.2.0 → errortools-1.3.0}/_errortools/typing.py +0 -0
  58. {errortools-1.2.0 → errortools-1.3.0}/errortools/__main__.py +0 -0
  59. {errortools-1.2.0 → errortools-1.3.0}/errortools.egg-info/dependency_links.txt +0 -0
  60. {errortools-1.2.0 → errortools-1.3.0}/errortools.egg-info/entry_points.txt +0 -0
  61. {errortools-1.2.0 → errortools-1.3.0}/errortools.egg-info/top_level.txt +0 -0
  62. {errortools-1.2.0 → errortools-1.3.0}/setup.cfg +0 -0
  63. {errortools-1.2.0 → errortools-1.3.0}/tests/conftest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: errortools
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: errortools - a toolset for working with Python exceptions and warnings and logging.
5
5
  Home-page: https://github.com/more-abc/errortools
6
6
  Author: Evan Yang
@@ -35,7 +35,7 @@ A lightweight Python exception handling utility library.
35
35
 
36
36
  ## Features
37
37
  - **Raise Exceptions**: `raises()`, `raises_all()`, `reraise()` — batch raising and exception conversion
38
- - **Catch & Suppress**: `ignore()`, `ignore_subclass()`, `ignore_warns()`, `fast_ignore()`, `super_fast_ignore()`, `timeout()` — graceful suppression of exceptions and warnings
38
+ - **Catch & Suppress**: `ignore()`, `ignore_subclass()`, `ignore_warns()`, `fast_ignore()`, `super_fast_ignore()`, `timeout()`, `retry()` — graceful suppression of exceptions and warnings, with automatic retry
39
39
  - **Exception Caching**: `error_cache` — cache exceptions raised by functions (similar to `lru_cache`)
40
40
  - **Custom Exceptions**: `PureBaseException`, `ContextException`, `BaseErrorCodes`, `BaseWarning` — structured exception classes with error codes, trace IDs, and context
41
41
  - **Attribute Error Mixin**: Customize error behavior for attribute access, assignment, and deletion
@@ -54,7 +54,7 @@ pip install errortools
54
54
  ```python
55
55
  import warnings
56
56
  from errortools import (
57
- ignore, fast_ignore, ignore_subclass, ignore_warns, timeout,
57
+ ignore, fast_ignore, ignore_subclass, ignore_warns, timeout, retry,
58
58
  reraise, raises, raises_all, assert_raises,
59
59
  error_cache,
60
60
  PureBaseException, ContextException, BaseErrorCodes, BaseWarning,
@@ -103,14 +103,29 @@ async def fetch_data(url: str) -> bytes:
103
103
 
104
104
  # asyncio.TimeoutError raised automatically if it exceeds 5 s
105
105
 
106
- # ── 6. reraise ── convert exception types on the fly ─────────────────────────
106
+ # ── 6. retry ── automatically retry on failure ───────────────────────────────
107
+ @retry(times=3, on=ConnectionError, delay=1.0)
108
+ def connect(host: str) -> None:
109
+ ... # retried up to 3 times on ConnectionError
110
+
111
+ # works with async functions too
112
+ @retry(times=5, on=TimeoutError, delay=0.5)
113
+ async def fetch(url: str) -> bytes:
114
+ ...
115
+
116
+ # multiple exception types
117
+ @retry(times=2, on=(ValueError, KeyError))
118
+ def parse(data: dict) -> str:
119
+ return data["key"]
120
+
121
+ # ── 7. reraise ── convert exception types on the fly ─────────────────────────
107
122
  with reraise(KeyError, ValueError):
108
123
  raise KeyError("missing key") # → ValueError: 'missing key'
109
124
 
110
125
  with reraise((KeyError, IndexError), RuntimeError):
111
126
  _ = [][99] # → RuntimeError: list index out of range
112
127
 
113
- # ── 7. raises / raises_all ── batch raise ────────────────────────────────────
128
+ # ── 8. raises / raises_all ── batch raise ────────────────────────────────────
114
129
  raises([ValueError], ["bad input"]) # → ValueError: bad input
115
130
 
116
131
  raises_all(
@@ -118,11 +133,11 @@ raises_all(
118
133
  ["bad input"],
119
134
  ) # → ExceptionGroup (2 sub-exceptions)
120
135
 
121
- # ── 8. assert_raises ── assert a callable raises ─────────────────────────────
136
+ # ── 9. assert_raises ── assert a callable raises ─────────────────────────────
122
137
  exc = assert_raises(int, [ValueError], "not-a-number")
123
138
  print(exc) # invalid literal for int() with base 10: 'not-a-number'
124
139
 
125
- # ── 9. error_cache ── cache exceptions by call arguments ─────────────────────
140
+ # ── 10. error_cache ── cache exceptions by call arguments ─────────────────────
126
141
  @error_cache(maxsize=64)
127
142
  def load(user_id: int) -> dict:
128
143
  if user_id < 0:
@@ -135,7 +150,7 @@ with ignore(ValueError):
135
150
  print(load.cache_info()) # CacheInfo(hits=0, misses=1, maxsize=64, currsize=1)
136
151
  load.clear_cache()
137
152
 
138
- # ── 10. Custom exceptions — three layers ──────────────────────────────────────
153
+ # ── 11. Custom exceptions — three layers ──────────────────────────────────────
139
154
 
140
155
  # Layer 1: PureBaseException — code + detail only
141
156
  class AppError(PureBaseException):
@@ -171,7 +186,7 @@ raise BaseErrorCodes.runtime_failure("crash") # RuntimeFailure [
171
186
  raise BaseErrorCodes.timeout_failure() # TimeoutFailure [4002]
172
187
  raise BaseErrorCodes.configuration_error("missing key") # ConfigurationError [5001]
173
188
 
174
- # ── 11. BaseWarning ── structured warnings with factory methods ───────────────
189
+ # ── 12. BaseWarning ── structured warnings with factory methods ───────────────
175
190
  class ExperimentalWarning(BaseWarning):
176
191
  default_detail = "This feature is experimental."
177
192
 
@@ -3,7 +3,7 @@ A lightweight Python exception handling utility library.
3
3
 
4
4
  ## Features
5
5
  - **Raise Exceptions**: `raises()`, `raises_all()`, `reraise()` — batch raising and exception conversion
6
- - **Catch & Suppress**: `ignore()`, `ignore_subclass()`, `ignore_warns()`, `fast_ignore()`, `super_fast_ignore()`, `timeout()` — graceful suppression of exceptions and warnings
6
+ - **Catch & Suppress**: `ignore()`, `ignore_subclass()`, `ignore_warns()`, `fast_ignore()`, `super_fast_ignore()`, `timeout()`, `retry()` — graceful suppression of exceptions and warnings, with automatic retry
7
7
  - **Exception Caching**: `error_cache` — cache exceptions raised by functions (similar to `lru_cache`)
8
8
  - **Custom Exceptions**: `PureBaseException`, `ContextException`, `BaseErrorCodes`, `BaseWarning` — structured exception classes with error codes, trace IDs, and context
9
9
  - **Attribute Error Mixin**: Customize error behavior for attribute access, assignment, and deletion
@@ -22,7 +22,7 @@ pip install errortools
22
22
  ```python
23
23
  import warnings
24
24
  from errortools import (
25
- ignore, fast_ignore, ignore_subclass, ignore_warns, timeout,
25
+ ignore, fast_ignore, ignore_subclass, ignore_warns, timeout, retry,
26
26
  reraise, raises, raises_all, assert_raises,
27
27
  error_cache,
28
28
  PureBaseException, ContextException, BaseErrorCodes, BaseWarning,
@@ -71,14 +71,29 @@ async def fetch_data(url: str) -> bytes:
71
71
 
72
72
  # asyncio.TimeoutError raised automatically if it exceeds 5 s
73
73
 
74
- # ── 6. reraise ── convert exception types on the fly ─────────────────────────
74
+ # ── 6. retry ── automatically retry on failure ───────────────────────────────
75
+ @retry(times=3, on=ConnectionError, delay=1.0)
76
+ def connect(host: str) -> None:
77
+ ... # retried up to 3 times on ConnectionError
78
+
79
+ # works with async functions too
80
+ @retry(times=5, on=TimeoutError, delay=0.5)
81
+ async def fetch(url: str) -> bytes:
82
+ ...
83
+
84
+ # multiple exception types
85
+ @retry(times=2, on=(ValueError, KeyError))
86
+ def parse(data: dict) -> str:
87
+ return data["key"]
88
+
89
+ # ── 7. reraise ── convert exception types on the fly ─────────────────────────
75
90
  with reraise(KeyError, ValueError):
76
91
  raise KeyError("missing key") # → ValueError: 'missing key'
77
92
 
78
93
  with reraise((KeyError, IndexError), RuntimeError):
79
94
  _ = [][99] # → RuntimeError: list index out of range
80
95
 
81
- # ── 7. raises / raises_all ── batch raise ────────────────────────────────────
96
+ # ── 8. raises / raises_all ── batch raise ────────────────────────────────────
82
97
  raises([ValueError], ["bad input"]) # → ValueError: bad input
83
98
 
84
99
  raises_all(
@@ -86,11 +101,11 @@ raises_all(
86
101
  ["bad input"],
87
102
  ) # → ExceptionGroup (2 sub-exceptions)
88
103
 
89
- # ── 8. assert_raises ── assert a callable raises ─────────────────────────────
104
+ # ── 9. assert_raises ── assert a callable raises ─────────────────────────────
90
105
  exc = assert_raises(int, [ValueError], "not-a-number")
91
106
  print(exc) # invalid literal for int() with base 10: 'not-a-number'
92
107
 
93
- # ── 9. error_cache ── cache exceptions by call arguments ─────────────────────
108
+ # ── 10. error_cache ── cache exceptions by call arguments ─────────────────────
94
109
  @error_cache(maxsize=64)
95
110
  def load(user_id: int) -> dict:
96
111
  if user_id < 0:
@@ -103,7 +118,7 @@ with ignore(ValueError):
103
118
  print(load.cache_info()) # CacheInfo(hits=0, misses=1, maxsize=64, currsize=1)
104
119
  load.clear_cache()
105
120
 
106
- # ── 10. Custom exceptions — three layers ──────────────────────────────────────
121
+ # ── 11. Custom exceptions — three layers ──────────────────────────────────────
107
122
 
108
123
  # Layer 1: PureBaseException — code + detail only
109
124
  class AppError(PureBaseException):
@@ -139,7 +154,7 @@ raise BaseErrorCodes.runtime_failure("crash") # RuntimeFailure [
139
154
  raise BaseErrorCodes.timeout_failure() # TimeoutFailure [4002]
140
155
  raise BaseErrorCodes.configuration_error("missing key") # ConfigurationError [5001]
141
156
 
142
- # ── 11. BaseWarning ── structured warnings with factory methods ───────────────
157
+ # ── 12. BaseWarning ── structured warnings with factory methods ───────────────
143
158
  class ExperimentalWarning(BaseWarning):
144
159
  default_detail = "This feature is experimental."
145
160
 
@@ -1,81 +1,82 @@
1
- """A helper tool for caching exceptions raised by functions."""
2
-
3
- import functools
4
- from typing import (
5
- Callable,
6
- Any,
7
- Optional,
8
- TypeVar,
9
- overload,
10
- )
11
-
12
- from .wrappers.cache import ErrorCacheWrapper
13
-
14
- _T = TypeVar("_T", bound=Callable[..., Any])
15
-
16
- # fmt: off
17
-
18
-
19
- @overload
20
- def error_cache(func: _T) -> ErrorCacheWrapper[_T]:
21
- ...
22
-
23
-
24
- @overload
25
- def error_cache(maxsize: Optional[int] = 128) -> Callable[[_T], ErrorCacheWrapper[_T]]:
26
- ...
27
- # fmt: on
28
-
29
-
30
- def error_cache( # type: ignore
31
- func: Optional[_T] = None, maxsize: Optional[int] = 128
32
- ) -> Any:
33
- """
34
- Decorator to cache exceptions raised by a function.
35
-
36
- Usage:
37
-
38
- @error_cache # Default maxsize=128
39
- def risky_func(x: int) -> int: ...
40
-
41
- @error_cache(maxsize=32) # Custom maxsize
42
- def risky_func(x: int) -> int: ...
43
-
44
- @error_cache(maxsize=None) # Unlimited cache
45
- def risky_func(x: int) -> int: ...
46
-
47
- @error_cache() # Explicit empty args (maxsize=128)
48
- def risky_func(x: int) -> int: ...
49
-
50
- Args:
51
- func: The function to wrap (auto-passed when using @error_cache without args).
52
- maxsize: Maximum number of cached errors (None = unlimited, default=128).
53
-
54
- Returns:
55
- Wrapped function with error caching functionality.
56
-
57
- Raises:
58
- TypeError: If non-hashable arguments are passed.
59
- """
60
-
61
- # NOTE: This decorator automatically caches exceptions thrown by the wrapped function,
62
- # keyed by the function's arguments. If the function succeeds, the cached exception
63
- # (if any) for those arguments is removed.
64
-
65
- # Key features:
66
- # - maxsize: Maximum number of cached errors (None = unlimited, default=128)
67
- # - LRU eviction: Evicts least recently used entries when maxsize is reached
68
- # - cache_info(): Returns hits/misses/maxsize/currsize stats
69
- # - clear_cache(): Clears cache and resets statistics
70
- def decorator(f: _T) -> ErrorCacheWrapper[_T]:
71
- if not callable(f):
72
- raise TypeError(f"Expected a callable, got {type(f).__name__} instead")
73
-
74
- wrapper = ErrorCacheWrapper(f, maxsize=maxsize)
75
- functools.update_wrapper(wrapper, f)
76
- return wrapper
77
-
78
- # Handle both @error_cache and @error_cache(...) usage
79
- if func is None:
80
- return decorator
81
- return decorator(func)
1
+ """A helper tool for caching exceptions raised by functions."""
2
+
3
+ import functools
4
+ from typing import (
5
+ Callable,
6
+ Any,
7
+ Optional,
8
+ TypeVar,
9
+ overload,
10
+ )
11
+
12
+ from .const import DEFAULT_ERROR_CACHE_SIZE
13
+ from .wrappers.cache import ErrorCacheWrapper
14
+
15
+ _T = TypeVar("_T", bound=Callable[..., Any])
16
+
17
+ # fmt: off
18
+
19
+
20
+ @overload
21
+ def error_cache(func: _T) -> ErrorCacheWrapper[_T]:
22
+ ...
23
+
24
+
25
+ @overload
26
+ def error_cache(maxsize: Optional[int] = 128) -> Callable[[_T], ErrorCacheWrapper[_T]]:
27
+ ...
28
+ # fmt: on
29
+
30
+
31
+ def error_cache( # type: ignore
32
+ func: Optional[_T] = None, maxsize: Optional[int] = DEFAULT_ERROR_CACHE_SIZE
33
+ ) -> Any:
34
+ """
35
+ Decorator to cache exceptions raised by a function.
36
+
37
+ Usage:
38
+
39
+ @error_cache # Default maxsize=128
40
+ def risky_func(x: int) -> int: ...
41
+
42
+ @error_cache(maxsize=32) # Custom maxsize
43
+ def risky_func(x: int) -> int: ...
44
+
45
+ @error_cache(maxsize=None) # Unlimited cache
46
+ def risky_func(x: int) -> int: ...
47
+
48
+ @error_cache() # Explicit empty args (maxsize=128)
49
+ def risky_func(x: int) -> int: ...
50
+
51
+ Args:
52
+ func: The function to wrap (auto-passed when using @error_cache without args).
53
+ maxsize: Maximum number of cached errors (None = unlimited, default=128).
54
+
55
+ Returns:
56
+ Wrapped function with error caching functionality.
57
+
58
+ Raises:
59
+ TypeError: If non-hashable arguments are passed.
60
+ """
61
+
62
+ # NOTE: This decorator automatically caches exceptions thrown by the wrapped function,
63
+ # keyed by the function's arguments. If the function succeeds, the cached exception
64
+ # (if any) for those arguments is removed.
65
+
66
+ # Key features:
67
+ # - maxsize: Maximum number of cached errors (None = unlimited, default=128)
68
+ # - LRU eviction: Evicts least recently used entries when maxsize is reached
69
+ # - cache_info(): Returns hits/misses/maxsize/currsize stats
70
+ # - clear_cache(): Clears cache and resets statistics
71
+ def decorator(f: _T) -> ErrorCacheWrapper[_T]:
72
+ if not callable(f):
73
+ raise TypeError(f"Expected a callable, got {type(f).__name__} instead")
74
+
75
+ wrapper = ErrorCacheWrapper(f, maxsize=maxsize)
76
+ functools.update_wrapper(wrapper, f)
77
+ return wrapper
78
+
79
+ # Handle both @error_cache and @error_cache(...) usage
80
+ if func is None:
81
+ return decorator
82
+ return decorator(func)