errortools 3.0.0__tar.gz → 3.2.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 (92) hide show
  1. errortools-3.2.0/AUTHORS.txt +12 -0
  2. errortools-3.2.0/PKG-INFO +151 -0
  3. errortools-3.2.0/README.md +105 -0
  4. errortools-3.2.0/_errortools/__main__.py +109 -0
  5. errortools-3.2.0/_errortools/_speedup.c +175 -0
  6. {errortools-3.0.0 → errortools-3.2.0}/_errortools/classes/abc.py +0 -4
  7. errortools-3.2.0/_errortools/classes/group.py +121 -0
  8. {errortools-3.0.0 → errortools-3.2.0}/_errortools/cli.py +34 -41
  9. {errortools-3.0.0 → errortools-3.2.0}/_errortools/decorator/cache.py +1 -3
  10. errortools-3.2.0/_errortools/decorator/handlers.py +90 -0
  11. errortools-3.2.0/_errortools/decorator/retry.py +99 -0
  12. errortools-3.2.0/_errortools/decorator/timeout.py +38 -0
  13. errortools-3.2.0/_errortools/descriptor/base.py +25 -0
  14. {errortools-3.0.0 → errortools-3.2.0}/_errortools/descriptor/errormsg.py +4 -8
  15. {errortools-3.0.0 → errortools-3.2.0}/_errortools/descriptor/nonblankmsg.py +46 -52
  16. {errortools-3.0.0 → errortools-3.2.0}/_errortools/future.py +14 -8
  17. errortools-3.2.0/_errortools/ignore.py +163 -0
  18. {errortools-3.0.0 → errortools-3.2.0}/_errortools/logging/base.py +3 -8
  19. {errortools-3.0.0 → errortools-3.2.0}/_errortools/metadata.py +1 -3
  20. {errortools-3.0.0 → errortools-3.2.0}/_errortools/partial.py +1 -3
  21. {errortools-3.0.0 → errortools-3.2.0}/_errortools/raises.py +2 -5
  22. {errortools-3.0.0 → errortools-3.2.0}/_errortools/typing.py +16 -4
  23. errortools-3.2.0/_errortools/version.py +17 -0
  24. {errortools-3.0.0 → errortools-3.2.0}/_errortools/wrappers/cache.py +4 -12
  25. {errortools-3.0.0 → errortools-3.2.0}/_errortools/wrappers/ignore.py +1 -3
  26. errortools-3.2.0/docs/conf.py +56 -0
  27. {errortools-3.0.0 → errortools-3.2.0}/errortools/__init__.py +37 -10
  28. errortools-3.2.0/errortools.egg-info/PKG-INFO +151 -0
  29. {errortools-3.0.0 → errortools-3.2.0}/errortools.egg-info/SOURCES.txt +29 -18
  30. {errortools-3.0.0 → errortools-3.2.0}/errortools.egg-info/entry_points.txt +0 -1
  31. errortools-3.2.0/errortools.egg-info/requires.txt +2 -0
  32. errortools-3.2.0/errortools.egg-info/top_level.txt +8 -0
  33. errortools-3.2.0/pyproject.toml +88 -0
  34. errortools-3.2.0/testing/__init__.py +33 -0
  35. errortools-3.2.0/testing/__main__.py +4 -0
  36. errortools-3.2.0/testing/benchmark/__init__.py +1 -0
  37. errortools-3.2.0/testing/benchmark/test_future_perf.py +239 -0
  38. {errortools-3.0.0/tests → errortools-3.2.0/testing}/run_tests.py +3 -3
  39. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_abc.py +0 -3
  40. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_decorator.py +288 -0
  41. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_descriptor.py +54 -10
  42. errortools-3.2.0/testing/test_groups.py +130 -0
  43. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_ignore.py +1 -174
  44. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_logging.py +0 -1
  45. errortools-3.2.0/testing/test_testing/__init__.py +1 -0
  46. errortools-3.2.0/testing/test_testing/test_testing.py +41 -0
  47. errortools-3.0.0/AUTHORS.txt +0 -3
  48. errortools-3.0.0/PKG-INFO +0 -366
  49. errortools-3.0.0/README.md +0 -328
  50. errortools-3.0.0/_errortools/_speedup.c +0 -58
  51. errortools-3.0.0/_errortools/classes/group.py +0 -121
  52. errortools-3.0.0/_errortools/ignore.py +0 -288
  53. errortools-3.0.0/_errortools/version.py +0 -7
  54. errortools-3.0.0/errortools.egg-info/PKG-INFO +0 -366
  55. errortools-3.0.0/errortools.egg-info/requires.txt +0 -2
  56. errortools-3.0.0/errortools.egg-info/top_level.txt +0 -3
  57. errortools-3.0.0/setup.py +0 -58
  58. errortools-3.0.0/tests/__init__.py +0 -12
  59. errortools-3.0.0/tests/test_groups.py +0 -128
  60. {errortools-3.0.0 → errortools-3.2.0}/LICENSE.txt +0 -0
  61. {errortools-3.0.0 → errortools-3.2.0}/_errortools/__init__.py +0 -0
  62. {errortools-3.0.0 → errortools-3.2.0}/_errortools/_cli.py +0 -0
  63. {errortools-3.0.0 → errortools-3.2.0}/_errortools/classes/__init__.py +0 -0
  64. {errortools-3.0.0 → errortools-3.2.0}/_errortools/classes/errorcodes.py +0 -0
  65. {errortools-3.0.0 → errortools-3.2.0}/_errortools/classes/warn.py +0 -0
  66. {errortools-3.0.0 → errortools-3.2.0}/_errortools/const.py +0 -0
  67. {errortools-3.0.0 → errortools-3.2.0}/_errortools/decorator/__init__.py +0 -0
  68. {errortools-3.0.0 → errortools-3.2.0}/_errortools/decorator/deprecated.py +0 -0
  69. {errortools-3.0.0 → errortools-3.2.0}/_errortools/descriptor/__init__.py +0 -0
  70. {errortools-3.0.0 → errortools-3.2.0}/_errortools/errno.py +0 -0
  71. {errortools-3.0.0 → errortools-3.2.0}/_errortools/logging/__init__.py +0 -0
  72. {errortools-3.0.0 → errortools-3.2.0}/_errortools/logging/level.py +0 -0
  73. {errortools-3.0.0 → errortools-3.2.0}/_errortools/logging/logger.py +0 -0
  74. {errortools-3.0.0 → errortools-3.2.0}/_errortools/logging/record.py +0 -0
  75. {errortools-3.0.0 → errortools-3.2.0}/_errortools/logging/sink.py +0 -0
  76. {errortools-3.0.0 → errortools-3.2.0}/_errortools/py.typed +0 -0
  77. {errortools-3.0.0 → errortools-3.2.0}/_errortools/wrappers/__init__.py +0 -0
  78. {errortools-3.0.0 → errortools-3.2.0}/errortools/__main__.py +0 -0
  79. {errortools-3.0.0 → errortools-3.2.0}/errortools/future.py +0 -0
  80. {errortools-3.0.0 → errortools-3.2.0}/errortools/logging.py +0 -0
  81. {errortools-3.0.0 → errortools-3.2.0}/errortools/partial.py +0 -0
  82. {errortools-3.0.0 → errortools-3.2.0}/errortools.egg-info/dependency_links.txt +0 -0
  83. {errortools-3.0.0 → errortools-3.2.0}/setup.cfg +0 -0
  84. {errortools-3.0.0/tests → errortools-3.2.0/testing}/conftest.py +0 -0
  85. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_const.py +0 -0
  86. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_errno.py +0 -0
  87. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_errorcodes.py +0 -0
  88. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_future.py +0 -0
  89. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_partials.py +0 -0
  90. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_raises.py +0 -0
  91. {errortools-3.0.0/tests → errortools-3.2.0/testing}/test_typing.py +0 -0
  92. {errortools-3.0.0/tests → errortools-3.2.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]
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: errortools
3
+ Version: 3.2.0
4
+ Summary: errortools - a toolset for working with Python exceptions and warnings and logging.
5
+ Author-email: Evan Yang <quantbit@126.com>
6
+ License: Copyright (c) 2026 authors see AUTHORS.txt
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining
9
+ a copy of this software and associated documentation files (the
10
+ "Software"), to deal in the Software without restriction, including
11
+ without limitation the rights to use, copy, modify, merge, publish,
12
+ distribute, sublicense, and/or sell copies of the Software, and to
13
+ permit persons to whom the Software is furnished to do so, subject to
14
+ the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be
17
+ included in all copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
+
27
+ Project-URL: Homepage, https://github.com/more-abc/errortools
28
+ Classifier: License :: OSI Approved :: MIT License
29
+ Classifier: Programming Language :: Python :: 3
30
+ Classifier: Programming Language :: Python :: 3.8
31
+ Classifier: Programming Language :: Python :: 3.9
32
+ Classifier: Programming Language :: Python :: 3.10
33
+ Classifier: Programming Language :: Python :: 3.11
34
+ Classifier: Programming Language :: Python :: 3.12
35
+ Classifier: Programming Language :: Python :: 3.13
36
+ Classifier: Programming Language :: Python :: 3.14
37
+ Classifier: Operating System :: OS Independent
38
+ Classifier: Typing :: Typed
39
+ Requires-Python: >=3.8
40
+ Description-Content-Type: text/markdown
41
+ License-File: LICENSE.txt
42
+ License-File: AUTHORS.txt
43
+ Requires-Dist: namebyauthor==1.0.0
44
+ Requires-Dist: typing_extensions>=4.15.0
45
+ Dynamic: license-file
46
+
47
+ # errortools
48
+
49
+ A lightweight Python exception handling utility library.
50
+
51
+ [![Code Style: Google](https://img.shields.io/badge/style-google-3666d6.svg)](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings)
52
+ [![PyPI Version](https://img.shields.io/pypi/v/errortools)](https://pypi.org/project/errortools/)
53
+ [![Python Versions](https://img.shields.io/pypi/pyversions/errortools)](https://pypi.org/project/errortools/)
54
+ ![This week commits](https://img.shields.io/github/commit-activity/w/more-abc/errortools)
55
+ ![This month commits](https://img.shields.io/github/commit-activity/m/more-abc/errortools)
56
+ ![Past year commits](https://img.shields.io/github/commit-activity/y/more-abc/errortools)
57
+ ![Total commits badge](https://img.shields.io/github/commit-activity/t/more-abc/errortools)
58
+ ![OS support](https://img.shields.io/badge/OS-macOS%20Linux%20Windows-red)
59
+
60
+ ## Installation
61
+ ### Use pip...
62
+ ```bash
63
+ pip install errortools
64
+ ```
65
+ ### ...or uv
66
+ ```bash
67
+ uv add errortools
68
+ ```
69
+
70
+ ## Features
71
+
72
+ - **Suppress**: `ignore()`, `super_fast_ignore()`, `@suppress()` — silence exceptions gracefully
73
+ - **Retry & Timeout**: `@retry()`, `@timeout()` — auto retry with delay, async timeout
74
+ - **Raise & Convert**: `raises()`, `reraise()`, `@convert()` — batch raise, type conversion
75
+ - **Catch & Collect**: `super_fast_catch()`, `ExceptionCollector` — inspect or batch exceptions
76
+ - **Caching**: `@error_cache` — LRU exception cache, like `functools.lru_cache`
77
+ - **Custom Exceptions**: `PureBaseException`, `ContextException`, `BaseErrorCodes`
78
+ - **Logging**: structured logger with sinks, context binding, and exception capture
79
+
80
+ ## Quick Start
81
+
82
+ ```python
83
+ from errortools import ignore, retry, reraise, error_cache, suppress, convert
84
+ from errortools.future import super_fast_ignore, super_fast_catch, ExceptionCollector
85
+
86
+ # ── Suppress ─────────────────────────────────────────────────
87
+ with ignore(KeyError) as err: # full metadata
88
+ _ = {}["missing"]
89
+ # err.be_ignore=True, err.name='KeyError', err.traceback=...
90
+
91
+ with super_fast_ignore(ValueError): # zero-overhead
92
+ int("bad")
93
+
94
+ @suppress(ZeroDivisionError, default=0) # decorator form
95
+ def divide(a, b): return a / b
96
+
97
+ # ── Retry ────────────────────────────────────────────────────
98
+ @retry(times=3, on=ConnectionError, delay=1.0)
99
+ def connect(host: str): ...
100
+
101
+ # ── Convert ──────────────────────────────────────────────────
102
+ @convert(KeyError, ValueError) # decorator
103
+ def lookup(d, key): return d[key]
104
+
105
+ with reraise(KeyError, ValueError): # context manager
106
+ raise KeyError("x") # → ValueError
107
+
108
+ # ── Catch & Collect ──────────────────────────────────────────
109
+ with super_fast_catch(ValueError) as ctx:
110
+ raise ValueError("oops")
111
+ # ctx.exception → ValueError('oops')
112
+
113
+ collector = ExceptionCollector()
114
+ collector.catch(int, "bad1")
115
+ collector.catch(int, "bad2")
116
+ collector.raise_all() # → ExceptionGroup
117
+
118
+ # ── Cache ────────────────────────────────────────────────────
119
+ @error_cache(maxsize=64)
120
+ def load(uid: int):
121
+ if uid < 0: raise ValueError(f"invalid: {uid}")
122
+ return {"id": uid}
123
+ ```
124
+
125
+ ## Custom Exceptions
126
+
127
+ ```python
128
+ from errortools import PureBaseException, ContextException, BaseErrorCodes
129
+
130
+ class AppError(PureBaseException):
131
+ code = 9000
132
+ default_detail = "Application error."
133
+
134
+ err = ContextException("failed").with_context(service="db")
135
+ raise BaseErrorCodes.not_found("user #42") # NotFoundError [3001]
136
+ ```
137
+
138
+ ## Logging
139
+
140
+ ```python
141
+ from errortools.logging import logger
142
+
143
+ logger.info("Server started on port {}", 8080)
144
+ logger.add("app.log", rotation=10_485_760, retention=5)
145
+ with logger.catch():
146
+ int("not a number") # logged + suppressed
147
+ ```
148
+
149
+ ## Documentation
150
+
151
+ Full docs: [docs](https://errortools.readthedocs.io) | License: [LICENSE](LICENSE.txt)
@@ -0,0 +1,105 @@
1
+ # errortools
2
+
3
+ A lightweight Python exception handling utility library.
4
+
5
+ [![Code Style: Google](https://img.shields.io/badge/style-google-3666d6.svg)](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings)
6
+ [![PyPI Version](https://img.shields.io/pypi/v/errortools)](https://pypi.org/project/errortools/)
7
+ [![Python Versions](https://img.shields.io/pypi/pyversions/errortools)](https://pypi.org/project/errortools/)
8
+ ![This week commits](https://img.shields.io/github/commit-activity/w/more-abc/errortools)
9
+ ![This month commits](https://img.shields.io/github/commit-activity/m/more-abc/errortools)
10
+ ![Past year commits](https://img.shields.io/github/commit-activity/y/more-abc/errortools)
11
+ ![Total commits badge](https://img.shields.io/github/commit-activity/t/more-abc/errortools)
12
+ ![OS support](https://img.shields.io/badge/OS-macOS%20Linux%20Windows-red)
13
+
14
+ ## Installation
15
+ ### Use pip...
16
+ ```bash
17
+ pip install errortools
18
+ ```
19
+ ### ...or uv
20
+ ```bash
21
+ uv add errortools
22
+ ```
23
+
24
+ ## Features
25
+
26
+ - **Suppress**: `ignore()`, `super_fast_ignore()`, `@suppress()` — silence exceptions gracefully
27
+ - **Retry & Timeout**: `@retry()`, `@timeout()` — auto retry with delay, async timeout
28
+ - **Raise & Convert**: `raises()`, `reraise()`, `@convert()` — batch raise, type conversion
29
+ - **Catch & Collect**: `super_fast_catch()`, `ExceptionCollector` — inspect or batch exceptions
30
+ - **Caching**: `@error_cache` — LRU exception cache, like `functools.lru_cache`
31
+ - **Custom Exceptions**: `PureBaseException`, `ContextException`, `BaseErrorCodes`
32
+ - **Logging**: structured logger with sinks, context binding, and exception capture
33
+
34
+ ## Quick Start
35
+
36
+ ```python
37
+ from errortools import ignore, retry, reraise, error_cache, suppress, convert
38
+ from errortools.future import super_fast_ignore, super_fast_catch, ExceptionCollector
39
+
40
+ # ── Suppress ─────────────────────────────────────────────────
41
+ with ignore(KeyError) as err: # full metadata
42
+ _ = {}["missing"]
43
+ # err.be_ignore=True, err.name='KeyError', err.traceback=...
44
+
45
+ with super_fast_ignore(ValueError): # zero-overhead
46
+ int("bad")
47
+
48
+ @suppress(ZeroDivisionError, default=0) # decorator form
49
+ def divide(a, b): return a / b
50
+
51
+ # ── Retry ────────────────────────────────────────────────────
52
+ @retry(times=3, on=ConnectionError, delay=1.0)
53
+ def connect(host: str): ...
54
+
55
+ # ── Convert ──────────────────────────────────────────────────
56
+ @convert(KeyError, ValueError) # decorator
57
+ def lookup(d, key): return d[key]
58
+
59
+ with reraise(KeyError, ValueError): # context manager
60
+ raise KeyError("x") # → ValueError
61
+
62
+ # ── Catch & Collect ──────────────────────────────────────────
63
+ with super_fast_catch(ValueError) as ctx:
64
+ raise ValueError("oops")
65
+ # ctx.exception → ValueError('oops')
66
+
67
+ collector = ExceptionCollector()
68
+ collector.catch(int, "bad1")
69
+ collector.catch(int, "bad2")
70
+ collector.raise_all() # → ExceptionGroup
71
+
72
+ # ── Cache ────────────────────────────────────────────────────
73
+ @error_cache(maxsize=64)
74
+ def load(uid: int):
75
+ if uid < 0: raise ValueError(f"invalid: {uid}")
76
+ return {"id": uid}
77
+ ```
78
+
79
+ ## Custom Exceptions
80
+
81
+ ```python
82
+ from errortools import PureBaseException, ContextException, BaseErrorCodes
83
+
84
+ class AppError(PureBaseException):
85
+ code = 9000
86
+ default_detail = "Application error."
87
+
88
+ err = ContextException("failed").with_context(service="db")
89
+ raise BaseErrorCodes.not_found("user #42") # NotFoundError [3001]
90
+ ```
91
+
92
+ ## Logging
93
+
94
+ ```python
95
+ from errortools.logging import logger
96
+
97
+ logger.info("Server started on port {}", 8080)
98
+ logger.add("app.log", rotation=10_485_760, retention=5)
99
+ with logger.catch():
100
+ int("not a number") # logged + suppressed
101
+ ```
102
+
103
+ ## Documentation
104
+
105
+ Full docs: [docs](https://errortools.readthedocs.io) | License: [LICENSE](LICENSE.txt)
@@ -0,0 +1,109 @@
1
+ """Private debug CLI — run via `python -m _errortools`."""
2
+
3
+ import argparse
4
+ import shutil
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ from _errortools.version import __version__
9
+
10
+
11
+ def _parse_args(args: list[str] | None = None) -> argparse.Namespace:
12
+ desc = "errortools internal debug tools"
13
+
14
+ if sys.version_info >= (3, 14):
15
+ parser = argparse.ArgumentParser(prog="_errortools", description=desc, color=True)
16
+ else:
17
+ parser = argparse.ArgumentParser(prog="_errortools", description=desc)
18
+
19
+ parser.add_argument("--debug", action="store_true", help="Show debug/environment information")
20
+ parser.add_argument(
21
+ "--reset", action="store_true", help="Clear all cached data (__pycache__, htmlcov, .pytest_cache, .mypy_cache)"
22
+ )
23
+ parser.add_argument("--check", action="store_true", help="Verify installation and dependencies")
24
+
25
+ return parser.parse_args(args)
26
+
27
+
28
+ def _debug_info() -> None:
29
+ import platform
30
+
31
+ print(f"errortools v{__version__}")
32
+ print(f" Python: {sys.version}")
33
+ print(f" Platform: {platform.platform()}")
34
+ print(f" Arch: {platform.machine()}")
35
+ print(f" Prefix: {sys.prefix}")
36
+ print(f" Exec: {sys.executable}")
37
+
38
+ try:
39
+ __import__("_errortools._speedup")
40
+ print(" C speedup: available")
41
+ except ImportError:
42
+ print(" C speedup: not available (pure Python fallback)")
43
+
44
+
45
+ def _reset_cache() -> None:
46
+ root = Path(__file__).resolve().parent.parent
47
+ count = 0
48
+
49
+ for d in root.rglob("__pycache__"):
50
+ if d.is_dir():
51
+ shutil.rmtree(d)
52
+ count += 1
53
+
54
+ for name in ["htmlcov", ".pytest_cache", ".mypy_cache"]:
55
+ d = root / name
56
+ if d.is_dir():
57
+ shutil.rmtree(d)
58
+ count += 1
59
+
60
+ print(f"Reset complete. Cleared {count} cached directories.")
61
+
62
+
63
+ def _check_install() -> None:
64
+ checks: list[tuple[str, bool]] = [("errortools importable", True)]
65
+
66
+ for label, import_path in [
67
+ ("C extension (_speedup)", "_errortools._speedup"),
68
+ ("pytest", "pytest"),
69
+ ("logging module", "_errortools.logging"),
70
+ ("future module", "_errortools.future"),
71
+ ("typing_extensions", "typing_extensions"),
72
+ ]:
73
+ try:
74
+ __import__(import_path)
75
+ checks.append((label, True))
76
+ except ImportError:
77
+ checks.append((label, False))
78
+
79
+ for name, ok in checks:
80
+ status = "OK" if ok else "MISSING"
81
+ print(f" [{status:>7s}] {name}")
82
+
83
+ print()
84
+ if all(ok for _, ok in checks):
85
+ print("All checks passed.")
86
+ else:
87
+ print("Some checks failed. Install missing dependencies.")
88
+
89
+
90
+ _FLAG_ACTIONS = {
91
+ "debug": _debug_info,
92
+ "reset": _reset_cache,
93
+ "check": _check_install,
94
+ }
95
+
96
+
97
+ def main() -> None:
98
+ args = _parse_args(sys.argv[1:])
99
+
100
+ for flag, action in _FLAG_ACTIONS.items():
101
+ if getattr(args, flag, False):
102
+ action()
103
+ return
104
+
105
+ _parse_args(["--help"])
106
+
107
+
108
+ if __name__ == "__main__":
109
+ main()
@@ -0,0 +1,175 @@
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
+ /* Fast suppress exit for context managers
91
+ *
92
+ * Combined None-check + issubclass optimized for __exit__ methods.
93
+ * Returns True (suppress) if typ is not None and is a subclass of excs,
94
+ * False otherwise. Never raises on None — just returns False.
95
+ *
96
+ * Args:
97
+ * typ: The exception type (or None if no exception)
98
+ * excs: The exception class(es) to match against (tuple)
99
+ *
100
+ * Returns:
101
+ * True if exception should be suppressed, False otherwise
102
+ * NULL on error with exception set
103
+ */
104
+ static PyObject* fast_suppress_exit(PyObject* self, PyObject* const* args, Py_ssize_t nargs) {
105
+ if (nargs != 2) {
106
+ PyErr_Format(PyExc_TypeError,
107
+ "fast_suppress_exit() takes exactly 2 arguments (%zd given)",
108
+ nargs);
109
+ return NULL;
110
+ }
111
+
112
+ PyObject *typ = args[0];
113
+
114
+ if (typ == Py_None) {
115
+ Py_RETURN_FALSE;
116
+ }
117
+
118
+ int result = PyObject_IsSubclass(typ, args[1]);
119
+ if (result == -1) {
120
+ return NULL;
121
+ }
122
+
123
+ if (result) {
124
+ Py_RETURN_TRUE;
125
+ }
126
+ Py_RETURN_FALSE;
127
+ }
128
+
129
+ /* Method definitions */
130
+ static PyMethodDef SpeedupMethods[] = {
131
+ {
132
+ "fast_issubclass_check",
133
+ (PyCFunction)fast_issubclass_check,
134
+ METH_FASTCALL,
135
+ "fast_issubclass_check(typ, excs) -> bool\n\n"
136
+ "Check if typ is a subclass of excs exception type(s).\n"
137
+ "Returns False if typ is None, otherwise uses PyObject_IsSubclass.\n"
138
+ "Optimized for exception handling performance."
139
+ },
140
+ {
141
+ "fast_append_exception",
142
+ (PyCFunction)fast_append_exception,
143
+ METH_FASTCALL,
144
+ "fast_append_exception(list, exc) -> None\n\n"
145
+ "Append an exception to a list with minimal overhead.\n"
146
+ "Validates that the first argument is a list."
147
+ },
148
+ {
149
+ "fast_suppress_exit",
150
+ (PyCFunction)fast_suppress_exit,
151
+ METH_FASTCALL,
152
+ "fast_suppress_exit(typ, excs) -> bool\n\n"
153
+ "Return True if typ is not None and is a subclass of excs.\n"
154
+ "Optimized for context manager __exit__ methods."
155
+ },
156
+ {NULL, NULL, 0, NULL} /* Sentinel */
157
+ };
158
+
159
+ /* Module definition */
160
+ static struct PyModuleDef speedupmodule = {
161
+ PyModuleDef_HEAD_INIT,
162
+ "_speedup", /* name */
163
+ "C speedup module for errortools\n\n"
164
+ "Provides optimized implementations of exception handling operations:\n"
165
+ " - fast_issubclass_check: Quick exception type hierarchy checking\n"
166
+ " - fast_append_exception: Efficient exception list append operations\n"
167
+ " - fast_suppress_exit: Combined None-check + issubclass for __exit__",
168
+ -1, /* size */
169
+ SpeedupMethods /* methods */
170
+ };
171
+
172
+ /* Module initialization */
173
+ PyMODINIT_FUNC PyInit__speedup(void) {
174
+ return PyModule_Create(&speedupmodule);
175
+ }
@@ -69,13 +69,11 @@ class ErrorCodeable(ABC):
69
69
  @abstractmethod
70
70
  def code(self) -> int:
71
71
  """Integer error code identifying this exception type."""
72
- pass
73
72
 
74
73
  @property
75
74
  @abstractmethod
76
75
  def default_detail(self) -> str:
77
76
  """Fallback human-readable message used when no detail is provided."""
78
- pass
79
77
 
80
78
 
81
79
  # ----------------------------------------------------------------------
@@ -123,7 +121,6 @@ class Warnable(ABC):
123
121
  stacklevel: Passed to ``warnings.warn``; ``2`` points at the
124
122
  caller of ``emit``.
125
123
  """
126
- pass
127
124
 
128
125
 
129
126
  # ----------------------------------------------------------------------
@@ -166,7 +163,6 @@ class Raiseable(ABC):
166
163
  Raises:
167
164
  self: Or a derived exception wrapping this object.
168
165
  """
169
- pass
170
166
 
171
167
 
172
168
  # ----------------------------------------------------------------------