errortools 2.5.0__tar.gz → 3.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.5.0/errortools.egg-info → errortools-3.0.0}/PKG-INFO +4 -3
- {errortools-2.5.0 → errortools-3.0.0}/README.md +0 -1
- errortools-3.0.0/_errortools/_speedup.c +58 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/classes/abc.py +0 -148
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/classes/group.py +41 -38
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/future.py +24 -10
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/logging/record.py +8 -1
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/logging/sink.py +8 -1
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/raises.py +42 -39
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/typing.py +7 -1
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/version.py +2 -2
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/wrappers/cache.py +7 -1
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/wrappers/ignore.py +8 -1
- {errortools-2.5.0 → errortools-3.0.0}/errortools/__init__.py +0 -12
- {errortools-2.5.0 → errortools-3.0.0/errortools.egg-info}/PKG-INFO +4 -3
- {errortools-2.5.0 → errortools-3.0.0}/errortools.egg-info/SOURCES.txt +1 -6
- errortools-3.0.0/setup.py +58 -0
- {errortools-2.5.0 → errortools-3.0.0}/tests/__init__.py +1 -1
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_abc.py +0 -4
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_const.py +0 -5
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_decorator.py +0 -5
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_descriptor.py +0 -5
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_errno.py +0 -5
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_errorcodes.py +0 -5
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_future.py +0 -6
- errortools-3.0.0/tests/test_groups.py +128 -0
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_ignore.py +0 -5
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_logging.py +0 -5
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_partials.py +0 -5
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_raises.py +36 -37
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_typing.py +0 -5
- {errortools-2.5.0 → errortools-3.0.0}/tests/test_warnings.py +0 -5
- errortools-2.5.0/_errortools/methods/__init__.py +0 -11
- errortools-2.5.0/_errortools/methods/errorattr.py +0 -37
- errortools-2.5.0/_errortools/methods/errordelattr.py +0 -35
- errortools-2.5.0/_errortools/methods/errorhasattr.py +0 -27
- errortools-2.5.0/_errortools/methods/errorsetattr.py +0 -40
- errortools-2.5.0/setup.py +0 -79
- errortools-2.5.0/tests/test_groups.py +0 -129
- errortools-2.5.0/tests/test_mixins.py +0 -256
- {errortools-2.5.0 → errortools-3.0.0}/AUTHORS.txt +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/LICENSE.txt +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/__init__.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/_cli.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/classes/__init__.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/classes/errorcodes.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/classes/warn.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/cli.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/const.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/decorator/__init__.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/decorator/cache.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/decorator/deprecated.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/descriptor/__init__.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/descriptor/errormsg.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/descriptor/nonblankmsg.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/errno.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/ignore.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/logging/__init__.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/logging/base.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/logging/level.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/logging/logger.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/metadata.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/partial.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/py.typed +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/_errortools/wrappers/__init__.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/errortools/__main__.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/errortools/future.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/errortools/logging.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/errortools/partial.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/errortools.egg-info/dependency_links.txt +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/errortools.egg-info/entry_points.txt +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/errortools.egg-info/requires.txt +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/errortools.egg-info/top_level.txt +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/setup.cfg +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/tests/conftest.py +0 -0
- {errortools-2.5.0 → errortools-3.0.0}/tests/run_tests.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: errortools
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.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
|
|
@@ -8,6 +8,8 @@ Author-email: quantbit@126.com
|
|
|
8
8
|
License: MIT
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -16,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
16
18
|
Classifier: Programming Language :: Python :: 3.15
|
|
17
19
|
Classifier: Operating System :: OS Independent
|
|
18
20
|
Classifier: Typing :: Typed
|
|
19
|
-
Requires-Python: >=3.
|
|
21
|
+
Requires-Python: >=3.8
|
|
20
22
|
Description-Content-Type: text/markdown
|
|
21
23
|
License-File: LICENSE.txt
|
|
22
24
|
License-File: AUTHORS.txt
|
|
@@ -51,7 +53,6 @@ A lightweight Python exception handling utility library.
|
|
|
51
53
|
- **Future Utilities**: `super_fast_catch()`, `super_fast_reraise()`, `ExceptionCollector` — lightweight exception handling for high-performance scenarios
|
|
52
54
|
- **Exception Caching**: `error_cache` — cache exceptions raised by functions (similar to `lru_cache`)
|
|
53
55
|
- **Custom Exceptions**: `PureBaseException`, `ContextException`, `BaseErrorCodes`, `BaseWarning` — structured exception classes with error codes, trace IDs, and context
|
|
54
|
-
- **Attribute Error Mixin**: Customize error behavior for attribute access, assignment, and deletion
|
|
55
56
|
- **Type Aliases**: `ExceptionType`, `AnyErrorCode`, `BaseErrorCodesType`, and more
|
|
56
57
|
- **Logging**: `logger` — loguru-inspired structured logger with leveled output, multiple sinks, context binding, and exception capture
|
|
57
58
|
|
|
@@ -15,7 +15,6 @@ A lightweight Python exception handling utility library.
|
|
|
15
15
|
- **Future Utilities**: `super_fast_catch()`, `super_fast_reraise()`, `ExceptionCollector` — lightweight exception handling for high-performance scenarios
|
|
16
16
|
- **Exception Caching**: `error_cache` — cache exceptions raised by functions (similar to `lru_cache`)
|
|
17
17
|
- **Custom Exceptions**: `PureBaseException`, `ContextException`, `BaseErrorCodes`, `BaseWarning` — structured exception classes with error codes, trace IDs, and context
|
|
18
|
-
- **Attribute Error Mixin**: Customize error behavior for attribute access, assignment, and deletion
|
|
19
18
|
- **Type Aliases**: `ExceptionType`, `AnyErrorCode`, `BaseErrorCodesType`, and more
|
|
20
19
|
- **Logging**: `logger` — loguru-inspired structured logger with leveled output, multiple sinks, context binding, and exception capture
|
|
21
20
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#define PY_SSIZE_T_CLEAN
|
|
2
|
+
#include <Python.h>
|
|
3
|
+
|
|
4
|
+
/* Fast exception type checking */
|
|
5
|
+
static PyObject* fast_issubclass_check(PyObject* self, PyObject* args) {
|
|
6
|
+
PyObject *typ, *excs;
|
|
7
|
+
if (!PyArg_ParseTuple(args, "OO", &typ, &excs)) {
|
|
8
|
+
return NULL;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (typ == Py_None) {
|
|
12
|
+
Py_RETURN_FALSE;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
int result = PyObject_IsSubclass(typ, excs);
|
|
16
|
+
if (result == -1) {
|
|
17
|
+
return NULL;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return PyBool_FromLong(result);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Fast exception collector append */
|
|
24
|
+
static PyObject* fast_append_exception(PyObject* self, PyObject* args) {
|
|
25
|
+
PyObject *list, *exc;
|
|
26
|
+
if (!PyArg_ParseTuple(args, "OO", &list, &exc)) {
|
|
27
|
+
return NULL;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (PyList_Append(list, exc) == -1) {
|
|
31
|
+
return NULL;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Py_RETURN_NONE;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Method definitions */
|
|
38
|
+
static PyMethodDef SpeedupMethods[] = {
|
|
39
|
+
{"fast_issubclass_check", fast_issubclass_check, METH_VARARGS,
|
|
40
|
+
"Fast exception type checking"},
|
|
41
|
+
{"fast_append_exception", fast_append_exception, METH_VARARGS,
|
|
42
|
+
"Fast exception list append"},
|
|
43
|
+
{NULL, NULL, 0, NULL}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/* Module definition */
|
|
47
|
+
static struct PyModuleDef speedupmodule = {
|
|
48
|
+
PyModuleDef_HEAD_INIT,
|
|
49
|
+
"_speedup",
|
|
50
|
+
"C speedup for errortools",
|
|
51
|
+
-1,
|
|
52
|
+
SpeedupMethods
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/* Module initialization */
|
|
56
|
+
PyMODINIT_FUNC PyInit__speedup(void) {
|
|
57
|
+
return PyModule_Create(&speedupmodule);
|
|
58
|
+
}
|
|
@@ -11,13 +11,6 @@ if sys.version_info >= (3, 15):
|
|
|
11
11
|
else:
|
|
12
12
|
from typing_extensions import disjoint_base
|
|
13
13
|
|
|
14
|
-
from ..methods import (
|
|
15
|
-
ErrorAttrMixin,
|
|
16
|
-
ErrorAttrCheckMixin,
|
|
17
|
-
ErrorAttrDeletionMixin,
|
|
18
|
-
ErrorSetAttrMixin,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
14
|
|
|
22
15
|
def _check_methods(C: type[Any], *methods: str) -> Union[bool, Literal[NotImplemented]]: # type: ignore
|
|
23
16
|
"""Check methods in `C`. If has, return `True`, else `NotImplemented`."""
|
|
@@ -36,147 +29,6 @@ def _check_methods(C: type[Any], *methods: str) -> Union[bool, Literal[NotImplem
|
|
|
36
29
|
return True
|
|
37
30
|
|
|
38
31
|
|
|
39
|
-
@disjoint_base
|
|
40
|
-
class ErrorAttrable(ABC):
|
|
41
|
-
"""
|
|
42
|
-
Abstract Base Class (ABC) for classes supporting custom attribute error handling.
|
|
43
|
-
|
|
44
|
-
This class follows the design pattern of `collections.abc` (e.g., Iterable, Mapping):
|
|
45
|
-
- Uses `__subclasshook__` + `_check_methods` to validate subclass compliance
|
|
46
|
-
- Enforces implementation of attribute error handling methods via abstract methods
|
|
47
|
-
- Implements native attribute magic methods to forward errors to custom handlers
|
|
48
|
-
|
|
49
|
-
Core behavior:
|
|
50
|
-
When attribute operations (get/delete/check/set) fail, the corresponding native
|
|
51
|
-
magic methods automatically invoke custom error handling methods implemented by subclasses.
|
|
52
|
-
"""
|
|
53
|
-
|
|
54
|
-
__slots__ = ()
|
|
55
|
-
|
|
56
|
-
@classmethod
|
|
57
|
-
def __subclasshook__(cls, C: type[Any]) -> Union[bool, Literal[NotImplemented]]: # type: ignore
|
|
58
|
-
"""
|
|
59
|
-
Check if a class is a subclass of ErrorAttrable (per `collections.abc` style).
|
|
60
|
-
|
|
61
|
-
This method enables `issubclass()` to recognize classes that implement the core
|
|
62
|
-
__errorattr__ method (base requirement), matching the behavior of standard ABCs.
|
|
63
|
-
|
|
64
|
-
Args:
|
|
65
|
-
C: The class to check for compliance with ErrorAttrable interface
|
|
66
|
-
|
|
67
|
-
Returns:
|
|
68
|
-
True if C implements __errorattr__, NotImplemented otherwise
|
|
69
|
-
"""
|
|
70
|
-
if cls is ErrorAttrable:
|
|
71
|
-
return _check_methods(C, "__errorattr__")
|
|
72
|
-
return NotImplemented
|
|
73
|
-
|
|
74
|
-
def __getattr__(self, name: str) -> Any:
|
|
75
|
-
"""
|
|
76
|
-
Native magic method: Automatically invoked for missing attribute access.
|
|
77
|
-
|
|
78
|
-
Forwards the attribute lookup failure to the custom `__errorattr__` method.
|
|
79
|
-
"""
|
|
80
|
-
return self.__errorattr__(name)
|
|
81
|
-
|
|
82
|
-
@abstractmethod
|
|
83
|
-
def __errorattr__(self, name: str) -> Any:
|
|
84
|
-
"""
|
|
85
|
-
Abstract method for custom missing attribute handling (MUST be implemented).
|
|
86
|
-
|
|
87
|
-
Args:
|
|
88
|
-
name: Name of the missing attribute being accessed
|
|
89
|
-
|
|
90
|
-
Raises:
|
|
91
|
-
NotImplementedError: If not overridden
|
|
92
|
-
AttributeError: Recommended error type for missing attributes
|
|
93
|
-
"""
|
|
94
|
-
raise NotImplementedError(
|
|
95
|
-
"Subclasses of ErrorAttrable must implement __errorattr__(self, name: str).\n"
|
|
96
|
-
"See `collections.abc` for similar abstract method requirements (e.g., __iter__ for Iterable)."
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
def __delattr__(self, name: str) -> None:
|
|
100
|
-
"""
|
|
101
|
-
Native magic method: Automatically invoked for attribute deletion errors.
|
|
102
|
-
|
|
103
|
-
Forwards to __errordelattr__ if implemented, else raises standard error.
|
|
104
|
-
"""
|
|
105
|
-
if hasattr(self, "__errordelattr__"):
|
|
106
|
-
self.__errordelattr__(name)
|
|
107
|
-
else:
|
|
108
|
-
super().__delattr__(name)
|
|
109
|
-
|
|
110
|
-
def __errordelattr__(self, name: str) -> None:
|
|
111
|
-
"""
|
|
112
|
-
Custom handler for attribute deletion errors (OPTIONAL to implement).
|
|
113
|
-
|
|
114
|
-
Args:
|
|
115
|
-
name: Name of the attribute being deleted
|
|
116
|
-
"""
|
|
117
|
-
raise NotImplementedError(
|
|
118
|
-
"Subclasses of ErrorAttrable must implement __errordelattr__(self, name: str).\n"
|
|
119
|
-
"See `collections.abc` for similar abstract method requirements (e.g., __iter__ for Iterable)."
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
def __contains__(self, name: str) -> bool:
|
|
123
|
-
"""
|
|
124
|
-
Alternative to __hasattr__: Check if attribute exists (customizable).
|
|
125
|
-
|
|
126
|
-
Forwards to __errorhasattr__ if implemented, else uses standard check.
|
|
127
|
-
"""
|
|
128
|
-
if hasattr(self, "__errorhasattr__"):
|
|
129
|
-
return self.__errorhasattr__(name)
|
|
130
|
-
else:
|
|
131
|
-
return hasattr(super(), name)
|
|
132
|
-
|
|
133
|
-
def __errorhasattr__(self, name: str) -> bool:
|
|
134
|
-
"""
|
|
135
|
-
Custom handler for attribute existence checks (OPTIONAL to implement).
|
|
136
|
-
|
|
137
|
-
Args:
|
|
138
|
-
name: Name of the attribute to check
|
|
139
|
-
|
|
140
|
-
Returns:
|
|
141
|
-
bool: True if attribute exists (custom logic), False otherwise
|
|
142
|
-
"""
|
|
143
|
-
raise NotImplementedError(
|
|
144
|
-
"Subclasses of ErrorAttrable must implement __errorhasattr__(self, name: str).\n"
|
|
145
|
-
"See `collections.abc` for similar abstract method requirements (e.g., __iter__ for Iterable)."
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
def __setattr__(self, name: str, value: Any) -> None:
|
|
149
|
-
"""
|
|
150
|
-
Native magic method: Automatically invoked for attribute setting errors.
|
|
151
|
-
|
|
152
|
-
Forwards to __errorsetattr__ if implemented, else uses standard setting.
|
|
153
|
-
"""
|
|
154
|
-
if hasattr(self, "__errorsetattr__"):
|
|
155
|
-
self.__errorsetattr__(name, value)
|
|
156
|
-
else:
|
|
157
|
-
super().__setattr__(name, value)
|
|
158
|
-
|
|
159
|
-
def __errorsetattr__(self, name: str, value: Any) -> None:
|
|
160
|
-
"""
|
|
161
|
-
Custom handler for attribute setting errors (OPTIONAL to implement).
|
|
162
|
-
|
|
163
|
-
Args:
|
|
164
|
-
name: Name of the attribute to set
|
|
165
|
-
value: Value to assign to the attribute
|
|
166
|
-
"""
|
|
167
|
-
raise NotImplementedError(
|
|
168
|
-
"Subclasses of ErrorAttrable must implement __errorsetattr__(self, name: str, value: Any).\n"
|
|
169
|
-
"See `collections.abc` for similar abstract method requirements (e.g., __iter__ for Iterable)."
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
# register four Mixin's
|
|
174
|
-
ErrorAttrable.register(ErrorAttrMixin)
|
|
175
|
-
ErrorAttrable.register(ErrorAttrDeletionMixin)
|
|
176
|
-
ErrorAttrable.register(ErrorAttrCheckMixin)
|
|
177
|
-
ErrorAttrable.register(ErrorSetAttrMixin)
|
|
178
|
-
|
|
179
|
-
|
|
180
32
|
# ----------------------------------------------------------------------
|
|
181
33
|
# ErrorCodeable
|
|
182
34
|
# ----------------------------------------------------------------------
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Base and concrete group classes for collecting and raising exceptions together."""
|
|
2
2
|
|
|
3
3
|
from abc import abstractmethod, ABC
|
|
4
|
+
import sys
|
|
4
5
|
|
|
5
6
|
__all__ = [
|
|
6
7
|
"BaseGroup",
|
|
@@ -67,52 +68,54 @@ class BaseGroup(Exception, ABC):
|
|
|
67
68
|
)
|
|
68
69
|
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
"""A collector that accumulates exceptions and raises them as an ExceptionGroup.
|
|
71
|
+
if sys.version_info >= (3, 11):
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
collected without raising.
|
|
73
|
+
class GroupErrors(BaseGroup):
|
|
74
|
+
"""A collector that accumulates exceptions and raises them as an ExceptionGroup.
|
|
76
75
|
|
|
77
|
-
|
|
76
|
+
Call `collect` to add exceptions one by one, then `raise_group`
|
|
77
|
+
to raise them all at once. Use `errors` to inspect what has been
|
|
78
|
+
collected without raising.
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
>>> g.collect(TypeError("expected str"))
|
|
81
|
-
>>> g.collect(ValueError("value out of range"))
|
|
82
|
-
>>> g.raise_group()
|
|
83
|
-
Traceback (most recent call last):
|
|
84
|
-
...
|
|
85
|
-
ExceptionGroup: validation failed (2 sub-exceptions)
|
|
86
|
-
"""
|
|
80
|
+
Example:
|
|
87
81
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
>>> g = GroupErrors("validation failed")
|
|
83
|
+
>>> g.collect(TypeError("expected str"))
|
|
84
|
+
>>> g.collect(ValueError("value out of range"))
|
|
85
|
+
>>> g.raise_group()
|
|
86
|
+
Traceback (most recent call last):
|
|
87
|
+
...
|
|
88
|
+
ExceptionGroup: validation failed (2 sub-exceptions)
|
|
89
|
+
"""
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return list(self._errors)
|
|
91
|
+
def __init__(self, group_msg: str = "multiple errors") -> None:
|
|
92
|
+
super().__init__(group_msg)
|
|
93
|
+
self._errors: list[Exception] = []
|
|
96
94
|
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
@property
|
|
96
|
+
def errors(self) -> list[Exception]:
|
|
97
|
+
"""A copy of the collected exceptions."""
|
|
98
|
+
return list(self._errors)
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
exc
|
|
102
|
-
"""
|
|
103
|
-
self._errors.append(exc)
|
|
100
|
+
def collect(self, exc: Exception) -> None:
|
|
101
|
+
"""Add *exc* to the group without raising it.
|
|
104
102
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
Args:
|
|
104
|
+
exc: The exception instance to collect.
|
|
105
|
+
"""
|
|
106
|
+
self._errors.append(exc)
|
|
108
107
|
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
def clear(self) -> None:
|
|
109
|
+
"""Remove all collected exceptions."""
|
|
110
|
+
self._errors.clear()
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
def raise_group(self) -> None:
|
|
113
|
+
"""Raise all collected exceptions as an `ExceptionGroup`.
|
|
113
114
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
Does nothing if no exceptions have been collected.
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
ExceptionGroup: Containing every exception added via `collect`.
|
|
119
|
+
"""
|
|
120
|
+
if self._errors:
|
|
121
|
+
raise ExceptionGroup(self.group_msg, self._errors)
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
"""Future-focused lightweight exception handling utilities."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
if sys.version_info <= (3, 10):
|
|
7
|
+
from typing_extensions import TypeAlias
|
|
8
|
+
else:
|
|
9
|
+
from typing import TypeAlias
|
|
10
|
+
from typing import cast, Literal
|
|
11
|
+
|
|
12
|
+
# Try to import C speedup
|
|
13
|
+
try:
|
|
14
|
+
from _errortools._speedup import fast_issubclass_check, fast_append_exception # type: ignore[import-not-found]
|
|
15
|
+
except ImportError:
|
|
16
|
+
|
|
17
|
+
def fast_issubclass_check(typ, excs) -> bool:
|
|
18
|
+
return typ is not None and issubclass(typ, excs)
|
|
19
|
+
|
|
20
|
+
def fast_append_exception(lst, exc) -> None:
|
|
21
|
+
lst.append(exc)
|
|
4
22
|
|
|
5
|
-
from typing import TypeAlias, cast, Literal
|
|
6
23
|
|
|
7
24
|
__all__ = [
|
|
8
25
|
"super_fast_ignore",
|
|
@@ -26,10 +43,7 @@ class super_fast_ignore:
|
|
|
26
43
|
return
|
|
27
44
|
|
|
28
45
|
def __exit__(self, typ: _ExcType | None, *_) -> bool:
|
|
29
|
-
|
|
30
|
-
return False
|
|
31
|
-
excs = self.excs
|
|
32
|
-
return issubclass(typ, excs)
|
|
46
|
+
return cast(bool, fast_issubclass_check(typ, self.excs))
|
|
33
47
|
|
|
34
48
|
|
|
35
49
|
class super_fast_catch:
|
|
@@ -50,11 +64,11 @@ class super_fast_catch:
|
|
|
50
64
|
self.excs = excs if excs else (BaseException,)
|
|
51
65
|
self.exception: BaseException | None = None
|
|
52
66
|
|
|
53
|
-
def __enter__(self) ->
|
|
67
|
+
def __enter__(self) -> super_fast_catch:
|
|
54
68
|
return self
|
|
55
69
|
|
|
56
70
|
def __exit__(self, typ: _ExcType | None, val, *_) -> bool:
|
|
57
|
-
if
|
|
71
|
+
if not fast_issubclass_check(typ, self.excs):
|
|
58
72
|
return False
|
|
59
73
|
self.exception = val
|
|
60
74
|
return True
|
|
@@ -117,7 +131,7 @@ class ExceptionCollector:
|
|
|
117
131
|
|
|
118
132
|
def __exit__(self, exc_typ, exc_val, *_) -> bool:
|
|
119
133
|
if exc_typ is not None:
|
|
120
|
-
self._exceptions
|
|
134
|
+
fast_append_exception(self._exceptions, exc_val)
|
|
121
135
|
if self._stop_on_first:
|
|
122
136
|
return False
|
|
123
137
|
return True
|
|
@@ -129,14 +143,14 @@ class ExceptionCollector:
|
|
|
129
143
|
func(*args, **kwargs)
|
|
130
144
|
return False
|
|
131
145
|
except BaseException as exc:
|
|
132
|
-
self._exceptions
|
|
146
|
+
fast_append_exception(self._exceptions, exc)
|
|
133
147
|
if self._stop_on_first:
|
|
134
148
|
raise
|
|
135
149
|
return True
|
|
136
150
|
|
|
137
151
|
def add(self, exc: BaseException) -> None:
|
|
138
152
|
"""Manually add an exception."""
|
|
139
|
-
self._exceptions
|
|
153
|
+
fast_append_exception(self._exceptions, exc)
|
|
140
154
|
if self._stop_on_first:
|
|
141
155
|
raise exc
|
|
142
156
|
|
|
@@ -6,10 +6,17 @@ import sys
|
|
|
6
6
|
import threading
|
|
7
7
|
import traceback
|
|
8
8
|
from dataclasses import dataclass, field
|
|
9
|
-
from datetime import datetime
|
|
9
|
+
from datetime import datetime
|
|
10
10
|
from types import TracebackType
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
|
+
if sys.version_info >= (3, 9):
|
|
14
|
+
from datetime import UTC
|
|
15
|
+
else:
|
|
16
|
+
from datetime import timezone
|
|
17
|
+
|
|
18
|
+
UTC = timezone.utc
|
|
19
|
+
|
|
13
20
|
from .level import Level
|
|
14
21
|
|
|
15
22
|
|
|
@@ -5,10 +5,17 @@ from __future__ import annotations
|
|
|
5
5
|
import sys
|
|
6
6
|
import threading
|
|
7
7
|
from abc import ABC, abstractmethod
|
|
8
|
-
from datetime import datetime
|
|
8
|
+
from datetime import datetime
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import IO, Any
|
|
11
11
|
|
|
12
|
+
if sys.version_info >= (3, 9):
|
|
13
|
+
from datetime import UTC
|
|
14
|
+
else:
|
|
15
|
+
from datetime import timezone
|
|
16
|
+
|
|
17
|
+
UTC = timezone.utc
|
|
18
|
+
|
|
12
19
|
from .level import Level
|
|
13
20
|
from .record import Record
|
|
14
21
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Utilities for raising exceptions."""
|
|
2
2
|
|
|
3
|
+
import sys
|
|
3
4
|
from contextlib import contextmanager
|
|
4
5
|
from itertools import product
|
|
5
6
|
from typing import Callable, Any
|
|
@@ -107,45 +108,47 @@ def assert_raises(
|
|
|
107
108
|
)
|
|
108
109
|
|
|
109
110
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
111
|
+
if sys.version_info >= (3, 11):
|
|
112
|
+
|
|
113
|
+
def raises_all(
|
|
114
|
+
errors: Iterable[type[Exception]],
|
|
115
|
+
msgs: Iterable[str],
|
|
116
|
+
baseerror: type[Exception] = Exception,
|
|
117
|
+
group_msg: str = "multiple errors",
|
|
118
|
+
) -> None:
|
|
119
|
+
"""Validate exception types and raise all (error, message) combinations as an ExceptionGroup.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
errors: An iterable of exception types to include in the group.
|
|
123
|
+
msgs: An iterable of message strings to pair with each exception type.
|
|
124
|
+
baseerror: The base exception class that every type in *errors* must
|
|
125
|
+
inherit from. Defaults to `Exception`.
|
|
126
|
+
group_msg: The message attached to the `ExceptionGroup` itself.
|
|
127
|
+
Defaults to ``"multiple errors"``.
|
|
128
|
+
|
|
129
|
+
Raises:
|
|
130
|
+
TypeError: If any type in *errors* is not a subclass of *baseerror*.
|
|
131
|
+
ExceptionGroup: Containing one ``errors[i](msgs[j])`` instance for
|
|
132
|
+
every ``(i, j)`` pair in the Cartesian product.
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
>>> raises_all([ValueError, TypeError], ["bad input"])
|
|
136
|
+
Traceback (most recent call last):
|
|
137
|
+
...
|
|
138
|
+
ExceptionGroup: multiple errors (2 sub-exceptions)
|
|
139
|
+
"""
|
|
140
|
+
# NOTE: Computes the Cartesian product of *errors* x *msgs*, validates that every
|
|
141
|
+
# error type is a subclass of *baseerror*, then raises a single
|
|
142
|
+
# `ExceptionGroup` containing one instantiated exception per pair.
|
|
143
|
+
# If the product is empty (either iterable is empty) the function returns
|
|
144
|
+
# without raising.
|
|
145
|
+
pairs = _warp_list_product(errors, msgs)
|
|
146
|
+
if not pairs:
|
|
147
|
+
return
|
|
148
|
+
for error, _ in pairs:
|
|
149
|
+
if not _is_base_subclass(error=error, baseerror=baseerror):
|
|
150
|
+
raise TypeError(f"{error!r} is not a subclass of {baseerror.__name__}")
|
|
151
|
+
raise ExceptionGroup(group_msg, [error(msg) for error, msg in pairs])
|
|
149
152
|
|
|
150
153
|
|
|
151
154
|
@contextmanager
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"""Public type aliases for errortools exception classes."""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
if sys.version_info <= (3, 10):
|
|
6
|
+
from typing_extensions import TypeAlias
|
|
7
|
+
else:
|
|
8
|
+
from typing import TypeAlias
|
|
9
|
+
from typing import Union
|
|
4
10
|
|
|
5
11
|
from .classes.errorcodes import (
|
|
6
12
|
PureBaseException,
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
from collections import OrderedDict
|
|
2
2
|
from collections.abc import Hashable, Callable
|
|
3
|
-
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
if sys.version_info <= (3, 10):
|
|
6
|
+
from typing_extensions import TypeAlias
|
|
7
|
+
else:
|
|
8
|
+
from typing import TypeAlias
|
|
9
|
+
from typing import Any, Generic, NamedTuple, Optional, TypeVar
|
|
4
10
|
|
|
5
11
|
_T = TypeVar("_T", bound=Callable[..., Any])
|
|
6
12
|
_Key: TypeAlias = tuple[
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import traceback
|
|
2
2
|
from collections.abc import Callable
|
|
3
|
-
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
if sys.version_info <= (3, 10):
|
|
6
|
+
from typing_extensions import TypeAlias
|
|
7
|
+
else:
|
|
8
|
+
from typing import TypeAlias
|
|
9
|
+
|
|
10
|
+
from typing import Any, Generic, TypeVar, Optional
|
|
4
11
|
|
|
5
12
|
_T = TypeVar("_T", bound=Callable[..., Any])
|
|
6
13
|
_ExcType: TypeAlias = type[Exception]
|
|
@@ -39,14 +39,7 @@ from _errortools.classes.warn import (
|
|
|
39
39
|
RuntimeBehaviourWarning,
|
|
40
40
|
ConfigurationWarning,
|
|
41
41
|
)
|
|
42
|
-
from _errortools.methods import (
|
|
43
|
-
ErrorSetAttrMixin,
|
|
44
|
-
ErrorAttrCheckMixin,
|
|
45
|
-
ErrorAttrDeletionMixin,
|
|
46
|
-
ErrorAttrMixin,
|
|
47
|
-
)
|
|
48
42
|
from _errortools.classes.abc import (
|
|
49
|
-
ErrorAttrable,
|
|
50
43
|
ErrorCodeable,
|
|
51
44
|
Warnable,
|
|
52
45
|
Raiseable,
|
|
@@ -130,11 +123,6 @@ __all__ = [
|
|
|
130
123
|
"ResourceUsageWarning",
|
|
131
124
|
"RuntimeBehaviourWarning",
|
|
132
125
|
"ConfigurationWarning",
|
|
133
|
-
"ErrorAttrMixin",
|
|
134
|
-
"ErrorAttrDeletionMixin",
|
|
135
|
-
"ErrorAttrCheckMixin",
|
|
136
|
-
"ErrorSetAttrMixin",
|
|
137
|
-
"ErrorAttrable",
|
|
138
126
|
"ErrorCodeable",
|
|
139
127
|
"Warnable",
|
|
140
128
|
"Raiseable",
|