Skip to content

feat(cli): add the model command-line interface - #138

Merged
vistart merged 18 commits into
release/v1.0.0.dev30from
feature/activerecord-cli
Nov 10, 2026
Merged

vistart merged 18 commits into
release/v1.0.0.dev30from
feature/activerecord-cli

Conversation

@vistart

@vistart vistart commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds the model-level command-line interface to the core package: rhosocial.activerecord.cli, exposed as the rhosocial-activerecord console script and as python -m rhosocial.activerecord.cli.

Motivation: the framework has a complete Python API but no supported way to run model operations from a shell. The CLI gives scripts and agents the same model-level semantics as the Python API without introducing a second ORM or a SQL-string layer.

The CLI is a thin adapter over existing APIs. It never builds SQL: DML calls ActiveRecord methods, DQL calls ActiveQuery, and set / cte execute user-supplied SetOperationQuery / CTEQuery objects.

Command groups:

  • dml: create, update, delete, bulk create, bulk update, bulk delete
  • dql: get, list, count, exists, aggregate, run
  • set: union, union-all, intersect, except, run
  • cte: run

Behaviour highlights:

  • Model references are module:Class; backends and connection configs are named explicitly (--backend-class, --config-class, --connection), with the model module's configure() as the fallback.
  • Destructive commands require --yes in a non-interactive context (exit 5), support --dry-run, and report soft_delete for soft-delete models.
  • Read-only models refuse DML before connecting (exit 6); DQL stays available.
  • dql list, grouped dql aggregate, and dql run --mode list|aggregate inject a default limit of 100 and explain it on stderr; --no-limit, --limit 0, and --quiet control this.
  • Machine formats (json, jsonl, csv, tsv, value) keep stdout payload-only; table uses Rich when installed and falls back to JSON otherwise.
  • Bulk input accepts JSON arrays and JSONL (--data @-, --input-format, --batch-size).
  • CLI-layer errors are translated from model/backend exceptions with stable exit codes (1, 3, 4, 5, 6, 141); model-layer RecordNotFound maps to CLI-layer CliNotFoundError, keeping the original exception in the chain.
  • Validation failures name the model, primary key, or bulk item and never write partial data.
  • --where is equality-only; complex predicates are expressed through dql run factories.
  • Declining a confirmation is not an error: exit 0 and no Error: prefix.

Core robustness included, because the CLI exercised these paths directly:

  • find_one_or_create keeps a failed race re-read in __cause__ instead of swallowing it.
  • Savepoint capability probing logs probe failures at ERROR while a normal "unsupported" result degrades quietly.
  • StaleRecordError no longer asserts the row was updated; the message says it may have been updated or deleted.

Dependencies: no new runtime dependency. rich remains optional (pip install "rhosocial-activerecord[cli]") and only affects -o table.

Adds #138

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 changes are expected for typical end-user applications. The CLI is additive: existing Python APIs, exception classes, and model behaviour are unchanged. The intermediate crud command group existed only on this feature branch and was replaced by dml / dql / set / cte before any release.

Impact on Backend Developers (Custom Implementations):
No backward-incompatible changes are expected for backend developers using custom implementations. Backends already provide StorageBackend / AsyncStorageBackend and dialect implementations; the CLI only consumes them.

Related Repositories/Packages

  • python-activerecord-testsuite (release/v1.0.0.dev17): adds CLI validation contracts (test_cli_validation.py, test_cli_validation_async.py), exposes validated_model in the generic CLI providers, and updates optimistic-lock assertions to the clarified StaleRecordError message. Changes are on the pinned testsuite branch; no separate PR.
  • Nine backend repositories (mysql, mariadb, postgres, sqlserver, oracle, snowflake, clickhouse, bigquery, firebird): CLI contract wrappers and provider registrations are already committed on their feature branches. Their CLI CI can only pass once this change is available through a core release that contains rhosocial.activerecord.cli; they must not be rerun before that.
  • python-github-actions-tracker (application): no coordination needed. Its feedback informed the robustness fixes above.

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) across Python 3.8–3.15.

Test Plan:

core CLI                    165 passed
testsuite CLI (SQLite)       64 passed
find_one_or_create           44 passed
mixins (optimistic lock)     passed with the updated testsuite assertions
ruff                         src/ and tests/ clean

CI:

  • PR run 42889951416 (654bbc94): 12/12 jobs success (3.8–3.15, including 3.13t / 3.14t / 3.15t and coverage/benchmarks).
  • Pre-PR run 42880612587 (56bdb62e): 12/12 jobs success.

