DataExcept 0.1.0__tar.gz → 0.2.1__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 (35) hide show
  1. dataexcept-0.2.1/CHANGELOG.md +132 -0
  2. {dataexcept-0.1.0 → dataexcept-0.2.1}/CITATION.cff +2 -2
  3. {dataexcept-0.1.0 → dataexcept-0.2.1}/PKG-INFO +23 -21
  4. {dataexcept-0.1.0 → dataexcept-0.2.1}/README.md +20 -17
  5. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/__init__.py +18 -5
  6. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/__main__.py +13 -2
  7. dataexcept-0.2.1/dataexcept/_deprecation.py +38 -0
  8. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/datascience_exceptions/__init__.py +13 -2
  9. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/datascience_exceptions/operations.py +2 -2
  10. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/exceptions/__init__.py +18 -4
  11. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/exceptions/external.py +2 -2
  12. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/job_exceptions.py +18 -6
  13. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/logging_helpers.py +7 -0
  14. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/pipeline_exceptions.py +13 -2
  15. {dataexcept-0.1.0 → dataexcept-0.2.1}/pyproject.toml +40 -23
  16. dataexcept-0.1.0/CHANGELOG.md +0 -42
  17. {dataexcept-0.1.0 → dataexcept-0.2.1}/LICENSE +0 -0
  18. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/database_exceptions.py +0 -0
  19. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/dataengineering_exceptions.py +0 -0
  20. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/datascience_exceptions/base.py +0 -0
  21. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/datascience_exceptions/ingestion.py +0 -0
  22. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/datascience_exceptions/training.py +0 -0
  23. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/exceptions/authentication.py +0 -0
  24. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/exceptions/base.py +0 -0
  25. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/exceptions/configuration.py +0 -0
  26. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/exceptions/lifecycle.py +0 -0
  27. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/exceptions/notification.py +0 -0
  28. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/exceptions/parsing.py +0 -0
  29. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/exceptions/scheduling.py +0 -0
  30. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/exceptions/validation.py +0 -0
  31. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/io_exceptions.py +0 -0
  32. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/network_exceptions.py +0 -0
  33. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/pandas_exceptions.py +0 -0
  34. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/py.typed +0 -0
  35. {dataexcept-0.1.0 → dataexcept-0.2.1}/dataexcept/security_exceptions.py +0 -0
@@ -0,0 +1,132 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.2.1] - 2026-08-24
11
+
12
+ Documentation and CLI fixes. 0.2.0's README is what PyPI renders as the
13
+ project description, and its quick-start example did not run.
14
+
15
+ ### Fixed
16
+
17
+ - `python -m dataexcept --version` reported `__main__.py` as the program name
18
+ instead of `dataexcept`, because argparse defaults `prog` to `sys.argv[0]`.
19
+ - `dataexcept list` imported the deprecated `job_exceptions` shim to build its
20
+ output, so it emitted a `DeprecationWarning` at anyone who merely wanted to
21
+ see what the package offers, and it advertised `ConnectionError` and
22
+ `TimeoutError` alongside their replacements. Deprecated modules are now
23
+ skipped; the listing is 98 names, matching the classes the package defines.
24
+ - README's quick-start example began `from dataexcept import ValidationError,
25
+ ModelTrainingError`, which raises `ImportError` — `ModelTrainingError` is in
26
+ `dataexcept.datascience_exceptions` and is not re-exported at the top level.
27
+ - `docs/advanced_usage.md` taught `from dataexcept.job_exceptions import
28
+ JobError`, the deprecated path.
29
+ - README's comparison table showed exception messages without the
30
+ `[ClassName]` prefix the classes actually emit, its sample `dataexcept list`
31
+ output did not match the real alphabetical listing, its exception count and
32
+ CLI version were stale, and its end-to-end example used `np.log` without
33
+ importing numpy.
34
+
35
+ ### Added
36
+
37
+ - Tests that read the documentation: every `from dataexcept... import ...` in
38
+ README and `docs/` must resolve, no example may import a deprecated module,
39
+ and the README's exception count must match the package. Checked that these
40
+ fail when the original defects are reintroduced.
41
+
42
+ ## [0.2.0] - 2026-08-24
43
+
44
+ ### Changed
45
+
46
+ - **`ConnectionError` is now `ServiceConnectionError`, and `TimeoutError` is
47
+ now `OperationTimeoutError`.** The old names shadowed Python builtins without
48
+ inheriting from them, so after `from dataexcept import ConnectionError` an
49
+ `except ConnectionError:` in that module silently stopped catching real
50
+ socket failures.
51
+ - **`datascience_exceptions.SerializationError` is now
52
+ `ModelSerializationError`**, and **`pipeline_exceptions.FeatureEngineeringError`
53
+ is now `FeaturePreprocessingError`.** Each of those names previously referred
54
+ to two different classes in different modules, so catching one silently
55
+ missed the other.
56
+ - Project metadata moved from Poetry's `[tool.poetry]` table to the standard
57
+ PEP 621 `[project]` table, clearing every `poetry check` deprecation. The
58
+ license is now an SPDX expression (PEP 639), so the built metadata carries
59
+ `License-Expression: MIT` and the redundant license classifier is gone.
60
+ Wheel and sdist contents are otherwise unchanged.
61
+ - `dataexcept.job_exceptions` now names its removal version, 1.0.0, in both the
62
+ warning and the module docstring.
63
+
64
+ ### Deprecated
65
+
66
+ - `ConnectionError`, `TimeoutError`, `datascience_exceptions.SerializationError`
67
+ and `pipeline_exceptions.FeatureEngineeringError`. All four still resolve, to
68
+ the **same class object** as their replacement, so existing `except` clauses
69
+ keep working; touching one emits a `DeprecationWarning` naming the
70
+ replacement and 1.0.0 as the removal. Importing the package does not warn.
71
+ Find remaining uses with `python -W error::DeprecationWarning -m pytest`.
72
+
73
+ ### Added
74
+
75
+ - A published [API stability policy](https://diogoribeiro7.github.io/DataExcept/stability/)
76
+ stating what is public, what each kind of change costs in version terms, the
77
+ deprecation process, and how to migrate off the 0.2.0 renames.
78
+ - Security scanning: CodeQL, and pip-audit against the runtime and
79
+ documentation dependency sets, on push, pull request and weekly — advisories
80
+ are published against code that has not changed. Pull requests also get a
81
+ dependency review failing at moderate severity.
82
+ - Complexity and security linting via ruff's mccabe (`C90`) and flake8-bandit
83
+ (`S`) rule sets, so neither needs a separate tool. Complexity is capped at 8;
84
+ the highest score in the package is 6.
85
+ - A regression guard that fails if any exported name ever shadows a builtin
86
+ again.
87
+ - `__all__` on `dataexcept.logging_helpers`, the one public module without one.
88
+
89
+ ### Fixed
90
+
91
+ - `dataexcept.__version__` and `tests/test_version.py` read the version out of
92
+ `pyproject.toml` when the package is not installed, and were still looking in
93
+ `[tool.poetry]`. They now read `[project]`.
94
+ - `examples/example_usage.py` raised `TimeoutError` with keyword arguments the
95
+ builtin does not accept — a live instance of the shadowing hazard.
96
+
97
+
98
+ ## [0.1.0] - 2026-08-24
99
+
100
+ First public release.
101
+
102
+ ### Added
103
+
104
+ - Hierarchical exception classes for data science, machine learning and data
105
+ engineering workflows. Catch a specific failure or a broad category, and get
106
+ a message that names the value that caused it rather than a bare
107
+ `ValueError`.
108
+ - Domain modules for validation, configuration, authentication, parsing,
109
+ serialization, scheduling, notification, lifecycle and external-service
110
+ errors, plus dedicated pandas, database, network, I/O, pipeline and security
111
+ exception groups.
112
+ - `dataexcept.logging_helpers` with `log_exception`, `log_and_raise` and
113
+ `log_then_raise`, for logging exceptions with structured context and
114
+ re-raising without losing the traceback.
115
+ - A `dataexcept` command-line entry point that lists the exported exception
116
+ classes and reports the installed version.
117
+ - A `py.typed` marker, backed by a mypy-clean codebase that CI enforces, so
118
+ downstream type checkers get annotations that are actually correct.
119
+ - Documentation at
120
+ [diogoribeiro7.github.io/DataExcept](https://diogoribeiro7.github.io/DataExcept/),
121
+ including an API reference generated from the docstrings.
122
+
123
+ ### Notes
124
+
125
+ - Supports Python 3.10 through 3.13.
126
+ - Published to PyPI via OIDC trusted publishing; no long-lived API token is
127
+ involved in a release.
128
+
129
+ [Unreleased]: https://github.com/DiogoRibeiro7/DataExcept/compare/v0.2.1...HEAD
130
+ [0.2.1]: https://github.com/DiogoRibeiro7/DataExcept/compare/v0.2.0...v0.2.1
131
+ [0.2.0]: https://github.com/DiogoRibeiro7/DataExcept/compare/v0.1.0...v0.2.0
132
+ [0.1.0]: https://github.com/DiogoRibeiro7/DataExcept/releases/tag/v0.1.0
@@ -1,7 +1,7 @@
1
1
  cff-version: 1.2.0
2
2
  message: "If you use this software, please cite it using the following metadata."
3
3
  title: "DataExcept"
4
- version: "0.1.0"
4
+ version: "0.2.1"
5
5
  authors:
6
6
  - family-names: "Ribeiro"
7
7
  given-names: "Diogo"
@@ -11,4 +11,4 @@ authors:
11
11
  type: software
12
12
  url: "https://github.com/DiogoRibeiro7/DataExcept"
13
13
  repository-code: "https://github.com/DiogoRibeiro7/DataExcept"
14
- date-released: "2025-06-16"
14
+ date-released: "2026-08-24"
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DataExcept
3
- Version: 0.1.0
3
+ Version: 0.2.1
4
4
  Summary: A Python package providing structured, easily-extendable custom exception types.
5
- License: MIT
5
+ License-Expression: MIT
6
6
  License-File: LICENSE
7
7
  Keywords: exceptions,errors,logging
8
8
  Author: Diogo Ribeiro
@@ -10,13 +10,12 @@ Author-email: dfr@esmad.ipp.pt
10
10
  Maintainer: Diogo Ribeiro
11
11
  Maintainer-email: diogo.debastos.ribeiro@gmail.com
12
12
  Requires-Python: >=3.10,<3.14
13
- Classifier: License :: OSI Approved :: MIT License
14
13
  Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
- Classifier: Programming Language :: Python :: 3 :: Only
20
19
  Requires-Dist: tomli ; python_version < "3.11"
21
20
  Project-URL: Changelog, https://github.com/DiogoRibeiro7/DataExcept/releases
22
21
  Project-URL: Documentation, https://diogoribeiro7.github.io/DataExcept/
@@ -35,15 +34,15 @@ Description-Content-Type: text/markdown
35
34
 
36
35
  ❌ Without DataExcept | ✅ With DataExcept
37
36
  ------------------------------- | -------------------------------------------------------------------------------------
38
- `ValueError: Invalid value` | `DataValidationError: Invalid value for 'age': -1`
39
- `RuntimeError: Training failed` | `ConvergenceError: Model 'RandomForest' failed to converge after 100 iterations`
40
- `Exception: Prediction error` | `ModelInferenceError: Inference failed for model 'CNN': CUDA out of memory`
41
- `KeyError: column not found` | `MissingColumnError: Missing required column 'customer_id' in DataFrame 'sales_data'`
37
+ `ValueError: Invalid value` | `DataValidationError: [DataValidationError:age] Invalid value for 'age': -1`
38
+ `RuntimeError: Training failed` | `ConvergenceError: [ConvergenceError] Model 'RandomForest' failed to converge after 100 iterations`
39
+ `Exception: Prediction error` | `ModelInferenceError: [ModelInferenceError:CNN] Inference failed for model 'CNN': CUDA out of memory`
40
+ `KeyError: column not found` | `MissingColumnError: [MissingColumnError] Missing required column 'customer_id' in DataFrame 'sales_data'`
42
41
 
43
42
  ## 🎯 Key Features
44
43
 
45
44
  - **🏗️ Hierarchical Structure**: Catch specific errors or broad categories
46
- - **📊 Data Science Focused**: 40+ exceptions covering ML pipelines, feature engineering, model training
45
+ - **📊 Data Science Focused**: 98 exception classes covering ML pipelines, feature engineering, model training
47
46
  - **🔧 Production Ready**: Comprehensive logging helpers and error context
48
47
  - **📚 Academic Quality**: Proper documentation, type hints, and citation support
49
48
  - **🐍 Python 3.10+**: Modern Python with full type safety
@@ -68,8 +67,8 @@ poetry install
68
67
  ### Basic Usage
69
68
 
70
69
  ```python
