diff --git a/.agents/project.md b/.agents/project.md index e126214..27cdfd6 100644 --- a/.agents/project.md +++ b/.agents/project.md @@ -14,7 +14,7 @@ | Install dependencies | `uv sync --all-extras --group dev` (`make install`). Without `--all-extras`, the test modules in `tests/unit/settings/`, `tests/unit/metrics/` and `tests/unit/providers/` fail at collection. | | Format | `make fmt`: `uv run ruff format .`, then `uv run ruff check --fix .` | | Lint and type-check | `make check`: `uv run ruff check .`, `uv run ruff format --check .`, `uv run mypy redis_client_kit` | -| Test | `make test-unit` (`uv run pytest -m unit`) needs no Docker. `make test-integration` (`uv run pytest -m integration`) needs Docker; without it those tests are skipped. `make test` runs both with `--cov-fail-under=90`. CI runs `uv run pytest -m unit` on Python 3.11, 3.12 and 3.13 and `uv run pytest -m integration` once, both without coverage. | +| Test | `make test-unit` (`uv run pytest -m unit`) needs no Docker. `make test-integration` (`uv run pytest -m integration`) needs Docker; without it those tests are skipped. `make test` runs both with `--cov-fail-under=90`. CI runs `uv run pytest -m unit` on every Python from 3.10 to 3.14, and the whole suite once with Docker and the coverage options of `make test`, then uploads `coverage.xml` to Codecov. | | Run locally | A library: nothing to run. The docs: `make docs-serve`. | | Stop the local run | Ctrl+C in the terminal of `make docs-serve`. | @@ -29,7 +29,7 @@ ## Boundaries -- `CHANGELOG.md` is generated by release-please: never edit it by hand, and never add an `## [Unreleased]` section, even though the pull request template still has a checkbox for one. `docs/changelog.md` is a git-ignored copy that the docs build makes. +- `CHANGELOG.md` is generated by release-please: never edit it by hand, and never add an `## [Unreleased]` section. `docs/changelog.md` is a git-ignored copy that the docs build makes. - release-please bumps the version in three places: `redis_client_kit/__version__.py` (the line marked `# x-release-please-version`), the fallback `__version__` in `redis_client_kit/__init__.py`, and `.release-please-manifest.json`. Never change them by hand. - `docs/agents.md` is part of the public API: it changes in the same pull request as the API (CONTRIBUTING.md, "The agents page"). - Files from the engineering-assets hub (`AGENTS.md` explains how it works) include: @@ -44,6 +44,6 @@ - Releases: release-please opens its pull request with the workflow's own token, so CI does not start on it and the required `All checks passed` check never reports. Close and reopen the release pull request to run CI, then merge it. `publish.yml` runs after every Release Please run on `master`. It publishes to PyPI only when that commit has a `redis-client-kit-v` tag, which is the case once the release pull request is merged. Before 1.0, a breaking change bumps the minor version (`bump-minor-pre-major`). - Tests: `tests/unit/conftest.py` and `tests/integration/conftest.py` add the marker `unit` or `integration` by directory (`--strict-markers`), so a new test goes in the right directory rather than taking a marker by hand. Integration tests start `redis:7-alpine` containers through testcontainers: a plain server, a primary full under `noeviction`, and a read-only replica. -- Python 3.10: CI's test matrix starts at 3.11, so only mypy and ruff check 3.10; both target it. +- Python 3.10: mypy and ruff target it, and CI's unit-test matrix starts there. On 3.10 `unittest.mock.patch` looks a dotted target up attribute by attribute, not in `sys.modules`, so a test that re-imports a package must restore the package's attribute on its parent too, as `tests/unit/providers/test_providers_init.py` does. - `redis_client_kit/aio/` and `redis_client_kit/sync/` hold the same modules (`factory`, `instrumented`, `lifecycle`, `types`). A change to one side usually belongs on the other too, with tests in `tests/unit/aio/` and `tests/unit/sync/`. - Code style (CONTRIBUTING.md): type hints on every function, tests included; Google-style docstrings on the public API only; lines up to 120 characters; double quotes; comments only for a non-obvious why. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 599cafa..3ba13e0 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1 @@ blank_issues_enabled: false -contact_links: - - name: Question or discussion - url: https://github.com/bedrock-python/redis-client-kit/discussions - about: Ask a question or start a discussion about redis-client-kit diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5073550..1a7034a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -14,7 +14,7 @@ - [ ] Tests added or updated - [ ] `make check` passes locally (`ruff` + `mypy`) -- [ ] `CHANGELOG.md` updated under `[Unreleased]` +- [ ] Conventional-commit title (the changelog is generated by Release Please — do not edit it by hand) - [ ] Documentation updated (if the public API changed), `docs/agents.md` included ## Related issues diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b862b7b..a1e1363 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v7 - uses: astral-sh/setup-uv@v7 @@ -34,15 +34,22 @@ jobs: - run: uv sync --all-extras --group dev - run: uv run pytest -m unit + # Runs the whole suite, as `make test` does: only this job has the Docker the + # integration tests need, and the coverage threshold counts both kinds of test. test-integration: - name: Integration tests + name: Integration tests and coverage needs: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - uses: astral-sh/setup-uv@v7 - run: uv sync --all-extras --group dev - - run: uv run pytest -m integration + - run: uv run pytest --cov=redis_client_kit --cov-report=term --cov-fail-under=90 --cov-report=xml:coverage.xml + - uses: codecov/codecov-action@v7 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: coverage.xml + continue-on-error: true # Sentinel job — use this as the single required status check in branch protection. all-checks-passed: diff --git a/pyproject.toml b/pyproject.toml index 33bc0b5..8839c2c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Database", "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed", diff --git a/tests/unit/providers/test_providers_init.py b/tests/unit/providers/test_providers_init.py index 3c89f4e..d3326fc 100644 --- a/tests/unit/providers/test_providers_init.py +++ b/tests/unit/providers/test_providers_init.py @@ -5,6 +5,8 @@ import pytest +import redis_client_kit + def _unload_providers(monkeypatch: pytest.MonkeyPatch) -> None: """Drop the providers package from sys.modules for the duration of one test. @@ -14,6 +16,10 @@ def _unload_providers(monkeypatch: pytest.MonkeyPatch) -> None: """ for module in [key for key in sys.modules if key.startswith("redis_client_kit.providers")]: monkeypatch.delitem(sys.modules, module) + # A successful import also binds the new package onto its parent. Restore that + # too: mock.patch on Python 3.10 resolves a target attribute by attribute and + # would otherwise find this test's stand-in package. + monkeypatch.delattr(redis_client_kit, "providers", raising=False) def test__providers_init__dishka_not_installed__raises_import_error(monkeypatch: pytest.MonkeyPatch) -> None: