diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 36883d7e..00000000 --- a/.codecov.yml +++ /dev/null @@ -1,11 +0,0 @@ -codecov: - ci: - - "!ci.appveyor.com" -coverage: - status: - patch: false - project: - default: - target: 90 - -comment: false diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index 6bcd2625..b1a0a025 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -6,18 +6,24 @@ jobs: style: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 + - name: Download source + uses: actions/checkout@v6 + - name: Install Python + uses: actions/setup-python@v6 with: - python-version: '3.11' - - name: Install Python dependencies + python-version: '3.13' + - name: Install Hatch run: | - python -m pip install --upgrade hatch + pip install hatch + - name: Install dependencies + run: | + hatch run style:pip freeze - name: Fix code style - run: hatch run style:fix --fix-only + run: | + hatch run style:fix --fix-only - name: Check if any edits are necessary - run: git diff --color --exit-code + run: | + git diff --color --exit-code - name: Apply automatic fixes using pre-commit-ci-lite if: failure() && github.event_name == 'pull_request' - uses: pre-commit-ci/lite-action@v1.0.1 + uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51cada0c..111353d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,72 +4,92 @@ on: pull_request: schedule: - cron: '0 6 * * 6' +defaults: + run: + shell: bash jobs: test: strategy: fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.9-v7.x'] - os: [ubuntu-latest, windows-latest, macos-latest] include: - - python-version: pypy-3.9-v7.x - py: pypy3 - # Just to slim down the test matrix: - exclude: - - python-version: '3.9' + - python: '3.14' + os: ubuntu-latest + - python: '3.14' + os: windows-latest + - python: '3.14' + os: macos-latest + - python: '3.13' + os: ubuntu-latest + - python: '3.13' + os: windows-latest + - python: '3.12' os: macos-latest - - python-version: '3.9' + - python: '3.12' + os: ubuntu-latest + - python: '3.12' os: windows-latest - - python-version: '3.10' + - python: '3.11' + os: ubuntu-latest + - python: '3.11' + os: macos-latest + - python: '3.10' os: ubuntu-latest - - python-version: '3.11' + - python: '3.10' os: macos-latest - - python-version: '3.11' + - python: '3.9' os: windows-latest - runs-on: ${{ matrix.os }} + - python: '3.9' + os: ubuntu-latest + versions: minimal + runs-on: ${{matrix.os}} steps: - - uses: actions/checkout@v4 - - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - name: Download source + uses: actions/checkout@v6 + - name: Install Python + uses: actions/setup-python@v6 with: - python-version: ${{ matrix.python-version }} + python-version: ${{matrix.python}} + - name: Pin to lowest versions + if: matrix.versions == 'minimal' + run: | + sed -i -E 's/#min //; s/\b >=([0-9])/ ==\1/' pyproject.toml + - name: Install Hatch + run: | + pip install hatch - name: Install dependencies run: | - python -m pip install --upgrade hatch + hatch run test:pip freeze - name: Run tests run: | - hatch run +py=${{ matrix.py || matrix.python-version }} test:with-coverage + hatch run +py=${{matrix.python}} test:test - name: Run integration tests + if: matrix.versions != 'minimal' run: | - hatch run +py=${{ matrix.py || matrix.python-version }} integration:test - shell: bash - - name: Upload Codecov Results - if: success() - uses: codecov/codecov-action@v3 - with: - file: ./coverage.xml - flags: unittests - name: ${{ matrix.os }}/${{ matrix.python-version }} - fail_ci_if_error: false - - lint: + hatch run +py=${{matrix.python}} integration:test + style: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 + - name: Download source + uses: actions/checkout@v6 + - name: Install Python + uses: actions/setup-python@v6 with: - python-version: '3.11' - - name: Install Python dependencies + python-version: '3.13' + - name: Install Hatch run: | - python -m pip install --upgrade hatch - - name: Setup Node - uses: actions/setup-node@v4 + pip install hatch + - name: Install Node + uses: actions/setup-node@v6 with: - node-version: 20 + node-version: 24 + - name: Install dependencies + run: | + hatch run style:pip freeze + hatch run types:pip freeze - name: Check with ruff if: always() - run: hatch run style:lint + run: hatch run style:check - name: Check with mypy if: always() run: hatch run types:check @@ -86,11 +106,11 @@ jobs: package: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version: '3.13' - name: Install dependencies run: pip install -U build - name: Build package diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 00000000..6c71103b --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,38 @@ +name: Deploy docs +on: + push: + pull_request: + schedule: + - cron: '0 6 * * 6' +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Download source + uses: actions/checkout@v6 + - name: Install Python + uses: actions/setup-python@v6 + with: + python-version: '3.13' + - name: Install dependencies + run: pip install --no-deps -r requirements/requirements-docs.txt && pip install . + - name: Build site + run: properdocs build --strict + - name: Upload to GitHub Pages + uses: actions/upload-pages-artifact@v4 + with: + path: site + deploy: + if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch + needs: build + permissions: + pages: write + id-token: write + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml index 7db41a5c..fa736720 100644 --- a/.github/workflows/deploy-release.yml +++ b/.github/workflows/deploy-release.yml @@ -9,11 +9,11 @@ jobs: id-token: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version: '3.13' - name: Install dependencies run: pip install -U build - name: Build package diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 5c5d028e..00000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Docs -on: - push: - pull_request: - schedule: - - cron: '0 6 * * 6' -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Download source - uses: actions/checkout@v4 - - name: Install Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - name: Install dependencies - run: pip install --no-deps -r requirements/requirements-docs.txt - - name: Build site - run: properdocs build --strict diff --git a/README.md b/README.md index a63d6605..1f001fed 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ [![PyPI Version][pypi-v-image]][pypi-v-link] [![Build Status][GHAction-image]][GHAction-link] -[![Coverage Status][codecov-image]][codecov-link] ProperDocs is a **fast**, **simple** and **downright gorgeous** static site generator that's geared towards building project documentation. Documentation @@ -58,8 +57,6 @@ Everyone interacting in the ProperDocs project's codebases, issue trackers, and discussion forums is expected to follow the [PyPA Code of Conduct]. -[codecov-image]: https://codecov.io/github/properdocs/properdocs/coverage.svg?branch=master -[codecov-link]: https://codecov.io/github/properdocs/properdocs?branch=master [pypi-v-image]: https://img.shields.io/pypi/v/properdocs.svg [pypi-v-link]: https://pypi.org/project/properdocs/ [GHAction-image]: https://github.com/properdocs/properdocs/actions/workflows/ci.yml/badge.svg diff --git a/properdocs/__main__.py b/properdocs/__main__.py index 8e700a6d..bd0c5795 100644 --- a/properdocs/__main__.py +++ b/properdocs/__main__.py @@ -12,8 +12,12 @@ import click -from properdocs import replacement # noqa: F401 -from properdocs import __version__, config, utils +from properdocs import ( + __version__, + config, + replacement, # noqa: F401 + utils, +) if sys.platform.startswith("win"): try: @@ -122,16 +126,13 @@ def __del__(self): reload_help = "Enable the live reloading in the development server (this is the default)" no_reload_help = "Disable the live reloading in the development server." serve_dirty_help = "Only re-build files that have changed." -serve_clean_help = ( - "Build the site without any effects of `properdocs serve` - pure `properdocs build`, then serve." -) +serve_clean_help = "Build the site without any effects of `properdocs serve` - pure `properdocs build`, then serve." commit_message_help = ( "A commit message to use when committing to the " "GitHub Pages remote branch. Commit {sha} and ProperDocs {version} are available as expansions" ) remote_branch_help = ( - "The remote branch to commit to for GitHub Pages. This " - "overrides the value specified in config" + "The remote branch to commit to for GitHub Pages. This overrides the value specified in config" ) remote_name_help = ( "The remote name to commit to for GitHub Pages. This overrides the value specified in config" @@ -243,7 +244,7 @@ def callback(ctx, param, value): __version__, '-V', '--version', - message=f'%(prog)s, version %(version)s from { PKG_DIR } (Python { PYTHON_VERSION })', + message=f'%(prog)s, version %(version)s from {PKG_DIR} (Python {PYTHON_VERSION})', ) @common_options @color_option diff --git a/properdocs/commands/build.py b/properdocs/commands/build.py index 217a38d2..de2c946e 100644 --- a/properdocs/commands/build.py +++ b/properdocs/commands/build.py @@ -4,7 +4,8 @@ import logging import os import time -from typing import TYPE_CHECKING, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING from urllib.parse import urljoin, urlsplit import jinja2 @@ -16,8 +17,10 @@ from properdocs.structure.files import File, Files, InclusionLevel, get_files, set_exclusions from properdocs.structure.nav import Navigation, get_navigation from properdocs.structure.pages import Page -from properdocs.utils import DuplicateFilter # noqa: F401 - legacy re-export -from properdocs.utils import templates +from properdocs.utils import ( + DuplicateFilter, # noqa: F401 - legacy re-export + templates, +) if TYPE_CHECKING: from properdocs.config.defaults import ProperDocsConfig diff --git a/properdocs/config/base.py b/properdocs/config/base.py index 558f24a4..6fbf69eb 100644 --- a/properdocs/config/base.py +++ b/properdocs/config/base.py @@ -6,20 +6,9 @@ import sys import warnings from collections import UserDict +from collections.abc import Iterator, Mapping, Sequence from contextlib import contextmanager -from typing import ( - IO, - TYPE_CHECKING, - Any, - Generic, - Iterator, - List, - Mapping, - Sequence, - Tuple, - TypeVar, - overload, -) +from typing import IO, TYPE_CHECKING, Any, Generic, TypeVar, overload from properdocs import exceptions, utils from properdocs.utils import weak_property @@ -86,12 +75,10 @@ def __set_name__(self, owner, name): self._name = name @overload - def __get__(self, obj: Config, type=None) -> T: - ... + def __get__(self, obj: Config, type=None) -> T: ... @overload - def __get__(self, obj, type=None) -> BaseConfigOption: - ... + def __get__(self, obj, type=None) -> BaseConfigOption: ... def __get__(self, obj, type=None): if not isinstance(obj, Config): @@ -113,11 +100,11 @@ def __eq__(self, other): return type(self) is type(other) and str(self) == str(other) -PlainConfigSchemaItem = Tuple[str, BaseConfigOption] +PlainConfigSchemaItem = tuple[str, BaseConfigOption] PlainConfigSchema = Sequence[PlainConfigSchemaItem] -ConfigErrors = List[Tuple[str, Exception]] -ConfigWarnings = List[Tuple[str, str]] +ConfigErrors = list[tuple[str, Exception]] +ConfigWarnings = list[tuple[str, str]] class Config(UserDict): @@ -270,7 +257,7 @@ def user_configs(self) -> Sequence[Mapping[str, Any]]: return self.__user_configs -@functools.lru_cache(maxsize=None) +@functools.cache def get_schema(cls: type) -> PlainConfigSchema: """Extract ConfigOptions defined in a class (used just as a container) and put them into a schema tuple.""" if issubclass(cls, Config): diff --git a/properdocs/config/config_options.py b/properdocs/config/config_options.py index 0902e1fa..6f936e81 100644 --- a/properdocs/config/config_options.py +++ b/properdocs/config/config_options.py @@ -10,22 +10,9 @@ import types import warnings from collections import Counter, UserString +from collections.abc import Collection, Iterator, Mapping, MutableMapping from types import SimpleNamespace -from typing import ( - Any, - Callable, - Collection, - Dict, - Generic, - Iterator, - List, - Mapping, - MutableMapping, - NamedTuple, - TypeVar, - Union, - overload, -) +from typing import Any, Callable, Generic, NamedTuple, TypeVar, Union, overload from urllib.parse import quote as urlquote from urllib.parse import urlsplit, urlunsplit @@ -154,12 +141,10 @@ class OptionallyRequired(Generic[T], BaseConfigOption[T]): """ @overload - def __init__(self, default=None): - ... + def __init__(self, default=None): ... @overload - def __init__(self, default=None, *, required: bool): - ... + def __init__(self, default=None, *, required: bool): ... def __init__(self, default=None, required=None): super().__init__() @@ -186,7 +171,7 @@ def validate(self, value): return self.run_validation(value) -class ListOfItems(Generic[T], BaseConfigOption[List[T]]): +class ListOfItems(Generic[T], BaseConfigOption[list[T]]): """ Validates a homogeneous list of items. @@ -241,7 +226,7 @@ def run_validation(self, value: object) -> list[T]: return [fake_config[k] for k in fake_keys] -class DictOfItems(Generic[T], BaseConfigOption[Dict[str, T]]): +class DictOfItems(Generic[T], BaseConfigOption[dict[str, T]]): """ Validates a dict of items. Keys are always strings. @@ -307,12 +292,10 @@ class ConfigItems(ListOfItems[LegacyConfig]): """ @overload - def __init__(self, *config_options: PlainConfigSchemaItem): - ... + def __init__(self, *config_options: PlainConfigSchemaItem): ... @overload - def __init__(self, *config_options: PlainConfigSchemaItem, required: bool): - ... + def __init__(self, *config_options: PlainConfigSchemaItem, required: bool): ... def __init__(self, *config_options: PlainConfigSchemaItem, required=None) -> None: super().__init__(SubConfig(*config_options), default=[]) @@ -328,12 +311,10 @@ class Type(Generic[T], OptionallyRequired[T]): """ @overload - def __init__(self, type_: type[T], /, length: int | None = None, **kwargs): - ... + def __init__(self, type_: type[T], /, length: int | None = None, **kwargs): ... @overload - def __init__(self, type_: tuple[type[T], ...], /, length: int | None = None, **kwargs): - ... + def __init__(self, type_: tuple[type[T], ...], /, length: int | None = None, **kwargs): ... def __init__(self, type_, /, length=None, **kwargs) -> None: super().__init__(**kwargs) @@ -497,12 +478,10 @@ class URL(OptionallyRequired[str]): """ @overload - def __init__(self, default=None, *, is_dir: bool = False): - ... + def __init__(self, default=None, *, is_dir: bool = False): ... @overload - def __init__(self, default=None, *, required: bool, is_dir: bool = False): - ... + def __init__(self, default=None, *, required: bool, is_dir: bool = False): ... def __init__(self, default=None, required=None, is_dir: bool = False) -> None: self.is_dir = is_dir @@ -759,12 +738,10 @@ class ListOfPaths(ListOfItems[str]): """ @overload - def __init__(self, default=[]): - ... + def __init__(self, default=[]): ... @overload - def __init__(self, default=[], *, required: bool): - ... + def __init__(self, default=[], *, required: bool): ... def __init__(self, default=[], required=None) -> None: super().__init__(FilesystemObject(exists=True), default) @@ -952,7 +929,7 @@ def run_validation(self, value: object) -> ExtraScriptValue | str: return self.option_type.run_validation(value) -class MarkdownExtensions(OptionallyRequired[List[str]]): +class MarkdownExtensions(OptionallyRequired[list[str]]): """ Markdown Extensions Config Option. @@ -1088,8 +1065,7 @@ def _parse_configs(cls, value: list | tuple | dict) -> Iterator[tuple[str, dict] def load_plugin_with_namespace(self, name: str, config) -> tuple[str, plugins.BasePlugin]: if '/' in name: # It's already specified with a namespace. # Special case: allow to explicitly skip namespaced loading: - if name.startswith('/'): - name = name[1:] + name = name.removeprefix('/') else: # Attempt to load with prepended namespace for the current theme. if self.theme_key and self._config: @@ -1165,7 +1141,7 @@ def load_plugin(self, name: str, config) -> plugins.BasePlugin: return plugin -class Hooks(BaseConfigOption[List[types.ModuleType]]): +class Hooks(BaseConfigOption[list[types.ModuleType]]): """A list of Python scripts to be treated as instances of plugins.""" def __init__(self, plugins_key: str) -> None: @@ -1187,7 +1163,7 @@ def run_validation(self, value: object) -> Mapping[str, Any]: hooks[name] = self._load_hook(name, path) return hooks - @functools.lru_cache(maxsize=None) + @functools.cache def _load_hook(self, name, path): import importlib.util diff --git a/properdocs/config/defaults.py b/properdocs/config/defaults.py index a14fc4bf..3614f74d 100644 --- a/properdocs/config/defaults.py +++ b/properdocs/config/defaults.py @@ -1,7 +1,8 @@ from __future__ import annotations import logging -from typing import IO, Dict, Mapping +from collections.abc import Mapping +from typing import IO from properdocs.config import base from properdocs.config import config_options as c @@ -134,7 +135,7 @@ class ProperDocsConfig(base.Config): ) """PyMarkdown extension names.""" - mdx_configs = c.Private[Dict[str, dict]]() + mdx_configs = c.Private[dict[str, dict]]() """PyMarkdown extension configs. Populated from `markdown_extensions`.""" strict = c.Type(bool, default=False) diff --git a/properdocs/contrib/search/__init__.py b/properdocs/contrib/search/__init__.py index 5b939a83..ba6d9d47 100644 --- a/properdocs/contrib/search/__init__.py +++ b/properdocs/contrib/search/__init__.py @@ -2,7 +2,7 @@ import logging import os -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING from properdocs import utils from properdocs.config import base @@ -20,7 +20,7 @@ base_path = os.path.dirname(os.path.abspath(__file__)) -class LangOption(c.OptionallyRequired[List[str]]): +class LangOption(c.OptionallyRequired[list[str]]): """Validate Language(s) provided in config are known languages.""" def get_lunr_supported_lang(self, lang): diff --git a/properdocs/livereload/__init__.py b/properdocs/livereload/__init__.py index 7f71566c..f9e0a0b2 100644 --- a/properdocs/livereload/__init__.py +++ b/properdocs/livereload/__init__.py @@ -21,7 +21,8 @@ import webbrowser import wsgiref.simple_server import wsgiref.util -from typing import Any, BinaryIO, Callable, Iterable +from collections.abc import Iterable +from typing import Any, BinaryIO, Callable import watchdog.events import watchdog.observers.polling diff --git a/properdocs/localization.py b/properdocs/localization.py index 41b5f702..abcc7b06 100644 --- a/properdocs/localization.py +++ b/properdocs/localization.py @@ -2,7 +2,8 @@ import logging import os -from typing import TYPE_CHECKING, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING from jinja2.ext import Extension, InternationalizationExtension diff --git a/properdocs/plugins.py b/properdocs/plugins.py index e121d373..ff1b347f 100644 --- a/properdocs/plugins.py +++ b/properdocs/plugins.py @@ -4,7 +4,8 @@ import logging import sys -from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, MutableMapping, TypeVar, overload +from collections.abc import MutableMapping +from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, TypeVar, overload if sys.version_info >= (3, 10): from importlib.metadata import EntryPoint, entry_points @@ -45,7 +46,7 @@ def get_plugins() -> dict[str, EntryPoint]: """Return a dict of all installed Plugins as {name: EntryPoint}.""" - pluginmaps = {'properdocs': {}, 'mkdocs': {}} + pluginmaps: dict[str, dict[str, EntryPoint]] = {'properdocs': {}, 'mkdocs': {}} for prefix in pluginmaps: for plugin in entry_points(group=f'{prefix}.plugins'): @@ -548,12 +549,10 @@ def __setitem__(self, key: str, value: BasePlugin) -> None: self._register_event(event_name[3:], method, plugin_name=key) @overload - def run_event(self, name: str, **kwargs) -> Any: - ... + def run_event(self, name: str, **kwargs) -> Any: ... @overload - def run_event(self, name: str, item: T, **kwargs) -> T: - ... + def run_event(self, name: str, item: T, **kwargs) -> T: ... def run_event(self, name: str, item=None, **kwargs): """ diff --git a/properdocs/structure/__init__.py b/properdocs/structure/__init__.py index d7ad60a9..636e7f98 100644 --- a/properdocs/structure/__init__.py +++ b/properdocs/structure/__init__.py @@ -1,7 +1,8 @@ from __future__ import annotations import abc -from typing import TYPE_CHECKING, Iterable +from collections.abc import Iterable +from typing import TYPE_CHECKING if TYPE_CHECKING: from properdocs.structure.nav import Section @@ -11,8 +12,7 @@ class StructureItem(metaclass=abc.ABCMeta): """An item in ProperDocs structure - see concrete subclasses Section, Page or Link.""" @abc.abstractmethod - def __init__(self): - ... + def __init__(self): ... parent: Section | None = None """The immediate parent of the item in the site navigation. `None` if it's at the top level.""" diff --git a/properdocs/structure/files.py b/properdocs/structure/files.py index a384af22..67c372bc 100644 --- a/properdocs/structure/files.py +++ b/properdocs/structure/files.py @@ -7,9 +7,10 @@ import posixpath import shutil import warnings +from collections.abc import Iterable, Iterator, Mapping, Sequence from functools import cached_property from pathlib import PurePath, PurePosixPath -from typing import TYPE_CHECKING, Callable, Iterable, Iterator, Mapping, Sequence, overload +from typing import TYPE_CHECKING, Callable, overload from urllib.parse import quote as urlquote import pathspec diff --git a/properdocs/structure/nav.py b/properdocs/structure/nav.py index 435e965e..d6bd8d1b 100644 --- a/properdocs/structure/nav.py +++ b/properdocs/structure/nav.py @@ -1,7 +1,8 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, Iterator, TypeVar +from collections.abc import Iterator +from typing import TYPE_CHECKING, TypeVar from urllib.parse import urlsplit from properdocs.exceptions import BuildError diff --git a/properdocs/structure/pages.py b/properdocs/structure/pages.py index 63ef132b..8e141a0c 100644 --- a/properdocs/structure/pages.py +++ b/properdocs/structure/pages.py @@ -4,21 +4,20 @@ import logging import posixpath import warnings -from typing import TYPE_CHECKING, Any, Callable, Iterator, MutableMapping, Sequence +from collections.abc import Iterator, MutableMapping, Sequence +from typing import TYPE_CHECKING, Any, Callable from urllib.parse import unquote as urlunquote from urllib.parse import urljoin, urlsplit, urlunsplit import markdown -import markdown.extensions.toc import markdown.htmlparser # type: ignore -import markdown.postprocessors import markdown.treeprocessors from markdown.util import AMP_SUBSTITUTE from properdocs import utils from properdocs.structure import StructureItem from properdocs.structure.toc import get_toc -from properdocs.utils import _removesuffix, get_build_date, get_markdown_title, meta, weak_property +from properdocs.utils import get_build_date, get_markdown_title, meta, weak_property from properdocs.utils.rendering import get_heading_text if TYPE_CHECKING: @@ -408,7 +407,7 @@ def _possible_target_uris( and not path.endswith('.md') and (use_directory_urls or not path.endswith('/')) ): - suffixes.append(lambda p: _removesuffix(p, '.html') + '.md') + suffixes.append(lambda p: p.removesuffix('.html') + '.md') for pref in prefixes: for suf in suffixes: @@ -534,7 +533,9 @@ def run(self, lines: list[str]) -> list[str]: def _register(self, md: markdown.Markdown) -> None: md.preprocessors.register( - self, "properdocs_raw_html", priority=21 # Right before 'html_block'. + self, + "properdocs_raw_html", + priority=21, # Right before 'html_block'. ) @@ -563,7 +564,11 @@ def run(self, root: etree.Element) -> etree.Element: def _register(self, md: markdown.Markdown) -> None: self.md = md - md.treeprocessors.register(self, "properdocs_extract_title", priority=1) # Close to the end. + md.treeprocessors.register( + self, + "properdocs_extract_title", + priority=1, # Close to the end. + ) class _AbsoluteLinksValidationValue(enum.IntEnum): diff --git a/properdocs/structure/toc.py b/properdocs/structure/toc.py index e1df40be..7a72f5a9 100644 --- a/properdocs/structure/toc.py +++ b/properdocs/structure/toc.py @@ -5,9 +5,11 @@ generate a list of dicts for each toc item, and then store it as AnchorLinks to maintain compatibility with older versions of MkDocs. """ + from __future__ import annotations -from typing import Iterable, Iterator, TypedDict +from collections.abc import Iterable, Iterator +from typing import TypedDict class _TocToken(TypedDict): diff --git a/properdocs/tests/config/base_tests.py b/properdocs/tests/config/base_tests.py index 11a44c8f..f7af072f 100644 --- a/properdocs/tests/config/base_tests.py +++ b/properdocs/tests/config/base_tests.py @@ -2,9 +2,8 @@ import unittest from properdocs import exceptions -from properdocs.config import base +from properdocs.config import base, defaults from properdocs.config import config_options as c -from properdocs.config import defaults from properdocs.config.base import ValidationError from properdocs.tests.base import change_dir, tempdir diff --git a/properdocs/tests/config/config_options_tests.py b/properdocs/tests/config/config_options_tests.py index c97a572b..26d63939 100644 --- a/properdocs/tests/config/config_options_tests.py +++ b/properdocs/tests/config/config_options_tests.py @@ -9,7 +9,7 @@ import sys import textwrap import unittest -from typing import TYPE_CHECKING, Any, Dict, List, Optional, TypeVar, Union +from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union from unittest import mock if TYPE_CHECKING: @@ -584,7 +584,7 @@ class Schema(Config): option = c.ListOfItems(c.Type(int)) conf = self.get_config(Schema, {'option': [1, 2, 3]}) - assert_type(conf.option, List[int]) + assert_type(conf.option, list[int]) self.assertEqual(conf.option, [1, 2, 3]) with self.expect_error( @@ -609,7 +609,7 @@ class Schema(Config): option = c.ListOfItems(c.Type(int), default=[]) conf = self.get_config(Schema, {}) - assert_type(conf.option, List[int]) + assert_type(conf.option, list[int]) self.assertEqual(conf.option, []) with self.expect_error(option="Required configuration not provided."): @@ -633,7 +633,7 @@ class Schema(Config): option = c.Optional(c.ListOfItems(c.Type(str))) conf = self.get_config(Schema, {}) - assert_type(conf.option, Optional[List[str]]) + assert_type(conf.option, Optional[list[str]]) self.assertEqual(conf.option, None) conf = self.get_config(Schema, {'option': None}) @@ -647,7 +647,7 @@ class Schema(Config): option = c.ListOfItems(c.Optional(c.Type(int)), default=[]) conf = self.get_config(Schema, {}) - assert_type(conf.option, List[Optional[int]]) + assert_type(conf.option, list[Optional[int]]) self.assertEqual(conf.option, []) conf = self.get_config(Schema, {'option': [4, None]}) @@ -691,7 +691,7 @@ class Schema(Config): option = c.ListOfItems(c.ExtraScript(), default=[]) conf = self.get_config(Schema, {'option': ['foo.js', {'path': 'bar.js', 'async': True}]}) - assert_type(conf.option, List[Union[c.ExtraScriptValue, str]]) + assert_type(conf.option, list[Union[c.ExtraScriptValue, str]]) self.assertEqual(len(conf.option), 2) self.assertIsInstance(conf.option[1], c.ExtraScriptValue) self.assertEqual( @@ -709,7 +709,7 @@ class Schema(Config): conf = self.get_config( Schema, {'option': ['foo.mjs', {'path': 'bar.js', 'type': 'module'}]} ) - assert_type(conf.option, List[Union[c.ExtraScriptValue, str]]) + assert_type(conf.option, list[Union[c.ExtraScriptValue, str]]) self.assertEqual(len(conf.option), 2) self.assertIsInstance(conf.option[0], c.ExtraScriptValue) self.assertEqual( @@ -750,7 +750,7 @@ class Schema(Config): option = c.DictOfItems(c.Type(int)) conf = self.get_config(Schema, {'option': {"a": 1, "b": 2}}) - assert_type(conf.option, Dict[str, int]) + assert_type(conf.option, dict[str, int]) self.assertEqual(conf.option, {"a": 1, "b": 2}) with self.expect_error( @@ -775,7 +775,7 @@ class Schema(Config): option = c.DictOfItems(c.Type(int), default={}) conf = self.get_config(Schema, {}) - assert_type(conf.option, Dict[str, int]) + assert_type(conf.option, dict[str, int]) self.assertEqual(conf.option, {}) with self.expect_error(option="Required configuration not provided."): @@ -799,7 +799,7 @@ class Schema(Config): option = c.Optional(c.DictOfItems(c.Type(str))) conf = self.get_config(Schema, {}) - assert_type(conf.option, Optional[Dict[str, str]]) + assert_type(conf.option, Optional[dict[str, str]]) self.assertEqual(conf.option, None) conf = self.get_config(Schema, {'option': None}) @@ -813,7 +813,7 @@ class Schema(Config): option = c.DictOfItems(c.Optional(c.Type(int)), default={}) conf = self.get_config(Schema, {}) - assert_type(conf.option, Dict[str, Optional[int]]) + assert_type(conf.option, dict[str, Optional[int]]) self.assertEqual(conf.option, {}) conf = self.get_config(Schema, {'option': {"a": 1, "b": None}}) @@ -1014,7 +1014,7 @@ class Schema(Config): option = c.ListOfPaths() conf = self.get_config(Schema, {'option': []}) - assert_type(conf.option, List[str]) + assert_type(conf.option, list[str]) self.assertEqual(conf.option, []) def test_none(self) -> None: @@ -1040,7 +1040,7 @@ class Schema(Config): option = c.ListOfPaths() conf = self.get_config(Schema, {'option': paths}) - assert_type(conf.option, List[str]) + assert_type(conf.option, list[str]) @tempdir() def test_paths_localized_to_config(self, base_path) -> None: @@ -1459,7 +1459,7 @@ class Schema(Config): ] }, ) - assert_type(conf.the_items, List[Sub]) + assert_type(conf.the_items, list[Sub]) self.assertEqual(conf.the_items, [{'value': 'a'}, {'value': 'b'}]) assert_type(conf.the_items[1].value, str) self.assertEqual(conf.the_items[1].value, 'b') @@ -1478,7 +1478,7 @@ class Schema(Config): self.assertEqual(conf.sub, None) conf = self.get_config(Schema, {'sub': [{'opt': 1}, {}]}) - assert_type(conf.sub, Optional[List[Sub]]) + assert_type(conf.sub, Optional[list[Sub]]) self.assertEqual(conf.sub, [{'opt': 1}, {'opt': None}]) assert conf.sub is not None assert_type(conf.sub[0].opt, Optional[int]) @@ -1510,7 +1510,7 @@ class Schema(Config): conf = self.get_config(Schema, {'sub': []}) conf = self.get_config(Schema, {'sub': [{'opt': 1}, {'opt': 2}]}) - assert_type(conf.sub, List[Sub]) + assert_type(conf.sub, list[Sub]) self.assertEqual(conf.sub, [{'opt': 1}, {'opt': 2}]) assert_type(conf.sub[0].opt, int) self.assertEqual(conf.sub[0].opt, 1) @@ -1539,7 +1539,7 @@ class Schema(Config): sub = c.ListOfItems(c.SubConfig(Sub), default=[]) conf = self.get_config(Schema, {}) - assert_type(conf.sub, List[Sub]) + assert_type(conf.sub, list[Sub]) self.assertEqual(conf.sub, []) with self.expect_error(sub="Required configuration not provided."): @@ -1660,14 +1660,14 @@ class MarkdownExtensionsTest(TestCase): def test_simple_list(self) -> None: class Schema(Config): markdown_extensions = c.MarkdownExtensions() - mdx_configs = c.Private[Dict[str, dict]]() + mdx_configs = c.Private[dict[str, dict]]() config = { 'markdown_extensions': ['foo', 'bar'], } conf = self.get_config(Schema, config) - assert_type(conf.markdown_extensions, List[str]) - assert_type(conf.mdx_configs, Dict[str, dict]) + assert_type(conf.markdown_extensions, list[str]) + assert_type(conf.mdx_configs, dict[str, dict]) self.assertEqual(conf.markdown_extensions, ['foo', 'bar']) self.assertEqual(conf.mdx_configs, {}) @@ -1675,7 +1675,7 @@ class Schema(Config): def test_list_dicts(self) -> None: class Schema(Config): markdown_extensions = c.MarkdownExtensions() - mdx_configs = c.Private[Dict[str, dict]]() + mdx_configs = c.Private[dict[str, dict]]() config = { 'markdown_extensions': [ @@ -1698,7 +1698,7 @@ class Schema(Config): def test_mixed_list(self) -> None: class Schema(Config): markdown_extensions = c.MarkdownExtensions() - mdx_configs = c.Private[Dict[str, dict]]() + mdx_configs = c.Private[dict[str, dict]]() config = { 'markdown_extensions': [ @@ -1719,7 +1719,7 @@ class Schema(Config): def test_dict_of_dicts(self) -> None: class Schema(Config): markdown_extensions = c.MarkdownExtensions() - mdx_configs = c.Private[Dict[str, dict]]() + mdx_configs = c.Private[dict[str, dict]]() config = { 'markdown_extensions': { @@ -1742,7 +1742,7 @@ class Schema(Config): def test_builtins(self) -> None: class Schema(Config): markdown_extensions = c.MarkdownExtensions(builtins=['meta', 'toc']) - mdx_configs = c.Private[Dict[str, dict]]() + mdx_configs = c.Private[dict[str, dict]]() config = { 'markdown_extensions': ['foo', 'bar'], @@ -1754,7 +1754,7 @@ class Schema(Config): def test_duplicates(self) -> None: class Schema(Config): markdown_extensions = c.MarkdownExtensions(builtins=['meta', 'toc']) - mdx_configs = c.Private[Dict[str, dict]]() + mdx_configs = c.Private[dict[str, dict]]() config = { 'markdown_extensions': ['meta', 'toc'], @@ -1766,7 +1766,7 @@ class Schema(Config): def test_builtins_config(self) -> None: class Schema(Config): markdown_extensions = c.MarkdownExtensions(builtins=['meta', 'toc']) - mdx_configs = c.Private[Dict[str, dict]]() + mdx_configs = c.Private[dict[str, dict]]() config = { 'markdown_extensions': [ @@ -1781,7 +1781,7 @@ class Schema(Config): def test_configkey(self) -> None: class Schema(Config): markdown_extensions = c.MarkdownExtensions(configkey='bar') - bar = c.Private[Dict[str, dict]]() + bar = c.Private[dict[str, dict]]() config = { 'markdown_extensions': [ @@ -1800,7 +1800,7 @@ class Schema(Config): def test_missing_default(self) -> None: class Schema(Config): markdown_extensions = c.MarkdownExtensions() - mdx_configs = c.Private[Dict[str, dict]]() + mdx_configs = c.Private[dict[str, dict]]() conf = self.get_config(Schema, {}) self.assertEqual(conf.markdown_extensions, []) @@ -1809,7 +1809,7 @@ class Schema(Config): def test_none(self) -> None: class Schema(Config): markdown_extensions = c.MarkdownExtensions(default=[]) - mdx_configs = c.Private[Dict[str, dict]]() + mdx_configs = c.Private[dict[str, dict]]() config = { 'markdown_extensions': None, @@ -1884,7 +1884,7 @@ def test_multiple_markdown_config_instances(self) -> None: # config instances that didn't specify extensions. class Schema(Config): markdown_extensions = c.MarkdownExtensions() - mdx_configs = c.Private[Dict[str, dict]]() + mdx_configs = c.Private[dict[str, dict]]() conf = self.get_config( Schema, @@ -2277,7 +2277,11 @@ class Schema(Config): with self.expect_error(plugins="Invalid Plugins configuration"): self.get_config(Schema, cfg) - cfg = { + def test_plugin_config_multivalue_dict_empty(self) -> None: + class Schema(Config): + plugins = c.Plugins() + + cfg: dict[str, list[dict[str, Any]]] = { 'plugins': [ {}, ], @@ -2407,7 +2411,7 @@ class B(A): self.assertEqual(conf.foo, 1) assert_type(conf.bar, Optional[str]) self.assertEqual(conf.bar, None) - assert_type(conf.baz, List[str]) + assert_type(conf.baz, list[str]) self.assertEqual(conf.baz, ['b']) with self.expect_error(baz="Required configuration not provided."): diff --git a/properdocs/tests/integration.py b/properdocs/tests/integration.py index 372625b8..ada9a4da 100644 --- a/properdocs/tests/integration.py +++ b/properdocs/tests/integration.py @@ -14,7 +14,6 @@ - Build documentation other than just ProperDocs as it is relatively simple. """ - import logging import os import subprocess diff --git a/properdocs/tests/livereload_tests.py b/properdocs/tests/livereload_tests.py index cbeb494e..d76790ab 100644 --- a/properdocs/tests/livereload_tests.py +++ b/properdocs/tests/livereload_tests.py @@ -318,19 +318,19 @@ def test_serves_modified_html(self, site_dir): server.watch(site_dir) headers, output = do_request(server, "GET /normal.html") - self.assertRegex(output, fr"^hello{SCRIPT_REGEX}$") + self.assertRegex(output, rf"^hello{SCRIPT_REGEX}$") self.assertEqual(headers.get("content-type"), "text/html") self.assertEqual(headers.get("content-length"), str(len(output))) _, output = do_request(server, "GET /no_body.html") - self.assertRegex(output, fr"^

hi{SCRIPT_REGEX}$") + self.assertRegex(output, rf"^

hi{SCRIPT_REGEX}$") headers, output = do_request(server, "GET /empty.html") - self.assertRegex(output, fr"^{SCRIPT_REGEX}$") + self.assertRegex(output, rf"^{SCRIPT_REGEX}$") self.assertEqual(headers.get("content-length"), str(len(output))) _, output = do_request(server, "GET /multi_body.html") - self.assertRegex(output, fr"^foobar{SCRIPT_REGEX}$") + self.assertRegex(output, rf"^foobar{SCRIPT_REGEX}$") @tempdir({"index.html": "aaa", "foo/index.html": "bbb"}) def test_serves_directory_index(self, site_dir): diff --git a/properdocs/theme.py b/properdocs/theme.py index 7d90a308..65afed8f 100644 --- a/properdocs/theme.py +++ b/properdocs/theme.py @@ -3,7 +3,8 @@ import logging import os import warnings -from typing import Any, Collection, MutableMapping +from collections.abc import Collection, MutableMapping +from typing import Any import jinja2 import yaml diff --git a/properdocs/utils/__init__.py b/properdocs/utils/__init__.py index 76cf386e..6bf97ffb 100644 --- a/properdocs/utils/__init__.py +++ b/properdocs/utils/__init__.py @@ -4,6 +4,7 @@ Nothing in this module should have an knowledge of config or the layout and structure of the site and pages in the site. """ + from __future__ import annotations import functools @@ -15,9 +16,10 @@ import sys import warnings from collections import defaultdict +from collections.abc import Collection, Iterable, MutableSequence from datetime import datetime, timezone from pathlib import PurePath -from typing import TYPE_CHECKING, Collection, Iterable, MutableSequence, TypeVar +from typing import TYPE_CHECKING, TypeVar from urllib.parse import urlsplit if sys.version_info >= (3, 10): @@ -83,14 +85,7 @@ def get_build_date() -> str: return get_build_datetime().strftime('%Y-%m-%d') -if sys.version_info >= (3, 9): - _removesuffix = str.removesuffix -else: - - def _removesuffix(s: str, suffix: str) -> str: - if suffix and s.endswith(suffix): - return s[: -len(suffix)] - return s +_removesuffix = str.removesuffix def reduce_list(data_set: Iterable[T]) -> list[T]: @@ -166,7 +161,7 @@ def is_error_template(path: str) -> bool: return bool(_ERROR_TEMPLATE_RE.match(path)) -@functools.lru_cache(maxsize=None) +@functools.cache def _norm_parts(path: str) -> list[str]: if not path.startswith('/'): path = '/' + path @@ -216,7 +211,7 @@ def normalize_url(path: str, page: Page | None = None, base: str = '') -> str: return posixpath.join(base, path) -@functools.lru_cache(maxsize=None) +@functools.cache def _get_norm_url(path: str) -> tuple[str, int]: if not path: path = '.' @@ -259,7 +254,7 @@ def get_theme_dir(name: str) -> str: return os.path.dirname(os.path.abspath(theme.load().__file__)) -@functools.lru_cache(maxsize=None) +@functools.cache def get_themes() -> dict[str, EntryPoint]: """Return a dict of all installed themes as {name: EntryPoint}.""" # Ordered set of preferred entry points. diff --git a/properdocs/utils/cache.py b/properdocs/utils/cache.py index 9a21feb0..eeb778e8 100644 --- a/properdocs/utils/cache.py +++ b/properdocs/utils/cache.py @@ -8,7 +8,9 @@ def download_url(url: str) -> bytes: - req = urllib.request.Request(url, headers={"User-Agent": f"properdocs/{properdocs.__version__}"}) + req = urllib.request.Request( + url, headers={"User-Agent": f"properdocs/{properdocs.__version__}"} + ) with urllib.request.urlopen(req) as resp: return resp.read() diff --git a/properdocs/utils/meta.py b/properdocs/utils/meta.py index aa9e8980..174adb20 100644 --- a/properdocs/utils/meta.py +++ b/properdocs/utils/meta.py @@ -32,6 +32,7 @@ Extracts, parses and transforms MultiMarkdown style data from documents. """ + from __future__ import annotations import re diff --git a/properdocs/utils/templates.py b/properdocs/utils/templates.py index 1700f4d3..388fc826 100644 --- a/properdocs/utils/templates.py +++ b/properdocs/utils/templates.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Sequence, TypedDict +from collections.abc import Sequence +from typing import TYPE_CHECKING, TypedDict if TYPE_CHECKING: import datetime diff --git a/pyproject.toml b/pyproject.toml index f5454b44..4fe71bac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,20 +18,21 @@ classifiers = [ "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", + # Begin Python versions "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + # End Python versions "Topic :: Documentation", "Topic :: Text Processing", + "Typing :: Typed", ] dynamic = ["version"] -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ "click >=7.0", "Jinja2 >=2.11.1", @@ -52,29 +53,12 @@ dependencies = [ i18n = [ "babel >=2.9.0", ] -min-versions = [ - "click ==7.0", - "Jinja2 ==2.11.1", - "markupsafe ==2.0.1", - "Markdown ==3.3.6", - "PyYAML ==5.1", - "watchdog ==2.0", - "ghp-import ==1.0", - "pyyaml_env_tag ==0.1", - "importlib-metadata ==4.4; python_version < '3.10'", - "packaging ==20.5", - "mergedeep ==1.3.4", - "pathspec ==0.11.1", - "mkdocs-get-deps ==0.2.0", - "colorama ==0.4; platform_system == 'Windows'", - "babel ==2.9.0", -] [project.urls] Documentation = "https://properdocs.org/" Source = "https://github.com/properdocs/properdocs" Issues = "https://github.com/properdocs/properdocs/issues" -History = "https://properdocs.org/about/release-notes/" +History = "https://github.com/properdocs/properdocs/releases" [project.scripts] properdocs = "properdocs.__main__:cli" @@ -102,7 +86,10 @@ dependencies = [ ] [tool.hatch.env] -requires = ["hatch-mkdocs", "hatch-pip-compile"] +requires = [ + "hatch-mkdocs", + "hatch-pip-compile >=1.7.0", +] [tool.hatch.envs.default.scripts] all = [ @@ -127,12 +114,8 @@ _coverage = [ ] with-coverage = "test" [[tool.hatch.envs.test.matrix]] -python = ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"] -type = ["default", "min-req"] +python = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] [tool.hatch.envs.test.overrides] -matrix.type.features = [ - { value = "min-versions", if = ["min-req"] }, -] matrix.type.scripts = [ { key = "with-coverage", value = "_coverage", if = ["default"] }, ] @@ -144,7 +127,7 @@ detached = false [tool.hatch.envs.integration.scripts] test = "python -m properdocs.tests.integration" [[tool.hatch.envs.integration.matrix]] -python = ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"] +python = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] type = ["default", "no-babel"] [tool.hatch.envs.integration.overrides] matrix.type.features = [ @@ -162,34 +145,21 @@ dependencies = [ "typing-extensions", ] [tool.hatch.envs.types.scripts] -check = "mypy properdocs" +check = "mypy {args} properdocs" [tool.hatch.envs.style] type = "pip-compile" detached = true dependencies = [ - "black", - "isort", "ruff", ] [tool.hatch.envs.style.scripts] -check = [ - "isort --check-only --diff properdocs docs", - "black -q --check --diff properdocs docs", - "lint", -] -lint = [ - "ruff check properdocs docs {args}" -] +check = "ruff check properdocs {args}" +format = "ruff format -q properdocs" fix = [ - "lint --fix", + "check --fix --unsafe-fixes", "format", ] -format = [ - "isort -q properdocs docs", - "black -q properdocs docs", -] - [tool.hatch.envs.lint] detached = true dependencies = [ @@ -207,33 +177,45 @@ path = "mkdocs.yml" type = "pip-compile" detached = false -[tool.black] +[tool.ruff] line-length = 100 -skip-string-normalization = true - -[tool.isort] -profile = "black" -line_length = 100 +[tool.ruff.format] +quote-style = "preserve" -[tool.ruff] +[tool.ruff.lint] +preview = true select = [ - "F", "W", "E", "UP", "YTT", "C4", "DTZ", "FA", "ISC", "PIE", "T20", "RSE", "TCH", - "B002", "B003", "B005", "B007", "B009", "B012", "B013", "B014", "B015", "B018", "B020", "B021", "B023", "B026", "B033", "B034", "B905", + "F", "E", "W", "I", "UP", "YTT", "ASYNC", "C4", "DTZ", "T10", "FA", "ISC", "PIE", "T20", "RSE", + "N803", "N804", "N805", "N806", "N807", "N815", "N999", + "S201", "S202", "S303", "S304", "S305", "S306", "S602", "S604", "S605", "S612", + "B002", "B003", "B004", "B005", "B008", "B009", "B010", "B011", "B012", "B013", "B014", "B015", "B016", "B017", "B018", "B020", "B021", "B022", "B023", "B025", "B026", "B029", "B030", "B031", "B032", "B033", "B034", "B035", "B039", "B909", "COM818", "D200", "D201", "D202", "D204", "D207", "D208", "D209", "D210", "D211", "D213", "D214", "D300", "D301", "D400", "D402", "D403", "D405", "D412", "D414", "D415", "D416", "D417", "D419", - "PERF101", - "PGH002", "PGH004", "PGH005", + "LOG001", "LOG007", "LOG009", "LOG015", + "G001", "G002", "G010", "G101", "G201", "G202", + "PYI001", "PYI002", "PYI003", "PYI004", "PYI005", "PYI006", "PYI007", "PYI008", "PYI009", "PYI010", "PYI011", "PYI012", "PYI013", "PYI014", "PYI015", "PYI016", "PYI017", "PYI018", "PYI020", "PYI021", "PYI024", "PYI025", "PYI026", "PYI029", "PYI030", "PYI032", "PYI033", "PYI035", "PYI036", "PYI041", "PYI042", "PYI043", "PYI044", "PYI045", "PYI046", "PYI047", "PYI048", "PYI049", "PYI050", "PYI051", "PYI052", "PYI053", "PYI054", "PYI055", "PYI056", "PYI057", "PYI058", "PYI061", "PYI062", "PYI063", "PYI064", "PYI066", + "Q004", + "SIM101", "SIM103", "SIM107", "SIM109", "SIM113", "SIM118", "SIM201", "SIM202", "SIM208", "SIM210", "SIM211", "SIM212", "SIM220", "SIM221", "SIM222", "SIM223", "SIM300", "SIM401", "SIM905", "SIM910", "SIM911", + "TC001", "TC002", "TC003", "TC004", "TC005", "TC006", "TC007", "TC008", "TC010", + "PTH201", + "TD004", "TD005", "TD006", "TD007", + "PGH004", "PGH005", + "PLC0105", "PLC0131", "PLC0132", "PLC0205", "PLC0208", "PLC0414", "PLC2401", "PLC2403", "PLC2701", "PLC2801", "PLC3002", + "PLR0124", "PLR0133", "PLR0202", "PLR0203", "PLR0206", "PLR0402", "PLR1716", "PLR1722", "PLR1733", "PLR1736", "PLR2044", + "TRY201", "TRY203", "TRY401", "FLY002", - "PLC", "PLE", "PLR0124", "PLR0133", "PLR0206", "PLR0402", "PLR1701", "PLR1722", "PLW0120", "PLW0127", "PLW0129", "PLW0131", "PLW0406", "PLW0602", "PLW0603", "PLW0711", - "RUF001", "RUF005", "RUF007", "RUF010", "RUF013", "RUF100", "RUF200", - "SIM101", "SIM107", "SIM201", "SIM202", "SIM208", "SIM210", "SIM211", "SIM300", "SIM401", "SIM910", -] -ignore = ["E501", "E731"] - -[tool.ruff.flake8-comprehensions] + "PERF101", "PERF102", "PERF402", "PERF403", + "FURB105", "FURB110", "FURB116", "FURB129", "FURB131", "FURB132", "FURB136", "FURB142", "FURB145", "FURB148", "FURB154", "FURB156", "FURB157", "FURB161", "FURB163", "FURB164", "FURB166", "FURB168", "FURB169", "FURB171", "FURB177", "FURB181", "FURB188", "FURB192", + "RUF001", "RUF002", "RUF003", "RUF005", "RUF006", "RUF007", "RUF008", "RUF009", "RUF010", "RUF013", "RUF016", "RUF017", "RUF018", "RUF019", "RUF020", "RUF021", "RUF023", "RUF024", "RUF026", "RUF028", "RUF029", "RUF030", "RUF031", "RUF032", "RUF033", "RUF034", "RUF036", "RUF037", "RUF038", "RUF039", "RUF040", "RUF041", "RUF046", "RUF047", "RUF048", "RUF049", "RUF051", "RUF055", "RUF056", "RUF057", "RUF058", "RUF100", "RUF101", "RUF200", +] +ignore = ["E501", "E731", "UP038", "UP045", "UP007", "F841"] +[tool.ruff.lint.per-file-ignores] +"properdocs/tests/**" = ["PLC2701", "PLR6301"] +[tool.ruff.lint.flake8-comprehensions] allow-dict-calls-with-keyword-arguments = true +[tool.ruff.lint.flake8-type-checking] +exempt-modules = ["typing", "collections.abc"] [tool.mypy] warn_unreachable = true -no_implicit_optional = true -show_error_codes = true +allow_redefinition = true diff --git a/requirements/requirements-docs.txt b/requirements/requirements-docs.txt index f9cd05d6..4817df17 100644 --- a/requirements/requirements-docs.txt +++ b/requirements/requirements-docs.txt @@ -71,7 +71,7 @@ mergedeep==1.3.4 # hatch.envs.docs # properdocs # mkdocs-get-deps -properdocs==1.5.3 +#properdocs==1.5.3 # via # hatch.envs.docs # mkdocs-autorefs diff --git a/requirements/requirements-style.txt b/requirements/requirements-style.txt index 596540f5..8d988031 100644 --- a/requirements/requirements-style.txt +++ b/requirements/requirements-style.txt @@ -1,24 +1,8 @@ # -# This file is autogenerated by hatch-pip-compile with Python 3.11 +# This file is autogenerated by hatch-pip-compile with Python 3.13 # -# - black -# - isort # - ruff # -black==23.12.1 - # via hatch.envs.style -click==8.1.7 - # via black -isort==5.13.2 - # via hatch.envs.style -mypy-extensions==1.0.0 - # via black -packaging==23.2 - # via black -pathspec==0.12.1 - # via black -platformdirs==4.1.0 - # via black -ruff==0.1.14 +ruff==0.9.6 # via hatch.envs.style