errortools 3.0.0__tar.gz → 3.1.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 (73) hide show
  1. errortools-3.1.0/AUTHORS.txt +12 -0
  2. {errortools-3.0.0/errortools.egg-info → errortools-3.1.0}/PKG-INFO +3 -2
  3. {errortools-3.0.0 → errortools-3.1.0}/README.md +2 -1
  4. errortools-3.1.0/_errortools/_speedup.c +127 -0
  5. {errortools-3.0.0 → errortools-3.1.0}/_errortools/cli.py +1 -1
  6. {errortools-3.0.0 → errortools-3.1.0}/_errortools/ignore.py +8 -0
  7. {errortools-3.0.0 → errortools-3.1.0}/_errortools/typing.py +16 -4
  8. {errortools-3.0.0 → errortools-3.1.0}/_errortools/version.py +2 -2
  9. {errortools-3.0.0 → errortools-3.1.0/errortools.egg-info}/PKG-INFO +3 -2
  10. errortools-3.1.0/errortools.egg-info/SOURCES.txt +47 -0
  11. {errortools-3.0.0 → errortools-3.1.0}/errortools.egg-info/entry_points.txt +0 -1
  12. {errortools-3.0.0 → errortools-3.1.0}/errortools.egg-info/top_level.txt +1 -1
  13. {errortools-3.0.0 → errortools-3.1.0}/setup.py +4 -4
  14. {errortools-3.0.0/tests → errortools-3.1.0/testing}/__init__.py +1 -2
  15. errortools-3.1.0/testing/__main__.py +4 -0
  16. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_abc.py +0 -3
  17. errortools-3.1.0/testing/test_groups.py +130 -0
  18. errortools-3.0.0/AUTHORS.txt +0 -3
  19. errortools-3.0.0/_errortools/_speedup.c +0 -58
  20. errortools-3.0.0/_errortools/classes/__init__.py +0 -1
  21. errortools-3.0.0/_errortools/classes/abc.py +0 -211
  22. errortools-3.0.0/_errortools/classes/errorcodes.py +0 -273
  23. errortools-3.0.0/_errortools/classes/group.py +0 -121
  24. errortools-3.0.0/_errortools/classes/warn.py +0 -124
  25. errortools-3.0.0/_errortools/decorator/__init__.py +0 -1
  26. errortools-3.0.0/_errortools/decorator/cache.py +0 -82
  27. errortools-3.0.0/_errortools/decorator/deprecated.py +0 -61
  28. errortools-3.0.0/_errortools/descriptor/__init__.py +0 -2
  29. errortools-3.0.0/_errortools/descriptor/errormsg.py +0 -37
  30. errortools-3.0.0/_errortools/descriptor/nonblankmsg.py +0 -52
  31. errortools-3.0.0/_errortools/logging/__init__.py +0 -43
  32. errortools-3.0.0/_errortools/logging/base.py +0 -467
  33. errortools-3.0.0/_errortools/logging/level.py +0 -85
  34. errortools-3.0.0/_errortools/logging/logger.py +0 -13
  35. errortools-3.0.0/_errortools/logging/record.py +0 -116
  36. errortools-3.0.0/_errortools/logging/sink.py +0 -243
  37. errortools-3.0.0/_errortools/wrappers/__init__.py +0 -2
  38. errortools-3.0.0/_errortools/wrappers/cache.py +0 -101
  39. errortools-3.0.0/_errortools/wrappers/ignore.py +0 -122
  40. errortools-3.0.0/errortools.egg-info/SOURCES.txt +0 -66
  41. errortools-3.0.0/tests/test_groups.py +0 -128
  42. {errortools-3.0.0 → errortools-3.1.0}/LICENSE.txt +0 -0
  43. {errortools-3.0.0 → errortools-3.1.0}/_errortools/__init__.py +0 -0
  44. {errortools-3.0.0 → errortools-3.1.0}/_errortools/_cli.py +0 -0
  45. {errortools-3.0.0 → errortools-3.1.0}/_errortools/const.py +0 -0
  46. {errortools-3.0.0 → errortools-3.1.0}/_errortools/errno.py +0 -0
  47. {errortools-3.0.0 → errortools-3.1.0}/_errortools/future.py +0 -0
  48. {errortools-3.0.0 → errortools-3.1.0}/_errortools/metadata.py +0 -0
  49. {errortools-3.0.0 → errortools-3.1.0}/_errortools/partial.py +0 -0
  50. {errortools-3.0.0 → errortools-3.1.0}/_errortools/py.typed +0 -0
  51. {errortools-3.0.0 → errortools-3.1.0}/_errortools/raises.py +0 -0
  52. {errortools-3.0.0 → errortools-3.1.0}/errortools/__init__.py +0 -0
  53. {errortools-3.0.0 → errortools-3.1.0}/errortools/__main__.py +0 -0
  54. {errortools-3.0.0 → errortools-3.1.0}/errortools/future.py +0 -0
  55. {errortools-3.0.0 → errortools-3.1.0}/errortools/logging.py +0 -0
  56. {errortools-3.0.0 → errortools-3.1.0}/errortools/partial.py +0 -0
  57. {errortools-3.0.0 → errortools-3.1.0}/errortools.egg-info/dependency_links.txt +0 -0
  58. {errortools-3.0.0 → errortools-3.1.0}/errortools.egg-info/requires.txt +0 -0
  59. {errortools-3.0.0 → errortools-3.1.0}/setup.cfg +0 -0
  60. {errortools-3.0.0/tests → errortools-3.1.0/testing}/conftest.py +0 -0
  61. {errortools-3.0.0/tests → errortools-3.1.0/testing}/run_tests.py +0 -0
  62. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_const.py +0 -0
  63. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_decorator.py +0 -0
  64. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_descriptor.py +0 -0
  65. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_errno.py +0 -0
  66. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_errorcodes.py +0 -0
  67. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_future.py +0 -0
  68. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_ignore.py +0 -0
  69. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_logging.py +0 -0
  70. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_partials.py +0 -0
  71. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_raises.py +0 -0
  72. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_typing.py +0 -0
  73. {errortools-3.0.0/tests → errortools-3.1.0/testing}/test_warnings.py +0 -0
