errortools 1.3.0__tar.gz → 2.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.
- errortools-2.1.0/AUTHORS.txt +3 -0
- {errortools-1.3.0/errortools.egg-info → errortools-2.1.0}/PKG-INFO +19 -3
- {errortools-1.3.0 → errortools-2.1.0}/README.md +16 -2
- errortools-2.1.0/_errortools/_cli.py +32 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/cli.py +34 -8
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/const.py +10 -10
- {errortools-1.3.0/_errortools → errortools-2.1.0/_errortools/decorator}/cache.py +2 -2
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/ignore.py +50 -10
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/logging/sink.py +1 -1
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/metadata.py +7 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/partial.py +1 -1
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/version.py +2 -2
- errortools-2.1.0/_errortools/wrappers/__init__.py +0 -0
- errortools-2.1.0/_errortools/wrappers/cache.py +95 -0
- errortools-2.1.0/_errortools/wrappers/ignore.py +115 -0
- {errortools-1.3.0 → errortools-2.1.0}/errortools/__init__.py +9 -1
- {errortools-1.3.0 → errortools-2.1.0/errortools.egg-info}/PKG-INFO +19 -3
- {errortools-1.3.0 → errortools-2.1.0}/errortools.egg-info/SOURCES.txt +7 -1
- errortools-2.1.0/errortools.egg-info/entry_points.txt +3 -0
- errortools-2.1.0/errortools.egg-info/requires.txt +1 -0
- {errortools-1.3.0 → errortools-2.1.0}/setup.py +4 -3
- {errortools-1.3.0 → errortools-2.1.0}/tests/__init__.py +12 -12
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_abc.py +1 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_cache.py +2 -1
- errortools-2.1.0/tests/test_const.py +33 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_decorator.py +1 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_descriptor.py +2 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_errorcodes.py +2 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_groups.py +2 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_ignore.py +2 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_logging.py +2 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_mixins.py +2 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_partials.py +1 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_raises.py +1 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_typing.py +1 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/test_warnings.py +2 -0
- errortools-1.3.0/AUTHORS.txt +0 -2
- errortools-1.3.0/errortools.egg-info/entry_points.txt +0 -2
- {errortools-1.3.0 → errortools-2.1.0}/LICENSE.txt +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/__init__.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/classes/__init__.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/classes/abc.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/classes/errorcodes.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/classes/group.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/classes/warn.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/decorator/__init__.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/decorator/deprecated.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/future.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/logging/__init__.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/logging/base.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/logging/level.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/logging/logger.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/logging/record.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/methods/__init__.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/methods/errorattr.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/methods/errordelattr.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/methods/errorhasattr.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/methods/errorsetattr.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/py.typed +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/raises.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/_errortools/typing.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/errortools/__main__.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/errortools.egg-info/dependency_links.txt +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/errortools.egg-info/top_level.txt +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/setup.cfg +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/conftest.py +0 -0
- {errortools-1.3.0 → errortools-2.1.0}/tests/run_tests.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: errortools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 2.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
|
|
@@ -19,6 +19,7 @@ Requires-Python: >=3.10
|
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE.txt
|
|
21
21
|
License-File: AUTHORS.txt
|
|
22
|
+
Requires-Dist: namebyauthor==1.0.0
|
|
22
23
|
Dynamic: author
|
|
23
24
|
Dynamic: author-email
|
|
24
25
|
Dynamic: classifier
|
|
@@ -27,6 +28,7 @@ Dynamic: description-content-type
|
|
|
27
28
|
Dynamic: home-page
|
|
28
29
|
Dynamic: license
|
|
29
30
|
Dynamic: license-file
|
|
31
|
+
Dynamic: requires-dist
|
|
30
32
|
Dynamic: requires-python
|
|
31
33
|
Dynamic: summary
|
|
32
34
|
|
|
@@ -49,7 +51,7 @@ pip install errortools
|
|
|
49
51
|
|
|
50
52
|
---
|
|
51
53
|
|
|
52
|
-
##
|
|
54
|
+
## Examples
|
|
53
55
|
|
|
54
56
|
```python
|
|
55
57
|
import warnings
|
|
@@ -70,8 +72,22 @@ assert err.name == "KeyError" # exception class name
|
|
|
70
72
|
assert err.count == 1 # how many times suppressed
|
|
71
73
|
assert err.exception # the original KeyError instance
|
|
72
74
|
assert err.traceback # full formatted traceback string
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Attributes on the returned object:**
|
|
78
|
+
|
|
79
|
+
| Attribute | Type | Description |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `be_ignore` | `bool` | `True` if an exception was suppressed |
|
|
82
|
+
| `name` | `str \| None` | Class name of the suppressed exception |
|
|
83
|
+
| `count` | `int` | Number of suppressed exceptions |
|
|
84
|
+
| `exception` | `Exception \| None` | The original exception instance |
|
|
85
|
+
| `traceback` | `str \| None` | Formatted traceback string for debugging |
|
|
86
|
+
|
|
87
|
+
## Examples
|
|
73
88
|
|
|
74
|
-
|
|
89
|
+
```python
|
|
90
|
+
# ── ignore as a decorator ──
|
|
75
91
|
@ignore(ValueError, TypeError)
|
|
76
92
|
def parse(x: str) -> int:
|
|
77
93
|
return int(x)
|
|
@@ -17,7 +17,7 @@ pip install errortools
|
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## Examples
|
|
21
21
|
|
|
22
22
|
```python
|
|
23
23
|
import warnings
|
|
@@ -38,8 +38,22 @@ assert err.name == "KeyError" # exception class name
|
|
|
38
38
|
assert err.count == 1 # how many times suppressed
|
|
39
39
|
assert err.exception # the original KeyError instance
|
|
40
40
|
assert err.traceback # full formatted traceback string
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Attributes on the returned object:**
|
|
44
|
+
|
|
45
|
+
| Attribute | Type | Description |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `be_ignore` | `bool` | `True` if an exception was suppressed |
|
|
48
|
+
| `name` | `str \| None` | Class name of the suppressed exception |
|
|
49
|
+
| `count` | `int` | Number of suppressed exceptions |
|
|
50
|
+
| `exception` | `Exception \| None` | The original exception instance |
|
|
51
|
+
| `traceback` | `str \| None` | Formatted traceback string for debugging |
|
|
52
|
+
|
|
53
|
+
## Examples
|
|
41
54
|
|
|
42
|
-
|
|
55
|
+
```python
|
|
56
|
+
# ── ignore as a decorator ──
|
|
43
57
|
@ignore(ValueError, TypeError)
|
|
44
58
|
def parse(x: str) -> int:
|
|
45
59
|
return int(x)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from _errortools.metadata import (
|
|
4
|
+
__author__,
|
|
5
|
+
__author_email__,
|
|
6
|
+
__copyright__,
|
|
7
|
+
__description__,
|
|
8
|
+
__license__,
|
|
9
|
+
__url__,
|
|
10
|
+
)
|
|
11
|
+
from _errortools.version import __version__
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _cmd_log(message: str, level: str, output: str) -> None:
|
|
15
|
+
"""Emit a single log message via the errortools logger."""
|
|
16
|
+
from .logging import BaseLogger
|
|
17
|
+
from .logging.level import get_level
|
|
18
|
+
|
|
19
|
+
stream = sys.stdout if output == "stdout" else sys.stderr
|
|
20
|
+
log = BaseLogger(name="errortools-cli")
|
|
21
|
+
log.add(stream, level="TRACE", colorize=None)
|
|
22
|
+
log.log(get_level(level), message)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _print_info() -> None:
|
|
26
|
+
"""Print a summary of all package metadata."""
|
|
27
|
+
print(f"errortools v{__version__}")
|
|
28
|
+
print(f" {__description__}")
|
|
29
|
+
print(f" Author: {__author__} <{__author_email__}>")
|
|
30
|
+
print(f" License: {__license__}")
|
|
31
|
+
print(f" URL: {__url__}")
|
|
32
|
+
print(f" Copyright: {__copyright__}")
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import argparse
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
|
+
from _errortools._cli import _cmd_log, _print_info
|
|
7
|
+
|
|
6
8
|
from .metadata import (
|
|
7
9
|
__description__,
|
|
8
10
|
__copyright__,
|
|
@@ -49,17 +51,41 @@ def parse_args(args: list[str] | None = None) -> argparse.Namespace:
|
|
|
49
51
|
action="store_true",
|
|
50
52
|
help="Run tests for errortools module. (Using pytest)",
|
|
51
53
|
)
|
|
54
|
+
|
|
55
|
+
subparsers = parser.add_subparsers(dest="subcommand")
|
|
56
|
+
|
|
57
|
+
log_parser = subparsers.add_parser(
|
|
58
|
+
"log",
|
|
59
|
+
help="Emit a log message from the command line",
|
|
60
|
+
)
|
|
61
|
+
log_parser.add_argument(
|
|
62
|
+
"message",
|
|
63
|
+
help="The message to log",
|
|
64
|
+
)
|
|
65
|
+
log_parser.add_argument(
|
|
66
|
+
"--level",
|
|
67
|
+
"-l",
|
|
68
|
+
default="info",
|
|
69
|
+
choices=["trace", "debug", "info", "success", "warning", "error", "critical"],
|
|
70
|
+
help="Log level (default: info)",
|
|
71
|
+
)
|
|
72
|
+
log_parser.add_argument(
|
|
73
|
+
"--output",
|
|
74
|
+
"-o",
|
|
75
|
+
choices=["stderr", "stdout"],
|
|
76
|
+
default="stderr",
|
|
77
|
+
help="Output stream (default: stderr)",
|
|
78
|
+
)
|
|
79
|
+
|
|
52
80
|
return parser.parse_args(args)
|
|
53
81
|
|
|
54
82
|
|
|
55
|
-
def
|
|
56
|
-
"""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
print(f" URL: {__url__}")
|
|
62
|
-
print(f" Copyright: {__copyright__}")
|
|
83
|
+
def log_main() -> None:
|
|
84
|
+
"""Logging main CLI entry point."""
|
|
85
|
+
args = parse_args(sys.argv[1:])
|
|
86
|
+
|
|
87
|
+
if args.subcommand == "log":
|
|
88
|
+
_cmd_log(args.message, args.level, args.output)
|
|
63
89
|
|
|
64
90
|
|
|
65
91
|
def main() -> None:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"""Constants for errortools module."""
|
|
2
|
-
|
|
3
|
-
# ------------------------------------------------------------------
|
|
4
|
-
# error_cache constants
|
|
5
|
-
# ------------------------------------------------------------------
|
|
6
|
-
|
|
7
|
-
DEFAULT_ERROR_CACHE_SIZE: int = 128
|
|
8
|
-
SMALL_ERROR_CACHE_SIZE: int = 64
|
|
9
|
-
LARGE_ERROR_CACHE_SIZE: int = 1024
|
|
10
|
-
UNLIMITED_ERROR_CACHE: None = None
|
|
1
|
+
"""Constants for errortools module."""
|
|
2
|
+
|
|
3
|
+
# ------------------------------------------------------------------
|
|
4
|
+
# error_cache constants
|
|
5
|
+
# ------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
DEFAULT_ERROR_CACHE_SIZE: int = 128
|
|
8
|
+
SMALL_ERROR_CACHE_SIZE: int = 64
|
|
9
|
+
LARGE_ERROR_CACHE_SIZE: int = 1024
|
|
10
|
+
UNLIMITED_ERROR_CACHE: None = None
|
|
@@ -9,8 +9,8 @@ from typing import (
|
|
|
9
9
|
overload,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
12
|
+
from ..const import DEFAULT_ERROR_CACHE_SIZE
|
|
13
|
+
from ..wrappers.cache import ErrorCacheWrapper
|
|
14
14
|
|
|
15
15
|
_T = TypeVar("_T", bound=Callable[..., Any])
|
|
16
16
|
|
|
@@ -33,20 +33,60 @@ __all__ = [
|
|
|
33
33
|
# It captures the full exception traceback and metadata, which results in higher overhead.
|
|
34
34
|
# Use it for error handling and diagnostics, not for tight loops.
|
|
35
35
|
ignore = ErrorIgnoreWrapper
|
|
36
|
-
"""Context manager that
|
|
36
|
+
"""Context manager that suppresses specified exceptions and captures metadata.
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
Catches and suppresses the given exception types within a ``with`` block,
|
|
39
|
+
while recording detailed information about any suppressed exception.
|
|
40
40
|
|
|
41
41
|
Args:
|
|
42
42
|
*excs: One or more exception types to suppress.
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
Returns:
|
|
45
|
+
An ``IgnoredError`` instance (via ``as``) that provides access to
|
|
46
|
+
exception metadata after the block executes.
|
|
47
|
+
|
|
48
|
+
Attributes on the returned ``IgnoredError``:
|
|
49
|
+
be_ignore (bool):
|
|
50
|
+
``True`` if an exception was suppressed during the block,
|
|
51
|
+
``False`` otherwise.
|
|
52
|
+
|
|
53
|
+
name (str | None):
|
|
54
|
+
The class name of the suppressed exception
|
|
55
|
+
(e.g. ``'KeyError'``, ``'ValueError'``).
|
|
56
|
+
``None`` if no exception occurred.
|
|
57
|
+
|
|
58
|
+
count (int):
|
|
59
|
+
Number of exceptions suppressed in this context block.
|
|
60
|
+
Typically 1 unless the context manager is reused.
|
|
61
|
+
|
|
62
|
+
exception (Exception | None):
|
|
63
|
+
The original exception instance that was caught and suppressed.
|
|
64
|
+
``None`` if no exception occurred.
|
|
65
|
+
|
|
66
|
+
traceback (str | None):
|
|
67
|
+
Formatted traceback string showing where the suppressed exception
|
|
68
|
+
occurred. Useful for debugging. ``None`` if no exception occurred.
|
|
69
|
+
|
|
70
|
+
Examples:
|
|
71
|
+
|
|
72
|
+
>>> with ignore(KeyError, ValueError) as err:
|
|
73
|
+
... data = {}["missing_key"]
|
|
74
|
+
...
|
|
75
|
+
>>> print(err.be_ignore) # True
|
|
76
|
+
>>> print(err.name) # 'KeyError'
|
|
77
|
+
>>> print(err.count) # 1
|
|
78
|
+
>>> print(err.exception) # KeyError('missing_key')
|
|
79
|
+
>>> print(err.traceback) # Formatted traceback string
|
|
80
|
+
|
|
81
|
+
Note:
|
|
82
|
+
Use `fast_ignore` or `super_fast_ignore`(in `future` submodule) for zero-overhead
|
|
83
|
+
suppression when metadata collection is not needed.
|
|
84
|
+
|
|
85
|
+
See Also:
|
|
86
|
+
- `fast_ignore` — minimal overhead, no metadata
|
|
87
|
+
- `ignore_subclass` — suppress exceptions including subclasses
|
|
88
|
+
- `retry` — automatic retry on exception
|
|
89
|
+
"""
|
|
50
90
|
|
|
51
91
|
|
|
52
92
|
class fast_ignore:
|
|
@@ -59,7 +99,7 @@ class fast_ignore:
|
|
|
59
99
|
Args:
|
|
60
100
|
*excs: One or more exception types to suppress.
|
|
61
101
|
|
|
62
|
-
|
|
102
|
+
Examples:
|
|
63
103
|
>>> with fast_ignore(KeyError):
|
|
64
104
|
... d = {}
|
|
65
105
|
... _ = d["missing"]
|
|
@@ -48,7 +48,7 @@ def _format_record(record: Record, colorize: bool, fmt: str | None) -> str:
|
|
|
48
48
|
)
|
|
49
49
|
else:
|
|
50
50
|
# Default format (loguru-inspired)
|
|
51
|
-
# 2024-01-01 12:00:00.000 | INFO | module
|
|
51
|
+
# 2024-01-01 12:00:00.000 | INFO | module 10 - message
|
|
52
52
|
level_tag = f"{lv.name:<8}"
|
|
53
53
|
location = f"{Path(record.file).stem}:{record.function}:{record.line}"
|
|
54
54
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import namebyauthor as na # type: ignore
|
|
2
|
+
|
|
1
3
|
__author__: str = "Evan Yang"
|
|
2
4
|
__author_email__: str = "quantbit@126.com"
|
|
3
5
|
__copyright__: str = "Copyright 2026 (C) aiwonderland Evan Yang"
|
|
@@ -7,3 +9,8 @@ __description__: str = (
|
|
|
7
9
|
__license__: str = "MIT"
|
|
8
10
|
__title__: str = "errortools"
|
|
9
11
|
__url__: str = "https://github.com/more-abc/errortools"
|
|
12
|
+
|
|
13
|
+
__fullname__ = na.generate_name(__title__, __author__)
|
|
14
|
+
__slug__ = na.generate_slug(__title__, __author__)
|
|
15
|
+
__signature__ = na.generate_signature(__title__, __author__)
|
|
16
|
+
__uid__ = na.generate_id(__title__, __author__)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from functools import partial
|
|
4
4
|
|
|
5
5
|
from .ignore import ignore, ignore_subclass, ignore_warns, fast_ignore, timeout, retry
|
|
6
|
-
from .cache import error_cache
|
|
6
|
+
from .decorator.cache import error_cache
|
|
7
7
|
from .const import (
|
|
8
8
|
LARGE_ERROR_CACHE_SIZE,
|
|
9
9
|
SMALL_ERROR_CACHE_SIZE,
|
|
File without changes
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
from collections.abc import Hashable, Callable
|
|
3
|
+
from typing import Any, Generic, TypeVar, Optional, TypeAlias, NamedTuple
|
|
4
|
+
|
|
5
|
+
_T = TypeVar("_T", bound=Callable[..., Any])
|
|
6
|
+
_Key: TypeAlias = tuple[
|
|
7
|
+
str, tuple[Hashable, ...], tuple[tuple[Hashable, Hashable], ...]
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CacheInfo(NamedTuple):
|
|
12
|
+
"""Cache statistics, compatible with functools.lru_cache CacheInfo."""
|
|
13
|
+
|
|
14
|
+
hits: int
|
|
15
|
+
misses: int
|
|
16
|
+
maxsize: int | None
|
|
17
|
+
currsize: int
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ErrorCacheWrapper(Generic[_T]):
|
|
21
|
+
"""Wrapper class for error-cached functions."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, func: _T, maxsize: int | None = 128) -> None:
|
|
24
|
+
self.__wrapped__ = func # Required for inspect module compatibility
|
|
25
|
+
self._func_name = func.__name__
|
|
26
|
+
|
|
27
|
+
# Validate maxsize
|
|
28
|
+
if maxsize is not None and maxsize < 0:
|
|
29
|
+
raise ValueError(
|
|
30
|
+
f"maxsize must be None or a non-negative integer, got {maxsize!r}"
|
|
31
|
+
)
|
|
32
|
+
self._maxsize = maxsize
|
|
33
|
+
self._cache: OrderedDict[_Key, Exception] = OrderedDict()
|
|
34
|
+
|
|
35
|
+
# Cache statistics
|
|
36
|
+
self._hits = 0
|
|
37
|
+
self._misses = 0
|
|
38
|
+
|
|
39
|
+
def __call__(self, *args: Hashable, **kwargs: Hashable) -> Any:
|
|
40
|
+
"""Execute the wrapped function and cache exceptions if raised (with LRU)."""
|
|
41
|
+
cache_key = self._make_key(args, kwargs)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
result = self.__wrapped__(*args, **kwargs)
|
|
45
|
+
except Exception as exc:
|
|
46
|
+
if cache_key in self._cache:
|
|
47
|
+
self._hits += 1
|
|
48
|
+
else:
|
|
49
|
+
self._misses += 1
|
|
50
|
+
|
|
51
|
+
# Store in cache only if maxsize allows it (maxsize=0 means no caching)
|
|
52
|
+
if self._maxsize != 0:
|
|
53
|
+
self._cache[cache_key] = exc
|
|
54
|
+
# Evict least recently used if maxsize is exceeded
|
|
55
|
+
if self._maxsize is not None and len(self._cache) > self._maxsize:
|
|
56
|
+
self._cache.popitem(last=False) # FIFO = LRU for insert order
|
|
57
|
+
raise
|
|
58
|
+
else:
|
|
59
|
+
# Auto-clear cache for successful calls
|
|
60
|
+
self._cache.pop(cache_key, None)
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
def _make_key(
|
|
64
|
+
self, args: tuple[Hashable, ...], kwargs: dict[str, Hashable]
|
|
65
|
+
) -> _Key:
|
|
66
|
+
"""Generate a unique hashable key."""
|
|
67
|
+
sorted_kwargs = tuple(sorted(kwargs.items()))
|
|
68
|
+
return (self._func_name, args, sorted_kwargs)
|
|
69
|
+
|
|
70
|
+
# ---------------------- Cache control methods (like lru_cache) ----------------------
|
|
71
|
+
def get_cached_error(
|
|
72
|
+
self, *args: Hashable, **kwargs: Hashable
|
|
73
|
+
) -> Optional[Exception]:
|
|
74
|
+
"""Get the cached exception for the given arguments (if exists)."""
|
|
75
|
+
cache_key = self._make_key(args, kwargs)
|
|
76
|
+
if cache_key in self._cache:
|
|
77
|
+
self._hits += 1
|
|
78
|
+
self._cache.move_to_end(cache_key) # Update LRU order (most recent)
|
|
79
|
+
return self._cache[cache_key]
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
def clear_cache(self) -> None:
|
|
83
|
+
"""Clear all cached exceptions and reset statistics."""
|
|
84
|
+
self._cache.clear()
|
|
85
|
+
self._hits = 0
|
|
86
|
+
self._misses = 0
|
|
87
|
+
|
|
88
|
+
def cache_info(self) -> CacheInfo:
|
|
89
|
+
"""Return cache statistics as a named tuple (compatible with lru_cache)."""
|
|
90
|
+
return CacheInfo(
|
|
91
|
+
hits=self._hits,
|
|
92
|
+
misses=self._misses,
|
|
93
|
+
maxsize=self._maxsize,
|
|
94
|
+
currsize=len(self._cache),
|
|
95
|
+
)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import traceback
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import Any, Generic, TypeVar, Optional, TypeAlias
|
|
4
|
+
|
|
5
|
+
_T = TypeVar("_T", bound=Callable[..., Any])
|
|
6
|
+
_ExcType: TypeAlias = type[Exception]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class IgnoredError:
|
|
10
|
+
"""Information holder for ignored exceptions."""
|
|
11
|
+
|
|
12
|
+
__slots__ = (
|
|
13
|
+
"name",
|
|
14
|
+
"be_ignore",
|
|
15
|
+
"count",
|
|
16
|
+
"traceback",
|
|
17
|
+
"exception",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
self.name: Optional[str] = None
|
|
22
|
+
self.be_ignore: bool = False
|
|
23
|
+
self.count: int = 0
|
|
24
|
+
self.traceback: Optional[str] = None
|
|
25
|
+
self.exception: Optional[Exception] = None
|
|
26
|
+
|
|
27
|
+
def reset(self) -> None:
|
|
28
|
+
self.name = None
|
|
29
|
+
self.be_ignore = False
|
|
30
|
+
self.traceback = None
|
|
31
|
+
self.exception = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ErrorIgnoreWrapper(Generic[_T]):
|
|
35
|
+
"""Context manager & decorator to ignore specified exceptions with rich info.
|
|
36
|
+
|
|
37
|
+
Catches and suppresses the given exception types within a ``with`` block
|
|
38
|
+
or when used as a decorator, while recording detailed information about
|
|
39
|
+
any suppressed exception.
|
|
40
|
+
|
|
41
|
+
When used as a context manager, ``__enter__`` returns an ``IgnoredError``
|
|
42
|
+
instance that provides the following attributes after the block executes.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
# Attributes:
|
|
46
|
+
# be_ignore (bool):
|
|
47
|
+
# ``True`` if an exception was suppressed during the block,
|
|
48
|
+
# ``False`` otherwise.
|
|
49
|
+
|
|
50
|
+
# name (str | None):
|
|
51
|
+
# The class name of the suppressed exception
|
|
52
|
+
# (e.g. ``'KeyError'``, ``'ValueError'``).
|
|
53
|
+
# ``None`` if no exception occurred.
|
|
54
|
+
|
|
55
|
+
# count (int):
|
|
56
|
+
# Number of exceptions suppressed in this context block.
|
|
57
|
+
# Typically 1 unless the context manager is reused.
|
|
58
|
+
|
|
59
|
+
# exception (Exception | None):
|
|
60
|
+
# The original exception instance that was caught and suppressed.
|
|
61
|
+
# ``None`` if no exception occurred.
|
|
62
|
+
|
|
63
|
+
# traceback (str | None):
|
|
64
|
+
# Formatted traceback string showing where the suppressed exception
|
|
65
|
+
# occurred. Useful for debugging. ``None`` if no exception occurred.
|
|
66
|
+
|
|
67
|
+
# Example:
|
|
68
|
+
# >>> from errortools import ignore
|
|
69
|
+
# >>> with ignore(KeyError) as err:
|
|
70
|
+
# ... _ = {}["missing"]
|
|
71
|
+
# >>> err.be_ignore
|
|
72
|
+
# True
|
|
73
|
+
# >>> err.name
|
|
74
|
+
# 'KeyError'
|
|
75
|
+
|
|
76
|
+
def __init__(self, *excs: _ExcType) -> None:
|
|
77
|
+
for exc in excs:
|
|
78
|
+
if not isinstance(exc, type) or not issubclass(exc, Exception):
|
|
79
|
+
raise TypeError(f"Expected Exception subclass, got {exc!r}")
|
|
80
|
+
|
|
81
|
+
self._excs = excs
|
|
82
|
+
self._info = IgnoredError()
|
|
83
|
+
|
|
84
|
+
def __enter__(self) -> IgnoredError:
|
|
85
|
+
self._info.reset()
|
|
86
|
+
return self._info
|
|
87
|
+
|
|
88
|
+
def __exit__(
|
|
89
|
+
self,
|
|
90
|
+
exc_type: Optional[_ExcType],
|
|
91
|
+
exc_val: Optional[Exception],
|
|
92
|
+
exc_tb: Optional[Any],
|
|
93
|
+
) -> bool:
|
|
94
|
+
if exc_type is None:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
if exc_type not in self._excs:
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
self._info.name = exc_type.__name__
|
|
101
|
+
self._info.be_ignore = True
|
|
102
|
+
self._info.count += 1
|
|
103
|
+
self._info.traceback = "".join(
|
|
104
|
+
traceback.format_exception(exc_type, exc_val, exc_tb)
|
|
105
|
+
)
|
|
106
|
+
self._info.exception = exc_val
|
|
107
|
+
return True
|
|
108
|
+
|
|
109
|
+
def __call__(self, func: _T) -> _T:
|
|
110
|
+
def wrapped(*args: Any, **kwargs: Any) -> Any:
|
|
111
|
+
with self:
|
|
112
|
+
return func(*args, **kwargs)
|
|
113
|
+
|
|
114
|
+
wrapped.__wrapped__ = func # type: ignore
|
|
115
|
+
return wrapped # type: ignore
|
|
@@ -12,7 +12,7 @@ from _errortools.ignore import (
|
|
|
12
12
|
retry,
|
|
13
13
|
)
|
|
14
14
|
from _errortools.classes.group import BaseGroup, GroupErrors
|
|
15
|
-
from _errortools.cache import error_cache
|
|
15
|
+
from _errortools.decorator.cache import error_cache
|
|
16
16
|
from _errortools.decorator.deprecated import deprecated
|
|
17
17
|
from _errortools.classes.errorcodes import (
|
|
18
18
|
PureBaseException,
|
|
@@ -77,6 +77,10 @@ from _errortools.metadata import (
|
|
|
77
77
|
__license__,
|
|
78
78
|
__title__,
|
|
79
79
|
__url__,
|
|
80
|
+
__fullname__,
|
|
81
|
+
__signature__,
|
|
82
|
+
__slug__,
|
|
83
|
+
__uid__,
|
|
80
84
|
)
|
|
81
85
|
import _errortools.future as future
|
|
82
86
|
import _errortools.logging as logging
|
|
@@ -151,6 +155,10 @@ __all__ = [
|
|
|
151
155
|
"__license__",
|
|
152
156
|
"__title__",
|
|
153
157
|
"__url__",
|
|
158
|
+
"__fullname__",
|
|
159
|
+
"__signature__",
|
|
160
|
+
"__slug__",
|
|
161
|
+
"__uid__",
|
|
154
162
|
# submodules
|
|
155
163
|
"future",
|
|
156
164
|
"logging",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: errortools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 2.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
|
|
@@ -19,6 +19,7 @@ Requires-Python: >=3.10
|
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE.txt
|
|
21
21
|
License-File: AUTHORS.txt
|
|
22
|
+
Requires-Dist: namebyauthor==1.0.0
|
|
22
23
|
Dynamic: author
|
|
23
24
|
Dynamic: author-email
|
|
24
25
|
Dynamic: classifier
|
|
@@ -27,6 +28,7 @@ Dynamic: description-content-type
|
|
|
27
28
|
Dynamic: home-page
|
|
28
29
|
Dynamic: license
|
|
29
30
|
Dynamic: license-file
|
|
31
|
+
Dynamic: requires-dist
|
|
30
32
|
Dynamic: requires-python
|
|
31
33
|
Dynamic: summary
|
|
32
34
|
|
|
@@ -49,7 +51,7 @@ pip install errortools
|
|
|
49
51
|
|
|
50
52
|
---
|
|
51
53
|
|
|
52
|
-
##
|
|
54
|
+
## Examples
|
|
53
55
|
|
|
54
56
|
```python
|
|
55
57
|
import warnings
|
|
@@ -70,8 +72,22 @@ assert err.name == "KeyError" # exception class name
|
|
|
70
72
|
assert err.count == 1 # how many times suppressed
|
|
71
73
|
assert err.exception # the original KeyError instance
|
|
72
74
|
assert err.traceback # full formatted traceback string
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Attributes on the returned object:**
|
|
78
|
+
|
|
79
|
+
| Attribute | Type | Description |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `be_ignore` | `bool` | `True` if an exception was suppressed |
|
|
82
|
+
| `name` | `str \| None` | Class name of the suppressed exception |
|
|
83
|
+
| `count` | `int` | Number of suppressed exceptions |
|
|
84
|
+
| `exception` | `Exception \| None` | The original exception instance |
|
|
85
|
+
| `traceback` | `str \| None` | Formatted traceback string for debugging |
|
|
86
|
+
|
|
87
|
+
## Examples
|
|
73
88
|
|
|
74
|
-
|
|
89
|
+
```python
|
|
90
|
+
# ── ignore as a decorator ──
|
|
75
91
|
@ignore(ValueError, TypeError)
|
|
76
92
|
def parse(x: str) -> int:
|
|
77
93
|
return int(x)
|
|
@@ -3,7 +3,7 @@ LICENSE.txt
|
|
|
3
3
|
README.md
|
|
4
4
|
setup.py
|
|
5
5
|
_errortools/__init__.py
|
|
6
|
-
_errortools/
|
|
6
|
+
_errortools/_cli.py
|
|
7
7
|
_errortools/cli.py
|
|
8
8
|
_errortools/const.py
|
|
9
9
|
_errortools/future.py
|
|
@@ -20,6 +20,7 @@ _errortools/classes/errorcodes.py
|
|
|
20
20
|
_errortools/classes/group.py
|
|
21
21
|
_errortools/classes/warn.py
|
|
22
22
|
_errortools/decorator/__init__.py
|
|
23
|
+
_errortools/decorator/cache.py
|
|
23
24
|
_errortools/decorator/deprecated.py
|
|
24
25
|
_errortools/logging/__init__.py
|
|
25
26
|
_errortools/logging/base.py
|
|
@@ -32,18 +33,23 @@ _errortools/methods/errorattr.py
|
|
|
32
33
|
_errortools/methods/errordelattr.py
|
|
33
34
|
_errortools/methods/errorhasattr.py
|
|
34
35
|
_errortools/methods/errorsetattr.py
|
|
36
|
+
_errortools/wrappers/__init__.py
|
|
37
|
+
_errortools/wrappers/cache.py
|
|
38
|
+
_errortools/wrappers/ignore.py
|
|
35
39
|
errortools/__init__.py
|
|
36
40
|
errortools/__main__.py
|
|
37
41
|
errortools.egg-info/PKG-INFO
|
|
38
42
|
errortools.egg-info/SOURCES.txt
|
|
39
43
|
errortools.egg-info/dependency_links.txt
|
|
40
44
|
errortools.egg-info/entry_points.txt
|
|
45
|
+
errortools.egg-info/requires.txt
|
|
41
46
|
errortools.egg-info/top_level.txt
|
|
42
47
|
tests/__init__.py
|
|
43
48
|
tests/conftest.py
|
|
44
49
|
tests/run_tests.py
|
|
45
50
|
tests/test_abc.py
|
|
46
51
|
tests/test_cache.py
|
|
52
|
+
tests/test_const.py
|
|
47
53
|
tests/test_decorator.py
|
|
48
54
|
tests/test_descriptor.py
|
|
49
55
|
tests/test_errorcodes.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
namebyauthor==1.0.0
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="errortools",
|
|
5
|
-
version="1.
|
|
5
|
+
version="2.1.0",
|
|
6
6
|
description="errortools - a toolset for working with Python exceptions and warnings and logging.",
|
|
7
7
|
long_description=open("README.md", encoding="utf-8").read(),
|
|
8
8
|
long_description_content_type="text/markdown",
|
|
@@ -25,10 +25,11 @@ setup(
|
|
|
25
25
|
package_data={"errortools": ["py.typed"]},
|
|
26
26
|
include_package_data=True,
|
|
27
27
|
python_requires=">=3.10",
|
|
28
|
-
install_requires=[],
|
|
28
|
+
install_requires=["namebyauthor==1.0.0"],
|
|
29
29
|
entry_points={
|
|
30
30
|
"console_scripts": [
|
|
31
|
-
"errortools = _errortools.cli:main",
|
|
31
|
+
"python -m errortools = _errortools.cli:main",
|
|
32
|
+
"logger = _errortools.cli:log_main",
|
|
32
33
|
]
|
|
33
34
|
},
|
|
34
35
|
)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
"""Tests for `errortools` module. Using pytest."""
|
|
2
|
-
|
|
3
|
-
__version__ = "1.
|
|
4
|
-
|
|
5
|
-
try:
|
|
6
|
-
import pytest
|
|
7
|
-
except ImportError:
|
|
8
|
-
# A constant used to determine whether the pytest module exists.
|
|
9
|
-
# If it exists, tests can run normally; otherwise, a warning will be printed.
|
|
10
|
-
HAS_PYTEST = False
|
|
11
|
-
else:
|
|
12
|
-
HAS_PYTEST = True
|
|
1
|
+
"""Tests for `errortools` module. Using pytest."""
|
|
2
|
+
|
|
3
|
+
__version__ = "1.5"
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
import pytest
|
|
7
|
+
except ImportError:
|
|
8
|
+
# A constant used to determine whether the pytest module exists.
|
|
9
|
+
# If it exists, tests can run normally; otherwise, a warning will be printed.
|
|
10
|
+
HAS_PYTEST = False
|
|
11
|
+
else:
|
|
12
|
+
HAS_PYTEST = True
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
|
-
from _errortools.cache import error_cache
|
|
5
|
+
from _errortools.decorator.cache import error_cache
|
|
6
6
|
from . import HAS_PYTEST
|
|
7
7
|
|
|
8
8
|
if not HAS_PYTEST:
|
|
9
9
|
print("pytest is required to run these tests, skip run test_cache.py")
|
|
10
|
+
exit(0)
|
|
10
11
|
|
|
11
12
|
# =============================================================================
|
|
12
13
|
# error_cache — basic decoration patterns
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Tests for _errortools/const.py — constants."""
|
|
2
|
+
|
|
3
|
+
from _errortools.const import (
|
|
4
|
+
LARGE_ERROR_CACHE_SIZE,
|
|
5
|
+
SMALL_ERROR_CACHE_SIZE,
|
|
6
|
+
DEFAULT_ERROR_CACHE_SIZE,
|
|
7
|
+
UNLIMITED_ERROR_CACHE,
|
|
8
|
+
)
|
|
9
|
+
from . import HAS_PYTEST
|
|
10
|
+
|
|
11
|
+
if not HAS_PYTEST:
|
|
12
|
+
print("pytest is required to run these tests, skip run test_const.py")
|
|
13
|
+
exit(0)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_constant_types():
|
|
17
|
+
assert isinstance(LARGE_ERROR_CACHE_SIZE, int)
|
|
18
|
+
assert isinstance(SMALL_ERROR_CACHE_SIZE, int)
|
|
19
|
+
assert isinstance(DEFAULT_ERROR_CACHE_SIZE, int)
|
|
20
|
+
# ``UNLIMITED_ERROR_CACHE`` was NoneType
|
|
21
|
+
assert not isinstance(UNLIMITED_ERROR_CACHE, int)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_default_cache_is_valid():
|
|
25
|
+
assert DEFAULT_ERROR_CACHE_SIZE > 0
|
|
26
|
+
assert SMALL_ERROR_CACHE_SIZE <= DEFAULT_ERROR_CACHE_SIZE <= LARGE_ERROR_CACHE_SIZE
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_cache_constant_value():
|
|
30
|
+
assert DEFAULT_ERROR_CACHE_SIZE == 128
|
|
31
|
+
assert SMALL_ERROR_CACHE_SIZE == 64
|
|
32
|
+
assert LARGE_ERROR_CACHE_SIZE == 1024
|
|
33
|
+
assert UNLIMITED_ERROR_CACHE == None
|
|
@@ -8,6 +8,8 @@ from . import HAS_PYTEST
|
|
|
8
8
|
|
|
9
9
|
if not HAS_PYTEST:
|
|
10
10
|
print("pytest is required to run these tests, skip run test_descriptor.py")
|
|
11
|
+
exit(0)
|
|
12
|
+
|
|
11
13
|
# =============================================================================
|
|
12
14
|
# ErrorMsg Descriptor
|
|
13
15
|
# =============================================================================
|
|
@@ -17,6 +17,8 @@ from . import HAS_PYTEST
|
|
|
17
17
|
|
|
18
18
|
if not HAS_PYTEST:
|
|
19
19
|
print("pytest is required to run these tests, skip run test_errorcodes.py")
|
|
20
|
+
exit(0)
|
|
21
|
+
|
|
20
22
|
# =============================================================================
|
|
21
23
|
# PureBaseException
|
|
22
24
|
# =============================================================================
|
|
@@ -7,6 +7,8 @@ from . import HAS_PYTEST
|
|
|
7
7
|
|
|
8
8
|
if not HAS_PYTEST:
|
|
9
9
|
print("pytest is required to run these tests, skip run test_groups.py")
|
|
10
|
+
exit(0)
|
|
11
|
+
|
|
10
12
|
# =============================================================================
|
|
11
13
|
# GroupErrors — collect / raise_group / clear
|
|
12
14
|
# =============================================================================
|
|
@@ -17,6 +17,8 @@ from . import HAS_PYTEST
|
|
|
17
17
|
|
|
18
18
|
if not HAS_PYTEST:
|
|
19
19
|
print("pytest is required to run these tests, skip run test_ignore.py")
|
|
20
|
+
exit(0)
|
|
21
|
+
|
|
20
22
|
# =============================================================================
|
|
21
23
|
# ignore() (ErrorIgnoreWrapper)
|
|
22
24
|
# =============================================================================
|
|
@@ -23,6 +23,8 @@ from . import HAS_PYTEST
|
|
|
23
23
|
|
|
24
24
|
if not HAS_PYTEST:
|
|
25
25
|
print("pytest is required to run these tests, skip run test_logging.py")
|
|
26
|
+
exit(0)
|
|
27
|
+
|
|
26
28
|
# ---------------------------------------------------------------------------
|
|
27
29
|
# Helpers
|
|
28
30
|
# ---------------------------------------------------------------------------
|
|
@@ -22,6 +22,8 @@ from . import HAS_PYTEST
|
|
|
22
22
|
|
|
23
23
|
if not HAS_PYTEST:
|
|
24
24
|
print("pytest is required to run these tests, skip run test_mixins.py")
|
|
25
|
+
exit(0)
|
|
26
|
+
|
|
25
27
|
# =============================================================================
|
|
26
28
|
# ErrorAttrMixin
|
|
27
29
|
# =============================================================================
|
|
@@ -7,6 +7,7 @@ from . import HAS_PYTEST
|
|
|
7
7
|
|
|
8
8
|
if not HAS_PYTEST:
|
|
9
9
|
print("pytest is required to run these teststests/test_raises.py")
|
|
10
|
+
exit(0)
|
|
10
11
|
# =============================================================================
|
|
11
12
|
# raises()
|
|
12
13
|
# =============================================================================
|
|
@@ -14,6 +14,8 @@ from . import HAS_PYTEST
|
|
|
14
14
|
|
|
15
15
|
if not HAS_PYTEST:
|
|
16
16
|
print("pytest is required to run these teststests/test_warnings.py")
|
|
17
|
+
exit(0)
|
|
18
|
+
|
|
17
19
|
# =============================================================================
|
|
18
20
|
# BaseWarning — basic behaviour
|
|
19
21
|
# =============================================================================
|
errortools-1.3.0/AUTHORS.txt
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|