From 7f4bf06b1b1d0442dcdfdc0e28ca7d831f112099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Sat, 7 Sep 2024 20:39:02 -0700 Subject: [PATCH] Declare 3.13 support and drop 3.9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernát Gábor --- .github/workflows/check.yml | 39 ++++++++++++---------------- .github/workflows/release.yml | 6 +++-- .pre-commit-config.yaml | 13 +++++++--- CHANGELOG.md | 2 +- README.md | 42 +++++++++++++++++-------------- pyproject.toml | 25 +++++++++++------- src/sphinx_argparse_cli/_logic.py | 6 +++-- tests/test_logic.py | 3 +-- tox.ini | 32 +++++++++++------------ 9 files changed, 89 insertions(+), 79 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 6022b5b..369d887 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -14,44 +14,35 @@ concurrency: jobs: test: - name: test on CPython ${{ matrix.py }} + name: test ${{ matrix.py }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: py: + - "3.13" - "3.12" - "3.11" - "3.10" - - "3.9" steps: + - name: setup uv for tox + uses: yezz123/setup-uv@v4 - name: setup python for tox uses: actions/setup-python@v5 with: python-version: "3.12" - name: install tox - run: python -m pip install tox-uv + run: uv pip install tox tox-uv --system - uses: actions/checkout@v4 - name: setup python for test ${{ matrix.py }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.py }} - - name: Pick environment to run - run: | - import codecs; import os; import sys - env = f"TOXENV=py3{sys.version_info[1]}\n" - print(f"Picked:\n{env}for{sys.version}") - with codecs.open(os.environ["GITHUB_ENV"], "a", "utf-8") as file_handler: - file_handler.write(env) - shell: python + allow-prereleases: true - name: setup test suite - run: tox -vv --notest + run: tox run -vv --notest --skip-missing-interpreters false -e ${{ matrix.py }} - name: run test suite - run: tox --skip-pkg-install - env: - PYTEST_ADDOPTS: "-vv --durations=20" - CI_RUN: "yes" - DIFF_AGAINST: HEAD + run: tox run --skip-pkg-install -e ${{ matrix.py }} check: name: tox env ${{ matrix.tox_env }} @@ -62,16 +53,18 @@ jobs: tox_env: - type - dev - - pkg_check + - readme steps: + - name: setup uv for tox + uses: yezz123/setup-uv@v4 - uses: actions/checkout@v4 - name: setup Python 3.12 uses: actions/setup-python@v5 with: python-version: "3.12" - name: install tox - run: python -m pip install tox - - name: run check for ${{ matrix.tox_env }} - run: python -m tox -e ${{ matrix.tox_env }} - env: - UPGRADE_ADVISORY: "yes" + run: uv pip install tox tox-uv --system + - name: Setup test suite + run: tox -vv --notest --skip-missing-interpreters false -e ${{ matrix.tox_env }} + - name: Run test suite + run: tox --skip-pkg-install -e ${{ matrix.tox_env }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3336f6e..91c1f47 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,12 +16,14 @@ jobs: uses: actions/setup-python@v5 with: python-version: "3.12" + - name: setup uv for tox + uses: yezz123/setup-uv@v4 - name: Install build - run: python -m pip install build + run: uv pip install build[uv] --system - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Build package - run: pyproject-build -s -w . -o dist + run: pyproject-build --installer uv --sdist --wheel . --outdir dist - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@v1.10.1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e101497..c46b622 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,14 +8,14 @@ repos: rev: 0.29.2 hooks: - id: check-github-workflows - args: [ "--verbose" ] + args: ["--verbose"] - repo: https://github.com/codespell-project/codespell rev: v2.3.0 hooks: - id: codespell - args: ["--write-changes"] + additional_dependencies: ["tomli>=2.0.1"] - repo: https://github.com/tox-dev/tox-ini-fmt - rev: "1.3.1" + rev: "1.3.2" hooks: - id: tox-ini-fmt args: ["-p", "fix"] @@ -24,11 +24,16 @@ repos: hooks: - id: pyproject-fmt - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.6.3" + rev: "v0.6.4" hooks: - id: ruff-format - id: ruff args: ["--fix", "--unsafe-fixes", "--exit-non-zero-on-fix"] + - repo: https://github.com/rbubley/mirrors-prettier + rev: "v3.3.3" + hooks: + - id: prettier + args: ["--print-width=120", "--prose-wrap=always"] - repo: meta hooks: - id: check-hooks-apply diff --git a/CHANGELOG.md b/CHANGELOG.md index bac7aae..1b9192b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file. - Allow to add content to directive. - Fix Sphinx warnings about parallel reads. -- Add `force_args_lower` to enable `:ref:` links with mixed-case program names and arguments. +- Add `force_args_lower` to enable `:ref:` links with mixed-case program names and arguments. ## 1.13.1 diff --git a/README.md b/README.md index c8018f1..5005ae4 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ Render CLI arguments (sub-commands friendly) defined by the argparse module. For python -m pip install sphinx-argparse-cli ``` +[README.md](README.md) + ## enable in your `conf.py` ```python @@ -29,22 +31,23 @@ extensions = ["sphinx_argparse_cli"] Within the reStructuredText files use the `sphinx_argparse_cli` directive that takes, at least, two arguments: -| Name | Description | -| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| module | the module path to where the parser is defined | -| func | the name of the function that once called with no arguments constructs the parser | -| prog | (optional) when provided, overwrites the `` name. | -| hook | (optional) hook `argparse` to retrieve the parser if `func` uses a parser instead of returning it. | -| title | (optional) when provided, overwrites the ` - CLI interface` title added by default and when empty, will not be included | -| description | (optional) when provided, overwrites the description and when empty, will not be included | -| epilog | (optional) when provided, overwrites the epilog and when empty, will not be included | -| usage_width | (optional) how large should usage examples be - defaults to 100 character | -| usage_first | (optional) show usage before description | -| group_title_prefix | (optional) groups subsections title prefixes, accepts the string `{prog}` as a replacement for the program name - defaults to `{prog}` | -| group_sub_title_prefix | (optional) subcommands groups subsections title prefixes, accepts replacement of `{prog}` and `{subcommand}` for program and subcommand name - defaults to `{prog} {subcommand}` | -| no_default_values | (optional) suppresses generation of `default` entries | -| force_refs_lower | (optional) Sphinx `:ref:` only supports lower-case references. With this, any capital letter in generated reference anchors are lowered and given an `_` prefix (i.e. `A` becomes `_a`) | -For example: +| Name | Description | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| module | the module path to where the parser is defined | +| func | the name of the function that once called with no arguments constructs the parser | +| prog | (optional) when provided, overwrites the `` name. | +| hook | (optional) hook `argparse` to retrieve the parser if `func` uses a parser instead of returning it. | +| title | (optional) when provided, overwrites the ` - CLI interface` title added by default and when empty, will not be included | +| description | (optional) when provided, overwrites the description and when empty, will not be included | +| epilog | (optional) when provided, overwrites the epilog and when empty, will not be included | +| usage_width | (optional) how large should usage examples be - defaults to 100 character | +| usage_first | (optional) show usage before description | +| group_title_prefix | (optional) groups subsections title prefixes, accepts the string `{prog}` as a replacement for the program name - defaults to `{prog}` | +| group_sub_title_prefix | (optional) subcommands groups subsections title prefixes, accepts replacement of `{prog}` and `{subcommand}` for program and subcommand name - defaults to `{prog} {subcommand}` | +| no_default_values | (optional) suppresses generation of `default` entries | +| force_refs_lower | (optional) Sphinx `:ref:` only supports lower-case references. With this, any capital letter in generated reference anchors are lowered and given an `_` prefix (i.e. `A` becomes `_a`) | + +> > > > > > > bda12fb (Declare 3.13 support and drop 3.9) For example: ```rst .. sphinx_argparse_cli:: @@ -86,11 +89,12 @@ being `cli`: - to refer to flag `--magic` of the `run` sub-command use ``:ref:`cli:tox-run---magic` ``. Due to Sphinx's `:ref:` only supporting lower-case values, if you need to distinguish mixed case program names or -arguments, set the `:force_refs_lower:` argument. With this flag, captial-letters in references will be converted to -their lower-case counterpart and prefixed with an `_`. For example: +arguments, set the `:force_refs_lower:` argument. With this flag, captial-letters in references will be converted to +their lower-case counterpart and prefixed with an `_`. For example: - A `prog` name `SampleProgram` will be referenced as ``:ref:`_sample_program...` ``. -- To distinguish between mixed case flags `-a` and `-A` use ``:ref:`_sample_program--a` `` and ``:ref:`_sample_program--_a` `` respectively +- To distinguish between mixed case flags `-a` and `-A` use ``:ref:`_sample_program--a` `` and + ``:ref:`_sample_program--_a` `` respectively Note that if you are _not_ concernced about using internal Sphinx `:ref:` cross-references, you may choose to leave this off to maintain mixed-case anchors in your output HTML; but be aware that later enabling it will change your anchors in diff --git a/pyproject.toml b/pyproject.toml index 39a6962..33268e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ license = "MIT" maintainers = [ { name = "Bernat Gabor", email = "gaborjbernat@gmail.com" }, ] # noqa: E999 -requires-python = ">=3.9" +requires-python = ">=3.10" classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", @@ -27,10 +27,10 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Documentation", "Topic :: Documentation :: Sphinx", @@ -39,12 +39,12 @@ dynamic = [ "version", ] dependencies = [ - "sphinx>=7.3.7", + "sphinx>=8.0.2", ] optional-dependencies.test = [ "covdefaults>=2.3", "defusedxml>=0.7.1", # needed for sphinx.testing - "pytest>=8.2.2", + "pytest>=8.3.2", "pytest-cov>=5", ] urls.Documentation = "https://github.com/tox-dev/sphinx-argparse-cli#sphinx-argparse-cli" @@ -64,8 +64,11 @@ version.source = "vcs" line-length = 120 [tool.ruff] -target-version = "py38" +target-version = "py310" line-length = 120 +format.preview = true +format.docstring-code-line-length = 100 +format.docstring-code-format = true lint.select = [ "ALL", ] @@ -76,6 +79,7 @@ lint.ignore = [ "CPY", # No copyright statements "D203", # `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible "D212", # `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible + "DOC", # not yet supported "ISC001", # Conflict with formatter "S104", # Possible binding to all interface ] @@ -85,14 +89,14 @@ lint.per-file-ignores."roots/**/*.py" = [ "INP001", # no namespace ] lint.per-file-ignores."tests/**/*.py" = [ - "D", # don"t care about documentation in tests - "FBT", # don"t care about booleans as positional arguments in tests + "D", # don't care about documentation in tests + "FBT", # don't care about booleans as positional arguments in tests "INP001", # no implicit namespace "PLC2701", # private import "PLR0913", # any number of arguments in tests "PLR0917", # any number of arguments in tests "PLR2004", # Magic value used in comparison, consider replacing with a constant variable - "S101", # asserts allowed in tests... + "S101", # asserts allowed in tests "S603", # `subprocess` call: check for execution of untrusted input ] lint.isort = { known-first-party = [ @@ -105,6 +109,9 @@ lint.isort = { known-first-party = [ builtin = "clear,usage,en-GB_to_en-US" count = true +[tool.pyproject-fmt] +max_supported_python = "3.13" + [tool.coverage] html.show_contexts = true html.skip_covered = false @@ -121,6 +128,6 @@ run.plugins = [ run.relative_files = true [tool.mypy] -python_version = "3.11" +python_version = "3.12" show_error_codes = true strict = true diff --git a/src/sphinx_argparse_cli/_logic.py b/src/sphinx_argparse_cli/_logic.py index 2cc7098..d9d4ffb 100644 --- a/src/sphinx_argparse_cli/_logic.py +++ b/src/sphinx_argparse_cli/_logic.py @@ -15,7 +15,7 @@ ) from collections import defaultdict from pathlib import Path -from typing import TYPE_CHECKING, Any, ClassVar, Iterator, NamedTuple, cast +from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple, cast from docutils.nodes import ( Element, @@ -41,6 +41,8 @@ from sphinx.util.logging import getLogger if TYPE_CHECKING: + from collections.abc import Iterator + from docutils.parsers.rst.states import RSTState, RSTStateMachine @@ -273,7 +275,7 @@ def _mk_option_line(self, action: Action, prefix: str) -> list_item: "no_default_values" not in self.options and action.default != SUPPRESS and not re.match(r".*[ (]default[s]? .*", (action.help or "")) - and not isinstance(action, (_StoreTrueAction, _StoreFalseAction)) + and not isinstance(action, _StoreTrueAction | _StoreFalseAction) ): line += Text(" (default: ") line += literal(text=str(action.default).replace(str(Path.cwd()), "{cwd}")) diff --git a/tests/test_logic.py b/tests/test_logic.py index a39545c..2c89b50 100644 --- a/tests/test_logic.py +++ b/tests/test_logic.py @@ -18,8 +18,7 @@ @pytest.fixture(scope="session") def opt_grp_name() -> tuple[str, str]: - if sys.version_info >= (3, 10): # pragma: no branch # https://bugs.python.org/issue9694 - return "options", "options" # pragma: no cover + return "options", "options" # pragma: no cover return "optional arguments", "optional-arguments" # pragma: no cover diff --git a/tox.ini b/tox.ini index 8b7cd05..d1f16e3 100644 --- a/tox.ini +++ b/tox.ini @@ -3,12 +3,12 @@ requires = tox>=4.2 env_list = fix - py312 - py311 - py310 - py39 type - pkg_check + readme + 3.13 + 3.12 + 3.11 + 3.10 skip_missing_interpreters = true [testenv] @@ -33,7 +33,7 @@ commands = description = run static analysis and style check using flake8 skip_install = true deps = - pre-commit>=3.7.1 + pre-commit>=3.8 pass_env = HOMEPATH PROGRAMDATA @@ -44,24 +44,23 @@ commands = [testenv:type] description = run type check on code base deps = - mypy==1.10.1 - types-docutils>=0.21.0.20240708 -set_env = - {tty:MYPY_FORCE_COLOR = 1} + mypy==1.11.2 + types-docutils>=0.21.0.20240907 commands = mypy src mypy tests -[testenv:pkg_check] +[testenv:readme] description = check that the long description is valid skip_install = true deps = - build[virtualenv]>=1.2.1 + build[uv]>=1.2.2 + check-wheel-contents>=0.6 twine>=5.1.1 -change_dir = {toxinidir} commands = - python -m build --sdist --wheel -o {envtmpdir} . - twine check {envtmpdir}/* + pyproject-build --installer uv --outdir {envtmpdir} --sdist --wheel . + twine check {envtmpdir}{/}* + check-wheel-contents --no-config {envtmpdir} [testenv:dev] description = generate a DEV environment @@ -70,6 +69,5 @@ extras = docs test commands = - python -m pip list --format=columns + uv pip tree python -c 'import sys; print(sys.executable)' -uv_seed = true