diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1a8de8e..4e7dbbc 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -10,9 +10,9 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.13'] + python-version: ['3.14'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Filter changed file paths to outputs uses: dorny/paths-filter@v3.0.2 @@ -36,7 +36,7 @@ jobs: run: echo "PUBLISH=$(echo true)" >> $GITHUB_ENV - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v7 if: env.PUBLISH == 'true' with: enable-cache: true diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4ad8721..1100994 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,13 +9,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] + python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] docutils-version: ['0.18', '0.19'] + pytest-version: ['7', '8', '9'] + exclude: + # Exclude pytest 7 from Python 3.14 to reduce matrix size + - python-version: '3.14' + pytest-version: '7' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v7 with: enable-cache: true @@ -25,10 +30,14 @@ jobs: - name: Install dependencies run: uv sync --all-extras --dev - - name: Print python versions + - name: Install specific pytest version + run: uv pip install "pytest~=${{ matrix.pytest-version }}.0" + + - name: Print python and pytest versions run: | python -V uv run python -V + uv run pytest --version - name: Lint with ruff check run: uv run ruff check . @@ -50,16 +59,19 @@ jobs: runs-on: ubuntu-latest needs: build if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + permissions: + id-token: write + attestations: write strategy: matrix: - python-version: ['3.13'] + python-version: ['3.14'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v7 with: enable-cache: true @@ -77,6 +89,5 @@ jobs: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@release/v1 with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} - skip_existing: true + attestations: true + skip-existing: true diff --git a/.gitignore b/.gitignore index 4552ce9..0621c10 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,8 @@ pip-wheel-metadata/ # Monkeytype monkeytype.sqlite3 + +# Claude code +**/CLAUDE.local.md +**/CLAUDE.*.md +**/.claude/settings.local.json diff --git a/.tool-versions b/.tool-versions index 7987ea1..ecb9f91 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -uv 0.5.11 -python 3.13.1 3.12.8 3.11.11 3.10.16 3.9.21 3.8.20 3.7.17 +uv 0.9.17 +python 3.14 3.13.11 3.12.12 3.11.14 3.10.19 diff --git a/.windsurfrules b/.windsurfrules new file mode 100644 index 0000000..e5c31a1 --- /dev/null +++ b/.windsurfrules @@ -0,0 +1,136 @@ +# libtmux Python Project Rules + + +- uv - Python package management and virtual environments +- ruff - Fast Python linter and formatter +- py.test - Testing framework + - pytest-watcher - Continuous test runner +- mypy - Static type checking +- doctest - Testing code examples in documentation + + + +- Use a consistent coding style throughout the project +- Format code with ruff before committing +- Run linting and type checking before finalizing changes +- Verify tests pass after each significant change + + + +- Use reStructuredText format for all docstrings in src/**/*.py files +- Keep the main description on the first line after the opening `"""` +- Use NumPy docstyle for parameter and return value documentation +- Format docstrings as follows: + ```python + """Short description of the function or class. + + Detailed description using reStructuredText format. + + Parameters + ---------- + param1 : type + Description of param1 + param2 : type + Description of param2 + + Returns + ------- + type + Description of return value + """ + ``` + + + +- Use narrative descriptions for test sections rather than inline comments +- Format doctests as follows: + ```python + """ + Examples + -------- + Create an instance: + + >>> obj = ExampleClass() + + Verify a property: + + >>> obj.property + 'expected value' + """ + ``` +- Add blank lines between test sections for improved readability +- Keep doctests simple and focused on demonstrating usage +- Move complex examples to dedicated test files at tests/examples//test_.py +- Utilize pytest fixtures via doctest_namespace for complex scenarios + + + +- Run tests with `uv run py.test` before committing changes +- Use pytest-watcher for continuous testing: `uv run ptw . --now --doctest-modules` +- Fix any test failures before proceeding with additional changes + + + +- Make atomic commits with conventional commit messages +- Start with an initial commit of functional changes +- Follow with separate commits for formatting, linting, and type checking fixes + + + +- Use the following commit message format: + ``` + Component/File(commit-type[Subcomponent/method]): Concise description + + why: Explanation of necessity or impact. + what: + - Specific technical changes made + - Focused on a single topic + + refs: #issue-number, breaking changes, or relevant links + ``` + +- Common commit types: + - **feat**: New features or enhancements + - **fix**: Bug fixes + - **refactor**: Code restructuring without functional change + - **docs**: Documentation updates + - **chore**: Maintenance (dependencies, tooling, config) + - **test**: Test-related updates + - **style**: Code style and formatting + +- Prefix Python package changes with: + - `py(deps):` for standard packages + - `py(deps[dev]):` for development packages + - `py(deps[extra]):` for extras/sub-packages + +- General guidelines: + - Subject line: Maximum 50 characters + - Body lines: Maximum 72 characters + - Use imperative mood (e.g., "Add", "Fix", not "Added", "Fixed") + - Limit to one topic per commit + - Separate subject from body with a blank line + - Mark breaking changes clearly: `BREAKING:` + + + +- Use fixtures from conftest.py instead of monkeypatch and MagicMock when available +- For instance, if using libtmux, use provided fixtures: server, session, window, and pane +- Document in test docstrings why standard fixtures weren't used for exceptional cases +- Use tmp_path (pathlib.Path) fixture over Python's tempfile +- Use monkeypatch fixture over unittest.mock + + + +- Prefer namespace imports over importing specific symbols +- Import modules and access attributes through the namespace: + - Use `import enum` and access `enum.Enum` instead of `from enum import Enum` + - This applies to standard library modules like pathlib, os, and similar cases +- For typing, use `import typing as t` and access via the namespace: + - Access typing elements as `t.NamedTuple`, `t.TypedDict`, etc. + - Note primitive types like unions can be done via `|` pipes + - Primitive types like list and dict can be done via `list` and `dict` directly +- Benefits of namespace imports: + - Improves code readability by making the source of symbols clear + - Reduces potential naming conflicts + - Makes import statements more maintainable + diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..03c55a7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,307 @@ +# AGENTS.md + +This file provides guidance to AI agents (including Claude Code, Cursor, and other LLM-powered tools) when working with code in this repository. + +## CRITICAL REQUIREMENTS + +### Test Success +- ALL tests MUST pass for code to be considered complete and working +- Never describe code as "working as expected" if there are ANY failing tests +- Even if specific feature tests pass, failing tests elsewhere indicate broken functionality +- Changes that break existing tests must be fixed before considering implementation complete +- A successful implementation must pass linting, type checking, AND all existing tests + +## Project Overview + +gp-libs is a Python library providing internal utilities and extensions for git-pull projects. It focuses on extending Sphinx documentation and pytest functionality with support for docutils-compatible markup formats. + +Key features: +- **doctest_docutils**: Reimplementation of Python's doctest with support for reStructuredText and Markdown +- **pytest_doctest_docutils**: pytest plugin for running doctests in documentation files +- **linkify_issues**: Sphinx extension that converts issue references (e.g., `#123`) to hyperlinks +- Supports testing doctest examples in `.rst` and `.md` files +- Powers documentation testing across the git-pull ecosystem + +## Development Environment + +This project uses: +- Python 3.10+ +- [uv](https://github.com/astral-sh/uv) for dependency management +- [ruff](https://github.com/astral-sh/ruff) for linting and formatting +- [mypy](https://github.com/python/mypy) for type checking +- [pytest](https://docs.pytest.org/) for testing + - [pytest-watcher](https://github.com/olzhasar/pytest-watcher) for continuous testing + +## Common Commands + +### Setting Up Environment + +```bash +# Install dependencies +uv pip install --editable . +uv pip sync + +# Install with development dependencies +uv pip install --editable . -G dev +``` + +### Running Tests + +```bash +# Run all tests +make test +# or directly with pytest +uv run pytest + +# Run a single test file +uv run pytest tests/test_doctest_docutils.py + +# Run a specific test +uv run pytest tests/test_doctest_docutils.py::test_function_name + +# Run tests with test watcher +make start +# or +uv run ptw . + +# Run tests with doctests +uv run ptw . --now --doctest-modules +``` + +### Linting and Type Checking + +```bash +# Run ruff for linting +make ruff +# or directly +uv run ruff check . + +# Format code with ruff +make ruff_format +# or directly +uv run ruff format . + +# Run ruff linting with auto-fixes +uv run ruff check . --fix --show-fixes + +# Run mypy for type checking +make mypy +# or directly +uv run mypy src tests + +# Watch mode for linting (using entr) +make watch_ruff +make watch_mypy +``` + +### Development Workflow + +Follow this workflow for code changes (see `.cursor/rules/dev-loop.mdc`): + +1. **Format First**: `uv run ruff format .` +2. **Run Tests**: `uv run pytest` +3. **Run Linting**: `uv run ruff check . --fix --show-fixes` +4. **Check Types**: `uv run mypy` +5. **Verify Tests Again**: `uv run pytest` + +### Documentation + +```bash +# Build documentation +make build_docs + +# Start documentation server with auto-reload +make start_docs + +# Update documentation CSS/JS +make design_docs +``` + +## Code Architecture + +gp-libs provides utilities for documentation testing and Sphinx extensions: + +``` +src/ +├── doctest_docutils.py # Core doctest reimplementation +├── pytest_doctest_docutils.py # pytest plugin +├── linkify_issues.py # Sphinx extension +├── docutils_compat.py # Compatibility layer +└── gp_libs.py # Package metadata +``` + +### Core Modules + +1. **doctest_docutils** (`src/doctest_docutils.py`) + - Reimplementation of Python's standard library `doctest` module + - Supports docutils-compatible markup (reStructuredText and Markdown) + - Handles `doctest_block`, `.. doctest::` directive, and ` ```{doctest} ` code blocks + - PEP-440 version specifier support for conditional tests + - Can be run directly: `python -m doctest_docutils README.md -v` + +2. **pytest_doctest_docutils** (`src/pytest_doctest_docutils.py`) + - pytest plugin integrating doctest_docutils with pytest + - Collects and runs doctests from `.rst` and `.md` files + - Full pytest fixture and conftest.py support + - Registered as `pytest11` entry point + +3. **linkify_issues** (`src/linkify_issues.py`) + - Sphinx extension for automatic issue linking + - Converts `#123` references to clickable hyperlinks + - Configured via `issue_url_tpl` in Sphinx conf.py + +4. **docutils_compat** (`src/docutils_compat.py`) + - Compatibility layer for cross-version docutils support + - Provides `findall()` abstraction for different docutils versions + +5. **gp_libs** (`src/gp_libs.py`) + - Package metadata (version, title, author, URLs) + +## Testing Strategy + +gp-libs uses pytest for testing with custom fixtures. The test suite includes: + +- Unit tests for doctest parsing and execution +- Integration tests for pytest plugin functionality +- Sphinx app factory for testing extensions + +### Test Structure + +``` +tests/ +├── test_doctest_docutils.py # Tests for doctest module +├── test_pytest_doctest_docutils.py # Tests for pytest plugin +├── test_linkify_issues.py # Tests for linkify extension +├── conftest.py # Fixtures and sphinx app factory +└── regressions/ # Regression tests +``` + +### Testing Guidelines + +1. **Use existing fixtures over mocks** (see `.cursor/rules/dev-loop.mdc`) + - Use fixtures from conftest.py instead of `monkeypatch` and `MagicMock` when available + - Document in test docstrings why standard fixtures weren't used for exceptional cases + +2. **Preferred pytest patterns** + - Use `tmp_path` (pathlib.Path) fixture over Python's `tempfile` + - Use `monkeypatch` fixture over `unittest.mock` + +3. **Running tests continuously** + - Use pytest-watcher during development: `uv run ptw .` + - For doctests: `uv run ptw . --now --doctest-modules` + +## Coding Standards + +For detailed coding standards, refer to `.cursor/rules/dev-loop.mdc`. Key highlights: + +### Imports + +- **Use namespace imports for stdlib**: `import enum` instead of `from enum import Enum`; third-party packages may use `from X import Y` +- **For typing**, use `import typing as t` and access via namespace: `t.NamedTuple`, etc. +- **Use `from __future__ import annotations`** at the top of all Python files + +### Docstrings + +Follow NumPy docstring style for all functions and methods (see `.cursor/rules/dev-loop.mdc`): + +```python +"""Short description of the function or class. + +Detailed description using reStructuredText format. + +Parameters +---------- +param1 : type + Description of param1 +param2 : type + Description of param2 + +Returns +------- +type + Description of return value +""" +``` + +### Doctest Guidelines + +1. **Use narrative descriptions** for test sections rather than inline comments +2. **Move complex examples** to dedicated test files at `tests/examples//test_.py` +3. **Keep doctests simple and focused** on demonstrating usage +4. **Add blank lines between test sections** for improved readability + +### Git Commit Standards + +See `.cursor/rules/git-commits.mdc` for detailed commit message standards. + +Format commit messages as: +``` +Component/File(commit-type[Subcomponent/method]): Concise description + +why: Explanation of necessity or impact. +what: +- Specific technical changes made +- Focused on a single topic +``` + +Common commit types: +- **feat**: New features or enhancements +- **fix**: Bug fixes +- **refactor**: Code restructuring without functional change +- **docs**: Documentation updates +- **chore**: Maintenance (dependencies, tooling, config) +- **test**: Test-related updates +- **style**: Code style and formatting +- **py(deps)**: Dependencies +- **py(deps[dev])**: Dev Dependencies +- **ai(rules[LLM type])**: AI Rule Updates + +Example: +``` +doctest_docutils(feat[parse]): Add support for myst-parser code blocks + +why: Enable doctest execution in Markdown documentation files +what: +- Add detection for ```{doctest} fence syntax +- Register myst directives automatically +- Add tests for Markdown doctest parsing +``` + +## Debugging Tips + +See `.cursor/rules/avoid-debug-loops.mdc` for detailed debugging guidance. + +When stuck in debugging loops: + +1. **Pause and acknowledge the loop** +2. **Minimize to MVP**: Remove all debugging cruft and experimental code +3. **Document the issue** comprehensively for a fresh approach +4. **Format for portability** (using quadruple backticks) + +## Sphinx/Docutils-Specific Considerations + +### Directive Registration + +- Use `_ensure_directives_registered()` to auto-register required directives +- Supports myst-parser directives (`{doctest}`, `{tab}`) +- Handles both reStructuredText and Markdown syntax + +### Document Parsing + +- Uses docutils for parsing `.rst` files +- Uses myst-parser for parsing `.md` files +- Both formats support doctest blocks + +### linkify_issues Configuration + +In your Sphinx `conf.py`: +```python +extensions = ["linkify_issues"] +issue_url_tpl = 'https://github.com/git-pull/gp-libs/issues/%7Bissue_id%7D' +``` + +## References + +- Documentation: https://gp-libs.git-pull.com/ +- GitHub: https://github.com/git-pull/gp-libs +- PyPI: https://pypi.org/project/gp-libs/ diff --git a/CHANGES b/CHANGES index 0eeae10..1fcb111 100644 --- a/CHANGES +++ b/CHANGES @@ -8,15 +8,110 @@ To install the unreleased gp-libs version, see [developmental releases](https:// $ pip install --user --upgrade --pre gp-libs ``` -## gp-libs 0.0.11 (unreleased) +[pipx](https://pypa.github.io/pipx/docs/): -- _Add your latest changes from PRs here_ +```console +$ pipx install --suffix=@next 'gp-libs' --pip-args '\--pre' --force +``` + +[uv](https://docs.astral.sh/uv/): + +```console +$ uv add gp-libs --prerelease allow +``` + +[uvx](https://docs.astral.sh/uv/guides/tools/): + +```console +$ uvx --from 'gp-libs' --prerelease allow gp-libs +``` + +## gp-libs 0.0.18 (unreleased) + + + +## gp-libs 0.0.17 (2025-12-07) + +### CI + +- Migrate to PyPI Trusted Publisher (#57) + +## gp-libs 0.0.16 (2025-11-25) + +### Features + +#### pytest_doctest_docutils + +- Add `_unblock_doctest()` helper for programmatic re-enabling of built-in doctest plugin (#56) + + Uses the public `pluginmanager.unblock()` API introduced in pytest 8.1.0, with graceful + fallback for older versions. + +### Bug fixes + +#### pytest_doctest_docutils + +- Autouse fixtures from `conftest.py` are now properly discovered for doctest files (#56) + + Backported from pytest commit [9cd14b4ff](https://github.com/pytest-dev/pytest/commit/9cd14b4ff) (2024-02-06). + +#### doctest_docutils + +- Doctest directive comments with leading whitespace (e.g., ` # doctest: +SKIP`) are now properly matched (#56) + + Backported from Sphinx commit [ad0c343d3](https://github.com/sphinx-doc/sphinx/commit/ad0c343d3) (2025-01-04). + +### Development + +- CI: Add pytest 9.x to test matrix, with pytest 7.x/8.x compatibility testing (#56) + +## gp-libs 0.0.15 (2025-11-01) + +### Breaking changes + +- Drop Python 3.9 (#54) + + The minimum version of Python in this and future releases is Python 3.10. + + Python 3.9 reached end-of-life status on October 5th, 2025 (see PEP 596). + +### Development + +- Add Python 3.14 to test matrix (#53) + +## gp-libs 0.0.14 (2025-10-26) + +### Bug fixes + +- Ensure docutils/myst doctest directives auto-register so Arch Linux packaging tests pass without manual setup (#52, fixes #48) + +## gp-libs 0.0.13 (2025-08-17) + +### Bug fixes + +- Type annotation fixes (#49) + +## gp-libs 0.0.12 (2025-07-12) + +### Bug fixes + +- Type annotation and linting fixes (resolves #48) + +### Deveopment + +- Internal package updates to uv, py.test, mypy, ruff, etc. + +## gp-libs 0.0.11 (2025-02-22) + +### Bug fixes + +- Fix for `pytest-asyncio` compatibility (#46) ### Development -##### chore: Implement PEP 563 deferred annotation resolution (#44) +#### chore: Implement PEP 563 deferred annotation resolution (#44) -- Add `from __future__ import annotations` to defer annotation resolution and reduce unnecessary symbol computations during type checking +- Add `from __future__ import annotations` to defer annotation resolution and reduce unnecessary runtime computations during type checking. - Enable Ruff checks for PEP-compliant annotations: - [non-pep585-annotation (UP006)](https://docs.astral.sh/ruff/rules/non-pep585-annotation/) - [non-pep604-annotation (UP007)](https://docs.astral.sh/ruff/rules/non-pep604-annotation/) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/README.md b/README.md index b7c8ac3..66b1bfb 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ issue on the tracker. # More information -- Python support: >= 3.9, pypy +- Python support: >= 3.10, pypy - Source: - Docs: - Changelog: diff --git a/docs/_templates/sidebar/projects.html b/docs/_templates/sidebar/projects.html index 217e418..7b46e0b 100644 --- a/docs/_templates/sidebar/projects.html +++ b/docs/_templates/sidebar/projects.html @@ -1,7 +1,7 @@