71
- from dataexcept import ValidationError, ModelTrainingError
72
- from dataexcept.datascience_exceptions import DataLoadingError
70
+ from dataexcept import ValidationError
71
+ from dataexcept.datascience_exceptions import DataLoadingError, ModelTrainingError
73
72
  import pandas as pd
74
73
 
75
74
  # Data validation with context
@@ -215,17 +214,18 @@ except Exception as exc:
215
214
  ### Command Line Interface
216
215
 
217
216
  ```bash
218
- # List all available exception classes
217
+ # List every exception class the package exports (98 of them, alphabetically)
219
218
  $ dataexcept list
220
- JobError
221
- ValidationError
222
- DataScienceError
223
- ModelTrainingError
224
- ... (40+ more)
219
+ ApiError
220
+ AuthenticationError
221
+ AuthorizationError
222
+ BatchProcessingError
223
+ BiasDetectionError
224
+ ...
225
225
 
226
226
  # Check version
227
227
  $ dataexcept --version
228
- dataexcept 0.1.0
228
+ dataexcept 0.2.1
229
229
  ```
230
230
 
231
231
  ## 🎯 Use Cases
@@ -256,6 +256,7 @@ dataexcept 0.1.0
256
256
  """
257
257
  Complete ML pipeline with DataExcept error handling
258
258
  """
259
+ import numpy as np
259
260
  import pandas as pd
260
261
  from sklearn.ensemble import RandomForestClassifier
261
262
  from dataexcept import ValidationError
@@ -360,6 +361,7 @@ through [SECURITY.md](SECURITY.md), not the public issue tracker.
360
361
 
361
362
  - **Full Documentation**: [diogoribeiro7.github.io/DataExcept](https://diogoribeiro7.github.io/DataExcept/)
362
363
  - **API Reference**: [API Docs](https://diogoribeiro7.github.io/DataExcept/api/)
364
+ - **API Stability**: [What is public and what may change](https://diogoribeiro7.github.io/DataExcept/stability/)
363
365
  - **Advanced Usage**: [Advanced Guide](https://diogoribeiro7.github.io/DataExcept/advanced_usage/)
364
366
  - **CLI Reference**: [CLI Guide](https://diogoribeiro7.github.io/DataExcept/cli/)
365
367
  - **Changelog**: [CHANGELOG.md](CHANGELOG.md)
@@ -369,12 +371,12 @@ through [SECURITY.md](SECURITY.md), not the public issue tracker.
369
371
  If you use DataExcept in your research, please cite it:
370
372
 
371
373
  ```bibtex
372
- @software{ribeiro_dataexcept_2025,
374
+ @software{ribeiro_dataexcept_2026,
373
375
  author = {Ribeiro, Diogo},
374
376
  title = {DataExcept: Structured Exception Handling for Data Science},
375
377
  url = {https://github.com/DiogoRibeiro7/DataExcept},
376
- version = {0.1.0},
377
- year = {2025},
378
+ version = {0.2.1},
379
+ year = {2026},
378
380
  publisher = {GitHub}
379
381
  }
380
382
  ```
@@ -8,15 +8,15 @@
8
8
 
9
9
  ❌ Without DataExcept | ✅ With DataExcept
10
10
  ------------------------------- | -------------------------------------------------------------------------------------
11
- `ValueError: Invalid value` | `DataValidationError: Invalid value for 'age': -1`
12
- `RuntimeError: Training failed` | `ConvergenceError: Model 'RandomForest' failed to converge after 100 iterations`
13
- `Exception: Prediction error` | `ModelInferenceError: Inference failed for model 'CNN': CUDA out of memory`
14
- `KeyError: column not found` | `MissingColumnError: Missing required column 'customer_id' in DataFrame 'sales_data'`
11
+ `ValueError: Invalid value` | `DataValidationError: [DataValidationError:age] Invalid value for 'age': -1`
12
+ `RuntimeError: Training failed` | `ConvergenceError: [ConvergenceError] Model 'RandomForest' failed to converge after 100 iterations`
13
+ `Exception: Prediction error` | `ModelInferenceError: [ModelInferenceError:CNN] Inference failed for model 'CNN': CUDA out of memory`
14
+ `KeyError: column not found` | `MissingColumnError: [MissingColumnError] Missing required column 'customer_id' in DataFrame 'sales_data'`
15
15
 
16
16
  ## 🎯 Key Features
17
17
 
18
18
  - **🏗️ Hierarchical Structure**: Catch specific errors or broad categories
19
- - **📊 Data Science Focused**: 40+ exceptions covering ML pipelines, feature engineering, model training
19
+ - **📊 Data Science Focused**: 98 exception classes covering ML pipelines, feature engineering, model training
20
20
  - **🔧 Production Ready**: Comprehensive logging helpers and error context
21
21
  - **📚 Academic Quality**: Proper documentation, type hints, and citation support
22
22
  - **🐍 Python 3.10+**: Modern Python with full type safety
@@ -41,8 +41,8 @@ poetry install
41
41
  ### Basic Usage
42
42
 
43
43
  ```python
44
- from dataexcept import ValidationError, ModelTrainingError
45
- from dataexcept.datascience_exceptions import DataLoadingError
44
+ from dataexcept import ValidationError
45
+ from dataexcept.datascience_exceptions import DataLoadingError, ModelTrainingError
46
46
  import pandas as pd
47
47
 
48
48
  # Data validation with context
@@ -188,17 +188,18 @@ except Exception as exc:
188
188
  ### Command Line Interface
189
189
 
190
190
  ```bash
191
- # List all available exception classes
191
+ # List every exception class the package exports (98 of them, alphabetically)
192
192
  $ dataexcept list
193
- JobError
194
- ValidationError
195
- DataScienceError
196
- ModelTrainingError
197
- ... (40+ more)
193
+ ApiError
194
+ AuthenticationError
195
+ AuthorizationError
196
+ BatchProcessingError
197
+ BiasDetectionError
198
+ ...
198
199
 
199
200
  # Check version
200
201
  $ dataexcept --version
201
- dataexcept 0.1.0
202
+ dataexcept 0.2.1
202
203
  ```
203
204
 
204
205
  ## 🎯 Use Cases
@@ -229,6 +230,7 @@ dataexcept 0.1.0
229
230
  """
230
231
  Complete ML pipeline with DataExcept error handling
231
232
  """
233
+ import numpy as np
232
234
  import pandas as pd
233
235
  from sklearn.ensemble import RandomForestClassifier
234
236
  from dataexcept import ValidationError
@@ -333,6 +335,7 @@ through [SECURITY.md](SECURITY.md), not the public issue tracker.
333
335
 
334
336
  - **Full Documentation**: [diogoribeiro7.github.io/DataExcept](https://diogoribeiro7.github.io/DataExcept/)
335
337
  - **API Reference**: [API Docs](https://diogoribeiro7.github.io/DataExcept/api/)
338
+ - **API Stability**: [What is public and what may change](https://diogoribeiro7.github.io/DataExcept/stability/)
336
339
  - **Advanced Usage**: [Advanced Guide](https://diogoribeiro7.github.io/DataExcept/advanced_usage/)
337
340
  - **CLI Reference**: [CLI Guide](https://diogoribeiro7.github.io/DataExcept/cli/)
338
341
  - **Changelog**: [CHANGELOG.md](CHANGELOG.md)
@@ -342,12 +345,12 @@ through [SECURITY.md](SECURITY.md), not the public issue tracker.
342
345
  If you use DataExcept in your research, please cite it:
343
346
 
344
347
  ```bibtex
345
- @software{ribeiro_dataexcept_2025,
348
+ @software{ribeiro_dataexcept_2026,
346
349
  author = {Ribeiro, Diogo},
347
350
  title = {DataExcept: Structured Exception Handling for Data Science},
348
351
  url = {https://github.com/DiogoRibeiro7/DataExcept},
349
- version = {0.1.0},
350
- year = {2025},
352
+ version = {0.2.1},
353
+ year = {2026},
351
354
  publisher = {GitHub}
352
355
  }
353
356
  ```
@@ -28,11 +28,11 @@ from . import (
28
28
  pipeline_exceptions,
29
29
  security_exceptions,
30
30
  )
31
+ from ._deprecation import resolve_deprecated
31
32
  from .exceptions import ( # noqa: F401
32
33
  AuthenticationError,
33
34
  AuthorizationError,
34
35
  ConfigurationError,
35
- ConnectionError,
36
36
  CronExpressionError,
37
37
  DependencyError,
38
38
  DeserializationError,
@@ -40,22 +40,27 @@ from .exceptions import ( # noqa: F401
40
40
  JobCancellationError,
41
41
  JobError,
42
42
  NotificationError,
43
+ OperationTimeoutError,
43
44
  ParsingError,
44
45
  ResourceNotFoundError,
45
46
  ScheduleConflictError,
46
47
  SerializationError,
47
- TimeoutError,
48
+ ServiceConnectionError,
48
49
  ValidationError,
49
50
  WebhookError,
50
51
  )
51
- from .logging_helpers import log_and_raise, log_exception, log_then_raise
52
+ from .logging_helpers import (
53
+ log_and_raise,
54
+ log_exception,
55
+ log_then_raise,
56
+ )
52
57
 
53
58
  try:
54
59
  __version__ = metadata.version("DataExcept")
55
60
  except metadata.PackageNotFoundError: # pragma: no cover - fallback during dev
56
61
  _root = Path(__file__).resolve().parents[1]
57
62
  with open(_root / "pyproject.toml", "rb") as _f:
58
- __version__ = tomllib.load(_f)["tool"]["poetry"]["version"]
63
+ __version__ = tomllib.load(_f)["project"]["version"]
59
64
 
60
65
  __all__ = list(_exceptions.__all__) + [
61
66
  "datascience_exceptions",
@@ -73,9 +78,17 @@ __all__ = list(_exceptions.__all__) + [
73
78
  ]
74
79
 
75
80
 
81
+ #: Renamed in 0.2.0 because they shadowed Python builtins without inheriting
82
+ #: from them. Each alias is the same class object as its replacement.
83
+ _DEPRECATED_ALIASES = {
84
+ "ConnectionError": ServiceConnectionError,
85
+ "TimeoutError": OperationTimeoutError,
86
+ }
87
+
88
+
76
89
  def __getattr__(name: str) -> Any:
77
90
  if name == "job_exceptions":
78
91
  module = import_module("dataexcept.job_exceptions")
79
92
  globals()[name] = module
80
93
  return module
81
- raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
94
+ return resolve_deprecated(__name__, _DEPRECATED_ALIASES, name)
@@ -12,9 +12,14 @@ from typing import Iterable
12
12
  from . import __path__ as _PKG_PATH
13
13
  from . import __version__
14
14
 
15
+ #: Deprecated compatibility shims. Listing these would advertise names that are
16
+ #: scheduled for removal, and importing them emits a DeprecationWarning at
17
+ #: anyone who merely wanted to see what the package offers.
18
+ _DEPRECATED_MODULES = frozenset({"dataexcept.job_exceptions"})
19
+
15
20
 
16
21
  def _iter_exception_modules() -> Iterable[ModuleType]:
17
- """Yield every submodule that explicitly defines ``__all__``."""
22
+ """Yield every non-deprecated submodule that explicitly defines ``__all__``."""
18
23
  allowed_suffixes = ("exceptions", "_exceptions")
19
24
 
20
25
  for module_info in pkgutil.walk_packages(
@@ -22,6 +27,8 @@ def _iter_exception_modules() -> Iterable[ModuleType]:
22
27
  ):
23
28
  if not module_info.name.endswith(allowed_suffixes):
24
29
  continue
30
+ if module_info.name in _DEPRECATED_MODULES:
31
+ continue
25
32
  try:
26
33
  module = import_module(module_info.name)
27
34
  except ImportError as exc: # pragma: no cover - defensive guard
@@ -53,7 +60,11 @@ def _list_exceptions() -> None:
53
60
 
54
61
  def main(argv: list[str] | None = None) -> None:
55
62
  """Entry point for the ``dataexcept`` command."""
56
- parser = argparse.ArgumentParser(description="Utilities for DataExcept")
63
+ parser = argparse.ArgumentParser(
64
+ # Without this, `python -m dataexcept --version` reports "__main__.py".
65
+ prog="dataexcept",
66
+ description="Utilities for DataExcept",
67
+ )
57
68
  parser.add_argument(
58
69
  "--version",
59
70
  action="version",
@@ -0,0 +1,38 @@
1
+ """Internal support for names that have been renamed.
2
+
3
+ A rename keeps the old name working as an alias bound to the *same* class
4
+ object, so an existing ``except OldName:`` keeps catching exactly what it
5
+ caught before -- the alias is not a separate class.
6
+
7
+ Access goes through a PEP 562 module ``__getattr__`` rather than a plain
8
+ ``OldName = NewName`` assignment, because an assignment cannot warn. This is
9
+ private; see the stability policy for the user-facing contract.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import warnings
15
+ from typing import Any, Mapping
16
+
17
+ #: Version in which every currently deprecated alias is removed.
18
+ REMOVED_IN = "1.0.0"
19
+
20
+
21
+ def resolve_deprecated(module: str, aliases: Mapping[str, Any], name: str) -> Any:
22
+ """Return the target of the deprecated *name*, warning first.
23
+
24
+ Raises ``AttributeError`` for anything not in *aliases*, so normal
25
+ attribute lookup on the module keeps behaving normally.
26
+ """
27
+ try:
28
+ target = aliases[name]
29
+ except KeyError:
30
+ raise AttributeError(f"module {module!r} has no attribute {name!r}") from None
31
+ warnings.warn(
32
+ f"{module}.{name} is deprecated and will be removed in {REMOVED_IN}; "
33
+ f"use {target.__name__} instead",
34
+ DeprecationWarning,
35
+ # resolve_deprecated -> __getattr__ -> the caller we want to blame.
36
+ stacklevel=3,
37
+ )
38
+ return target
@@ -1,5 +1,8 @@
1
1
  """Custom exceptions for data science workflows."""
2
2
 
3
+ from typing import Any
4
+
5
+ from .._deprecation import resolve_deprecated
3
6
  from .base import DataScienceError
4
7
  from .ingestion import (
5
8
  DataAugmentationError,
@@ -18,8 +21,8 @@ from .operations import (
18
21
  DataDriftError,
19
22
  DataExportError,
20
23
  DeploymentError,
24
+ ModelSerializationError,
21
25
  ResourceLimitError,
22
- SerializationError,
23
26
  )
24
27
  from .training import (
25
28
  BiasDetectionError,
@@ -59,7 +62,7 @@ __all__ = [
59
62
  "HyperparameterError",
60
63
  "ModelEvaluationError",
61
64
  "PredictionError",
62
- "SerializationError",
65
+ "ModelSerializationError",
63
66
  "DeploymentError",
64
67
  "DataDriftError",
65
68
  "ResourceLimitError",
@@ -83,3 +86,11 @@ __all__ = [
83
86
  "FeatureScalingError",
84
87
  "ModelCompatibilityError",
85
88
  ]
89
+
90
+ #: Renamed in 0.2.0: this named the same thing as
91
+ #: ``dataexcept.exceptions.SerializationError`` while being a different class.
92
+ _DEPRECATED_ALIASES = {"SerializationError": ModelSerializationError}
93
+
94
+
95
+ def __getattr__(name: str) -> Any:
96
+ return resolve_deprecated(__name__, _DEPRECATED_ALIASES, name)
@@ -7,7 +7,7 @@ from typing import Any, Optional
7
7
  from .base import DataScienceError
8
8
 
9
9
 
10
- class SerializationError(DataScienceError):
10
+ class ModelSerializationError(DataScienceError):
11
11
  """
12
12
  Raised when saving or loading a model fails.
13
13
 
@@ -30,7 +30,7 @@ class SerializationError(DataScienceError):
30
30
  super().__init__(message)
31
31
 
32
32
  def __str__(self) -> str:
33
- return f"[SerializationError] {self.path}"
33
+ return f"[ModelSerializationError] {self.path}"
34
34
 
35
35
 
36
36
  class DeploymentError(DataScienceError):
@@ -1,12 +1,15 @@
1
1
  # __init__.py
2
+ from typing import Any
3
+
4
+ from .._deprecation import resolve_deprecated
2
5
  from .authentication import AuthenticationError, AuthorizationError
3
6
  from .base import JobError
4
7
  from .configuration import ConfigurationError
5
8
  from .external import (
6
- ConnectionError,
7
9
  DependencyError,
10
+ OperationTimeoutError,
8
11
  ResourceNotFoundError,
9
- TimeoutError,
12
+ ServiceConnectionError,
10
13
  )
11
14
  from .lifecycle import JobCancellationError
12
15
  from .notification import EmailError, NotificationError, WebhookError
@@ -18,8 +21,8 @@ __all__ = [
18
21
  "JobError",
19
22
  "ValidationError",
20
23
  "ConfigurationError",
21
- "ConnectionError",
22
- "TimeoutError",
24
+ "ServiceConnectionError",
25
+ "OperationTimeoutError",
23
26
  "ResourceNotFoundError",
24
27
  "DependencyError",
25
28
  "AuthenticationError",
@@ -34,3 +37,14 @@ __all__ = [
34
37
  "WebhookError",
35
38
  "JobCancellationError",
36
39
  ]
40
+
41
+ #: Renamed in 0.2.0 because they shadowed Python builtins without inheriting
42
+ #: from them. The alias is the same class object, so ``except`` keeps working.
43
+ _DEPRECATED_ALIASES = {
44
+ "ConnectionError": ServiceConnectionError,
45
+ "TimeoutError": OperationTimeoutError,
46
+ }
47
+
48
+
49
+ def __getattr__(name: str) -> Any:
50
+ return resolve_deprecated(__name__, _DEPRECATED_ALIASES, name)
@@ -1,7 +1,7 @@
1
1
  from .base import JobError
2
2
 
3
3
 
4
- class ConnectionError(JobError):
4
+ class ServiceConnectionError(JobError):
5
5
  """Raised when a connection to an external service fails."""
6
6
 
7
7
  def __init__(self, service_name: str, original_exception: Exception | None = None):
@@ -13,7 +13,7 @@ class ConnectionError(JobError):
13
13
  super().__init__(msg)
14
14
 
15
15
 
16
- class TimeoutError(JobError):
16
+ class OperationTimeoutError(JobError):
17
17
  """Raised when an operation exceeds its time limit."""
18
18
 
19
19
  def __init__(self, operation: str, timeout: float):
@@ -1,9 +1,15 @@
1
1
  """Backward compatible job-related exceptions.
2
2
 
3
+ .. deprecated:: 0.1.0
4
+ Use :mod:`dataexcept.exceptions` instead. This module is scheduled for
5
+ removal in 1.0.0; see the stability policy in the documentation.
6
+
3
7
  This module re-exports the core job exceptions defined in
4
- :mod:`dataexcept.exceptions`. Applications should import from that
5
- package directly going forward. Importing from :mod:`dataexcept.job_exceptions`
6
- will raise a :class:`DeprecationWarning`.
8
+ :mod:`dataexcept.exceptions`. Importing it emits a :class:`DeprecationWarning`.
9
+ Every name it exports is available from :mod:`dataexcept.exceptions` and from
10
+ the top-level :mod:`dataexcept` package, so the migration is a change of import
11
+ line only -- the classes are identical objects, not replacements, so existing
12
+ ``except`` clauses keep working during the transition.
7
13
  """
8
14
 
9
15
  from __future__ import annotations
@@ -14,7 +20,6 @@ from .exceptions import (
14
20
  AuthenticationError,
15
21
  AuthorizationError,
16
22
  ConfigurationError,
17
- ConnectionError,
18
23
  CronExpressionError,
19
24
  DependencyError,
20
25
  DeserializationError,
@@ -22,15 +27,21 @@ from .exceptions import (
22
27
  JobCancellationError,
23
28
  JobError,
24
29
  NotificationError,
30
+ OperationTimeoutError,
25
31
  ParsingError,
26
32
  ResourceNotFoundError,
27
33
  ScheduleConflictError,
28
34
  SerializationError,
29
- TimeoutError,
35
+ ServiceConnectionError,
30
36
  ValidationError,
31
37
  WebhookError,
32
38
  )
33
39
 
40
+ # This module is itself deprecated, so it binds the pre-0.2.0 names directly
41
+ # rather than going through the alias machinery and warning a second time.
42
+ ConnectionError = ServiceConnectionError
43
+ TimeoutError = OperationTimeoutError
44
+
34
45
  __all__ = [
35
46
  "JobError",
36
47
  "ValidationError",
@@ -53,7 +64,8 @@ __all__ = [
53
64
  ]
54
65
 
55
66
  warnings.warn(
56
- "dataexcept.job_exceptions is deprecated; use dataexcept.exceptions instead",
67
+ "dataexcept.job_exceptions is deprecated and will be removed in 1.0.0; "
68
+ "use dataexcept.exceptions instead",
57
69
  DeprecationWarning,
58
70
  stacklevel=2,
59
71
  )
@@ -9,6 +9,13 @@ from typing import Any, Iterator, Mapping, Optional
9
9
 
10
10
  Context = Mapping[str, Any]
11
11
 
12
+ __all__ = [
13
+ "Context",
14
+ "log_and_raise",
15
+ "log_exception",
16
+ "log_then_raise",
17
+ ]
18
+
12
19
 
13
20
  def _normalize_context_value(value: Any) -> Any:
14
21
  try:
@@ -4,6 +4,8 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Any, Optional
6
6
 
7
+ from ._deprecation import resolve_deprecated
8
+
7
9
 
8
10
  class PipelineError(Exception):
9
11
  """Base exception for pipeline errors."""
@@ -22,7 +24,7 @@ class PreprocessingError(PipelineError):
22
24
  super().__init__(message)
23
25
 
24
26
 
25
- class FeatureEngineeringError(PreprocessingError):
27
+ class FeaturePreprocessingError(PreprocessingError):
26
28
  """Raised when feature engineering fails."""
27
29
 
28
30
  def __init__(self, feature: str, reason: Optional[str] = None) -> None:
@@ -190,7 +192,7 @@ class DataFetchError(PipelineError):
190
192
  __all__ = [
191
193
  "PipelineError",
192
194
  "PreprocessingError",
193
- "FeatureEngineeringError",
195
+ "FeaturePreprocessingError",
194
196
  "StorageError",
195
197
  "PipelineNotificationError",
196
198
  "RetryLimitExceededError",
@@ -203,3 +205,12 @@ __all__ = [
203
205
  "TypeCheckError",
204
206
  "DataFetchError",
205
207
  ]
208
+
209
+ #: Renamed in 0.2.0: this named the same thing as
210
+ #: ``dataexcept.datascience_exceptions.FeatureEngineeringError`` while being a
211
+ #: different class, so catching one silently missed the other.
212
+ _DEPRECATED_ALIASES = {"FeatureEngineeringError": FeaturePreprocessingError}
213
+
214
+
215
+ def __getattr__(name: str) -> Any:
216
+ return resolve_deprecated(__name__, _DEPRECATED_ALIASES, name)
@@ -1,24 +1,15 @@
1
- [tool.poetry]
1
+ [project]
2
2
  name = "DataExcept"
3
- version = "0.1.0"
3
+ version = "0.2.1"
4
4
  description = "A Python package providing structured, easily-extendable custom exception types."
5
- authors = ["Diogo Ribeiro <dfr@esmad.ipp.pt>"]
6
- maintainers = ["Diogo Ribeiro <diogo.debastos.ribeiro@gmail.com>"]
7
- license = "MIT"
8
5
  readme = "README.md"
9
- homepage = "https://github.com/DiogoRibeiro7/DataExcept"
10
- repository = "https://github.com/DiogoRibeiro7/DataExcept"
11
- documentation = "https://diogoribeiro7.github.io/DataExcept/"
12
- packages = [{ include = "dataexcept" }]
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.10,<3.14"
9
+ authors = [{ name = "Diogo Ribeiro", email = "dfr@esmad.ipp.pt" }]
10
+ maintainers = [{ name = "Diogo Ribeiro", email = "diogo.debastos.ribeiro@gmail.com" }]
13
11
  keywords = ["exceptions", "errors", "logging"]
14
- include = [
15
- "CITATION.cff",
16
- "LICENSE",
17
- "CHANGELOG.md",
18
- ]
19
-
20
12
  classifiers = [
21
- "License :: OSI Approved :: MIT License",
22
13
  "Programming Language :: Python :: 3",
23
14
  "Programming Language :: Python :: 3 :: Only",
24
15
  "Programming Language :: Python :: 3.10",
@@ -26,17 +17,26 @@ classifiers = [
26
17
  "Programming Language :: Python :: 3.12",
27
18
  "Programming Language :: Python :: 3.13",
28
19
  ]
20
+ dependencies = [
21
+ "tomli ; python_version < '3.11'",
22
+ ]
29
23
 
30
- [tool.poetry.urls]
24
+ [project.urls]
25
+ Homepage = "https://github.com/DiogoRibeiro7/DataExcept"
26
+ Repository = "https://github.com/DiogoRibeiro7/DataExcept"
27
+ Documentation = "https://diogoribeiro7.github.io/DataExcept/"
31
28
  Changelog = "https://github.com/DiogoRibeiro7/DataExcept/releases"
32
29
  Issues = "https://github.com/DiogoRibeiro7/DataExcept/issues"
33
30
 
34
- [tool.poetry.scripts]
31
+ [project.scripts]
35
32
  dataexcept = "dataexcept.__main__:main"
36
33
 
37
- [tool.poetry.dependencies]
38
- python = ">=3.10,<3.14"
39
- tomli = { version = "*", python = "<3.11" }
34
+ [tool.poetry]
35
+ packages = [{ include = "dataexcept" }]
36
+ include = [
37
+ "CITATION.cff",
38
+ "CHANGELOG.md",
39
+ ]
40
40
 
41
41
  [tool.poetry.group.dev.dependencies]
42
42
  black = "*"
@@ -54,7 +54,7 @@ mkdocs-material = ">=9.5,<10"
54
54
  mkdocstrings = { extras = ["python"], version = ">=0.26" }
55
55
 
56
56
  [build-system]
57
- requires = ["poetry-core>=1.0.0"]
57
+ requires = ["poetry-core>=2.0.0"]
58
58
  build-backend = "poetry.core.masonry.api"
59
59
 
60
60
  [tool.flake8]
@@ -72,9 +72,26 @@ line-length = 88
72
72
  target-version = "py310"
73
73
 
74
74
  [tool.ruff.lint]
75
- select = ["E", "F"]
75
+ # S = flake8-bandit (security), C90 = mccabe (complexity). Both ship with ruff,
76
+ # so neither needs a separate tool in CI.
77
+ select = ["E", "F", "S", "C90"]
76
78
  ignore = []
77
79
 
80
+ [tool.ruff.lint.mccabe]
81
+ # The most complex function in the package currently scores 6. Eight leaves
82
+ # room to breathe while still catching a function that grows out of hand.
83
+ max-complexity = 8
84
+
85
+ [tool.ruff.lint.per-file-ignores]
86
+ # assert is how a test asserts, and "/tmp/data" there is a string literal used
87
+ # as fixture data rather than a path that is ever opened.
88
+ "tests/**" = ["S101", "S108", "S603"]
89
+ # The lambda example asserts to narrow Optionals for the type checker.
90
+ "examples/**" = ["S101"]
91
+ # bump_version.py deliberately shells out to poetry, by name, on a maintainer's
92
+ # own machine.
93
+ "scripts/**" = ["S603", "S607"]
94
+
78
95
  [tool.mypy]
79
96
  python_version = "3.10"
80
97
  files = ["dataexcept"]
@@ -1,42 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project are documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
-
8
- ## [Unreleased]
9
-
10
- ## [0.1.0] - 2026-08-24
11
-
12
- First public release.
13
-
14
- ### Added
15
-
16
- - Hierarchical exception classes for data science, machine learning and data
17
- engineering workflows. Catch a specific failure or a broad category, and get
18
- a message that names the value that caused it rather than a bare
19
- `ValueError`.
20
- - Domain modules for validation, configuration, authentication, parsing,
21
- serialization, scheduling, notification, lifecycle and external-service
22
- errors, plus dedicated pandas, database, network, I/O, pipeline and security
23
- exception groups.
24
- - `dataexcept.logging_helpers` with `log_exception`, `log_and_raise` and
25
- `log_then_raise`, for logging exceptions with structured context and
26
- re-raising without losing the traceback.
27
- - A `dataexcept` command-line entry point that lists the exported exception
28
- classes and reports the installed version.
29
- - A `py.typed` marker, backed by a mypy-clean codebase that CI enforces, so
30
- downstream type checkers get annotations that are actually correct.
31
- - Documentation at
32
- [diogoribeiro7.github.io/DataExcept](https://diogoribeiro7.github.io/DataExcept/),
33
- including an API reference generated from the docstrings.
34
-
35
- ### Notes
36
-
37
- - Supports Python 3.10 through 3.13.
38
- - Published to PyPI via OIDC trusted publishing; no long-lived API token is
39
- involved in a release.
40
-
41
- [Unreleased]: https://github.com/DiogoRibeiro7/DataExcept/compare/v0.1.0...HEAD
42
- [0.1.0]: https://github.com/DiogoRibeiro7/DataExcept/releases/tag/v0.1.0
File without changes