Local verification commands:

PYTHONPATH=src pytest tests/rhosocial/activerecord_test/feature/cli
PYTHONPATH=src:tests:<testsuite>/src pytest <testsuite>/src/rhosocial/activerecord/testsuite/feature/cli
PYTHONPATH=src pytest tests/rhosocial/activerecord_test/feature/basic/find_one_or_create
PYTHONPATH=src python docs/examples/chapter_13_cli/01_dml.py

Backend/database matrix: not applicable here; the backend CLI wrappers are verified separately once the release branch contains this change.

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

  • Documentation is split into topic pages: docs/en_US/cli and docs/zh_CN/cli (DML, bulk DML, DQL, set/CTE, connections, output, errors), with docs/examples/chapter_13_cli containing runnable examples that invoke real CLI commands against a temporary SQLite database. Every page ends with guidance for AI agents.
  • Default pagination is deliberate: it protects users from accidentally selecting an entire table; --no-limit and --limit 0 opt out, and the note is stderr-only so machine output stays clean.
  • Backend CLI CI is currently blocked by design: the wrappers import rhosocial.activerecord.cli, which does not exist on the release branch yet.
  • Exception taxonomy refinements beyond the CLI mapping layer (semantic sub-bases such as DataError / RecordError / ConcurrencyError) are intentionally deferred to a separate change so this PR stays reviewable.

@vistart vistart added python Pull requests that update python code feature labels Nov 10, 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 10, 2026
@vistart vistart self-assigned this Nov 10, 2026
@codecov

codecov Bot commented Nov 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.26488% with 636 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.78%. Comparing base (659e51a) to head (654bbc9).

Files with missing lines Patch % Lines
src/rhosocial/activerecord/cli/backends.py 56.69% 76 Missing and 21 partials ⚠️
src/rhosocial/activerecord/cli/commands/dql.py 51.26% 87 Missing and 9 partials ⚠️
src/rhosocial/activerecord/cli/output.py 50.25% 73 Missing and 23 partials ⚠️
src/rhosocial/activerecord/cli/runtime.py 59.88% 49 Missing and 22 partials ⚠️
src/rhosocial/activerecord/cli/input.py 51.90% 51 Missing and 12 partials ⚠️
src/rhosocial/activerecord/cli/__init__.py 72.97% 47 Missing and 13 partials ⚠️
src/rhosocial/activerecord/cli/commands/dml.py 77.13% 43 Missing and 16 partials ⚠️
src/rhosocial/activerecord/cli/commands/setop.py 61.53% 26 Missing and 4 partials ⚠️
src/rhosocial/activerecord/cli/error_mapping.py 60.00% 18 Missing and 12 partials ⚠️
src/rhosocial/activerecord/cli/interaction.py 83.58% 6 Missing and 5 partials ⚠️
... and 4 more
Additional details and impacted files

Impacted file tree graph

@@                   Coverage Diff                    @@
##           release/v1.0.0.dev30     #138      +/-   ##
========================================================
- Coverage                 85.35%   84.78%   -0.57%     
========================================================
  Files                       706      720      +14     
  Lines                     59459    61278    +1819     
  Branches                   5917     6250     +333     
========================================================
+ Hits                      50750    51955    +1205     
- Misses                     6998     7475     +477     
- Partials                   1711     1848     +137     
Flag Coverage Δ
python-3.14 84.78% <65.26%> (-0.57%) ⬇️
unittests 84.78% <65.26%> (-0.57%) ⬇️

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 89.55% <ø> (ø)
src/rhosocial/activerecord/cli/errors.py 100.00% <100.00%> (ø)
src/rhosocial/activerecord/field/version.py 84.37% <ø> (ø)
src/rhosocial/activerecord/cli/args.py 97.29% <97.29%> (ø)
src/rhosocial/activerecord/base/base.py 80.91% <66.66%> (+1.36%) ⬆️
src/rhosocial/activerecord/cli/model_loader.py 84.78% <84.78%> (ø)
src/rhosocial/activerecord/cli/commands/cte.py 69.69% <69.69%> (ø)
src/rhosocial/activerecord/cli/interaction.py 83.58% <83.58%> (ø)
src/rhosocial/activerecord/cli/commands/setop.py 61.53% <61.53%> (ø)
src/rhosocial/activerecord/cli/error_mapping.py 60.00% <60.00%> (ø)
... and 7 more

... and 2 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 586730c into release/v1.0.0.dev30 Nov 10, 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