diff --git a/properdocs/__main__.py b/properdocs/__main__.py index 8d395af3..c6509295 100644 --- a/properdocs/__main__.py +++ b/properdocs/__main__.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from __future__ import annotations import logging @@ -9,6 +7,7 @@ import textwrap import traceback import warnings +from typing import ClassVar import click @@ -41,7 +40,7 @@ def _showwarning(message, category, filename, lineno, file=None, line=None): stack = [frame for frame in traceback.extract_stack() if frame.line][-4:-2] # Make sure the actual affected file's name is still present (the case of syntax warning): if not any(frame.filename == filename for frame in stack): - stack = stack[-1:] + [traceback.FrameSummary(filename, lineno, '')] + stack = [*stack[-1:], traceback.FrameSummary(filename, lineno, '')] tb = ''.join(traceback.format_list(stack)) except Exception: @@ -64,7 +63,7 @@ def _enable_warnings(): class ColorFormatter(logging.Formatter): - colors = { + colors: ClassVar = { 'CRITICAL': 'red', 'ERROR': 'red', 'WARNING': 'yellow', diff --git a/properdocs/commands/build.py b/properdocs/commands/build.py index de2c946e..2c8a02ed 100644 --- a/properdocs/commands/build.py +++ b/properdocs/commands/build.py @@ -87,9 +87,7 @@ def _build_template( output = template.render(context) # Run `post_template` plugin events. - output = config.plugins.on_post_template(output, template_name=name, config=config) - - return output + return config.plugins.on_post_template(output, template_name=name, config=config) def _build_theme_template( @@ -321,8 +319,8 @@ def build(config: ProperDocsConfig, *, serve_url: str | None = None, dirty: bool if excluded: log.info( "The following pages are being built only for the preview " - "but will be excluded from `properdocs build` per `draft_docs` config:\n - " - + "\n - ".join(excluded) + "but will be excluded from `properdocs build` per `draft_docs` config:\n - %s", + "\n - ".join(excluded), ) # Run `env` plugin events. diff --git a/properdocs/commands/get_deps.py b/properdocs/commands/get_deps.py index 7b61b29d..8a3b49d1 100644 --- a/properdocs/commands/get_deps.py +++ b/properdocs/commands/get_deps.py @@ -27,7 +27,7 @@ log = logging.getLogger(__name__) -class YamlLoaderWithSuppressions(SafeLoader): # type: ignore +class YamlLoaderWithSuppressions(SafeLoader): # type: ignore # noqa: PGH003 pass diff --git a/properdocs/commands/gh_deploy.py b/properdocs/commands/gh_deploy.py index 331bc463..de3e21f7 100644 --- a/properdocs/commands/gh_deploy.py +++ b/properdocs/commands/gh_deploy.py @@ -6,7 +6,7 @@ import subprocess from typing import TYPE_CHECKING -import ghp_import # type: ignore +import ghp_import # type: ignore[import-untyped] from packaging import version import properdocs @@ -43,8 +43,7 @@ def _get_current_sha(repo_path) -> str: ) stdout, _ = proc.communicate() - sha = stdout.decode('utf-8').strip() - return sha + return stdout.decode('utf-8').strip() def _get_remote_url(remote_name: str) -> tuple[str, str] | tuple[None, None]: @@ -78,7 +77,9 @@ def _check_version(branch: str) -> None: stdout, _ = proc.communicate() msg = stdout.decode('utf-8').strip() - m = re.search(r'\d+(\.\d+)+((a|b|rc)\d+)?(\.post\d+)?(\.dev\d+)?', msg, re.X | re.I) + m = re.search( + r'\d+(\.\d+)+((a|b|rc)\d+)?(\.post\d+)?(\.dev\d+)?', msg, re.VERBOSE | re.IGNORECASE + ) previousv = version.parse(m.group()) if m else None currentv = version.parse(properdocs.__version__) if not previousv: @@ -145,7 +146,7 @@ def gh_deploy( # Does this repository have a CNAME set for GitHub Pages? if os.path.isfile(cname_file): # This GitHub Pages repository has a CNAME configured. - with open(cname_file) as f: + with open(cname_file, encoding='utf-8') as f: cname_host = f.read().strip() log.info( f'Based on your CNAME file, your documentation should be ' @@ -163,7 +164,6 @@ def gh_deploy( log.info('Your documentation should be available shortly.') else: username, repo = path.split('/', 1) - if repo.endswith('.git'): - repo = repo[: -len('.git')] + repo = repo.removesuffix('.git') url = f'https://{username}.github.io/{repo}/' log.info(f"Your documentation should shortly be available at: {url}") diff --git a/properdocs/config/__init__.py b/properdocs/config/__init__.py index 6c0ad139..2df09766 100644 --- a/properdocs/config/__init__.py +++ b/properdocs/config/__init__.py @@ -1,3 +1,3 @@ from properdocs.config.base import Config, load_config -__all__ = ['load_config', 'Config'] +__all__ = ['Config', 'load_config'] diff --git a/properdocs/config/base.py b/properdocs/config/base.py index 5242c769..9c9910df 100644 --- a/properdocs/config/base.py +++ b/properdocs/config/base.py @@ -1,5 +1,6 @@ from __future__ import annotations +import contextlib import functools import logging import os @@ -7,7 +8,6 @@ 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, TypeVar, overload from properdocs import exceptions, utils @@ -136,7 +136,7 @@ def __init_subclass__(cls): "All values are required, or can be wrapped into config_options.Optional" ) - def __new__(cls, *args, **kwargs) -> Config: + def __new__(cls, *args, **kwargs) -> Config: # noqa: PYI034 """Compatibility: allow referring to `LegacyConfig(...)` constructor as `Config(...)`.""" if cls is Config: return LegacyConfig(*args, **kwargs) @@ -273,7 +273,7 @@ def __init__(self, schema: PlainConfigSchema, config_file_path: str | None = Non super().__init__(config_file_path) -@contextmanager +@contextlib.contextmanager def _open_config_file(config_file: str | IO | None) -> Iterator[IO]: """ A context manager which yields an open file descriptor ready to be read. @@ -314,10 +314,8 @@ def _open_config_file(config_file: str | IO | None) -> Iterator[IO]: else: log.debug(f"Loading configuration file: {result_config_file}") # Ensure file descriptor is at beginning - try: + with contextlib.suppress(OSError): result_config_file.seek(0) - except OSError: - pass try: yield result_config_file diff --git a/properdocs/config/config_options.py b/properdocs/config/config_options.py index 334cfdba..740fde20 100644 --- a/properdocs/config/config_options.py +++ b/properdocs/config/config_options.py @@ -1,5 +1,6 @@ from __future__ import annotations +import contextlib import functools import importlib.util import ipaddress @@ -13,7 +14,7 @@ from collections import Counter, UserString from collections.abc import Callable, Collection, Iterator, Mapping, MutableMapping from types import SimpleNamespace -from typing import Any, Generic, NamedTuple, TypeVar, Union, overload +from typing import Any, Generic, NamedTuple, TypeVar, overload from urllib.parse import quote as urlquote from urllib.parse import urlsplit, urlunsplit @@ -93,7 +94,7 @@ def pre_validation(self, config: Config, key_name: str): def run_validation(self, value: object) -> SomeConfig: config = self.config_class(config_file_path=self._config_file_path) try: - config.load_dict(value) # type: ignore + config.load_dict(value) # type: ignore[arg-type] failed, warnings = config.validate() except ConfigurationError as e: raise ValidationError(str(e)) @@ -204,15 +205,13 @@ def run_validation(self, value: object) -> list[T]: return value fake_config = LegacyConfig(()) - try: + with contextlib.suppress(AttributeError): fake_config.config_file_path = self._config.config_file_path - except AttributeError: - pass # Emulate a config-like environment for pre_validation and post_validation. parent_key_name = getattr(self, '_key_name', '') fake_keys = [f'{parent_key_name}[{i}]' for i in range(len(value))] - fake_config.data = dict(zip(fake_keys, value)) + fake_config.data = dict(zip(fake_keys, value, strict=True)) self.option_type.warnings = self.warnings for key_name in fake_config: @@ -259,10 +258,8 @@ def run_validation(self, value: object) -> dict[str, T]: return value fake_config = LegacyConfig(()) - try: + with contextlib.suppress(AttributeError): fake_config.config_file_path = self._config.config_file_path - except AttributeError: - pass # Emulate a config-like environment for pre_validation and post_validation. fake_config.data = value @@ -362,7 +359,7 @@ def __init__(self, choices: Collection[T], default: T | None = None, **kwargs) - def run_validation(self, value: object) -> T: if value not in self.choices: raise ValidationError(f"Expected one of: {self.choices} but received: {value!r}") - return value # type: ignore + return value # type: ignore[return-value] class Deprecated(BaseConfigOption): @@ -508,7 +505,7 @@ def run_validation(self, value: object) -> str: raise ValidationError("The URL isn't valid, it should include the http:// (scheme)") -class Optional(Generic[T], BaseConfigOption[Union[T, None]]): +class Optional(Generic[T], BaseConfigOption[T | None]): """ Wraps a field and makes a None value possible for it when no value is set. @@ -539,7 +536,7 @@ def run_validation(self, value: object) -> T | None: return self.option.validate(value) def post_validation(self, config: Config, key_name: str): - result = self.option.post_validation(config, key_name) # type: ignore + result = self.option.post_validation(config, key_name) # type: ignore[func-returns-value] self.warnings = self.option.warnings return result @@ -940,7 +937,7 @@ def __fspath__(self): return self.path -class ExtraScript(BaseConfigOption[Union[ExtraScriptValue, str]]): +class ExtraScript(BaseConfigOption[ExtraScriptValue | str]): def __init__(self): super().__init__() self.option_type = SubConfig[ExtraScriptValue]() diff --git a/properdocs/contrib/search/__init__.py b/properdocs/contrib/search/__init__.py index ba6d9d47..b22db1c3 100644 --- a/properdocs/contrib/search/__init__.py +++ b/properdocs/contrib/search/__init__.py @@ -30,6 +30,7 @@ def get_lunr_supported_lang(self, lang): lang_part = fallback.get(lang_part, lang_part) if os.path.isfile(os.path.join(base_path, 'lunr-language', f'lunr.{lang_part}.js')): return lang_part + return None def run_validation(self, value: object): if isinstance(value, str): @@ -70,7 +71,7 @@ def on_config(self, config: ProperDocsConfig, **kwargs) -> ProperDocsConfig: path = os.path.join(base_path, 'templates') config.theme.dirs.append(path) if 'search/main.js' not in config.extra_javascript: - config.extra_javascript.append('search/main.js') # type: ignore + config.extra_javascript.append('search/main.js') if self.config.lang is None: # lang setting undefined. Set default based on theme locale validate = _PluginConfig.lang.run_validation diff --git a/properdocs/contrib/search/search_index.py b/properdocs/contrib/search/search_index.py index 6fc56440..ba909efa 100644 --- a/properdocs/contrib/search/search_index.py +++ b/properdocs/contrib/search/search_index.py @@ -13,7 +13,7 @@ from properdocs.structure.toc import AnchorLink, TableOfContents try: - from lunr import lunr # type: ignore + from lunr import lunr # type: ignore[import-not-found] haslunrpy = True except ImportError: diff --git a/properdocs/livereload/__init__.py b/properdocs/livereload/__init__.py index d742252e..d2cead0d 100644 --- a/properdocs/livereload/__init__.py +++ b/properdocs/livereload/__init__.py @@ -1,5 +1,6 @@ from __future__ import annotations +import contextlib import functools import io import ipaddress @@ -375,8 +376,6 @@ def _timestamp() -> int: def _try_relativize_path(path: str) -> str: """Make the path relative to current directory if it's under that directory.""" p = pathlib.Path(path) - try: + with contextlib.suppress(ValueError): p = p.relative_to(os.getcwd()) - except ValueError: - pass return str(p) diff --git a/properdocs/localization.py b/properdocs/localization.py index abcc7b06..0b654726 100644 --- a/properdocs/localization.py +++ b/properdocs/localization.py @@ -18,7 +18,7 @@ has_babel = True except ImportError: # pragma: no cover - from properdocs.utils.babel_stub import Locale, UnknownLocaleError # type: ignore + from properdocs.utils.babel_stub import Locale, UnknownLocaleError # type: ignore[assignment] has_babel = False diff --git a/properdocs/plugins.py b/properdocs/plugins.py index 78dcafb6..7f856fe0 100644 --- a/properdocs/plugins.py +++ b/properdocs/plugins.py @@ -2,14 +2,12 @@ from __future__ import annotations +import contextlib import logging from collections.abc import Callable, MutableMapping from importlib.metadata import EntryPoint, entry_points from typing import TYPE_CHECKING, Any, Concatenate, Generic, Literal, TypeVar, overload -if TYPE_CHECKING: - import jinja2 - from properdocs import utils from properdocs.config.base import ( Config, @@ -20,6 +18,8 @@ ) if TYPE_CHECKING: + import jinja2 + from properdocs.config.defaults import ProperDocsConfig from properdocs.livereload import LiveReloadServer from properdocs.structure.files import Files @@ -67,7 +67,7 @@ class BasePlugin(Generic[SomeConfig]): config_class: type[SomeConfig] = LegacyConfig # type: ignore[assignment] config_scheme: PlainConfigSchema = () - config: SomeConfig = {} # type: ignore[assignment] + config: SomeConfig = {} # type: ignore[assignment] # noqa: RUF012 supports_multiple_instances: bool = False """Set to true in subclasses to declare support for adding the same plugin multiple times.""" @@ -90,7 +90,7 @@ def load_config( ) -> tuple[ConfigErrors, ConfigWarnings]: """Load config from a dict of options. Returns a tuple of (errors, warnings).""" if self.config_class is LegacyConfig: - self.config = LegacyConfig(self.config_scheme, config_file_path=config_file_path) # type: ignore + self.config = LegacyConfig(self.config_scheme, config_file_path=config_file_path) # type: ignore[assignment] else: self.config = self.config_class(config_file_path=config_file_path) @@ -527,10 +527,9 @@ def _register_event( ) utils.insort(events, method, key=lambda m: -getattr(m, 'mkdocs_priority', 0)) if plugin_name: - try: + # Suppress in case the method is somehow not hashable. + with contextlib.suppress(TypeError): self._event_origins[method] = plugin_name - except TypeError: # If the method is somehow not hashable. - pass def __getitem__(self, key: str) -> BasePlugin: return super().__getitem__(key) diff --git a/properdocs/replacement_warning.py b/properdocs/replacement_warning.py index 609e691e..98c3ed60 100644 --- a/properdocs/replacement_warning.py +++ b/properdocs/replacement_warning.py @@ -41,7 +41,7 @@ def setup(): - global _warning_message + global _warning_message # noqa: PLW0603 if not _warning_message: return diff --git a/properdocs/structure/__init__.py b/properdocs/structure/__init__.py index 636e7f98..86496af5 100644 --- a/properdocs/structure/__init__.py +++ b/properdocs/structure/__init__.py @@ -8,7 +8,7 @@ from properdocs.structure.nav import Section -class StructureItem(metaclass=abc.ABCMeta): +class StructureItem(abc.ABC): """An item in ProperDocs structure - see concrete subclasses Section, Page or Link.""" @abc.abstractmethod diff --git a/properdocs/structure/files.py b/properdocs/structure/files.py index c9277da3..7d7d15ff 100644 --- a/properdocs/structure/files.py +++ b/properdocs/structure/files.py @@ -1,5 +1,6 @@ from __future__ import annotations +import contextlib import enum import fnmatch import logging @@ -13,9 +14,7 @@ from typing import TYPE_CHECKING, overload from urllib.parse import quote as urlquote -import pathspec import pathspec.gitignore -import pathspec.util from properdocs import utils @@ -366,7 +365,7 @@ def edit_uri(self) -> str | None: def _get_stem(self) -> str: """Soft-deprecated, do not use.""" filename = posixpath.basename(self.src_uri) - stem, ext = posixpath.splitext(filename) + stem, _ext = posixpath.splitext(filename) return 'index' if stem == 'README' else stem name = cached_property(_get_stem) @@ -375,7 +374,7 @@ def _get_stem(self) -> str: def _get_dest_path(self, use_directory_urls: bool | None = None) -> str: """Soft-deprecated, do not use.""" if self.is_documentation_page(): - parent, filename = posixpath.split(self.src_uri) + parent, _filename = posixpath.split(self.src_uri) if use_directory_urls is None: use_directory_urls = self.use_directory_urls if not use_directory_urls or self.name == 'index': @@ -482,10 +481,9 @@ def copy_file(self, dirty: bool = False) -> None: content = self._content if content is None: assert self.abs_src_path is not None - try: + # Suppress errors to let plugins write directly into site_dir. + with contextlib.suppress(shutil.SameFileError): utils.copy_file(self.abs_src_path, output_path) - except shutil.SameFileError: - pass # Let plugins write directly into site_dir. elif isinstance(content, str): with open(output_path, 'w', encoding='utf-8') as output_file: output_file.write(content) @@ -576,15 +574,12 @@ def get_files(config: ProperDocsConfig) -> Files: log.warning( f"Excluding '{a.src_uri}' from the site because it conflicts with '{b.src_uri}'." ) - try: + # Suppressing this to avoid errors if attempting to remove the same file twice. + with contextlib.suppress(ValueError): files.remove(a) - except ValueError: - pass # Catching this to avoid errors if attempting to remove the same file twice. else: - try: + with contextlib.suppress(ValueError): files.remove(b) - except ValueError: - pass return Files(files) diff --git a/properdocs/structure/nav.py b/properdocs/structure/nav.py index c885e646..7845829c 100644 --- a/properdocs/structure/nav.py +++ b/properdocs/structure/nav.py @@ -159,15 +159,16 @@ def get_navigation(files: Files, config: ProperDocsConfig) -> Navigation: log.log( config.validation.nav.omitted_files, 'The following pages exist in the docs directory, but are not ' - 'included in the "nav" configuration:\n - ' + '\n - '.join(missing_from_config), + 'included in the "nav" configuration:\n - %s', + '\n - '.join(missing_from_config), ) links = _get_by_type(items, Link) for link in links: invalid: str | None = None try: - scheme, netloc, path, query, fragment = urlsplit(link.url) - except Exception as e: + scheme, netloc, _path, _query, _fragment = urlsplit(link.url) + except Exception: invalid = 'invalid' else: if scheme or netloc: @@ -254,6 +255,6 @@ def _add_parent_links(nav) -> None: def _add_previous_and_next_links(pages: list[Page]) -> None: bookended = [None, *pages, None] - zipped = zip(bookended[:-2], pages, bookended[2:]) + zipped = zip(bookended[:-2], pages, bookended[2:], strict=True) for page0, page1, page2 in zipped: page1.previous_page, page1.next_page = page0, page2 diff --git a/properdocs/structure/pages.py b/properdocs/structure/pages.py index 8c58e24f..379186be 100644 --- a/properdocs/structure/pages.py +++ b/properdocs/structure/pages.py @@ -10,7 +10,7 @@ from urllib.parse import urljoin, urlsplit, urlunsplit import markdown -import markdown.htmlparser # type: ignore +import markdown.htmlparser import markdown.treeprocessors from markdown.util import AMP_SUBSTITUTE @@ -55,7 +55,7 @@ def __init__(self, title: str | None, file: File, config: ProperDocsConfig) -> N self.markdown = None self._title_from_render: str | None = None self.content = None - self.toc = [] # type: ignore + self.toc = [] # type: ignore[assignment] self.meta = {} def __eq__(self, other) -> bool: diff --git a/properdocs/tests/__init__.py b/properdocs/tests/__init__.py index 7f41675e..aa81b516 100644 --- a/properdocs/tests/__init__.py +++ b/properdocs/tests/__init__.py @@ -13,4 +13,4 @@ def emit(self, record): raise AssertionError(f'Unexpected log: {self.format(record)!r}') -logging.lastResort = DisallowLogsHandler() # type: ignore +logging.lastResort = DisallowLogsHandler() diff --git a/properdocs/tests/build_tests.py b/properdocs/tests/build_tests.py index 9058b951..f9baaa27 100644 --- a/properdocs/tests/build_tests.py +++ b/properdocs/tests/build_tests.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python from __future__ import annotations import contextlib @@ -29,7 +28,7 @@ def build_page(title, path, config, md_src=''): """Helper which returns a Page object.""" files = Files([File(path, config.docs_dir, config.site_dir, config.use_directory_urls)]) - page = Page(title, list(files)[0], config) + page = Page(title, next(iter(files)), config) # Fake page.read_source() page.markdown, page.meta = meta.get_data(md_src) return page, files @@ -620,18 +619,18 @@ def test_draft_docs_with_comments_from_user_guide(self, site_dir, docs_dir): top_other_path = Path(site_dir, 'other_unpublished.html') self.assertTrue(top_other_path.is_file()) - self.assertIn('DRAFT', top_other_path.read_text()) + self.assertIn('DRAFT', top_other_path.read_text(encoding='utf-8')) sub_other_path = Path(site_dir, 'test', 'other_unpublished.html') self.assertPathIsFile(sub_other_path) - self.assertIn('DRAFT', sub_other_path.read_text()) + self.assertIn('DRAFT', sub_other_path.read_text(encoding='utf-8')) self.assertPathIsFile(site_dir, 'foo_unpublished.html') self.assertPathIsFile(site_dir, 'normal_file.html') good_path = Path(site_dir, 'test', 'normal_file.html') self.assertPathIsFile(good_path) - self.assertNotIn('DRAFT', good_path.read_text()) + self.assertNotIn('DRAFT', good_path.read_text(encoding='utf-8')) @tempdir( files={ @@ -674,11 +673,11 @@ def test_draft_pages_with_invalid_links(self, site_dir, docs_dir): foo_path = Path(site_dir, 'test', 'foo.html') self.assertTrue(foo_path.is_file()) - self.assertNotIn('DRAFT', foo_path.read_text()) + self.assertNotIn('DRAFT', foo_path.read_text(encoding='utf-8')) baz_path = Path(site_dir, 'test', 'baz.html') self.assertPathIsFile(baz_path) - self.assertIn('DRAFT', baz_path.read_text()) + self.assertIn('DRAFT', baz_path.read_text(encoding='utf-8')) self.assertPathNotExists(site_dir, '.zoo.html') @@ -702,7 +701,7 @@ def test_conflicting_readme_and_index(self, site_dir, docs_dir): index_path = Path(site_dir, 'foo', 'index.html') self.assertPathIsFile(index_path) - self.assertRegex(index_path.read_text(), r'page2 content') + self.assertRegex(index_path.read_text(encoding='utf-8'), r'page2 content') @tempdir( files={ @@ -723,7 +722,7 @@ def test_exclude_readme_and_index(self, site_dir, docs_dir): index_path = Path(site_dir, 'foo', 'index.html') self.assertPathIsFile(index_path) - self.assertRegex(index_path.read_text(), r'page1 content') + self.assertRegex(index_path.read_text(encoding='utf-8'), r'page1 content') @tempdir( files={ @@ -817,7 +816,7 @@ def test_anchor_warning_for_footnote(self, site_dir, docs_dir): def test_plugins_adding_files_and_interacting(self, tmp_dir, site_dir, docs_dir): def on_files_1(files: Files, config: ProperDocsConfig) -> Files: # Plugin 1 generates a file. - Path(tmp_dir, 'SUMMARY.md').write_text('foo.md\nbar.md\n') + Path(tmp_dir, 'SUMMARY.md').write_text('foo.md\nbar.md\n', encoding='utf-8') files.append(File('SUMMARY.md', tmp_dir, config.site_dir, config.use_directory_urls)) return files @@ -857,14 +856,12 @@ def on_files_2(files: Files, config: ProperDocsConfig) -> None: foo_path = Path(site_dir, 'foo.html') self.assertPathIsFile(foo_path) self.assertRegex( - foo_path.read_text(), + foo_path.read_text(encoding='utf-8'), r'href="foo.html"[\s\S]+href="bar.html"', # Nav order is respected ) summary_path = Path(site_dir, 'SUMMARY.html') - if exclude == 'full': - self.assertPathNotExists(summary_path) - elif exclude == 'drafts' and not serve_url: + if exclude == 'full' or (exclude == 'drafts' and not serve_url): self.assertPathNotExists(summary_path) else: self.assertPathExists(summary_path) @@ -923,7 +920,7 @@ def test_markdown_extension_with_relative(self, config_dir): build.build(config) main_path = Path(config_dir, 'site', 'main', 'main.html') self.assertTrue(main_path.is_file()) - self.assertIn(textwrap.dedent(expected), main_path.read_text()) + self.assertIn(textwrap.dedent(expected), main_path.read_text(encoding='utf-8')) # Test build.site_directory_contains_stale_files @@ -944,7 +941,7 @@ def run(self, lines: list[str]) -> list[str]: for i, line in enumerate(lines): if m := re.search(r'^--8<-- "(.+)"$', line): try: - lines[i] = Path(self.base_path, m[1]).read_text() + lines[i] = Path(self.base_path, m[1]).read_text(encoding='utf-8') except OSError: lines[i] = f"(Failed to read {m[1]!r})\n" return lines @@ -958,4 +955,4 @@ def extendMarkdown(self, md: markdown.Markdown) -> None: md.preprocessors.register(_TestPreprocessor(self.base_path), "properdocs_test", priority=32) -makeExtension = _TestExtension +makeExtension = _TestExtension # noqa: N816 diff --git a/properdocs/tests/cli_tests.py b/properdocs/tests/cli_tests.py index d6345d11..2bd2e803 100644 --- a/properdocs/tests/cli_tests.py +++ b/properdocs/tests/cli_tests.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - import gc import io import logging @@ -47,7 +45,7 @@ def test_serve_config_file(self, mock_serve): self.assertEqual(result.exit_code, 0) self.assertEqual(mock_serve.call_count, 1) - args, kwargs = mock_serve.call_args + _args, kwargs = mock_serve.call_args self.assertTrue('config_file' in kwargs) self.assertIsInstance(kwargs['config_file'], io.BufferedReader) self.assertEqual(kwargs['config_file'].name, 'properdocs.yml') @@ -229,7 +227,7 @@ def test_build_defaults(self, mock_build, mock_load_config): self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) - args, kwargs = mock_build.call_args + _args, kwargs = mock_build.call_args self.assertTrue('dirty' in kwargs) self.assertFalse(kwargs['dirty']) mock_load_config.assert_called_once_with( @@ -249,7 +247,7 @@ def test_build_clean(self, mock_build, mock_load_config): self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) - args, kwargs = mock_build.call_args + _args, kwargs = mock_build.call_args self.assertTrue('dirty' in kwargs) self.assertFalse(kwargs['dirty']) @@ -260,7 +258,7 @@ def test_build_dirty(self, mock_build, mock_load_config): self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) - args, kwargs = mock_build.call_args + _args, kwargs = mock_build.call_args self.assertTrue('dirty' in kwargs) self.assertTrue(kwargs['dirty']) @@ -274,7 +272,7 @@ def test_build_config_file(self, mock_build, mock_load_config): self.assertEqual(result.exit_code, 0) self.assertEqual(mock_build.call_count, 1) self.assertEqual(mock_load_config.call_count, 1) - args, kwargs = mock_load_config.call_args + _args, kwargs = mock_load_config.call_args self.assertTrue('config_file' in kwargs) self.assertIsInstance(kwargs['config_file'], io.BufferedReader) self.assertEqual(kwargs['config_file'].name, 'properdocs.yml') @@ -397,7 +395,7 @@ def test_gh_deploy_defaults(self, mock_gh_deploy, mock_build, mock_load_config): self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) - g_args, g_kwargs = mock_gh_deploy.call_args + _g_args, g_kwargs = mock_gh_deploy.call_args self.assertTrue('message' in g_kwargs) self.assertEqual(g_kwargs['message'], None) self.assertTrue('force' in g_kwargs) @@ -405,7 +403,7 @@ def test_gh_deploy_defaults(self, mock_gh_deploy, mock_build, mock_load_config): self.assertTrue('ignore_version' in g_kwargs) self.assertEqual(g_kwargs['ignore_version'], False) self.assertEqual(mock_build.call_count, 1) - b_args, b_kwargs = mock_build.call_args + _b_args, b_kwargs = mock_build.call_args self.assertTrue('dirty' in b_kwargs) self.assertFalse(b_kwargs['dirty']) mock_load_config.assert_called_once_with( @@ -427,7 +425,7 @@ def test_gh_deploy_clean(self, mock_gh_deploy, mock_build, mock_load_config): self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) self.assertEqual(mock_build.call_count, 1) - args, kwargs = mock_build.call_args + _args, kwargs = mock_build.call_args self.assertTrue('dirty' in kwargs) self.assertFalse(kwargs['dirty']) @@ -440,7 +438,7 @@ def test_gh_deploy_dirty(self, mock_gh_deploy, mock_build, mock_load_config): self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) self.assertEqual(mock_build.call_count, 1) - args, kwargs = mock_build.call_args + _args, kwargs = mock_build.call_args self.assertTrue('dirty' in kwargs) self.assertTrue(kwargs['dirty']) @@ -456,7 +454,7 @@ def test_gh_deploy_config_file(self, mock_gh_deploy, mock_build, mock_load_confi self.assertEqual(mock_gh_deploy.call_count, 1) self.assertEqual(mock_build.call_count, 1) self.assertEqual(mock_load_config.call_count, 1) - args, kwargs = mock_load_config.call_args + _args, kwargs = mock_load_config.call_args self.assertTrue('config_file' in kwargs) self.assertIsInstance(kwargs['config_file'], io.BufferedReader) self.assertEqual(kwargs['config_file'].name, 'properdocs.yml') @@ -471,7 +469,7 @@ def test_gh_deploy_message(self, mock_gh_deploy, mock_build, mock_load_config): self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) - g_args, g_kwargs = mock_gh_deploy.call_args + _g_args, g_kwargs = mock_gh_deploy.call_args self.assertTrue('message' in g_kwargs) self.assertEqual(g_kwargs['message'], 'A commit message') self.assertEqual(mock_build.call_count, 1) @@ -527,7 +525,7 @@ def test_gh_deploy_force(self, mock_gh_deploy, mock_build, mock_load_config): self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) - g_args, g_kwargs = mock_gh_deploy.call_args + _g_args, g_kwargs = mock_gh_deploy.call_args self.assertTrue('force' in g_kwargs) self.assertEqual(g_kwargs['force'], True) self.assertEqual(mock_build.call_count, 1) @@ -543,7 +541,7 @@ def test_gh_deploy_ignore_version(self, mock_gh_deploy, mock_build, mock_load_co self.assertEqual(result.exit_code, 0) self.assertEqual(mock_gh_deploy.call_count, 1) - g_args, g_kwargs = mock_gh_deploy.call_args + _g_args, g_kwargs = mock_gh_deploy.call_args self.assertTrue('ignore_version' in g_kwargs) self.assertEqual(g_kwargs['ignore_version'], True) self.assertEqual(mock_build.call_count, 1) diff --git a/properdocs/tests/config/base_tests.py b/properdocs/tests/config/base_tests.py index acecea22..d17f2fb9 100644 --- a/properdocs/tests/config/base_tests.py +++ b/properdocs/tests/config/base_tests.py @@ -17,7 +17,7 @@ def test_unrecognised_keys(self): } ) - failed, warnings = conf.validate() + _failed, warnings = conf.validate() self.assertEqual( warnings, @@ -45,7 +45,7 @@ def test_load_from_file(self, temp_dir): Users can explicitly set the config file using the '--config' option. Allows users to specify a config other than the default `properdocs.yml`. """ - with open(os.path.join(temp_dir, 'properdocs.yml'), 'w') as config_file: + with open(os.path.join(temp_dir, 'properdocs.yml'), 'w', encoding='utf-8') as config_file: config_file.write("site_name: ProperDocs Test\ntheme: mkdocs\n") os.mkdir(os.path.join(temp_dir, 'docs')) @@ -56,7 +56,7 @@ def test_load_from_file(self, temp_dir): @tempdir() def test_load_default_file(self, temp_dir): """Test that `properdocs.yml` will be loaded when '--config' is not set.""" - with open(os.path.join(temp_dir, 'properdocs.yml'), 'w') as config_file: + with open(os.path.join(temp_dir, 'properdocs.yml'), 'w', encoding='utf-8') as config_file: config_file.write("site_name: ProperDocs Test\ntheme: mkdocs\n") os.mkdir(os.path.join(temp_dir, 'docs')) with change_dir(temp_dir): @@ -67,7 +67,7 @@ def test_load_default_file(self, temp_dir): @tempdir() def test_load_default_file_with_yaml(self, temp_dir): """Test that `properdocs.yml` will be loaded when '--config' is not set.""" - with open(os.path.join(temp_dir, 'properdocs.yaml'), 'w') as config_file: + with open(os.path.join(temp_dir, 'properdocs.yaml'), 'w', encoding='utf-8') as config_file: config_file.write("site_name: ProperDocs Test\ntheme: mkdocs\n") os.mkdir(os.path.join(temp_dir, 'docs')) with change_dir(temp_dir): @@ -78,9 +78,9 @@ def test_load_default_file_with_yaml(self, temp_dir): @tempdir() def test_load_default_file_prefer_yml(self, temp_dir): """Test that `properdocs.yml` will be loaded when '--config' is not set.""" - with open(os.path.join(temp_dir, 'properdocs.yml'), 'w') as config_file1: + with open(os.path.join(temp_dir, 'properdocs.yml'), 'w', encoding='utf-8') as config_file1: config_file1.write("site_name: ProperDocs Test1\ntheme: mkdocs\n") - with open(os.path.join(temp_dir, 'properdocs.yaml'), 'w') as config_file2: + with open(os.path.join(temp_dir, 'properdocs.yaml'), 'w', encoding='utf-8') as config_file2: config_file2.write("site_name: ProperDocs Test2\ntheme: mkdocs\n") os.mkdir(os.path.join(temp_dir, 'docs')) @@ -99,7 +99,7 @@ def test_load_from_missing_file(self): def test_load_from_open_file(self, temp_path): """`load_config` can accept an open file descriptor.""" config_fname = os.path.join(temp_path, 'properdocs.yml') - config_file = open(config_fname, 'w+') + config_file = open(config_fname, 'w+', encoding='utf-8') config_file.write("site_name: ProperDocs Test\n") config_file.flush() os.mkdir(os.path.join(temp_path, 'docs')) @@ -121,7 +121,7 @@ def test_load_from_open_file(self, temp_path): @tempdir() def test_load_missing_required(self, temp_dir): """`site_name` is a required setting.""" - with open(os.path.join(temp_dir, 'properdocs.yml'), 'w') as config_file: + with open(os.path.join(temp_dir, 'properdocs.yml'), 'w', encoding='utf-8') as config_file: config_file.write("site_dir: output\nsite_url: https://properdocs.org\n") os.mkdir(os.path.join(temp_dir, 'docs')) @@ -241,7 +241,7 @@ def test_load_from_file_with_relative_paths(self, config_dir): config file, not the working directory. """ config_fname = os.path.join(config_dir, 'properdocs.yml') - with open(config_fname, 'w') as config_file: + with open(config_fname, 'w', encoding='utf-8') as config_file: config_file.write("docs_dir: src\nsite_name: ProperDocs Test\ntheme: mkdocs\n") docs_dir = os.path.join(config_dir, 'src') os.mkdir(docs_dir) diff --git a/properdocs/tests/config/config_options_legacy_tests.py b/properdocs/tests/config/config_options_legacy_tests.py index afe49e16..9273acc3 100644 --- a/properdocs/tests/config/config_options_legacy_tests.py +++ b/properdocs/tests/config/config_options_legacy_tests.py @@ -831,7 +831,7 @@ class Schema: @tempdir() def test_paths_localized_to_config(self, base_path): - with open(os.path.join(base_path, 'foo'), 'w') as f: + with open(os.path.join(base_path, 'foo'), 'w', encoding='utf-8') as f: f.write('hi') class Schema: diff --git a/properdocs/tests/config/config_options_tests.py b/properdocs/tests/config/config_options_tests.py index 258d3c0d..42b847b2 100644 --- a/properdocs/tests/config/config_options_tests.py +++ b/properdocs/tests/config/config_options_tests.py @@ -9,17 +9,9 @@ import sys import textwrap import unittest -from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union +from typing import TYPE_CHECKING, Any, TypeVar from unittest import mock -if TYPE_CHECKING: - from typing_extensions import assert_type -else: - - def assert_type(val, typ): - return None - - import properdocs from properdocs.config import config_options as c from properdocs.config import defaults @@ -29,6 +21,14 @@ def assert_type(val, typ): from properdocs.theme import Theme from properdocs.utils import write_file, yaml_load +if TYPE_CHECKING: + from typing_extensions import assert_type +else: + + def assert_type(val, typ): + return None + + SomeConfig = TypeVar('SomeConfig', bound=Config) @@ -119,7 +119,7 @@ class Schema(Config): option = c.Optional(c.Choice(('python', 'node'))) conf = self.get_config(Schema, {'option': 'python'}) - assert_type(conf.option, Optional[str]) + assert_type(conf.option, str | None) self.assertEqual(conf.option, 'python') conf = self.get_config(Schema, {}) @@ -468,7 +468,7 @@ def test_repo_name_github(self) -> None: self.Schema, {'repo_url': "https://github.com/properdocs/properdocs"}, ) - assert_type(conf.repo_name, Optional[str]) + assert_type(conf.repo_name, str | None) self.assertEqual(conf.repo_name, "GitHub") def test_repo_name_bitbucket(self) -> None: @@ -497,8 +497,8 @@ def test_edit_uri_github(self) -> None: self.Schema, {'repo_url': "https://github.com/properdocs/properdocs"}, ) - assert_type(conf.edit_uri, Optional[str]) - assert_type(conf.repo_url, Optional[str]) + assert_type(conf.edit_uri, str | None) + assert_type(conf.repo_url, str | None) self.assertEqual(conf.edit_uri, 'edit/master/docs/') self.assertEqual(conf.repo_url, "https://github.com/properdocs/properdocs") @@ -540,7 +540,7 @@ def test_edit_uri_template_ok(self) -> None: 'edit_uri_template': 'edit/foo/docs/{path}', }, ) - assert_type(conf.edit_uri_template, Optional[str]) + assert_type(conf.edit_uri_template, str | None) self.assertEqual(conf.edit_uri_template, 'edit/foo/docs/{path}') def test_edit_uri_template_errors(self) -> None: @@ -643,7 +643,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, list[str] | None) self.assertEqual(conf.option, None) conf = self.get_config(Schema, {'option': None}) @@ -657,7 +657,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[int | None]) self.assertEqual(conf.option, []) conf = self.get_config(Schema, {'option': [4, None]}) @@ -701,7 +701,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[c.ExtraScriptValue | str]) self.assertEqual(len(conf.option), 2) self.assertIsInstance(conf.option[1], c.ExtraScriptValue) self.assertEqual( @@ -719,7 +719,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[c.ExtraScriptValue | str]) self.assertEqual(len(conf.option), 2) self.assertIsInstance(conf.option[0], c.ExtraScriptValue) self.assertEqual( @@ -809,7 +809,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, dict[str, str] | None) self.assertEqual(conf.option, None) conf = self.get_config(Schema, {'option': None}) @@ -823,7 +823,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, int | None]) self.assertEqual(conf.option, {}) conf = self.get_config(Schema, {'option': {"a": 1, "b": None}}) @@ -1054,7 +1054,7 @@ class Schema(Config): @tempdir() def test_paths_localized_to_config(self, base_path) -> None: - with open(os.path.join(base_path, 'foo'), 'w') as f: + with open(os.path.join(base_path, 'foo'), 'w', encoding='utf-8') as f: f.write('hi') class Schema(Config): @@ -1132,7 +1132,7 @@ class Schema(Config): conf = self.get_config(Schema, {'option': "mkdocs"}) assert_type(conf.option, Theme) - assert_type(conf.option.name, Optional[str]) + assert_type(conf.option.name, str | None) self.assertEqual(conf.option.name, 'mkdocs') def test_uninstalled_theme_as_string(self) -> None: @@ -1488,10 +1488,10 @@ 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, list[Sub] | None) self.assertEqual(conf.sub, [{'opt': 1}, {'opt': None}]) assert conf.sub is not None - assert_type(conf.sub[0].opt, Optional[int]) + assert_type(conf.sub[0].opt, int | None) self.assertEqual(conf.sub[0].opt, 1) conf = self.get_config(Schema, {'sub': []}) @@ -2419,7 +2419,7 @@ class B(A): conf = self.get_config(B, {'foo': 1, 'baz': ['b']}) assert_type(conf.foo, int) self.assertEqual(conf.foo, 1) - assert_type(conf.bar, Optional[str]) + assert_type(conf.bar, str | None) self.assertEqual(conf.bar, None) assert_type(conf.baz, list[str]) self.assertEqual(conf.baz, ['b']) diff --git a/properdocs/tests/config/config_tests.py b/properdocs/tests/config/config_tests.py index 4706cf82..b4c282c1 100644 --- a/properdocs/tests/config/config_tests.py +++ b/properdocs/tests/config/config_tests.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - import dataclasses import os import unittest @@ -64,7 +62,7 @@ def test_invalid_config(self, temp_path): """ ) config_path = os.path.join(temp_path, 'foo.yml') - with open(config_path, 'w') as config_file: + with open(config_path, 'w', encoding='utf-8') as config_file: config_file.write(file_contents) with self.assertRaises(ConfigurationError): @@ -89,7 +87,7 @@ def test_config_option(self, temp_path): """ ) config_path = os.path.join(temp_path, 'properdocs.yml') - with open(config_path, 'w') as config_file: + with open(config_path, 'w', encoding='utf-8') as config_file: config_file.write(file_contents) os.mkdir(os.path.join(temp_path, 'docs')) @@ -235,7 +233,7 @@ def test_theme(self, mytheme, custom): }, ) - for config_contents, result in zip(configs, results): + for config_contents, result in zip(configs, results, strict=True): with self.subTest(config_contents): conf = config.Config(schema=(('theme', c.Theme(default='mkdocs')),)) conf.load_dict(config_contents) diff --git a/properdocs/tests/gh_deploy_tests.py b/properdocs/tests/gh_deploy_tests.py index b2e9d9b6..7d1f48c8 100644 --- a/properdocs/tests/gh_deploy_tests.py +++ b/properdocs/tests/gh_deploy_tests.py @@ -1,7 +1,7 @@ import unittest from unittest import mock -from ghp_import import GhpError # type: ignore +from ghp_import import GhpError # type: ignore[import-untyped] from properdocs import __version__ from properdocs.commands import gh_deploy @@ -142,8 +142,10 @@ def test_deploy_error(self): self.assertEqual( cm.output, [ - 'ERROR:properdocs.commands.gh_deploy:Failed to deploy to GitHub with error: \n' - 'TestError123' + ( + 'ERROR:properdocs.commands.gh_deploy:Failed to deploy to GitHub with error: \n' + 'TestError123' + ) ], ) diff --git a/properdocs/tests/livereload_tests.py b/properdocs/tests/livereload_tests.py index 2c7dbe9d..4f43b0eb 100644 --- a/properdocs/tests/livereload_tests.py +++ b/properdocs/tests/livereload_tests.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - import contextlib import email import io @@ -86,8 +84,9 @@ def test_basic_rebuild(self, site_dir, origin_dir): def rebuild(): started_building.set() Path(site_dir, "foo.site").write_text( - Path(docs_dir, "foo.docs").read_text() - + Path(origin_dir, "properdocs.yml").read_text() + Path(docs_dir, "foo.docs").read_text(encoding="utf-8") + + Path(origin_dir, "properdocs.yml").read_text(encoding="utf-8"), + encoding="utf-8", ) with testing_server(site_dir, rebuild) as server: @@ -98,14 +97,14 @@ def rebuild(): _, output = do_request(server, "GET /foo.site") self.assertEqual(output, "original") - Path(docs_dir, "foo.docs").write_text("docs2") + Path(docs_dir, "foo.docs").write_text("docs2", encoding="utf-8") self.assertTrue(started_building.wait(timeout=10)) started_building.clear() _, output = do_request(server, "GET /foo.site") self.assertEqual(output, "docs2yml1") - Path(origin_dir, "properdocs.yml").write_text("yml2") + Path(origin_dir, "properdocs.yml").write_text("yml2", encoding="utf-8") self.assertTrue(started_building.wait(timeout=10)) started_building.clear() @@ -125,7 +124,7 @@ def rebuild(): server.watch(docs_dir, rebuild) time.sleep(0.01) - Path(docs_dir, "foo.docs").write_text("b") + Path(docs_dir, "foo.docs").write_text("b", encoding="utf-8") self.assertTrue(started_building.wait(timeout=10)) with self.assertLogs("properdocs.livereload"): @@ -173,12 +172,12 @@ def test_unwatch(self, site_dir): server.unwatch(site_dir) time.sleep(0.01) - Path(site_dir, "foo").write_text("foo") + Path(site_dir, "foo").write_text("foo", encoding="utf-8") self.assertTrue(started_building.wait(timeout=10)) started_building.clear() server.unwatch(site_dir) - Path(site_dir, "foo").write_text("bar") + Path(site_dir, "foo").write_text("bar", encoding="utf-8") self.assertFalse(started_building.wait(timeout=0.5)) with self.assertRaises(KeyError): @@ -192,22 +191,22 @@ def test_multiple_dirs_can_cause_rebuild(self, site_dir, extra_dir, docs_dir): def rebuild(): started_building.wait(timeout=10) - content1 = Path(docs_dir, "foo.docs").read_text() - content2 = Path(extra_dir, "foo.extra").read_text() - Path(site_dir, "foo.site").write_text(content1 + content2) + content1 = Path(docs_dir, "foo.docs").read_text(encoding="utf-8") + content2 = Path(extra_dir, "foo.extra").read_text(encoding="utf-8") + Path(site_dir, "foo.site").write_text(content1 + content2, encoding="utf-8") with testing_server(site_dir, rebuild) as server: server.watch(docs_dir) server.watch(extra_dir) time.sleep(0.01) - Path(docs_dir, "foo.docs").write_text("docs2") + Path(docs_dir, "foo.docs").write_text("docs2", encoding="utf-8") started_building.wait(timeout=10) _, output = do_request(server, "GET /foo.site") self.assertEqual(output, "docs2extra1") - Path(extra_dir, "foo.extra").write_text("extra2") + Path(extra_dir, "foo.extra").write_text("extra2", encoding="utf-8") started_building.wait(timeout=10) _, output = do_request(server, "GET /foo.site") @@ -222,9 +221,9 @@ def test_multiple_dirs_changes_rebuild_only_once(self, site_dir, extra_dir, docs def rebuild(): self.assertFalse(started_building.is_set()) started_building.set() - content1 = Path(docs_dir, "foo.docs").read_text() - content2 = Path(extra_dir, "foo.extra").read_text() - Path(site_dir, "foo.site").write_text(content1 + content2) + content1 = Path(docs_dir, "foo.docs").read_text(encoding="utf-8") + content2 = Path(extra_dir, "foo.extra").read_text(encoding="utf-8") + Path(site_dir, "foo.site").write_text(content1 + content2, encoding="utf-8") with testing_server(site_dir, rebuild) as server: server.watch(docs_dir) @@ -232,8 +231,8 @@ def rebuild(): time.sleep(0.01) _, output = do_request(server, "GET /foo.site") - Path(docs_dir, "foo.docs").write_text("docs2") - Path(extra_dir, "foo.extra").write_text("extra2") + Path(docs_dir, "foo.docs").write_text("docs2", encoding="utf-8") + Path(extra_dir, "foo.extra").write_text("extra2", encoding="utf-8") self.assertTrue(started_building.wait(timeout=10)) _, output = do_request(server, "GET /foo.site") @@ -246,8 +245,8 @@ def test_change_is_detected_while_building(self, site_dir, docs_dir): can_finish_building = threading.Event() def rebuild(): - content = Path(docs_dir, "foo.docs").read_text() - Path(site_dir, "foo.site").write_text(content * 5) + content = Path(docs_dir, "foo.docs").read_text(encoding="utf-8") + Path(site_dir, "foo.site").write_text(content * 5, encoding="utf-8") before_finished_building.wait(timeout=10) self.assertTrue(can_finish_building.wait(timeout=10)) @@ -255,9 +254,9 @@ def rebuild(): server.watch(docs_dir) time.sleep(0.01) - Path(docs_dir, "foo.docs").write_text("b") + Path(docs_dir, "foo.docs").write_text("b", encoding="utf-8") before_finished_building.wait(timeout=10) - Path(docs_dir, "foo.docs").write_text("c") + Path(docs_dir, "foo.docs").write_text("c", encoding="utf-8") can_finish_building.set() _, output = do_request(server, "GET /foo.site") @@ -281,8 +280,8 @@ def rebuild(): if build_count == 1: raise ValueError("oh no") else: - content = Path(docs_dir, "foo.docs").read_text() - Path(site_dir, "foo.site").write_text(content * 5) + content = Path(docs_dir, "foo.docs").read_text(encoding="utf-8") + Path(site_dir, "foo.site").write_text(content * 5, encoding="utf-8") with testing_server(site_dir, rebuild) as server: server.watch(docs_dir) @@ -290,10 +289,10 @@ def rebuild(): err = io.StringIO() with contextlib.redirect_stderr(err), self.assertLogs("properdocs.livereload") as cm: - Path(docs_dir, "foo.docs").write_text("b") + Path(docs_dir, "foo.docs").write_text("b", encoding="utf-8") started_building.wait(timeout=10) - Path(docs_dir, "foo.docs").write_text("c") + Path(docs_dir, "foo.docs").write_text("c", encoding="utf-8") _, output = do_request(server, "GET /foo.site") @@ -411,7 +410,7 @@ def test_serves_polling_after_event(self, site_dir, docs_dir): server.watch(docs_dir) time.sleep(0.01) - Path(docs_dir, "foo.docs").write_text("b") + Path(docs_dir, "foo.docs").write_text("b", encoding="utf-8") _, output = do_request(server, f"GET /livereload/{initial_epoch}/0") @@ -536,13 +535,13 @@ def wait_for_build(): server.watch(Path(origin_dir, "properdocs.yml")) time.sleep(0.01) - Path(origin_dir, "unrelated.md").write_text("foo") + Path(origin_dir, "unrelated.md").write_text("foo", encoding="utf-8") self.assertFalse(started_building.wait(timeout=0.5)) - Path(tmp_dir, "properdocs.yml").write_text("edited") + Path(tmp_dir, "properdocs.yml").write_text("edited", encoding="utf-8") self.assertTrue(wait_for_build()) - Path(dest_docs_dir, "subdir", "foo.md").write_text("edited") + Path(dest_docs_dir, "subdir", "foo.md").write_text("edited", encoding="utf-8") self.assertTrue(wait_for_build()) @tempdir(["file_dest_1.md", "file_dest_2.md", "file_dest_unused.md"], prefix="tmp_dir") @@ -570,19 +569,19 @@ def wait_for_build(): server.watch(docs_dir) time.sleep(0.01) - Path(tmp_dir, "file_dest_1.md").write_text("edited") + Path(tmp_dir, "file_dest_1.md").write_text("edited", encoding="utf-8") self.assertTrue(wait_for_build()) - Path(dir_to_link_to, "file_under.md").write_text("edited") + Path(dir_to_link_to, "file_under.md").write_text("edited", encoding="utf-8") self.assertTrue(wait_for_build()) - Path(tmp_dir, "file_dest_2.md").write_text("edited") + Path(tmp_dir, "file_dest_2.md").write_text("edited", encoding="utf-8") self.assertTrue(wait_for_build()) Path(docs_dir, "link1.md").unlink() self.assertTrue(wait_for_build()) - Path(tmp_dir, "file_dest_unused.md").write_text("edited") + Path(tmp_dir, "file_dest_unused.md").write_text("edited", encoding="utf-8") self.assertFalse(started_building.wait(timeout=0.5)) @tempdir(prefix="site_dir") @@ -601,7 +600,7 @@ def test_watches_through_relative_symlinks(self, origin_dir, site_dir): server.watch(docs_dir) time.sleep(0.01) - Path(origin_dir, "README.md").write_text("edited") + Path(origin_dir, "README.md").write_text("edited", encoding="utf-8") self.assertTrue(started_building.wait(timeout=10)) @tempdir() @@ -623,5 +622,5 @@ def test_watch_with_broken_symlinks(self, docs_dir): server.watch(docs_dir) time.sleep(0.01) - Path(docs_dir, "subdir", "test").write_text("test") + Path(docs_dir, "subdir", "test").write_text("test", encoding="utf-8") self.assertTrue(started_building.wait(timeout=10)) diff --git a/properdocs/tests/localization_tests.py b/properdocs/tests/localization_tests.py index 8a744966..eb9f4f4d 100644 --- a/properdocs/tests/localization_tests.py +++ b/properdocs/tests/localization_tests.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python - - import unittest from unittest import mock @@ -67,6 +64,7 @@ def side_effet(*args, **kwargs): return theme_dir_translations else: self.fail() + return None with mock.patch('properdocs.localization.Translations.load', side_effect=side_effet): install_translations(self.env, parse_locale('en'), [custom_dir, theme_dir]) diff --git a/properdocs/tests/new_tests.py b/properdocs/tests/new_tests.py index 7c4b5ea6..aa1c23d0 100644 --- a/properdocs/tests/new_tests.py +++ b/properdocs/tests/new_tests.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - import os import unittest diff --git a/properdocs/tests/plugin_tests.py b/properdocs/tests/plugin_tests.py index 31125ccb..b6da28e6 100644 --- a/properdocs/tests/plugin_tests.py +++ b/properdocs/tests/plugin_tests.py @@ -1,9 +1,8 @@ -#!/usr/bin/env python from __future__ import annotations import os import unittest -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING if TYPE_CHECKING: from typing_extensions import assert_type @@ -77,7 +76,7 @@ def test_valid_plugin_options(self) -> None: assert_type(plugin.config.bar, int) self.assertEqual(plugin.config.bar, 0) - assert_type(plugin.config.dir, Optional[str]) + assert_type(plugin.config.dir, str | None) def test_invalid_plugin_options(self): plugin = DummyPlugin() diff --git a/properdocs/tests/search_tests.py b/properdocs/tests/search_tests.py index 9dc6cb08..da3ba044 100644 --- a/properdocs/tests/search_tests.py +++ b/properdocs/tests/search_tests.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - import json import unittest from unittest import mock @@ -374,7 +372,7 @@ def test_create_search_index(self): full_content = ''.join(f"Heading{i}Content{i}" for i in range(1, 4)) plugin = search.SearchPlugin() - errors, warnings = plugin.load_config({}) + _errors, _warnings = plugin.load_config({}) for page in pages: # Fake page.read_source() and page.render() diff --git a/properdocs/tests/structure/file_tests.py b/properdocs/tests/structure/file_tests.py index 744d09ae..47e69380 100644 --- a/properdocs/tests/structure/file_tests.py +++ b/properdocs/tests/structure/file_tests.py @@ -739,9 +739,9 @@ def test_files_move_to_end(self): ] files = Files(fs) self.assertEqual(len(files), 2) - self.assertEqual(list(files)[0].src_uri, 'a.md') + self.assertEqual(next(iter(files)).src_uri, 'a.md') with self.assertWarns(DeprecationWarning): files.append(fs[0]) self.assertEqual(len(files), 2) - self.assertEqual(list(files)[0].src_uri, 'b.jpg') + self.assertEqual(next(iter(files)).src_uri, 'b.jpg') self.assertEqual(list(files)[1].src_uri, 'a.md') diff --git a/properdocs/tests/structure/nav_tests.py b/properdocs/tests/structure/nav_tests.py index 7081535a..c197293c 100644 --- a/properdocs/tests/structure/nav_tests.py +++ b/properdocs/tests/structure/nav_tests.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - import sys import unittest @@ -25,7 +23,7 @@ def test_simple_nav(self): ) cfg = load_config(nav=nav_cfg, site_url='http://example.com/') fs = [ - File(list(item.values())[0], cfg.docs_dir, cfg.site_dir, cfg.use_directory_urls) + File(next(iter(item.values())), cfg.docs_dir, cfg.site_dir, cfg.use_directory_urls) for item in nav_cfg ] files = Files(fs) @@ -48,7 +46,7 @@ def test_nav_no_directory_urls(self): ) cfg = load_config(nav=nav_cfg, use_directory_urls=False, site_url='http://example.com/') fs = [ - File(list(item.values())[0], cfg.docs_dir, cfg.site_dir, cfg.use_directory_urls) + File(next(iter(item.values())), cfg.docs_dir, cfg.site_dir, cfg.use_directory_urls) for item in nav_cfg ] files = Files(fs) @@ -310,7 +308,7 @@ def test_nested_ungrouped_nav(self): ) cfg = load_config(nav=nav_cfg, site_url='http://example.com/') fs = [ - File(list(item.values())[0], cfg.docs_dir, cfg.site_dir, cfg.use_directory_urls) + File(next(iter(item.values())), cfg.docs_dir, cfg.site_dir, cfg.use_directory_urls) for item in nav_cfg ] files = Files(fs) @@ -461,7 +459,7 @@ class PageSubclass(Page): ) cfg = load_config(nav=nav_cfg, site_url='http://example.com/') fs = [ - File(list(item.values())[0], cfg.docs_dir, cfg.site_dir, cfg.use_directory_urls) + File(next(iter(item.values())), cfg.docs_dir, cfg.site_dir, cfg.use_directory_urls) for item in nav_cfg ] files = Files(fs) diff --git a/properdocs/tests/structure/toc_tests.py b/properdocs/tests/structure/toc_tests.py index ab56c4e4..f54cd843 100644 --- a/properdocs/tests/structure/toc_tests.py +++ b/properdocs/tests/structure/toc_tests.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - import unittest from properdocs.structure.toc import get_toc diff --git a/properdocs/tests/utils/utils_tests.py b/properdocs/tests/utils/utils_tests.py index 12367695..175180cb 100644 --- a/properdocs/tests/utils/utils_tests.py +++ b/properdocs/tests/utils/utils_tests.py @@ -1,8 +1,7 @@ -#!/usr/bin/env python - import dataclasses import datetime import logging +import operator import os import posixpath import stat @@ -200,7 +199,7 @@ def test_insort(self): def test_insort_key(self): a = [(1, 'a'), (1, 'b'), (2, 'c')] - utils.insort(a, (1, 'a'), key=lambda v: v[0]) + utils.insort(a, (1, 'a'), key=operator.itemgetter(0)) self.assertEqual(a, [(1, 'a'), (1, 'b'), (1, 'a'), (2, 'c')]) def test_nest_paths(self, j=posixpath.join): @@ -288,13 +287,13 @@ def test_yaml_inheritance(self, tdir): 'deep2-2': 'baz', }, } - with open(os.path.join(tdir, 'base.yml')) as fd: + with open(os.path.join(tdir, 'base.yml'), encoding='utf-8') as fd: result = utils.yaml_load(fd) self.assertEqual(result, expected) @tempdir(files={'base.yml': BASEYML}) def test_yaml_inheritance_missing_parent(self, tdir): - with open(os.path.join(tdir, 'base.yml')) as fd: + with open(os.path.join(tdir, 'base.yml'), encoding='utf-8') as fd: with self.assertRaises(exceptions.ConfigurationError): utils.yaml_load(fd) @@ -323,7 +322,7 @@ def test_copy_files(self, src_dir, dst_dir): src, dst, expected = case['src_path'], case['dst_path'], case['expected'] with self.subTest(src): src = os.path.join(src_dir, src) - with open(src, 'w') as f: + with open(src, 'w', encoding='utf-8') as f: f.write('content') dst = os.path.join(dst_dir, dst) utils.copy_file(src, dst) @@ -343,7 +342,7 @@ def test_copy_files_without_permissions(self, src_dir, dst_dir): src, expected = case['src_path'], case['expected'] with self.subTest(src): src = os.path.join(src_dir, src) - with open(src, 'w') as f: + with open(src, 'w', encoding='utf-8') as f: f.write('content') # Set src file to read-only os.chmod(src, stat.S_IRUSR) diff --git a/properdocs/theme.py b/properdocs/theme.py index 65afed8f..e3eea89d 100644 --- a/properdocs/theme.py +++ b/properdocs/theme.py @@ -9,14 +9,16 @@ import jinja2 import yaml +from properdocs import localization, utils +from properdocs.config.base import ValidationError +from properdocs.utils import templates + +SafeLoader: type[yaml.SafeLoader | yaml.CSafeLoader] try: from yaml import CSafeLoader as SafeLoader except ImportError: # pragma: no cover - from yaml import SafeLoader # type: ignore + from yaml import SafeLoader -from properdocs import localization, utils -from properdocs.config.base import ValidationError -from properdocs.utils import templates log = logging.getLogger(__name__) @@ -44,8 +46,8 @@ def __init__( ) -> None: self.name = name self._custom_dir = custom_dir - _vars: dict[str, Any] = {'name': name, 'locale': 'en'} - self.__vars = _vars + vars_: dict[str, Any] = {'name': name, 'locale': 'en'} + self.__vars = vars_ # ProperDocs provided static templates are always included package_dir = os.path.abspath(os.path.dirname(__file__)) @@ -66,12 +68,12 @@ def __init__( # Handle remaining user configs. Override theme configs (if set) self.static_templates.update(static_templates) - _vars.update(user_config) + vars_.update(user_config) # Validate locale and convert to Locale object if locale is None: - locale = _vars['locale'] - _vars['locale'] = localization.parse_locale(locale) + locale = vars_['locale'] + vars_['locale'] = localization.parse_locale(locale) name: str | None diff --git a/properdocs/utils/__init__.py b/properdocs/utils/__init__.py index a901548c..f44b7227 100644 --- a/properdocs/utils/__init__.py +++ b/properdocs/utils/__init__.py @@ -171,7 +171,7 @@ def get_relative_url(url: str, other: str) -> str: other_parts = _norm_parts(other) dest_parts = _norm_parts(url) common = 0 - for a, b in zip(other_parts, dest_parts): + for a, b in zip(other_parts, dest_parts, strict=False): if a != b: break common += 1 diff --git a/properdocs/utils/meta.py b/properdocs/utils/meta.py index 174adb20..ba303a33 100644 --- a/properdocs/utils/meta.py +++ b/properdocs/utils/meta.py @@ -40,10 +40,11 @@ import yaml +SafeLoader: type[yaml.SafeLoader | yaml.CSafeLoader] try: from yaml import CSafeLoader as SafeLoader except ImportError: # pragma: no cover - from yaml import SafeLoader # type: ignore + from yaml import SafeLoader ##################################################################### # Data Parser # diff --git a/properdocs/utils/rendering.py b/properdocs/utils/rendering.py index 0f1a3d7a..3b748bd6 100644 --- a/properdocs/utils/rendering.py +++ b/properdocs/utils/rendering.py @@ -38,8 +38,7 @@ def _strip_tags(text: str) -> str: text = text[:start] + text[end + 1 :] # Collapse whitespace - text = ' '.join(text.split()) - return text + return ' '.join(text.split()) def _render_inner_html(el: etree.Element, md: markdown.Markdown) -> str: diff --git a/properdocs/utils/templates.py b/properdocs/utils/templates.py index 388fc826..0c0f238e 100644 --- a/properdocs/utils/templates.py +++ b/properdocs/utils/templates.py @@ -9,9 +9,9 @@ from markupsafe import Markup try: - from jinja2 import pass_context as contextfilter # type: ignore + from jinja2 import pass_context as contextfilter except ImportError: - from jinja2 import contextfilter # type: ignore + from jinja2 import contextfilter # type: ignore # noqa: PGH003 from properdocs.utils import normalize_url @@ -54,4 +54,4 @@ def script_tag_filter(context: TemplateContext, extra_script: ExtraScriptValue) if extra_script.async_: html += ' async' html += '>' - return Markup(html).format(url_filter(context, str(extra_script)), extra_script) + return Markup(html).format(url_filter(context, str(extra_script)), extra_script) # noqa: S704 diff --git a/properdocs/utils/yaml.py b/properdocs/utils/yaml.py index ffba581a..0d4260ae 100644 --- a/properdocs/utils/yaml.py +++ b/properdocs/utils/yaml.py @@ -8,7 +8,7 @@ from typing import IO, TYPE_CHECKING, Any import yaml -import yaml_env_tag # type: ignore +import yaml_env_tag # type: ignore[import-untyped] from properdocs import exceptions @@ -131,7 +131,7 @@ def yaml_load( if loader is None: loader = get_yaml_loader() try: - result = yaml.load(source, Loader=loader) + result = yaml.load(source, Loader=loader) # noqa: S506 except yaml.YAMLError as e: raise exceptions.ConfigurationError( f"ProperDocs encountered an error parsing the configuration file: {e}" diff --git a/pyproject.toml b/pyproject.toml index 548d0e6f..f83e63f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -177,30 +177,32 @@ quote-style = "preserve" [tool.ruff.lint] preview = true select = [ - "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", "B019", "B020", "B021", "B022", "B023", "B025", "B026", "B029", "B030", "B031", "B032", "B033", "B034", "B035", "B039", "B909", + "YTT", "ASYNC", "C4", "DTZ", "T10", "EXE", "FA", "ISC", "PIE", "T20", "RSE", "I", "E", "W", "F", "UP", + "S201", "S202", "S303", "S304", "S305", "S306", "S506", "S602", "S604", "S605", "S612", "S704", + "B002", "B003", "B004", "B005", "B007", "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", "B043", "B901", "B905", "B909", "B911", "B912", "COM818", - "D200", "D201", "D202", "D204", "D207", "D208", "D209", "D210", "D211", "D213", "D214", "D300", "D301", "D400", "D402", "D403", "D405", "D412", "D414", "D415", "D416", "D417", "D419", - "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", + "LOG001", "LOG004", "LOG007", "LOG009", "LOG014", "LOG015", + "G001", "G002", "G003", "G010", "G101", "G201", "G202", + "PYI001", "PYI002", "PYI003", "PYI004", "PYI005", "PYI006", "PYI007", "PYI008", "PYI009", "PYI010", "PYI011", "PYI012", "PYI013", "PYI014", "PYI015", "PYI016", "PYI017", "PYI018", "PYI019", "PYI020", "PYI021", "PYI024", "PYI025", "PYI026", "PYI029", "PYI030", "PYI032", "PYI033", "PYI034", "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", + "RET502", "RET503", "RET504", + "SIM101", "SIM103", "SIM105", "SIM107", "SIM109", "SIM113", "SIM114", "SIM118", "SIM201", "SIM202", "SIM208", "SIM210", "SIM211", "SIM212", "SIM220", "SIM221", "SIM222", "SIM223", "SIM300", "SIM401", "SIM905", "SIM910", "SIM911", "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", + "TC001", "TC002", "TC003", "TC004", "TC005", "TC006", "TC007", "TC008", "TC010", + "PTH124", "PTH201", "PTH210", "FLY002", + "N803", "N804", "N805", "N806", "N807", "N815", "N816", "N999", "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", + "PGH003", "PGH004", "PGH005", + "PLC0105", "PLC0131", "PLC0132", "PLC0205", "PLC0207", "PLC0208", "PLC0414", "PLC2401", "PLC2403", "PLC2701", "PLC2801", "PLC3002", + "PLE0100", "PLE0101", "PLE0115", "PLE0116", "PLE0117", "PLE0118", "PLE0237", "PLE0241", "PLE0302", "PLE0303", "PLE0304", "PLE0305", "PLE0307", "PLE0308", "PLE0309", "PLE0604", "PLE0605", "PLE0643", "PLE0704", "PLE1132", "PLE1141", "PLE1142", "PLE1205", "PLE1206", "PLE1300", "PLE1307", "PLE1310", "PLE1507", "PLE1519", "PLE1520", "PLE1700", "PLE2502", "PLE2510", "PLE2512", "PLE2513", "PLE2514", "PLE2515", "PLE4703", + "PLR0124", "PLR0133", "PLR0202", "PLR0203", "PLR0206", "PLR0402", "PLR1704", "PLR1708", "PLR1712", "PLR1716", "PLR1722", "PLR1733", "PLR1736", "PLR2044", + "PLW0108", "PLW0120", "PLW0127", "PLW0128", "PLW0129", "PLW0131", "PLW0133", "PLW0177", "PLW0211", "PLW0244", "PLW0245", "PLW0406", "PLW0602", "PLW0603", "PLW0604", "PLW0642", "PLW0711", "PLW1501", "PLW1507", "PLW1508", "PLW1514", "PLW2101", "PLW3201", + "FURB105", "FURB110", "FURB116", "FURB118", "FURB122", "FURB129", "FURB131", "FURB132", "FURB136", "FURB142", "FURB145", "FURB148", "FURB152", "FURB154", "FURB156", "FURB157", "FURB161", "FURB162", "FURB163", "FURB164", "FURB166", "FURB167", "FURB168", "FURB169", "FURB171", "FURB177", "FURB180", "FURB181", "FURB188", "FURB192", + "RUF001", "RUF002", "RUF003", "RUF005", "RUF006", "RUF007", "RUF008", "RUF009", "RUF010", "RUF012", "RUF013", "RUF015", "RUF016", "RUF017", "RUF018", "RUF019", "RUF020", "RUF021", "RUF022", "RUF023", "RUF024", "RUF026", "RUF028", "RUF029", "RUF030", "RUF031", "RUF032", "RUF033", "RUF034", "RUF036", "RUF037", "RUF038", "RUF039", "RUF040", "RUF041", "RUF043", "RUF045", "RUF046", "RUF047", "RUF048", "RUF049", "RUF051", "RUF052", "RUF053", "RUF054", "RUF055", "RUF056", "RUF057", "RUF058", "RUF059", "RUF060", "RUF061", "RUF063", "RUF064", "RUF065", "RUF066", "RUF068", "RUF069", "RUF070", "RUF071", "RUF100", "RUF101", "RUF102", "RUF103", "RUF104", "RUF200", + "TRY002", "TRY201", "TRY203", "TRY300", "TRY401", ] -ignore = ["E501", "E731", "UP038", "UP045", "UP007", "F841"] +ignore = ["E501", "E731"] [tool.ruff.lint.per-file-ignores] "properdocs/tests/**" = ["PLC2701", "PLR6301"] [tool.ruff.lint.flake8-comprehensions] diff --git a/requirements/requirements-style.txt b/requirements/requirements-style.txt index 8d988031..6580b754 100644 --- a/requirements/requirements-style.txt +++ b/requirements/requirements-style.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by hatch-pip-compile with Python 3.13 +# This file is autogenerated by hatch-pip-compile with Python 3.14 # # - ruff # -ruff==0.9.6 +ruff==0.15.8 # via hatch.envs.style