@@ -0,0 +1,12 @@
1
+ # (Lines starting with # are comments)
2
+ # Tips:
3
+ # Contributors are not sorted by initials,
4
+ # but by contribution time.
5
+ # Here are the real contributors
6
+ aiwonderland
7
+ qorexdev
8
+ vgauraha62
9
+ yangphysics
10
+ # And here are bot contributors
11
+ dependabot[bot]
12
+ AbaAbaAba-bot-like[bot]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: errortools
3
- Version: 3.0.0
3
+ Version: 3.1.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
@@ -46,6 +46,7 @@ A lightweight Python exception handling utility library.
46
46
  ![This month commits](https://img.shields.io/github/commit-activity/m/more-abc/errortools)
47
47
  ![Past year commits](https://img.shields.io/github/commit-activity/y/more-abc/errortools)
48
48
  ![Total commits badge](https://img.shields.io/github/commit-activity/t/more-abc/errortools)
49
+ ![OS support](https://img.shields.io/badge/OS-macOS%20Linux%20Windows-red)
49
50
 
50
51
  ## Features
51
52
  - **Raise Exceptions**: `raises()`, `raises_all()`, `reraise()` — batch raising and exception conversion
@@ -96,7 +97,7 @@ assert err.traceback # full formatted traceback string
96
97
  | `exception` | `Exception \| None` | The original exception instance |
97
98
  | `traceback` | `str \| None` | Formatted traceback string for debugging |
98
99
 
99
- ## Examples
100
+ ## Sorry, but there was more code examples!
100
101
 
101
102
  ```python
102
103
  # ── ignore as a decorator ──
@@ -8,6 +8,7 @@ A lightweight Python exception handling utility library.
8
8
  ![This month commits](https://img.shields.io/github/commit-activity/m/more-abc/errortools)
9
9
  ![Past year commits](https://img.shields.io/github/commit-activity/y/more-abc/errortools)
10
10
  ![Total commits badge](https://img.shields.io/github/commit-activity/t/more-abc/errortools)
11
+ ![OS support](https://img.shields.io/badge/OS-macOS%20Linux%20Windows-red)
11
12
 
12
13
  ## Features
13
14
  - **Raise Exceptions**: `raises()`, `raises_all()`, `reraise()` — batch raising and exception conversion
@@ -58,7 +59,7 @@ assert err.traceback # full formatted traceback string
58
59
  | `exception` | `Exception \| None` | The original exception instance |
59
60
  | `traceback` | `str \| None` | Formatted traceback string for debugging |
60
61
 
61
- ## Examples
62
+ ## Sorry, but there was more code examples!
62
63
 
63
64
  ```python
64
65
  # ── ignore as a decorator ──
@@ -0,0 +1,127 @@
1
+ #define PY_SSIZE_T_CLEAN
2
+ #include <Python.h>
3
+
4
+ /* Fast exception type checking
5
+ *
6
+ * Optimized check for exception type hierarchy using PyObject_IsSubclass.
7
+ * Returns False immediately if typ is None, otherwise checks if typ is a
8
+ * subclass of excs.
9
+ *
10
+ * Args:
11
+ * typ: The type object to check (or None)
12
+ * excs: The exception class(es) to check against
13
+ *
14
+ * Returns:
15
+ * True if typ is a subclass of excs, False otherwise
16
+ * NULL on error with exception set
17
+ */
18
+ static PyObject* fast_issubclass_check(PyObject* self, PyObject* const* args, Py_ssize_t nargs) {
19
+ if (nargs != 2) {
20
+ PyErr_Format(PyExc_TypeError,
21
+ "fast_issubclass_check() takes exactly 2 arguments (%zd given)",
22
+ nargs);
23
+ return NULL;
24
+ }
25
+
26
+ PyObject *typ = args[0];
27
+ PyObject *excs = args[1];
28
+
29
+ /* Handle None case explicitly */
30
+ if (typ == Py_None) {
31
+ Py_RETURN_FALSE;
32
+ }
33
+
34
+ /* Validate that excs is a class or tuple of classes */
35
+ if (!PyType_Check(excs) && !PyTuple_Check(excs)) {
36
+ PyErr_SetString(PyExc_TypeError,
37
+ "second argument must be a class or tuple of classes");
38
+ return NULL;
39
+ }
40
+
41
+ int result = PyObject_IsSubclass(typ, excs);
42
+ if (result == -1) {
43
+ return NULL; /* Exception already set by PyObject_IsSubclass */
44
+ }
45
+
46
+ /* Return bool directly instead of going through PyBool_FromLong */
47
+ if (result == 1) {
48
+ Py_RETURN_TRUE;
49
+ } else {
50
+ Py_RETURN_FALSE;
51
+ }
52
+ }
53
+
54
+ /* Fast exception collector append
55
+ *
56
+ * Optimized append operation for adding exceptions to a list.
57
+ *
58
+ * Args:
59
+ * list: The list object to append to
60
+ * exc: The exception object to append
61
+ *
62
+ * Returns:
63
+ * None on success
64
+ * NULL on error with exception set
65
+ */
66
+ static PyObject* fast_append_exception(PyObject* self, PyObject* const* args, Py_ssize_t nargs) {
67
+ if (nargs != 2) {
68
+ PyErr_Format(PyExc_TypeError,
69
+ "fast_append_exception() takes exactly 2 arguments (%zd given)",
70
+ nargs);
71
+ return NULL;
72
+ }
73
+
74
+ PyObject *list = args[0];
75
+ PyObject *exc = args[1];
76
+
77
+ /* Validate that first argument is actually a list */
78
+ if (!PyList_Check(list)) {
79
+ PyErr_SetString(PyExc_TypeError, "first argument must be a list");
80
+ return NULL;
81
+ }
82
+
83
+ if (PyList_Append(list, exc) == -1) {
84
+ return NULL; /* Exception already set by PyList_Append */
85
+ }
86
+
87
+ Py_RETURN_NONE;
88
+ }
89
+
90
+ /* Method definitions */
91
+ static PyMethodDef SpeedupMethods[] = {
92
+ {
93
+ "fast_issubclass_check",
94
+ (PyCFunction)fast_issubclass_check,
95
+ METH_FASTCALL,
96
+ "fast_issubclass_check(typ, excs) -> bool\n\n"
97
+ "Check if typ is a subclass of excs exception type(s).\n"
98
+ "Returns False if typ is None, otherwise uses PyObject_IsSubclass.\n"
99
+ "Optimized for exception handling performance."
100
+ },
101
+ {
102
+ "fast_append_exception",
103
+ (PyCFunction)fast_append_exception,
104
+ METH_FASTCALL,
105
+ "fast_append_exception(list, exc) -> None\n\n"
106
+ "Append an exception to a list with minimal overhead.\n"
107
+ "Validates that the first argument is a list."
108
+ },
109
+ {NULL, NULL, 0, NULL} /* Sentinel */
110
+ };
111
+
112
+ /* Module definition */
113
+ static struct PyModuleDef speedupmodule = {
114
+ PyModuleDef_HEAD_INIT,
115
+ "_speedup", /* name */
116
+ "C speedup module for errortools\n\n"
117
+ "Provides optimized implementations of exception handling operations:\n"
118
+ " - fast_issubclass_check: Quick exception type hierarchy checking\n"
119
+ " - fast_append_exception: Efficient exception list append operations",
120
+ -1, /* size */
121
+ SpeedupMethods /* methods */
122
+ };
123
+
124
+ /* Module initialization */
125
+ PyMODINIT_FUNC PyInit__speedup(void) {
126
+ return PyModule_Create(&speedupmodule);
127
+ }
@@ -95,7 +95,7 @@ def main() -> None:
95
95
  elif args.url:
96
96
  print(f"URL: {__url__}")
97
97
  elif args.run_tests:
98
- from tests.run_tests import run_tests
98
+ from testing.run_tests import run_tests
99
99
 
100
100
  run_tests()
101
101
  elif args.info:
@@ -107,11 +107,19 @@ class fast_ignore:
107
107
  >>> with fast_ignore(KeyError):
108
108
  ... d = {}
109
109
  ... _ = d["missing"]
110
+
111
+ .. deprecated:: 3.0.0
112
+ This class is deprecated as it is functionally redundant.
110
113
  """
111
114
 
112
115
  __slots__ = ("_excs",)
113
116
 
114
117
  def __init__(self, *excs: ExceptionType) -> None:
118
+ warnings.warn(
119
+ "fast_ignore is deprecated as it is functionally redundant.",
120
+ DeprecationWarning,
121
+ stacklevel=2,
122
+ )
115
123
  for exc in excs:
116
124
  if not isinstance(exc, type) or not issubclass(exc, BaseException):
117
125
  raise TypeError(f"Expected Exception subclass, got {exc!r}")
@@ -63,16 +63,28 @@ AnyErrorCode: TypeAlias = Union[
63
63
  """Union of all predefined error-code subclasses."""
64
64
 
65
65
  InputError: TypeAlias = InvalidInputError
66
- """Alias for `InvalidInputError` (1001)."""
66
+ """Alias for `InvalidInputError` (1001).
67
+
68
+ .. deprecated:: 3.0.0
69
+ This type alias is deprecated as it is redundant."""
67
70
 
68
71
  AccessError: TypeAlias = AccessDeniedError
69
- """Alias for `AccessDeniedError` (2001)."""
72
+ """Alias for `AccessDeniedError` (2001).
73
+
74
+ .. deprecated:: 3.0.0
75
+ This type alias is deprecated as it is redundant."""
70
76
 
71
77
  LookupError_: TypeAlias = NotFoundError
72
- """Alias for `NotFoundError` (3001). Trailing underscore avoids shadowing builtins."""
78
+ """Alias for `NotFoundError` (3001). Trailing underscore avoids shadowing builtins.
79
+
80
+ .. deprecated:: 3.0.0
81
+ This type alias is deprecated as it is redundant."""
73
82
 
74
83
  RuntimeError_: TypeAlias = Union[RuntimeFailure, TimeoutFailure]
75
- """Union of runtime-related errors: `RuntimeFailure` (4001) and `TimeoutFailure` (4002)."""
84
+ """Union of runtime-related errors: `RuntimeFailure` (4001) and `TimeoutFailure` (4002).
85
+
86
+ .. deprecated:: 3.0.0
87
+ This type alias is deprecated as it is redundant."""
76
88
 
77
89
  # ---------------------------------------------------------------------------
78
90
  # More type aliases
@@ -1,5 +1,5 @@
1
- __version__: str = "3.0.0"
2
- __version_tuple__: tuple[int, int, int] = (3, 0, 0)
1
+ __version__: str = "3.1.0"
2
+ __version_tuple__: tuple[int, int, int] = (3, 1, 0)
3
3
  __commit_id__: str | None = None
4
4
 
5
5
  version = __version__
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: errortools
3
- Version: 3.0.0
3
+ Version: 3.1.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
@@ -46,6 +46,7 @@ A lightweight Python exception handling utility library.
46
46
  ![This month commits](https://img.shields.io/github/commit-activity/m/more-abc/errortools)
47
47
  ![Past year commits](https://img.shields.io/github/commit-activity/y/more-abc/errortools)
48
48
  ![Total commits badge](https://img.shields.io/github/commit-activity/t/more-abc/errortools)
49
+ ![OS support](https://img.shields.io/badge/OS-macOS%20Linux%20Windows-red)
49
50
 
50
51
  ## Features
51
52
  - **Raise Exceptions**: `raises()`, `raises_all()`, `reraise()` — batch raising and exception conversion
@@ -96,7 +97,7 @@ assert err.traceback # full formatted traceback string
96
97
  | `exception` | `Exception \| None` | The original exception instance |
97
98
  | `traceback` | `str \| None` | Formatted traceback string for debugging |
98
99
 
99
- ## Examples
100
+ ## Sorry, but there was more code examples!
100
101
 
101
102
  ```python
102
103
  # ── ignore as a decorator ──
@@ -0,0 +1,47 @@
1
+ AUTHORS.txt
2
+ LICENSE.txt
3
+ README.md
4
+ setup.py
5
+ _errortools/__init__.py
6
+ _errortools/_cli.py
7
+ _errortools/_speedup.c
8
+ _errortools/cli.py
9
+ _errortools/const.py
10
+ _errortools/errno.py
11
+ _errortools/future.py
12
+ _errortools/ignore.py
13
+ _errortools/metadata.py
14
+ _errortools/partial.py
15
+ _errortools/py.typed
16
+ _errortools/raises.py
17
+ _errortools/typing.py
18
+ _errortools/version.py
19
+ errortools/__init__.py
20
+ errortools/__main__.py
21
+ errortools/future.py
22
+ errortools/logging.py
23
+ errortools/partial.py
24
+ errortools.egg-info/PKG-INFO
25
+ errortools.egg-info/SOURCES.txt
26
+ errortools.egg-info/dependency_links.txt
27
+ errortools.egg-info/entry_points.txt
28
+ errortools.egg-info/requires.txt
29
+ errortools.egg-info/top_level.txt
30
+ testing/__init__.py
31
+ testing/__main__.py
32
+ testing/conftest.py
33
+ testing/run_tests.py
34
+ testing/test_abc.py
35
+ testing/test_const.py
36
+ testing/test_decorator.py
37
+ testing/test_descriptor.py
38
+ testing/test_errno.py
39
+ testing/test_errorcodes.py
40
+ testing/test_future.py
41
+ testing/test_groups.py
42
+ testing/test_ignore.py
43
+ testing/test_logging.py
44
+ testing/test_partials.py
45
+ testing/test_raises.py
46
+ testing/test_typing.py
47
+ testing/test_warnings.py
@@ -1,3 +1,2 @@
1
1
  [console_scripts]
2
2
  logger = _errortools.cli:main
3
- python -m errortools = _errortools.cli:main
@@ -1,3 +1,3 @@
1
1
  _errortools
2
2
  errortools
3
- tests
3
+ testing
@@ -1,11 +1,11 @@
1
1
  import sys
2
2
 
3
- from setuptools import setup, find_packages, Extension
3
+ from setuptools import setup, Extension
4
4
 
5
- _VERSION: str = "3.0.0"
5
+ _VERSION: str = "3.1.0"
6
6
  _CLI_INFO: dict[str, list[str]] = {
7
7
  "console_scripts": [
8
- "python -m errortools = _errortools.cli:main",
8
+ # "python -m errortools = _errortools.cli:main",
9
9
  "logger = _errortools.cli:main",
10
10
  ]
11
11
  }
@@ -48,7 +48,7 @@ setup(
48
48
  "Operating System :: OS Independent",
49
49
  "Typing :: Typed",
50
50
  ],
51
- packages=find_packages(),
51
+ packages=["_errortools", "errortools", "testing"],
52
52
  package_data={"errortools": ["py.typed"]},
53
53
  include_package_data=True,
54
54
  python_requires=">=3.8",
@@ -1,7 +1,6 @@
1
1
  """Tests for `errortools` module. Using pytest."""
2
2
 
3
- __version__ = "1.16"
4
-
3
+ __version__ = "1.1.19"
5
4
  try:
6
5
  import pytest
7
6
  except ImportError:
@@ -0,0 +1,4 @@
1
+ from .run_tests import run_tests
2
+
3
+ if __name__ == "__main__":
4
+ run_tests()
@@ -17,9 +17,6 @@ import configparser
17
17
  # Import the ABC classes to test
18
18
  from _errortools.classes.abc import ErrorCodeable, Warnable, Raiseable, Error
19
19
 
20
- # NOTE: Tests for `ErrorAttrable` was in `test_mixin.py`.
21
-
22
-
23
20
  # =============================================================================
24
21
  # ErrorCodeable ABC Tests
25
22
  # =============================================================================
@@ -0,0 +1,130 @@
1
+ """Tests for _errortools/classes/group.py — GroupErrors and BaseGroup."""
2
+
3
+ import sys
4
+
5
+ import pytest
6
+
7
+ from _errortools.classes.group import GroupErrors, BaseGroup
8
+
9
+ # =============================================================================
10
+ # GroupErrors — collect / raise_group / clear
11
+ # =============================================================================
12
+
13
+
14
+ class TestGroupErrors:
15
+ def test_starts_empty(self):
16
+ g = GroupErrors()
17
+ assert len(g) == 0
18
+ assert not g
19
+
20
+ def test_collect_adds_exception(self):
21
+ g = GroupErrors()
22
+ g.collect(ValueError("v"))
23
+ assert len(g) == 1
24
+
25
+ def test_collect_multiple(self):
26
+ g = GroupErrors()
27
+ g.collect(ValueError("v"))
28
+ g.collect(TypeError("t"))
29
+ g.collect(KeyError("k"))
30
+ assert len(g) == 3
31
+
32
+ def test_bool_true_when_not_empty(self):
33
+ g = GroupErrors()
34
+ g.collect(RuntimeError("r"))
35
+ assert bool(g) is True
36
+
37
+ def test_bool_false_when_empty(self):
38
+ assert bool(GroupErrors()) is False
39
+
40
+ def test_errors_returns_copy(self):
41
+ g = GroupErrors()
42
+ e = ValueError("v")
43
+ g.collect(e)
44
+ snapshot = g.errors
45
+ snapshot.clear() # mutating the copy must not affect the group
46
+ assert len(g) == 1
47
+
48
+ def test_raise_group_raises_exception_group(self):
49
+ g = GroupErrors()
50
+ g.collect(ValueError("v"))
51
+ g.collect(TypeError("t"))
52
+ with pytest.raises(ExceptionGroup) as exc_info:
53
+ g.raise_group()
54
+ assert len(exc_info.value.exceptions) == 2
55
+
56
+ def test_raise_group_uses_group_msg(self):
57
+ g = GroupErrors("my errors")
58
+ g.collect(ValueError("v"))
59
+ with pytest.raises(ExceptionGroup) as exc_info:
60
+ g.raise_group()
61
+ assert exc_info.value.message == "my errors"
62
+
63
+ def test_raise_group_default_msg(self):
64
+ g = GroupErrors()
65
+ g.collect(ValueError("v"))
66
+ with pytest.raises(ExceptionGroup) as exc_info:
67
+ g.raise_group()
68
+ assert exc_info.value.message == "multiple errors"
69
+
70
+ def test_raise_group_does_nothing_when_empty(self):
71
+ g = GroupErrors()
72
+ g.raise_group() # should not raise
73
+
74
+ def test_clear_removes_all_exceptions(self):
75
+ g = GroupErrors()
76
+ g.collect(ValueError("v"))
77
+ g.collect(TypeError("t"))
78
+ g.clear()
79
+ assert len(g) == 0
80
+
81
+ def test_clear_then_raise_group_noop(self):
82
+ g = GroupErrors()
83
+ g.collect(ValueError("v"))
84
+ g.clear()
85
+ g.raise_group() # no raise — nothing collected
86
+
87
+ def test_repr_contains_msg_and_count(self):
88
+ g = GroupErrors("test group")
89
+ g.collect(ValueError("v"))
90
+ r = repr(g)
91
+ assert "GroupErrors" in r
92
+ assert "test group" in r
93
+
94
+ def test_sub_exception_types_preserved(self):
95
+ g = GroupErrors()
96
+ v = ValueError("val")
97
+ t = TypeError("type")
98
+ g.collect(v)
99
+ g.collect(t)
100
+ with pytest.raises(ExceptionGroup) as exc_info:
101
+ g.raise_group()
102
+ exc_types = {type(e) for e in exc_info.value.exceptions}
103
+ assert exc_types == {ValueError, TypeError}
104
+
105
+
106
+ if sys.version_info <= (3, 10):
107
+ del TestGroupErrors
108
+
109
+
110
+ # =============================================================================
111
+ # BaseGroup — abstract interface
112
+ # =============================================================================
113
+
114
+
115
+ class TestBaseGroupAbstract:
116
+ def test_base_group_has_abstract_methods(self):
117
+ abstract = BaseGroup.__abstractmethods__
118
+ assert "errors" in abstract
119
+ assert "collect" in abstract
120
+ assert "clear" in abstract
121
+ assert "raise_group" in abstract
122
+
123
+ def test_concrete_subclass_is_valid(self):
124
+ g = GroupErrors()
125
+ assert isinstance(g, BaseGroup)
126
+
127
+ def test_group_errors_satisfies_abstract_interface(self):
128
+ abstract = BaseGroup.__abstractmethods__
129
+ for method in abstract:
130
+ assert hasattr(GroupErrors, method), f"GroupErrors missing: {method}"
@@ -1,3 +0,0 @@
1
- aiwonderland
2
- qorexdev
3
- vgauraha62
@@ -1,58 +0,0 @@
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
- }
@@ -1 +0,0 @@
1
- """Base classes."""