diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 5af0a95..0000000 --- a/.flake8 +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -max-line-length = 95 -ignore = E116,E241,E251 -exclude = .git,.tox,.venv diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8cd7db1..856df5b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,7 +71,9 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - env: [flake8, mypy] + env: + - ruff + - mypy steps: - uses: actions/checkout@v3 diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 0000000..4b7dd2a --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,53 @@ +target-version = "py39" # Pin Ruff to Python 3.9 +output-format = "full" +line-length = 95 + +[lint] +preview = true +select = [ +# "ANN", # flake8-annotations + "C4", # flake8-comprehensions + "COM", # flake8-commas + "B", # flake8-bugbear + "DTZ", # flake8-datetimez + "E", # pycodestyle + "EM", # flake8-errmsg + "EXE", # flake8-executable + "F", # pyflakes + "FA", # flake8-future-annotations + "FLY", # flynt + "FURB", # refurb + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "INT", # flake8-gettext + "LOG", # flake8-logging + "PERF", # perflint + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PT", # flake8-pytest-style + "SIM", # flake8-simplify + "SLOT", # flake8-slots + "TCH", # flake8-type-checking + "UP", # pyupgrade + "W", # pycodestyle + "YTT", # flake8-2020 +] +ignore = [ + "E116", + "E241", + "E251", +] + +[lint.per-file-ignores] +"tests/*" = [ + "ANN", # tests don't need annotations +] + +[lint.isort] +forced-separate = [ + "tests", +] +required-imports = [ + "from __future__ import annotations", +] diff --git a/Makefile b/Makefile index 26f411a..438ee54 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ clean-mypyfiles: .PHONY: style-check style-check: - @flake8 + @ruff check .PHONY: type-check type-check: diff --git a/pyproject.toml b/pyproject.toml index eef6260..dc7ba41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,9 +47,9 @@ test = [ "pytest", ] lint = [ - "flake8", + "ruff==0.5.5", "mypy", - "docutils-stubs", + "types-docutils", ] standalone = [ "Sphinx>=5", @@ -75,4 +75,34 @@ exclude = [ ] [tool.mypy] -ignore_missing_imports = true +python_version = "3.9" +packages = [ + "sphinxcontrib", + "tests", +] +exclude = [ + "tests/roots", +] +check_untyped_defs = true +disallow_any_generics = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +explicit_package_bases = true +extra_checks = true +no_implicit_reexport = true +show_column_numbers = true +show_error_context = true +strict_optional = true +warn_redundant_casts = true +warn_unused_configs = true +warn_unused_ignores = true +enable_error_code = [ + "type-arg", + "redundant-self", + "truthy-iterable", + "ignore-without-code", + "unused-awaitable", +] diff --git a/sphinxcontrib/applehelp/__init__.py b/sphinxcontrib/applehelp/__init__.py index 9511bed..b93b521 100644 --- a/sphinxcontrib/applehelp/__init__.py +++ b/sphinxcontrib/applehelp/__init__.py @@ -1,15 +1,16 @@ """Build Apple help books.""" +from __future__ import annotations + import plistlib import shlex import subprocess -from os import environ -from os import path -from subprocess import CalledProcessError, PIPE, STDOUT -from typing import Any +from os import environ, path +from pathlib import Path +from subprocess import PIPE, STDOUT, CalledProcessError +from typing import TYPE_CHECKING import sphinx -from sphinx.application import Sphinx from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.errors import SphinxError from sphinx.locale import get_translation @@ -18,11 +19,17 @@ from sphinx.util.matching import Matcher from sphinx.util.osutil import ensuredir, make_filename +if TYPE_CHECKING: + from typing import Any + + from sphinx.application import Sphinx + if sphinx.version_info[:2] >= (6, 1): from sphinx.util.display import SkipProgressMessage, progress_message else: - from sphinx.util import ( # type: ignore[attr-defined,no-redef] - SkipProgressMessage, progress_message + from sphinx.util import ( # type: ignore[no-redef] + SkipProgressMessage, + progress_message, ) __version__ = '1.0.8' @@ -75,16 +82,17 @@ def init(self) -> None: self.link_suffix = '.html' if self.config.applehelp_bundle_id is None: - raise SphinxError(__('You must set applehelp_bundle_id before ' - 'building Apple Help output')) + msg = __('You must set applehelp_bundle_id ' + 'before building Apple Help output') + raise SphinxError(msg) self.bundle_path = path.join(self.outdir, self.config.applehelp_bundle_name + '.help') - self.outdir = path.join( # type: ignore[assignment] + self.outdir = type(self.outdir)(Path( self.bundle_path, 'Contents', 'Resources', self.config.applehelp_locale + '.lproj', - ) + )) def handle_finish(self) -> None: super().handle_finish() @@ -94,7 +102,7 @@ def handle_finish(self) -> None: @progress_message(__('copying localized files')) def copy_localized_files(self) -> None: - source_dir = path.join(self.confdir, self.config.applehelp_locale + '.lproj') # type: ignore # NOQA + source_dir = path.join(self.confdir, self.config.applehelp_locale + '.lproj') target_dir = self.outdir if path.isdir(source_dir): @@ -175,14 +183,14 @@ def build_helpindex(self, language_dir: str) -> None: self.config.applehelp_indexer_path, '-Cf', path.join(language_dir, 'search.helpindex'), - language_dir + language_dir, ] if self.config.applehelp_index_anchors is not None: args.append('-a') if self.config.applehelp_min_term_length is not None: - args += ['-m', '%s' % self.config.applehelp_min_term_length] + args += ['-m', f'{self.config.applehelp_min_term_length}'] if self.config.applehelp_stopwords is not None: args += ['-s', self.config.applehelp_stopwords] @@ -196,10 +204,11 @@ def build_helpindex(self, language_dir: str) -> None: else: try: subprocess.run(args, stdout=PIPE, stderr=STDOUT, check=True) - except OSError: - raise AppleHelpIndexerFailed(__('Command not found: %s') % args[0]) - except CalledProcessError as exc: - raise AppleHelpIndexerFailed(exc.stdout) + except OSError as err: + msg = __('Command not found: %s') % args[0] + raise AppleHelpIndexerFailed(msg) from err + except CalledProcessError as err: + raise AppleHelpIndexerFailed(err.stdout) from err @progress_message(__('signing help book')) def do_codesign(self) -> None: @@ -207,7 +216,7 @@ def do_codesign(self) -> None: args = [ self.config.applehelp_codesign_path, '-s', self.config.applehelp_codesign_identity, - '-f' + '-f', ] args += self.config.applehelp_codesign_flags @@ -220,10 +229,11 @@ def do_codesign(self) -> None: else: try: subprocess.run(args, stdout=PIPE, stderr=STDOUT, check=True) - except OSError: - raise AppleHelpCodeSigningFailed(__('Command not found: %s') % args[0]) - except CalledProcessError as exc: - raise AppleHelpCodeSigningFailed(exc.stdout) + except OSError as err: + msg = __('Command not found: %s') % args[0] + raise AppleHelpCodeSigningFailed(msg) from err + except CalledProcessError as err: + raise AppleHelpCodeSigningFailed(err.stdout) from err def setup(app: Sphinx) -> dict[str, Any]: @@ -239,7 +249,7 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_config_value('applehelp_bundle_version', '1', 'applehelp') app.add_config_value('applehelp_icon', None, 'applehelp', [str]) app.add_config_value('applehelp_kb_product', - lambda self: '%s-%s' % (make_filename(self.project), self.release), + lambda self: f'{make_filename(self.project)}-{self.release}', 'applehelp') app.add_config_value('applehelp_kb_url', None, 'applehelp', [str]) app.add_config_value('applehelp_remote_url', None, 'applehelp', [str]) diff --git a/sphinxcontrib/applehelp/py.typed b/sphinxcontrib/applehelp/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py index d4b08e5..cae9635 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,17 +1,12 @@ +from __future__ import annotations + from pathlib import Path import pytest -import sphinx - pytest_plugins = 'sphinx.testing.fixtures' @pytest.fixture(scope='session') -def rootdir(): - if sphinx.version_info[:2] < (7, 2): - from sphinx.testing.path import path - - return path(__file__).parent.abspath() / 'roots' - +def rootdir() -> Path: return Path(__file__).resolve().parent / 'roots' diff --git a/tests/test_applehelp.py b/tests/test_applehelp.py index 707578d..862cb5a 100644 --- a/tests/test_applehelp.py +++ b/tests/test_applehelp.py @@ -1,12 +1,18 @@ """Test for applehelp extension.""" -from pathlib import Path +from __future__ import annotations + import plistlib +from pathlib import Path +from typing import TYPE_CHECKING import pytest +if TYPE_CHECKING: + from sphinx.application import Sphinx + -def check_structure(outdir): +def check_structure(outdir: Path) -> None: contentsdir = outdir / 'Contents' assert contentsdir.is_dir() assert (contentsdir / 'Info.plist').is_file() @@ -21,7 +27,7 @@ def check_structure(outdir): assert (contentsdir / 'Resources' / 'en.lproj').is_dir() -def check_localization(outdir): +def check_localization(outdir: Path) -> None: lprojdir = outdir / 'Contents' / 'Resources' / 'en.lproj' assert (lprojdir / 'localized.txt').is_file() @@ -30,7 +36,7 @@ def check_localization(outdir): 'applehelp', testroot='basic', srcdir='applehelp_output', confoverrides={'applehelp_bundle_id': 'org.sphinx-doc.Sphinx.help', 'applehelp_disable_external_tools': True}) -def test_applehelp_output(app, status, warning): +def test_applehelp_output(app: Sphinx) -> None: LPROJ_DIR = Path(app.srcdir / 'en.lproj') LPROJ_DIR.mkdir(parents=True, exist_ok=True) LPROJ_DIR.joinpath('localized.txt').touch() @@ -39,6 +45,6 @@ def test_applehelp_output(app, status, warning): # Have to use bundle_path, not outdir, because we alter the latter # to point to the lproj directory so that the HTML arrives in the # correct location. - bundle_path = Path(app.builder.bundle_path) + bundle_path = Path(app.builder.bundle_path) # type: ignore[attr-defined] check_structure(bundle_path) check_localization(bundle_path) diff --git a/tox.ini b/tox.ini index a63d1c6..00527f6 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ minversion = 2.4.0 envlist = py{39,310,311,312,313}, - flake8, + ruff, mypy isolated_build = True @@ -28,13 +28,13 @@ setenv = commands= python -X dev -X warn_default_encoding -m pytest --durations 25 {posargs} -[testenv:flake8] +[testenv:ruff] description = Run style checks. extras= lint commands= - flake8 + ruff check [testenv:mypy] description =