Skip to content

feat(errors): add error taxonomy and execute error contract - #139

Merged
vistart merged 5 commits into
release/v1.0.0.dev30from
feature/error-taxonomy
Nov 11, 2026
Merged

vistart merged 5 commits into
release/v1.0.0.dev30from
feature/error-taxonomy

Conversation

@vistart

@vistart vistart commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

Description

This PR introduces a semantic error taxonomy for model/backend errors and enforces a phase-specific error contract for execute across the core package and the nine backend extensions.

Motivation: applications could previously only distinguish database failures by concrete class names (and often had to catch DatabaseError and parse messages). Backend error translation was also inconsistent: some paths silently swallowed commit failures, some retried the wrong exception type, and framework errors were re-translated into driver errors.

What changes:

  • New semantic bases under DatabaseError: DataError, RecordError, PolicyError, ConcurrencyError, InfrastructureError. Existing classes are re-parented; names, constructors and raise sites are unchanged, and except DatabaseError keeps working.
  • retryable / transient hints; attach_diagnostics() attaches operation, sqlstate, driver_code, backend and transient without changing the message.
  • execute / execute_many now let framework DatabaseError subclasses pass through unchanged, guarantee a raise when a backend handler forgets to raise, and surface auto-commit failures as TransactionError.
  • CLI maps TransactionError; CliNotFoundError remains the command-layer not-found error.
  • Postgres retries now act on framework errors, execute_many uses the shared mapping + auto-commit, and COPY client errors still roll back explicit transactions.

Adds # (no issue)

Type of change

  • feat: A new feature
  • docs: Documentation only changes (if related to the new feature)
  • test: Adding missing tests or correcting existing tests (for the new feature)
  • perf: A code change that improves performance (if part of the feature)
  • chore: Changes to the build process or auxiliary tools and libraries suchs as documentation generation (if related to the new feature)
  • ci: Changes to our CI configuration files and scripts (if related to the new feature)
  • build: Changes that affect the build system or external dependencies (if related to the new feature)
  • revert: Reverts a previous commit

Breaking Change

  • Yes, for end-users (backward-incompatible API changes)
  • Yes, for backend developers (internal architectural changes that may affect custom implementations)
  • No breaking changes

Impact on End-Users:
No backward-incompatible API changes are expected: existing exception classes keep their names and constructors, and every concrete error is still a DatabaseError. Behavioural changes to note: a failed auto-commit now raises TransactionError instead of being logged and ignored; transient connection retries now actually run; and framework errors are no longer re-wrapped by backend handlers.

Impact on Backend Developers (Custom Implementations):
No signature changes. A custom backend that implements _handle_error should return to raising a taxonomy error; the core pipeline adds a defensive DatabaseError fallback if the handler returns instead of raising. Backends may call attach_diagnostics but are not required to.

Related Repositories/Packages

  • python-activerecord-testsuite (feature/error-taxonomy): taxonomy contract (feature/backend/errors/test_error_taxonomy.py).
  • Nine backend repositories (feature/error-taxonomy): driver classification, execute-phase contract tests, testsuite wrappers, and CI pinned to the core/testsuite feature branches:
    • mysql, mariadb, postgres, sqlserver, oracle, snowflake, clickhouse, bigquery, firebird.
  • CI pins in those backends currently reference feature/error-taxonomy for both core and testsuite, and will be restored to release branches after merge.

Testing

  • I have added new tests to cover my changes.
  • I have updated existing tests to reflect my changes.
  • All existing tests pass.
  • This change has been tested on SQLite (core) and the backend matrix via CI.

Test Plan:

Local (shared venv, SQLite/offline):

core taxonomy + execute contract    27 passed
core CLI suite                     165 passed
core CLI error mapping             48 passed (with taxonomy)
docs/examples                      51/52 runnable; only graphql_fastapi/main.py is a long-running server

CI (all green, core and testsuite pinned to feature/error-taxonomy):

core        42972822461  success (12/12 jobs)
mysql       42973483870  success
mariadb     42973490824  success
postgres    42976461108  success (rerun after COPY rollback fix)
sqlserver   42973502650  success
oracle      42973508500  success
snowflake   42973514422  success
clickhouse  42973520253  success
bigquery    42973525483  success
firebird    42973530733  success

Reproduction:

PYTHONPATH=src pytest tests/rhosocial/activerecord_test/feature/backend/error_taxonomy
PYTHONPATH=src pytest tests/rhosocial/activerecord_test/feature/cli

Checklist

  • My code follows the project's code style guidelines.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have made corresponding changes to the documentation (Docstrings, README.md, etc.).
  • My changes generate no new warnings.
  • I have added a Changelog fragment (changelog.d/<issue_number>.<type>.md).
  • I have verified that my changes do not introduce SQL injection vulnerabilities.
  • I have checked for potential performance regressions.

Additional Notes

  • Changelog fragment will be added as changelog.d/<PR>.added.md immediately after the PR number exists (the checker requires the fragment number to equal the PR number).
  • docs/examples were brought back in line with current APIs (DefaultTimestampMixin, DefaultOptimisticLockMixin, event handler signatures, async pool context getters, GraphQL loader/loop usage).
  • Retry semantics are now real: only transient framework ConnectionError/OperationalError are retried after reconnect; non-idempotent statements remain the caller's responsibility (documented in the execute docstring).
  • Firebird is intentionally sync-only; removing its exported async backend family is deferred to a separate feature/firebird-sync-only branch. This PR only aligns its sync execute phases and does not add forced async parity tests.
  • PostgreSQL COPY keeps the narrower rollback filter for pre-SQL errors but rolls back unconditionally on COPY client errors; covered by test_pg19_copy_repack.py.

@vistart vistart added python Pull requests that update python code feature labels Nov 11, 2026
@sonarqubecloud

Copy link
Copy Markdown

@vistart vistart added enhancement New feature or request and removed python Pull requests that update python code feature labels Nov 11, 2026
@vistart vistart self-assigned this Nov 11, 2026
@codecov

codecov Bot commented Nov 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.66667% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.80%. Comparing base (659e51a) to head (dee5aef).
⚠️ Report is 1 commits behind head on release/v1.0.0.dev30.

Files with missing lines Patch % Lines
src/rhosocial/activerecord/cli/error_mapping.py 60.00% 3 Missing and 3 partials ⚠️
...c/rhosocial/activerecord/backend/base/execution.py 76.47% 4 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@                   Coverage Diff                    @@
##           release/v1.0.0.dev30     #139      +/-   ##
========================================================
- Coverage                 85.35%   84.80%   -0.55%     
========================================================
  Files                       706      720      +14     
  Lines                     59459    61335    +1876     
  Branches                   5917     6267     +350     
========================================================
+ Hits                      50750    52017    +1267     
- Misses                     6998     7465     +467     
- Partials                   1711     1853     +142     
Flag Coverage Δ
python-3.14 84.80% <86.66%> (-0.55%) ⬇️
unittests 84.80% <86.66%> (-0.55%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/rhosocial/activerecord/backend/errors.py 92.78% <100.00%> (+3.23%) ⬆️
...c/rhosocial/activerecord/backend/base/execution.py 81.67% <76.47%> (+11.34%) ⬆️
src/rhosocial/activerecord/cli/error_mapping.py 59.55% <60.00%> (ø)

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vistart
vistart merged commit fc2098b into release/v1.0.0.dev30 Nov 11, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant