errortools 3.2.0__tar.gz → 3.4.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 (83) hide show
  1. {errortools-3.2.0 → errortools-3.4.0}/AUTHORS.txt +2 -1
  2. {errortools-3.2.0/errortools.egg-info → errortools-3.4.0}/PKG-INFO +2 -1
  3. {errortools-3.2.0 → errortools-3.4.0}/_errortools/_speedup.c +11 -11
  4. {errortools-3.2.0 → errortools-3.4.0}/_errortools/classes/abc.py +1 -17
  5. {errortools-3.2.0 → errortools-3.4.0}/_errortools/decorator/cache.py +1 -2
  6. {errortools-3.2.0 → errortools-3.4.0}/_errortools/logging/logger.py +1 -1
  7. {errortools-3.2.0 → errortools-3.4.0}/_errortools/partial.py +4 -10
  8. errortools-3.4.0/_errortools/plugins.py +134 -0
  9. {errortools-3.2.0 → errortools-3.4.0}/_errortools/version.py +17 -17
  10. {errortools-3.2.0 → errortools-3.4.0}/errortools/__init__.py +74 -4
  11. {errortools-3.2.0 → errortools-3.4.0/errortools.egg-info}/PKG-INFO +2 -1
  12. {errortools-3.2.0 → errortools-3.4.0}/errortools.egg-info/SOURCES.txt +3 -9
  13. {errortools-3.2.0 → errortools-3.4.0}/errortools.egg-info/top_level.txt +0 -5
  14. {errortools-3.2.0 → errortools-3.4.0}/pyproject.toml +4 -1
  15. {errortools-3.2.0 → errortools-3.4.0}/testing/__init__.py +4 -2
  16. errortools-3.4.0/testing/benchmark/__init__.py +4 -0
  17. {errortools-3.2.0 → errortools-3.4.0}/testing/test_groups.py +127 -130
  18. errortools-3.4.0/testing/test_plugins.py +200 -0
  19. errortools-3.4.0/testing/test_version.py +71 -0
  20. errortools-3.2.0/_errortools/const.py +0 -12
  21. errortools-3.2.0/docs/conf.py +0 -56
  22. errortools-3.2.0/testing/benchmark/__init__.py +0 -1
  23. errortools-3.2.0/testing/test_abc.py +0 -297
  24. errortools-3.2.0/testing/test_const.py +0 -28
  25. errortools-3.2.0/testing/test_errorcodes.py +0 -395
  26. errortools-3.2.0/testing/test_future.py +0 -296
  27. errortools-3.2.0/testing/test_logging.py +0 -673
  28. errortools-3.2.0/testing/test_partials.py +0 -228
  29. errortools-3.2.0/testing/test_warnings.py +0 -151
  30. {errortools-3.2.0 → errortools-3.4.0}/LICENSE.txt +0 -0
  31. {errortools-3.2.0 → errortools-3.4.0}/README.md +0 -0
  32. {errortools-3.2.0 → errortools-3.4.0}/_errortools/__init__.py +0 -0
  33. {errortools-3.2.0 → errortools-3.4.0}/_errortools/__main__.py +0 -0
  34. {errortools-3.2.0 → errortools-3.4.0}/_errortools/_cli.py +0 -0
  35. {errortools-3.2.0 → errortools-3.4.0}/_errortools/classes/__init__.py +0 -0
  36. {errortools-3.2.0 → errortools-3.4.0}/_errortools/classes/errorcodes.py +0 -0
  37. {errortools-3.2.0 → errortools-3.4.0}/_errortools/classes/group.py +0 -0
  38. {errortools-3.2.0 → errortools-3.4.0}/_errortools/classes/warn.py +0 -0
  39. {errortools-3.2.0 → errortools-3.4.0}/_errortools/cli.py +0 -0
  40. {errortools-3.2.0 → errortools-3.4.0}/_errortools/decorator/__init__.py +0 -0
  41. {errortools-3.2.0 → errortools-3.4.0}/_errortools/decorator/deprecated.py +0 -0
  42. {errortools-3.2.0 → errortools-3.4.0}/_errortools/decorator/handlers.py +0 -0
  43. {errortools-3.2.0 → errortools-3.4.0}/_errortools/decorator/retry.py +0 -0
  44. {errortools-3.2.0 → errortools-3.4.0}/_errortools/decorator/timeout.py +0 -0
  45. {errortools-3.2.0 → errortools-3.4.0}/_errortools/descriptor/__init__.py +0 -0
  46. {errortools-3.2.0 → errortools-3.4.0}/_errortools/descriptor/base.py +0 -0
  47. {errortools-3.2.0 → errortools-3.4.0}/_errortools/descriptor/errormsg.py +0 -0
  48. {errortools-3.2.0 → errortools-3.4.0}/_errortools/descriptor/nonblankmsg.py +0 -0
  49. {errortools-3.2.0 → errortools-3.4.0}/_errortools/errno.py +0 -0
  50. {errortools-3.2.0 → errortools-3.4.0}/_errortools/future.py +0 -0
  51. {errortools-3.2.0 → errortools-3.4.0}/_errortools/ignore.py +0 -0
  52. {errortools-3.2.0 → errortools-3.4.0}/_errortools/logging/__init__.py +0 -0
  53. {errortools-3.2.0 → errortools-3.4.0}/_errortools/logging/base.py +0 -0
  54. {errortools-3.2.0 → errortools-3.4.0}/_errortools/logging/level.py +0 -0
  55. {errortools-3.2.0 → errortools-3.4.0}/_errortools/logging/record.py +0 -0
  56. {errortools-3.2.0 → errortools-3.4.0}/_errortools/logging/sink.py +0 -0
  57. {errortools-3.2.0 → errortools-3.4.0}/_errortools/metadata.py +0 -0
  58. {errortools-3.2.0 → errortools-3.4.0}/_errortools/py.typed +0 -0
  59. {errortools-3.2.0 → errortools-3.4.0}/_errortools/raises.py +0 -0
  60. {errortools-3.2.0 → errortools-3.4.0}/_errortools/typing.py +0 -0
  61. {errortools-3.2.0 → errortools-3.4.0}/_errortools/wrappers/__init__.py +0 -0
  62. {errortools-3.2.0 → errortools-3.4.0}/_errortools/wrappers/cache.py +0 -0
  63. {errortools-3.2.0 → errortools-3.4.0}/_errortools/wrappers/ignore.py +0 -0
  64. {errortools-3.2.0 → errortools-3.4.0}/errortools/__main__.py +0 -0
  65. {errortools-3.2.0 → errortools-3.4.0}/errortools/future.py +0 -0
  66. {errortools-3.2.0 → errortools-3.4.0}/errortools/logging.py +0 -0
  67. {errortools-3.2.0 → errortools-3.4.0}/errortools/partial.py +0 -0
  68. {errortools-3.2.0 → errortools-3.4.0}/errortools.egg-info/dependency_links.txt +0 -0
  69. {errortools-3.2.0 → errortools-3.4.0}/errortools.egg-info/entry_points.txt +0 -0
  70. {errortools-3.2.0 → errortools-3.4.0}/errortools.egg-info/requires.txt +0 -0
  71. {errortools-3.2.0 → errortools-3.4.0}/setup.cfg +0 -0
  72. {errortools-3.2.0 → errortools-3.4.0}/testing/__main__.py +0 -0
  73. {errortools-3.2.0 → errortools-3.4.0}/testing/benchmark/test_future_perf.py +0 -0
  74. {errortools-3.2.0 → errortools-3.4.0}/testing/conftest.py +0 -0
  75. {errortools-3.2.0 → errortools-3.4.0}/testing/run_tests.py +0 -0
  76. {errortools-3.2.0 → errortools-3.4.0}/testing/test_decorator.py +0 -0
  77. {errortools-3.2.0 → errortools-3.4.0}/testing/test_descriptor.py +0 -0
  78. {errortools-3.2.0 → errortools-3.4.0}/testing/test_errno.py +0 -0
  79. {errortools-3.2.0 → errortools-3.4.0}/testing/test_ignore.py +0 -0
  80. {errortools-3.2.0 → errortools-3.4.0}/testing/test_raises.py +0 -0
  81. {errortools-3.2.0 → errortools-3.4.0}/testing/test_testing/__init__.py +0 -0
  82. {errortools-3.2.0 → errortools-3.4.0}/testing/test_testing/test_testing.py +0 -0
  83. {errortools-3.2.0 → errortools-3.4.0}/testing/test_typing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  # (Lines starting with # are comments)
