errortools 1.2.0__tar.gz → 2.0.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.
- errortools-2.0.0/AUTHORS.txt +3 -0
- {errortools-1.2.0/errortools.egg-info → errortools-2.0.0}/PKG-INFO +42 -11
- {errortools-1.2.0 → errortools-2.0.0}/README.md +39 -10
- errortools-2.0.0/_errortools/_cli.py +32 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/classes/abc.py +351 -351
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/classes/group.py +118 -118
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/cli.py +120 -85
- errortools-2.0.0/_errortools/const.py +10 -0
- errortools-2.0.0/_errortools/decorator/__init__.py +1 -0
- {errortools-1.2.0/_errortools → errortools-2.0.0/_errortools/decorator}/cache.py +82 -81
- errortools-2.0.0/_errortools/decorator/deprecated.py +33 -0
- errortools-2.0.0/_errortools/ignore.py +284 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/logging/base.py +2 -2
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/logging/sink.py +1 -1
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/metadata.py +7 -0
- errortools-2.0.0/_errortools/partial.py +110 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/raises.py +183 -166
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/version.py +7 -7
- {errortools-1.2.0 → errortools-2.0.0}/errortools/__init__.py +166 -152
- {errortools-1.2.0 → errortools-2.0.0/errortools.egg-info}/PKG-INFO +42 -11
- {errortools-1.2.0 → errortools-2.0.0}/errortools.egg-info/SOURCES.txt +10 -3
- errortools-2.0.0/errortools.egg-info/entry_points.txt +3 -0
- errortools-2.0.0/errortools.egg-info/requires.txt +1 -0
- {errortools-1.2.0 → errortools-2.0.0}/setup.py +35 -34
- errortools-2.0.0/tests/__init__.py +12 -0
- errortools-2.0.0/tests/run_tests.py +19 -0
- {errortools-1.2.0 → errortools-2.0.0}/tests/test_abc.py +304 -314
- {errortools-1.2.0 → errortools-2.0.0}/tests/test_cache.py +6 -1
- errortools-2.0.0/tests/test_const.py +33 -0
- errortools-2.0.0/tests/test_decorator.py +85 -0
- {errortools-1.2.0 → errortools-2.0.0}/tests/test_descriptor.py +5 -12
- {errortools-1.2.0 → errortools-2.0.0}/tests/test_errorcodes.py +6 -1
- {errortools-1.2.0 → errortools-2.0.0}/tests/test_groups.py +5 -3
- {errortools-1.2.0 → errortools-2.0.0}/tests/test_ignore.py +104 -1
- {errortools-1.2.0 → errortools-2.0.0}/tests/test_logging.py +5 -4
- {errortools-1.2.0 → errortools-2.0.0}/tests/test_mixins.py +5 -5
- errortools-2.0.0/tests/test_partials.py +233 -0
- {errortools-1.2.0 → errortools-2.0.0}/tests/test_raises.py +4 -20
- {errortools-1.2.0 → errortools-2.0.0}/tests/test_typing.py +6 -8
- {errortools-1.2.0 → errortools-2.0.0}/tests/test_warnings.py +5 -0
- errortools-1.2.0/AUTHORS.txt +0 -2
- errortools-1.2.0/_errortools/ignore.py +0 -155
- errortools-1.2.0/_errortools/tools/__init__.py +0 -1
- errortools-1.2.0/_errortools/tools/_warps.py +0 -27
- errortools-1.2.0/errortools.egg-info/entry_points.txt +0 -2
- errortools-1.2.0/tests/__init__.py +0 -3
- errortools-1.2.0/tests/run_tests.py +0 -13
- {errortools-1.2.0 → errortools-2.0.0}/LICENSE.txt +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/__init__.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/classes/__init__.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/classes/errorcodes.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/classes/warn.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/future.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/logging/__init__.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/logging/level.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/logging/logger.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/logging/record.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/methods/__init__.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/methods/errorattr.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/methods/errordelattr.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/methods/errorhasattr.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/methods/errorsetattr.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/py.typed +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/_errortools/typing.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/errortools/__main__.py +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/errortools.egg-info/dependency_links.txt +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/errortools.egg-info/top_level.txt +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/setup.cfg +0 -0
- {errortools-1.2.0 → errortools-2.0.0}/tests/conftest.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: errortools
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.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
|
|
@@ -19,6 +19,7 @@ Requires-Python: >=3.10
|
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE.txt
|
|
21
21
|
License-File: AUTHORS.txt
|
|
22
|
+
Requires-Dist: namebyauthor==1.0.0
|
|
22
23
|
Dynamic: author
|
|
23
24
|
Dynamic: author-email
|
|
24
25
|
Dynamic: classifier
|
|
@@ -27,6 +28,7 @@ Dynamic: description-content-type
|
|
|
27
28
|
Dynamic: home-page
|
|
28
29
|
Dynamic: license
|
|
29
30
|
Dynamic: license-file
|
|
31
|
+
Dynamic: requires-dist
|
|
30
32
|
Dynamic: requires-python
|
|
31
33
|
Dynamic: summary
|
|
32
34
|
|
|
@@ -35,7 +37,7 @@ A lightweight Python exception handling utility library.
|
|
|
35
37
|
|
|
36
38
|
## Features
|
|
37
39
|
- **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
|
|
40
|
+
- **Catch & Suppress**: `ignore()`, `ignore_subclass()`, `ignore_warns()`, `fast_ignore()`, `super_fast_ignore()`, `timeout()`, `retry()` — graceful suppression of exceptions and warnings, with automatic retry
|
|
39
41
|
- **Exception Caching**: `error_cache` — cache exceptions raised by functions (similar to `lru_cache`)
|
|
40
42
|
- **Custom Exceptions**: `PureBaseException`, `ContextException`, `BaseErrorCodes`, `BaseWarning` — structured exception classes with error codes, trace IDs, and context
|
|
41
43
|
- **Attribute Error Mixin**: Customize error behavior for attribute access, assignment, and deletion
|
|
@@ -49,12 +51,12 @@ pip install errortools
|
|
|
49
51
|
|
|
50
52
|
---
|
|
51
53
|
|
|
52
|
-
##
|
|
54
|
+
## Examples
|
|
53
55
|
|
|
54
56
|
```python
|
|
55
57
|
import warnings
|
|
56
58
|
from errortools import (
|
|
57
|
-
ignore, fast_ignore, ignore_subclass, ignore_warns, timeout,
|
|
59
|
+
ignore, fast_ignore, ignore_subclass, ignore_warns, timeout, retry,
|
|
58
60
|
reraise, raises, raises_all, assert_raises,
|
|
59
61
|
error_cache,
|
|
60
62
|
PureBaseException, ContextException, BaseErrorCodes, BaseWarning,
|
|
@@ -70,8 +72,22 @@ assert err.name == "KeyError" # exception class name
|
|
|
70
72
|
assert err.count == 1 # how many times suppressed
|
|
71
73
|
assert err.exception # the original KeyError instance
|
|
72
74
|
assert err.traceback # full formatted traceback string
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Attributes on the returned object:**
|
|
78
|
+
|
|
79
|
+
| Attribute | Type | Description |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `be_ignore` | `bool` | `True` if an exception was suppressed |
|
|
82
|
+
| `name` | `str \| None` | Class name of the suppressed exception |
|
|
83
|
+
| `count` | `int` | Number of suppressed exceptions |
|
|
84
|
+
| `exception` | `Exception \| None` | The original exception instance |
|
|
85
|
+
| `traceback` | `str \| None` | Formatted traceback string for debugging |
|
|
86
|
+
|
|
87
|
+
## Examples
|
|
73
88
|
|
|
74
|
-
|
|
89
|
+
```python
|
|
90
|
+
# ── ignore as a decorator ──
|
|
75
91
|
@ignore(ValueError, TypeError)
|
|
76
92
|
def parse(x: str) -> int:
|
|
77
93
|
return int(x)
|
|
@@ -103,14 +119,29 @@ async def fetch_data(url: str) -> bytes:
|
|
|
103
119
|
|
|
104
120
|
# asyncio.TimeoutError raised automatically if it exceeds 5 s
|
|
105
121
|
|
|
106
|
-
# ── 6.
|
|
122
|
+
# ── 6. retry ── automatically retry on failure ───────────────────────────────
|
|
123
|
+
@retry(times=3, on=ConnectionError, delay=1.0)
|
|
124
|
+
def connect(host: str) -> None:
|
|
125
|
+
... # retried up to 3 times on ConnectionError
|
|
126
|
+
|
|
127
|
+
# works with async functions too
|
|
128
|
+
@retry(times=5, on=TimeoutError, delay=0.5)
|
|
129
|
+
async def fetch(url: str) -> bytes:
|
|
130
|
+
...
|
|
131
|
+
|
|
132
|
+
# multiple exception types
|
|
133
|
+
@retry(times=2, on=(ValueError, KeyError))
|
|
134
|
+
def parse(data: dict) -> str:
|
|
135
|
+
return data["key"]
|
|
136
|
+
|
|
137
|
+
# ── 7. reraise ── convert exception types on the fly ─────────────────────────
|
|
107
138
|
with reraise(KeyError, ValueError):
|
|
108
139
|
raise KeyError("missing key") # → ValueError: 'missing key'
|
|
109
140
|
|
|
110
141
|
with reraise((KeyError, IndexError), RuntimeError):
|
|
111
142
|
_ = [][99] # → RuntimeError: list index out of range
|
|
112
143
|
|
|
113
|
-
# ──
|
|
144
|
+
# ── 8. raises / raises_all ── batch raise ────────────────────────────────────
|
|
114
145
|
raises([ValueError], ["bad input"]) # → ValueError: bad input
|
|
115
146
|
|
|
116
147
|
raises_all(
|
|
@@ -118,11 +149,11 @@ raises_all(
|
|
|
118
149
|
["bad input"],
|
|
119
150
|
) # → ExceptionGroup (2 sub-exceptions)
|
|
120
151
|
|
|
121
|
-
# ──
|
|
152
|
+
# ── 9. assert_raises ── assert a callable raises ─────────────────────────────
|
|
122
153
|
exc = assert_raises(int, [ValueError], "not-a-number")
|
|
123
154
|
print(exc) # invalid literal for int() with base 10: 'not-a-number'
|
|
124
155
|
|
|
125
|
-
# ──
|
|
156
|
+
# ── 10. error_cache ── cache exceptions by call arguments ─────────────────────
|
|
126
157
|
@error_cache(maxsize=64)
|
|
127
158
|
def load(user_id: int) -> dict:
|
|
128
159
|
if user_id < 0:
|
|
@@ -135,7 +166,7 @@ with ignore(ValueError):
|
|
|
135
166
|
print(load.cache_info()) # CacheInfo(hits=0, misses=1, maxsize=64, currsize=1)
|
|
136
167
|
load.clear_cache()
|
|
137
168
|
|
|
138
|
-
# ──
|
|
169
|
+
# ── 11. Custom exceptions — three layers ──────────────────────────────────────
|
|
139
170
|
|
|
140
171
|
# Layer 1: PureBaseException — code + detail only
|
|
141
172
|
class AppError(PureBaseException):
|
|
@@ -171,7 +202,7 @@ raise BaseErrorCodes.runtime_failure("crash") # RuntimeFailure [
|
|
|
171
202
|
raise BaseErrorCodes.timeout_failure() # TimeoutFailure [4002]
|
|
172
203
|
raise BaseErrorCodes.configuration_error("missing key") # ConfigurationError [5001]
|
|
173
204
|
|
|
174
|
-
# ──
|
|
205
|
+
# ── 12. BaseWarning ── structured warnings with factory methods ───────────────
|
|
175
206
|
class ExperimentalWarning(BaseWarning):
|
|
176
207
|
default_detail = "This feature is experimental."
|
|
177
208
|
|
|
@@ -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
|
|
@@ -17,12 +17,12 @@ pip install errortools
|
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## Examples
|
|
21
21
|
|
|
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,
|
|
@@ -38,8 +38,22 @@ assert err.name == "KeyError" # exception class name
|
|
|
38
38
|
assert err.count == 1 # how many times suppressed
|
|
39
39
|
assert err.exception # the original KeyError instance
|
|
40
40
|
assert err.traceback # full formatted traceback string
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Attributes on the returned object:**
|
|
44
|
+
|
|
45
|
+
| Attribute | Type | Description |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `be_ignore` | `bool` | `True` if an exception was suppressed |
|
|
48
|
+
| `name` | `str \| None` | Class name of the suppressed exception |
|
|
49
|
+
| `count` | `int` | Number of suppressed exceptions |
|
|
50
|
+
| `exception` | `Exception \| None` | The original exception instance |
|
|
51
|
+
| `traceback` | `str \| None` | Formatted traceback string for debugging |
|
|
52
|
+
|
|
53
|
+
## Examples
|
|
41
54
|
|
|
42
|
-
|
|
55
|
+
```python
|
|
56
|
+
# ── ignore as a decorator ──
|
|
43
57
|
@ignore(ValueError, TypeError)
|
|
44
58
|
def parse(x: str) -> int:
|
|
45
59
|
return int(x)
|
|
@@ -71,14 +85,29 @@ async def fetch_data(url: str) -> bytes:
|
|
|
71
85
|
|
|
72
86
|
# asyncio.TimeoutError raised automatically if it exceeds 5 s
|
|
73
87
|
|
|
74
|
-
# ── 6.
|
|
88
|
+
# ── 6. retry ── automatically retry on failure ───────────────────────────────
|
|
89
|
+
@retry(times=3, on=ConnectionError, delay=1.0)
|
|
90
|
+
def connect(host: str) -> None:
|
|
91
|
+
... # retried up to 3 times on ConnectionError
|
|
92
|
+
|
|
93
|
+
# works with async functions too
|
|
94
|
+
@retry(times=5, on=TimeoutError, delay=0.5)
|
|
95
|
+
async def fetch(url: str) -> bytes:
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
# multiple exception types
|
|
99
|
+
@retry(times=2, on=(ValueError, KeyError))
|
|
100
|
+
def parse(data: dict) -> str:
|
|
101
|
+
return data["key"]
|
|
102
|
+
|
|
103
|
+
# ── 7. reraise ── convert exception types on the fly ─────────────────────────
|
|
75
104
|
with reraise(KeyError, ValueError):
|
|
76
105
|
raise KeyError("missing key") # → ValueError: 'missing key'
|
|
77
106
|
|
|
78
107
|
with reraise((KeyError, IndexError), RuntimeError):
|
|
79
108
|
_ = [][99] # → RuntimeError: list index out of range
|
|
80
109
|
|
|
81
|
-
# ──
|
|
110
|
+
# ── 8. raises / raises_all ── batch raise ────────────────────────────────────
|
|
82
111
|
raises([ValueError], ["bad input"]) # → ValueError: bad input
|
|
83
112
|
|
|
84
113
|
raises_all(
|
|
@@ -86,11 +115,11 @@ raises_all(
|
|
|
86
115
|
["bad input"],
|
|
87
116
|
) # → ExceptionGroup (2 sub-exceptions)
|
|
88
117
|
|
|
89
|
-
# ──
|
|
118
|
+
# ── 9. assert_raises ── assert a callable raises ─────────────────────────────
|
|
90
119
|
exc = assert_raises(int, [ValueError], "not-a-number")
|
|
91
120
|
print(exc) # invalid literal for int() with base 10: 'not-a-number'
|
|
92
121
|
|
|
93
|
-
# ──
|
|
122
|
+
# ── 10. error_cache ── cache exceptions by call arguments ─────────────────────
|
|
94
123
|
@error_cache(maxsize=64)
|
|
95
124
|
def load(user_id: int) -> dict:
|
|
96
125
|
if user_id < 0:
|
|
@@ -103,7 +132,7 @@ with ignore(ValueError):
|
|
|
103
132
|
print(load.cache_info()) # CacheInfo(hits=0, misses=1, maxsize=64, currsize=1)
|
|
104
133
|
load.clear_cache()
|
|
105
134
|
|
|
106
|
-
# ──
|
|
135
|
+
# ── 11. Custom exceptions — three layers ──────────────────────────────────────
|
|
107
136
|
|
|
108
137
|
# Layer 1: PureBaseException — code + detail only
|
|
109
138
|
class AppError(PureBaseException):
|
|
@@ -139,7 +168,7 @@ raise BaseErrorCodes.runtime_failure("crash") # RuntimeFailure [
|
|
|
139
168
|
raise BaseErrorCodes.timeout_failure() # TimeoutFailure [4002]
|
|
140
169
|
raise BaseErrorCodes.configuration_error("missing key") # ConfigurationError [5001]
|
|
141
170
|
|
|
142
|
-
# ──
|
|
171
|
+
# ── 12. BaseWarning ── structured warnings with factory methods ───────────────
|
|
143
172
|
class ExperimentalWarning(BaseWarning):
|
|
144
173
|
default_detail = "This feature is experimental."
|
|
145
174
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from _errortools.metadata import (
|
|
4
|
+
__author__,
|
|
5
|
+
__author_email__,
|
|
6
|
+
__copyright__,
|
|
7
|
+
__description__,
|
|
8
|
+
__license__,
|
|
9
|
+
__url__,
|
|
10
|
+
)
|
|
11
|
+
from _errortools.version import __version__
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _cmd_log(message: str, level: str, output: str) -> None:
|
|
15
|
+
"""Emit a single log message via the errortools logger."""
|
|
16
|
+
from .logging import BaseLogger
|
|
17
|
+
from .logging.level import get_level
|
|
18
|
+
|
|
19
|
+
stream = sys.stdout if output == "stdout" else sys.stderr
|
|
20
|
+
log = BaseLogger(name="errortools-cli")
|
|
21
|
+
log.add(stream, level="TRACE", colorize=None)
|
|
22
|
+
log.log(get_level(level), message)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _print_info() -> None:
|
|
26
|
+
"""Print a summary of all package metadata."""
|
|
27
|
+
print(f"errortools v{__version__}")
|
|
28
|
+
print(f" {__description__}")
|
|
29
|
+
print(f" Author: {__author__} <{__author_email__}>")
|
|
30
|
+
print(f" License: {__license__}")
|
|
31
|
+
print(f" URL: {__url__}")
|
|
32
|
+
print(f" Copyright: {__copyright__}")
|