2
2
  # Tips:
3
- # Contributors are not sorted by initials,
3
+ # Contributors are not sorted by initials,
4
4
  # but by contribution time.
5
5
  # Here are the real contributors
6
6
  aiwonderland
@@ -10,3 +10,4 @@ yangphysics
10
10
  # And here are bot contributors
11
11
  dependabot[bot]
12
12
  AbaAbaAba-bot-like[bot]
13
+ pre-commit-ci[bot]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: errortools
3
- Version: 3.2.0
3
+ Version: 3.4.0
4
4
  Summary: errortools - a toolset for working with Python exceptions and warnings and logging.
5
5
  Author-email: Evan Yang <quantbit@126.com>
6
6
  License: Copyright (c) 2026 authors see AUTHORS.txt
@@ -25,6 +25,7 @@ License: Copyright (c) 2026 authors see AUTHORS.txt
25
25
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
26
 
27
27
  Project-URL: Homepage, https://github.com/more-abc/errortools
28
+ Project-URL: Documentation, https://errortools.readthedocs.io/
28
29
  Classifier: License :: OSI Approved :: MIT License
29
30
  Classifier: Programming Language :: Python :: 3
30
31
  Classifier: Programming Language :: Python :: 3.8
@@ -2,23 +2,23 @@
2
2
  #include <Python.h>
3
3
 
4
4
  /* Fast exception type checking
5
- *
5
+ *
6
6
  * Optimized check for exception type hierarchy using PyObject_IsSubclass.
7
7
  * Returns False immediately if typ is None, otherwise checks if typ is a
8
8
  * subclass of excs.
9
- *
9
+ *
10
10
  * Args:
11
11
  * typ: The type object to check (or None)
12
12
  * excs: The exception class(es) to check against
13
- *
13
+ *
14
14
  * Returns:
15
15
  * True if typ is a subclass of excs, False otherwise
16
16
  * NULL on error with exception set
17
17
  */
18
18
  static PyObject* fast_issubclass_check(PyObject* self, PyObject* const* args, Py_ssize_t nargs) {
19
19
  if (nargs != 2) {
20
- PyErr_Format(PyExc_TypeError,
21
- "fast_issubclass_check() takes exactly 2 arguments (%zd given)",
20
+ PyErr_Format(PyExc_TypeError,
21
+ "fast_issubclass_check() takes exactly 2 arguments (%zd given)",
22
22
  nargs);
23
23
  return NULL;
24
24
  }
@@ -33,7 +33,7 @@ static PyObject* fast_issubclass_check(PyObject* self, PyObject* const* args, Py
33
33
 
34
34
  /* Validate that excs is a class or tuple of classes */
35
35
  if (!PyType_Check(excs) && !PyTuple_Check(excs)) {
36
- PyErr_SetString(PyExc_TypeError,
36
+ PyErr_SetString(PyExc_TypeError,
37
37
  "second argument must be a class or tuple of classes");
38
38
  return NULL;
39
39
  }
@@ -52,21 +52,21 @@ static PyObject* fast_issubclass_check(PyObject* self, PyObject* const* args, Py
52
52
  }
53
53
 
54
54
  /* Fast exception collector append
55
- *
55
+ *
56
56
  * Optimized append operation for adding exceptions to a list.
57
- *
57
+ *
58
58
  * Args:
59
59
  * list: The list object to append to
60
60
  * exc: The exception object to append
61
- *
61
+ *
62
62
  * Returns:
63
63
  * None on success
64
64
  * NULL on error with exception set
65
65
  */
66
66
  static PyObject* fast_append_exception(PyObject* self, PyObject* const* args, Py_ssize_t nargs) {
67
67
  if (nargs != 2) {
68
- PyErr_Format(PyExc_TypeError,
69
- "fast_append_exception() takes exactly 2 arguments (%zd given)",
68
+ PyErr_Format(PyExc_TypeError,
69
+ "fast_append_exception() takes exactly 2 arguments (%zd given)",
70
70
  nargs);
71
71
  return NULL;
72
72
  }
@@ -1,5 +1,6 @@
1
1
  from typing import Any, Literal, Union
2
2
  from abc import ABC, abstractmethod
3
+ from _collections_abc import _check_methods # type: ignore[attr-defined]
3
4
  import copy
4
5
  import shutil
5
6
  import csv
@@ -12,23 +13,6 @@ else:
12
13
  from typing_extensions import disjoint_base
13
14
 
14
15
 
15
- def _check_methods(C: type[Any], *methods: str) -> Union[bool, Literal[NotImplemented]]: # type: ignore
16
- """Check methods in `C`. If has, return `True`, else `NotImplemented`."""
17
- # from `_collections_abc.py`.
18
- # Copyright 2007 Google, Inc. All Rights Reserved.
19
- # Licensed to PSF under a Contributor Agreement.
20
- mro: tuple[type[Any], ...] = C.__mro__ # Added type hints for mro var
21
- for method in methods:
22
- for B in mro:
23
- if method in B.__dict__:
24
- if B.__dict__[method] is None:
25
- return NotImplemented
26
- break
27
- else:
28
- return NotImplemented
29
- return True
30
-
31
-
32
16
  # ----------------------------------------------------------------------
33
17
  # ErrorCodeable
34
18
  # ----------------------------------------------------------------------
@@ -9,7 +9,6 @@ from typing import (
9
9
  overload,
10
10
  )
11
11
 
12
- from ..const import DEFAULT_ERROR_CACHE_SIZE
13
12
  from ..wrappers.cache import ErrorCacheWrapper
14
13
 
15
14
  _T = TypeVar("_T", bound=Callable[..., Any])
@@ -28,7 +27,7 @@ def error_cache(maxsize: Optional[int] = 128) -> Callable[[_T], ErrorCacheWrappe
28
27
  # fmt: on
29
28
 
30
29
 
31
- def error_cache(func: Optional[_T] = None, maxsize: Optional[int] = DEFAULT_ERROR_CACHE_SIZE) -> Any: # type: ignore
30
+ def error_cache(func: Optional[_T] = None, maxsize: Optional[int] = 128) -> Any: # type: ignore
32
31
  """
33
32
  Decorator to cache exceptions raised by a function.
34
33
 
@@ -10,4 +10,4 @@ from .level import Level
10
10
  # Create the default global logger.
11
11
  # It ships with a single stderr sink at DEBUG level (mirrors loguru's default).
12
12
  logger: BaseLogger = BaseLogger(name="errortools")
13
- logger.add(sys.stderr, level=Level.DEBUG, colorize=None)
13
+ logger.add(sys.stderr, level=Level.TRACE, colorize=None)
@@ -14,12 +14,6 @@ from .ignore import (
14
14
  retry,
15
15
  )
16
16
  from .decorator.cache import error_cache
17
- from .const import (
18
- LARGE_ERROR_CACHE_SIZE,
19
- SMALL_ERROR_CACHE_SIZE,
20
- DEFAULT_ERROR_CACHE_SIZE,
21
- UNLIMITED_ERROR_CACHE,
22
- )
23
17
 
24
18
  # ------------------------------------------------------------------
25
19
  # ignore: Common exception shortcuts
@@ -114,10 +108,10 @@ retry_5_delay_1s: Callable = partial(retry, times=5, delay=1)
114
108
  # error cache presets
115
109
  # ------------------------------------------------------------------
116
110
 
117
- unlimited_error_cache: Callable = partial(error_cache, maxsize=UNLIMITED_ERROR_CACHE)
118
- lru_error_cache: Callable = partial(error_cache, maxsize=DEFAULT_ERROR_CACHE_SIZE)
119
- small_error_cache: Callable = partial(error_cache, maxsize=SMALL_ERROR_CACHE_SIZE)
120
- large_error_cache: Callable = partial(error_cache, maxsize=LARGE_ERROR_CACHE_SIZE)
111
+ unlimited_error_cache: Callable = partial(error_cache, maxsize=1024)
112
+ lru_error_cache: Callable = partial(error_cache, maxsize=128)
113
+ small_error_cache: Callable = partial(error_cache, maxsize=64)
114
+ large_error_cache: Callable = partial(error_cache, maxsize=None)
121
115
 
122
116
 
123
117
  # ------------------------------------------------------------------
@@ -0,0 +1,134 @@
1
+ """Ultra-lightweight plugin system for errortools."""
2
+
3
+ from typing import Callable, Any
4
+
5
+ _REGISTRY: dict[str, Callable[..., Any]] = {}
6
+ _UNSET = object()
7
+
8
+ __all__ = [
9
+ "register",
10
+ "get",
11
+ "has",
12
+ "list_all",
13
+ "run",
14
+ "remove",
15
+ "clear",
16
+ "Registry",
17
+ ]
18
+
19
+
20
+ def register(name: str) -> Callable:
21
+ """Register plugin (decorator).
22
+
23
+ .. versionadded:: 3.2
24
+ """
25
+
26
+ def decorator(func: Callable) -> Callable:
27
+ _REGISTRY[name] = func
28
+ return func
29
+
30
+ return decorator
31
+
32
+
33
+ def get(name: str, default: Any = _UNSET) -> Any:
34
+ """Get registered plugin.
35
+
36
+ Args:
37
+ name: Plugin identifier.
38
+ default: Value returned when the plugin is missing.
39
+ If not provided, a `ValueError` is raised instead.
40
+
41
+ Raises:
42
+ ValueError: If the plugin does not exist and no *default* was supplied.
43
+
44
+ .. versionadded:: 3.2
45
+ """
46
+ try:
47
+ return _REGISTRY[name]
48
+ except KeyError:
49
+ if default is not _UNSET:
50
+ return default
51
+ raise ValueError(f"Plugin {name!r} is not registered")
52
+
53
+
54
+ def has(name: str) -> bool:
55
+ """Check whether a plugin is registered.
56
+
57
+ Args:
58
+ name: Plugin identifier.
59
+
60
+ Returns:
61
+ ``True`` if a plugin with the given *name* is registered,
62
+ otherwise ``False``.
63
+
64
+ .. versionadded:: 3.3
65
+ """
66
+ return name in _REGISTRY
67
+
68
+
69
+ def remove(name: str) -> None:
70
+ """Remove a plugin.
71
+
72
+ This is a no-op if the plugin does not exist.
73
+
74
+ .. versionadded:: 3.2
75
+ """
76
+ _REGISTRY.pop(name, None)
77
+
78
+
79
+ def clear() -> None:
80
+ """Remove all plugins from the registry.
81
+
82
+ .. versionadded:: 3.3
83
+ """
84
+ _REGISTRY.clear()
85
+
86
+
87
+ def list_all() -> list[str]:
88
+ """List all plugin names.
89
+
90
+ .. versionadded:: 3.2
91
+ """
92
+ return list(_REGISTRY.keys())
93
+
94
+
95
+ def run(name: str, *args, **kwargs) -> Any:
96
+ """Run plugin.
97
+
98
+ Raises:
99
+ ValueError: If the plugin does not exist.
100
+
101
+ .. versionadded:: 3.2
102
+ """
103
+ return get(name)(*args, **kwargs)
104
+
105
+
106
+ class Registry:
107
+ """Static class providing an alternative API for the plugin registry.
108
+
109
+ .. versionadded:: 3.2
110
+ """
111
+
112
+ @staticmethod
113
+ def register(name: str, func: Callable) -> None:
114
+ _REGISTRY[name] = func
115
+
116
+ @staticmethod
117
+ def list_all() -> list[str]:
118
+ return list_all()
119
+
120
+ @staticmethod
121
+ def get(name: str) -> Any:
122
+ return get(name)
123
+
124
+ @staticmethod
125
+ def has(name: str) -> bool:
126
+ return has(name)
127
+
128
+ @staticmethod
129
+ def remove(name: str) -> None:
130
+ remove(name)
131
+
132
+ @staticmethod
133
+ def clear() -> None:
134
+ clear()
@@ -1,17 +1,17 @@
1
- def _get_version_tuple(version: str) -> tuple[int, int, int]:
2
- parts = [int(p) for p in version.split(".")]
3
-
4
- major = parts[0] if len(parts) >= 1 else 0
5
- minor = parts[1] if len(parts) >= 2 else 0
6
- patch = parts[2] if len(parts) >= 3 else 0
7
-
8
- return (major, minor, patch)
9
-
10
-
11
- __version__: str = "3.2.0"
12
- __version_tuple__: tuple[int, int, int] = _get_version_tuple(__version__)
13
- __commit_id__: str | None = None
14
-
15
- version = __version__
16
- version_tuple = __version_tuple__
17
- commit_id = __commit_id__
1
+ def _get_version_tuple(version: str) -> tuple[int, int, int]:
2
+ parts = [int(p) for p in version.split(".")]
3
+
4
+ major = parts[0] if len(parts) >= 1 else 0
5
+ minor = parts[1] if len(parts) >= 2 else 0
6
+ patch = parts[2] if len(parts) >= 3 else 0
7
+
8
+ return (major, minor, patch)
9
+
10
+
11
+ __version__: str = "3.4.0"
12
+ __version_tuple__: tuple[int, int, int] = _get_version_tuple(__version__)
13
+ __commit_id__: str | None = None
14
+
15
+ version = __version__
16
+ version_tuple = __version_tuple__
17
+ commit_id = __commit_id__
@@ -2,6 +2,9 @@
2
2
  errortools - a toolset for working with Python exceptions and warnings and logging.
3
3
  """
4
4
 
5
+ import sys
6
+ from typing import Any
7
+
5
8
  from _errortools.raises import raises, assert_raises, raises_all, reraise
6
9
  from _errortools.ignore import (
7
10
  ignore,
@@ -16,7 +19,7 @@ from _errortools.errno import (
16
19
  get_all_errno_codes,
17
20
  is_valid_errno,
18
21
  )
19
- from _errortools.classes.group import BaseGroup, GroupErrors
22
+ from _errortools.classes.group import BaseGroup, GroupErrors # noqa: F401
20
23
  from _errortools.decorator.cache import error_cache
21
24
  from _errortools.decorator.deprecated import deprecated, experimental
22
25
  from _errortools.decorator.handlers import suppress, convert
@@ -45,6 +48,22 @@ from _errortools.classes.abc import (
45
48
  Raiseable,
46
49
  Error,
47
50
  )
51
+ from _errortools.classes.protocol import ( # noqa: F401
52
+ ExceptionLike,
53
+ ExceptionGroupLike,
54
+ BaseExceptionGroupLike,
55
+ BlockingIOErrorLike,
56
+ NameErrorLike,
57
+ StopIterationLike,
58
+ SyntaxErrorLike,
59
+ SystemExitLike,
60
+ ImportErrorLike,
61
+ UnicodeDecodeErrorLike,
62
+ UnicodeEncodeErrorLike,
63
+ UnicodeTranslateErrorLike,
64
+ AttributeErrorLike,
65
+ GroupErrorsLike,
66
+ )
48
67
  from _errortools.typing import (
49
68
  AnyErrorCode,
50
69
  BaseErrorCodesType,
@@ -55,6 +74,7 @@ from _errortools.typing import (
55
74
  TracebackType,
56
75
  FrameType,
57
76
  )
77
+ from _errortools.plugins import run, register, list_all, get, has, remove, clear, Registry
58
78
  from _errortools.descriptor.errormsg import ErrorMsg
59
79
  from _errortools.descriptor.nonblankmsg import NonBlankErrorMsg
60
80
  from _errortools.version import (
@@ -88,6 +108,10 @@ _DEPRECATED_NAMES: dict[str, tuple[str, str]] = {
88
108
  }
89
109
 
90
110
 
111
+ class ErrortoolsDeprecationWarning(DeprecationWarning):
112
+ """Base class for warnings about deprecated features in errortools module."""
113
+
114
+
91
115
  def __getattr__(name: str):
92
116
  import importlib
93
117
  import warnings
@@ -96,7 +120,7 @@ def __getattr__(name: str):
96
120
  attr_name, reason = _DEPRECATED_NAMES[name]
97
121
  warnings.warn(
98
122
  f"errortools.{attr_name} is deprecated. {reason}",
99
- DeprecationWarning,
123
+ ErrortoolsDeprecationWarning,
100
124
  stacklevel=2,
101
125
  )
102
126
  return globals()[f"_{name}"] if f"_{name}" in globals() else globals().get(name)
@@ -104,6 +128,11 @@ def __getattr__(name: str):
104
128
  if name in ("future", "logging", "partial"):
105
129
  return importlib.import_module(f"_errortools.{name}")
106
130
 
131
+ try:
132
+ return get(name)
133
+ except ValueError:
134
+ pass
135
+
107
136
  raise AttributeError(f"module 'errortools' has no attribute {name!r}")
108
137
 
109
138
 
@@ -111,6 +140,23 @@ def __dir__() -> list[str]:
111
140
  return __all__
112
141
 
113
142
 
143
+ class PluginNamespace:
144
+ def __getattr__(self, name: str) -> Any:
145
+ plugin = get(name)
146
+ if plugin is None:
147
+ raise AttributeError(f"Plugin {name!r} not found")
148
+ return plugin
149
+
150
+
151
+ plugins = PluginNamespace()
152
+
153
+ _PYTHON_3_11_CAN_USE: list[str] = [
154
+ "GroupErrors",
155
+ "BaseGroup",
156
+ "BaseExceptionGroupLike",
157
+ "ExceptionGroupLike",
158
+ "GroupErrorsLike",
159
+ ]
114
160
  __all__ = [
115
161
  # functions
116
162
  "raises",
@@ -135,8 +181,6 @@ __all__ = [
135
181
  "TracebackType",
136
182
  "FrameType",
137
183
  # classes
138
- "GroupErrors",
139
- "BaseGroup",
140
184
  "BaseErrorCodes",
141
185
  "InvalidInputError",
142
186
  "NotFoundError",
@@ -158,6 +202,18 @@ __all__ = [
158
202
  "PureBaseException",
159
203
  "ContextException",
160
204
  "Error",
205
+ "ExceptionLike",
206
+ "BlockingIOErrorLike",
207
+ "NameErrorLike",
208
+ "StopIterationLike",
209
+ "SystemExitLike",
210
+ "ImportErrorLike",
211
+ "SyntaxErrorLike",
212
+ "UnicodeDecodeErrorLike",
213
+ "UnicodeEncodeErrorLike",
214
+ "UnicodeTranslateErrorLike",
215
+ "AttributeErrorLike",
216
+ "ErrortoolsDeprecationWarning",
161
217
  # for type hints
162
218
  "PureBaseExceptionType",
163
219
  "ContextExceptionType",
@@ -169,6 +225,15 @@ __all__ = [
169
225
  "RuntimeError_",
170
226
  "ExceptionType",
171
227
  "WarningType",
228
+ # plugins
229
+ "register",
230
+ "get",
231
+ "has",
232
+ "list_all",
233
+ "run",
234
+ "remove",
235
+ "clear",
236
+ "Registry",
172
237
  # metadata
173
238
  "__version__",
174
239
  "__version_tuple__",
@@ -192,3 +257,8 @@ __all__ = [
192
257
  "logging",
193
258
  "partial",
194
259
  ]
260
+
261
+ __all__.append("plugins")
262
+
263
+ if sys.version_info >= (3, 11):
264
+ __all__.append(_PYTHON_3_11_CAN_USE)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: errortools
3
- Version: 3.2.0
3
+ Version: 3.4.0
4
4
  Summary: errortools - a toolset for working with Python exceptions and warnings and logging.
5
5
  Author-email: Evan Yang <quantbit@126.com>
6
6
  License: Copyright (c) 2026 authors see AUTHORS.txt
@@ -25,6 +25,7 @@ License: Copyright (c) 2026 authors see AUTHORS.txt
25
25
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
26
 
27
27
  Project-URL: Homepage, https://github.com/more-abc/errortools
28
+ Project-URL: Documentation, https://errortools.readthedocs.io/
28
29
  Classifier: License :: OSI Approved :: MIT License
29
30
  Classifier: Programming Language :: Python :: 3
30
31
  Classifier: Programming Language :: Python :: 3.8
@@ -7,12 +7,12 @@ _errortools/__main__.py
7
7
  _errortools/_cli.py
8
8
  _errortools/_speedup.c
9
9
  _errortools/cli.py
10
- _errortools/const.py
11
10
  _errortools/errno.py
12
11
  _errortools/future.py
13
12
  _errortools/ignore.py
14
13
  _errortools/metadata.py
15
14
  _errortools/partial.py
15
+ _errortools/plugins.py
16
16
  _errortools/py.typed
17
17
  _errortools/raises.py
18
18
  _errortools/typing.py
@@ -41,7 +41,6 @@ _errortools/logging/sink.py
41
41
  _errortools/wrappers/__init__.py
42
42
  _errortools/wrappers/cache.py
43
43
  _errortools/wrappers/ignore.py
44
- docs/conf.py
45
44
  errortools/__init__.py
46
45
  errortools/__main__.py
47
46
  errortools/future.py
@@ -57,20 +56,15 @@ testing/__init__.py
57
56
  testing/__main__.py
58
57
  testing/conftest.py
59
58
  testing/run_tests.py
60
- testing/test_abc.py
61
- testing/test_const.py
62
59
  testing/test_decorator.py
63
60
  testing/test_descriptor.py
64
61
  testing/test_errno.py
65
- testing/test_errorcodes.py
66
- testing/test_future.py
67
62
  testing/test_groups.py
68
63
  testing/test_ignore.py
69
- testing/test_logging.py
70
- testing/test_partials.py
64
+ testing/test_plugins.py
71
65
  testing/test_raises.py
72
66
  testing/test_typing.py
73
- testing/test_warnings.py
67
+ testing/test_version.py
74
68
  testing/benchmark/__init__.py
75
69
  testing/benchmark/test_future_perf.py
76
70
  testing/test_testing/__init__.py
@@ -1,8 +1,3 @@
1
1
  _errortools
2
- build
3
- dist
4
- docs
5
- emeps
6
2
  errortools
7
- htmlcov
8
3
  testing
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "errortools"
8
- version = "3.2.0"
8
+ version = "3.4.0"
9
9
  authors = [
10
10
  { name = "Evan Yang", email = "quantbit@126.com" }
11
11
  ]
@@ -38,6 +38,7 @@ classifiers = [
38
38
 
39
39
  [project.urls]
40
40
  Homepage = "https://github.com/more-abc/errortools"
41
+ Documentation = "https://errortools.readthedocs.io/"
41
42
 
42
43
 
43
44
  [project.scripts]
@@ -50,6 +51,8 @@ _errortools = ["py.typed"]
50
51
 
51
52
  [tool.setuptools.packages.find]
52
53
  where = ["."]
54
+ include = ["errortools", "_errortools", "testing"]
55
+ exclude = ["htmlcov", ".*", "docs"]
53
56
 
54
57
 
55
58
  [[tool.setuptools.ext-modules]]
@@ -2,14 +2,16 @@
2
2
 
3
3
  import warnings
4
4
 
5
+ from _errortools.version import _get_version_tuple
6
+
5
7
  __all__ = [
6
8
  "__version__",
7
9
  "__version_tuple__",
8
10
  "HAS_PYTEST",
9
11
  "NO_ONE_CHANGE_VERSION",
10
12
  ]
11
- __version__ = "1.2.0"
12
- __version_tuple__ = (1, 2, 0)
13
+ __version__ = "1.3.5"
14
+ __version_tuple__ = _get_version_tuple(__version__)
13
15
 
14
16
  try:
15
17
  import pytest
@@ -0,0 +1,4 @@
1
+ from _errortools.version import _get_version_tuple
2
+
3
+ __version__ = "0.1.0"
4
+ __version_tuple__ = _get_version_tuple(__version__)