diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7de2bc9..e399331 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: python-version: "3.11" py: py311 - platform: ubuntu-latest - python-version: 3.12.0-rc.1 + python-version: "3.12" py: py312 - platform: macos-latest python-version: 3.8 @@ -53,7 +53,7 @@ jobs: python-version: "3.11" py: py311 - platform: macos-latest - python-version: 3.12.0-rc.1 + python-version: "3.12" py: py312 - platform: windows-latest python-version: 3.8 @@ -62,7 +62,7 @@ jobs: python-version: "3.11" py: py311 - platform: windows-latest - python-version: 3.12.0-rc.1 + python-version: "3.12" py: py312 steps: - uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index 228c4b5..76e8db3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ __pycache__/ report.html .coverage .pytest_cache/ +.ruff_cache/ htmlcov/ *.egg-info/ venv*/ @@ -15,6 +16,5 @@ venv*/ .vscode .mypy_cache/ *.so -.flakeheaven_cache/ site/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5279673..90485dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,12 +25,6 @@ repos: name: check-hooks-apply - id: check-useless-excludes name: check-useless-excludes - - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 - hooks: - - id: pyupgrade - args: - - --py38-plus - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: @@ -38,33 +32,17 @@ repos: name: trailing-whitespace - id: end-of-file-fixer name: end-of-file-fixer - - id: double-quote-string-fixer - name: double-quote-string-fixer - - id: debug-statements - name: debug-statements - - repo: https://github.com/asottile/add-trailing-comma - rev: v3.0.1 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.290 hooks: - - id: add-trailing-comma - name: add-trailing-comma + - id: ruff + args: + - --fix + - --exit-non-zero-on-fix - repo: https://github.com/pre-commit/mirrors-autopep8 rev: v2.0.2 hooks: - id: autopep8 - - repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort - name: isort-src - alias: isort-src - files: ^src - args: - - --add-import - - from __future__ import annotations - - id: isort - name: isort-meta - alias: isort-meta - files: ^(docs|tests) - repo: https://github.com/mondeja/mdpo rev: v1.1.3 hooks: @@ -88,10 +66,6 @@ repos: rev: v1.32.0 hooks: - id: yamllint - files: .+\.(yml|yaml) - args: - - -c - - .yamllint.yaml - repo: https://github.com/DavidAnson/markdownlint-cli2 rev: v0.8.1 hooks: @@ -104,16 +78,6 @@ repos: - id: editorconfig-checker name: editorconfig-checker alias: ec - - repo: https://github.com/myint/autoflake - rev: v2.2.0 - hooks: - - id: autoflake - args: - - --in-place - - --remove-all-unused-imports - - --remove-unused-variables - - --remove-duplicate-keys - - --ignore-init-module-imports - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.4.1 hooks: @@ -132,25 +96,3 @@ repos: args: - --config - .markdown-link-check.json - - repo: https://github.com/flakeheaven/flakeheaven - rev: 3.3.0 - hooks: - - id: flakeheaven - additional_dependencies: - - flake8-builtins - - flake8-comprehensions - - flake8-docstrings - - flake8-executable - - flake8-implicit-str-concat - - flake8-print - - flake8-printf-formatting - - flake8-pytest-style - - flake8-bugbear - - flake8-encodings - - flake8-no-pep420 - - flake8-absolute-import - - flake8-slots - - flake8-unused-arguments - - dlint - - pysetenv - entry: pysetenv FLAKEHEAVEN_CACHE_TIMEOUT=0 flakeheaven lint diff --git a/pyproject.toml b/pyproject.toml index e903630..9dc10b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [project] name = "mkdocs-include-markdown-plugin" -version = "6.0.1" +version = "6.0.2" description = "Mkdocs Markdown includer plugin." readme = "README.md" license = "Apache-2.0" -requires-python = ">=3.8,<3.13" +requires-python = ">=3.8" classifiers = [ "Development Status :: 5 - Production/Stable", "Operating System :: OS Independent", @@ -81,9 +81,7 @@ matrix.mkdocs.dependencies = [ { value = "mkdocs==1.5.0", if = ["1.5.0"] }, { value = "mkdocs==1.5.1", if = ["1.5.1"] }, ] -matrix.cache.dependencies = [ - { value = "platformdirs", if = ["yes"] }, -] +matrix.cache.dependencies = [{ value = "platformdirs", if = ["yes"] }] [tool.hatch.envs.tests.scripts] all = "coverage run -m pytest" @@ -102,12 +100,9 @@ targets = [{ file = "pyproject.toml" }] [tool.project-config] cache = "2 days" style = [ - "gh://mondeja/project-config-styles@v4.3/base/pre-commit/md2po2md.json5", - "gh://mondeja/project-config-styles@v4.3/python/base.json5", - "gh://mondeja/project-config-styles@v4.3/python/single-quotes.json5", - "gh://mondeja/project-config-styles@v4.3/python/tests.json5", - "gh://mondeja/project-config-styles@v4.3/python/google-docstrings.json5", - "gh://mondeja/project-config-styles@v4.3/python/mypy.json5", + "gh://mondeja/project-config-styles@v5/base/pre-commit/md2po2md.json5", + "gh://mondeja/project-config-styles@v5/python/base.json5", + "gh://mondeja/project-config-styles@v5/python/mypy.json5", ] [tool.coverage.run] @@ -117,90 +112,87 @@ parallel = true data_file = ".coverage/.coverage" [tool.coverage.report] -exclude_lines = [ - "def __repr__\\(", - "@(abc\\.)?abstractmethod", -] +exclude_lines = ["def __repr__\\(", "@(abc\\.)?abstractmethod"] fail_under = 1 -[tool.flakeheaven] -inline-quotes = "single" -max-line-length = 80 -pytest-fixture-no-parentheses = true -pytest-parametrize-values-type = "tuple" -docstring-convention = "google" -unused-arguments-ignore-abstract-functions = true - -[tool.flakeheaven.plugins] -pycodestyle = ["+*", "-W503"] -pyflakes = ["+*"] -pylint = ["+*"] -flake8-builtins = ["+*"] -flake8-comprehensions = ["+*"] -flake8-docstrings = ["+*", "-D107", "-D105"] -flake8-executable = ["+*"] -flake8-implicit-str-concat = ["+*"] -flake8-print = ["+*"] -flake8-printf-formatting = ["+*"] -flake8-pytest-style = ["+*"] -flake8-bugbear = ["+*"] -flake8-encodings = ["+*"] -flake8-no-pep420 = ["+*"] -flake8-absolute-import = ["+*"] -flake8-unused-arguments = ["+*"] -flake8-slots = ["+*"] -dlint = ["+*"] - -[tool.flakeheaven.exceptions."tests/**"] -flake8-docstrings = [ - "-D100", - "-D101", - "-D102", - "-D103", - "-D104", - "-D107", - "-D205", - "-D415" +[tool.ruff] +line-length = 80 +target-version = "py38" +select = [ + "W", + "B", + "E", + "I", + "F", + "A", + "D", + "G", + "Q", + "PL", + "UP", + "PT", + "C4", + "EXE", + "ISC", + "T20", + "INP", + "ARG", + "SIM", + "RET", + "FBT", + "ERA", + "T10", + "COM", + "SLOT", ] -flake8-no-pep420 = ["-INP001"] - -[tool.flakeheaven.exceptions."setup.py"] -flake8-docstrings = ["-D205"] -flake8-no-pep420 = ["-INP001"] - -[tool.flakeheaven.exceptions."src/mkdocs_include_markdown_plugin/plugin.py"] -flake8-docstrings = ["-D100", "-D101", "-D102"] -flake8-unused-arguments = ["-U100"] - -[tool.isort] -lines_after_imports = 2 -multi_line_output = 3 -line_length = 79 -use_parentheses = true -combine_as_imports = true -include_trailing_comma = true -remove_redundant_aliases = true -known_tests = "tests" -sections = [ - "FUTURE", - "STDLIB", - "THIRDPARTY", - "FIRSTPARTY", - "TESTS", - "LOCALFOLDER" -] -py_version = 38 -extra_standard_library = [ - "contextvars", - "dataclasses", - "importlib.resources", - "importlib.metadata", + +[tool.ruff.pydocstyle] +convention = "google" + +[tool.ruff.flake8-quotes] +inline-quotes = "single" +multiline-quotes = "single" + +[tool.ruff.flake8-pytest-style] +fixture-parentheses = false +parametrize-values-type = "tuple" +parametrize-values-row-type = "tuple" + +[tool.ruff.isort] +lines-after-imports = 2 +combine-as-imports = true +force-wrap-aliases = true +known-local-folder = ["tests"] +required-imports = ["from __future__ import annotations"] +extra-standard-library = [ "zoneinfo", "graphlib", "tomllib", "wsgiref.types" ] +[tool.ruff.per-file-ignores] +"tests/**" = [ + "I002", + "D100", + "D101", + "D102", + "D103", + "D104", + "D107", + "D205", + "D415", + "INP001", + "PLR0913", + "PLR2004", +] +"setup.py" = ["D205", "INP001", "I002"] +"src/mkdocs_include_markdown_plugin/plugin.py" = [ + "D100", + "D101", + "D102", +] + [tool.mypy] strict = true python_version = "3.11" @@ -210,9 +202,3 @@ allow_any_generics = true [build-system] requires = ["hatchling"] build-backend = "hatchling.build" -# The next object is enough to build the wheel with mypyc, but just -# provides a ~10% performance improvement on Python < 3.11 (on Python3.11 -# is even slower): -#[tool.hatch.build.targets.wheel.hooks.mypyc] -#dependencies = ["hatch-mypyc", "mkdocs", "typing_extensions"] -#exclude = ["plugin.py"] diff --git a/src/mkdocs_include_markdown_plugin/__init__.py b/src/mkdocs_include_markdown_plugin/__init__.py index e69de29..e287ee9 100644 --- a/src/mkdocs_include_markdown_plugin/__init__.py +++ b/src/mkdocs_include_markdown_plugin/__init__.py @@ -0,0 +1 @@ +"""Mkdocs Markdown plugin to include files.""" diff --git a/src/mkdocs_include_markdown_plugin/cache.py b/src/mkdocs_include_markdown_plugin/cache.py index 558c92f..5a9b199 100644 --- a/src/mkdocs_include_markdown_plugin/cache.py +++ b/src/mkdocs_include_markdown_plugin/cache.py @@ -19,7 +19,11 @@ class Cache: """Cache for arbitrary content, one file per entry.""" - def __init__(self, cache_dir: str, expiration_seconds: int = 0): + def __init__( # noqa: D107 + self, + cache_dir: str, + expiration_seconds: int = 0, + ): self.cache_dir = cache_dir self.expiration_seconds = expiration_seconds @@ -46,11 +50,8 @@ def get_(self, url: str) -> str | None: # noqa: D102 creation_time = self.get_creation_time_from_fpath(fpath) if time.time() < creation_time + self.expiration_seconds: return self.read_file(fpath) - else: - os.remove(fpath) - return None - else: - return None + os.remove(fpath) + return None def set_(self, url: str, value: str) -> None: # noqa: D102 key = self.generate_unique_key_from_url(url) diff --git a/src/mkdocs_include_markdown_plugin/config.py b/src/mkdocs_include_markdown_plugin/config.py index 66c7757..6bd5d58 100644 --- a/src/mkdocs_include_markdown_plugin/config.py +++ b/src/mkdocs_include_markdown_plugin/config.py @@ -2,7 +2,11 @@ from __future__ import annotations -from mkdocs.config.config_options import ListOfItems, Optional, Type as MkType +from mkdocs.config.config_options import ( + ListOfItems, + Optional, + Type as MkType, +) CONFIG_DEFAULTS = { diff --git a/src/mkdocs_include_markdown_plugin/directive.py b/src/mkdocs_include_markdown_plugin/directive.py index eeeb597..f14ec7a 100644 --- a/src/mkdocs_include_markdown_plugin/directive.py +++ b/src/mkdocs_include_markdown_plugin/directive.py @@ -45,7 +45,7 @@ class DirectiveBoolArgument(TypedDict): # noqa: D101 # In the following regular expression, the substrings "$OPENING_TAG" # and "$CLOSING_TAG" will be replaced by the effective opening and # closing tags in the `on_config` plugin event. -INCLUDE_TAG_RE = rf""" +INCLUDE_TAG_RE = rf''' (?P<_includer_indent>[ \t\f\v\w{re.escape(string.punctuation)}]*?)$OPENING_TAG \s* include @@ -54,7 +54,7 @@ class DirectiveBoolArgument(TypedDict): # noqa: D101 (?P.*?) \s* $CLOSING_TAG -""" # noqa: E501 +''' # noqa: E501 TRUE_FALSE_STR_BOOL = { 'true': True, @@ -181,7 +181,8 @@ def resolve_file_paths_to_include( """Resolve the file paths to include for a directive.""" if process.is_url(filename_or_url): return [filename_or_url], True - elif process.is_absolute_path(filename_or_url): + + if process.is_absolute_path(filename_or_url): if os.name == 'nt': # Windows fpath = os.path.normpath(filename_or_url) @@ -198,7 +199,8 @@ def resolve_file_paths_to_include( ), ignore_paths, ), False - elif process.is_relative_path(filename_or_url): + + if process.is_relative_path(filename_or_url): root_dir = os.path.abspath( os.path.dirname(includer_page_src_path), ) @@ -236,7 +238,8 @@ def resolve_file_paths_to_exclude( root_dir = None if process.is_absolute_path(exclude_string): return glob.glob(exclude_string, flags=GLOB_FLAGS) - elif process.is_relative_path(exclude_string): + + if process.is_relative_path(exclude_string): root_dir = os.path.abspath( os.path.dirname(includer_page_src_path), ) @@ -249,6 +252,7 @@ def resolve_file_paths_to_exclude( root_dir=root_dir, ) ] + return glob.glob( exclude_string, flags=GLOB_FLAGS, diff --git a/src/mkdocs_include_markdown_plugin/event.py b/src/mkdocs_include_markdown_plugin/event.py index 38fdb0e..99b19fd 100644 --- a/src/mkdocs_include_markdown_plugin/event.py +++ b/src/mkdocs_include_markdown_plugin/event.py @@ -55,7 +55,7 @@ def lineno_from_content_start(content: str, start: int) -> int: return content[:start].count('\n') + 1 -def get_file_content( +def get_file_content( # noqa: PLR0913, PLR0915 markdown: str, page_src_path: str, docs_dir: str, @@ -67,7 +67,9 @@ def get_file_content( http_cache: Cache | None = None, ) -> str: """Return the content of the file to include.""" - def found_include_tag(match: re.Match[str]) -> str: + def found_include_tag( # noqa: PLR0912, PLR0915 + match: re.Match[str], + ) -> str: directive_match_start = match.start() includer_indent = match.group('_includer_indent') @@ -138,7 +140,8 @@ def found_include_tag(match: re.Match[str]) -> str: f' at {os.path.relpath(page_src_path, docs_dir)}' f':{lineno}', ) - elif files_watcher is not None and not is_url: + + if files_watcher is not None and not is_url: files_watcher.included_files.extend(file_paths_to_include) bool_options, invalid_bool_args = parse_bool_options( @@ -260,9 +263,9 @@ def found_include_tag(match: re.Match[str]) -> str: text_to_include += new_text_to_include # warn if expected start or ends haven't been found in included content - for i, argname in enumerate(['start', 'end']): + for i, delimiter_name in enumerate(['start', 'end']): if expected_but_any_found[i]: - value = locals()[argname] + delimiter_value = locals()[delimiter_name] readable_files_to_include = ', '.join([ os.path.relpath(fpath, docs_dir) for fpath in file_paths_to_include @@ -273,15 +276,32 @@ def found_include_tag(match: re.Match[str]) -> str: directive_match_start, ) logger.warning( - f"Delimiter {argname} '{value}' of 'include'" - f' directive at {os.path.relpath(page_src_path, docs_dir)}' - f':{lineno} not detected in the file{plural_suffix}' - f' {readable_files_to_include}', + ( + "Delimiter {delimiter_name} '{delimiter_value}'" + " of '{directive}' directive at" + ' {relative_path}:{line_number}' + ' not detected in the file{plural_suffix}' + ' {readable_files_to_include}' + ), + extra={ + 'delimiter_name': delimiter_name, + 'delimiter_value': delimiter_value, + 'directive': 'include', + 'relative_path': os.path.relpath( + page_src_path, + docs_dir, + ), + 'line_number': lineno, + 'plural_suffix': plural_suffix, + 'readable_files_to_include': readable_files_to_include, + }, ) return text_to_include - def found_include_markdown_tag(match: re.Match[str]) -> str: + def found_include_markdown_tag( # noqa: PLR0912, PLR0915 + match: re.Match[str], + ) -> str: directive_match_start = match.start() includer_indent = match.group('_includer_indent') @@ -353,7 +373,8 @@ def found_include_markdown_tag(match: re.Match[str]) -> str: f' {os.path.relpath(page_src_path, docs_dir)}' f':{lineno}', ) - elif files_watcher is not None and not is_url: + + if files_watcher is not None and not is_url: files_watcher.included_files.extend(file_paths_to_include) bool_options, invalid_bool_args = parse_bool_options( @@ -453,7 +474,7 @@ def found_include_markdown_tag(match: re.Match[str]) -> str: f"Invalid 'heading-offset' argument \"{offset}\" in" " 'include-markdown' directive at " f'{os.path.relpath(page_src_path, docs_dir)}:{lineno}', - ) + ) from None else: offset = defaults['heading-offset'] @@ -552,9 +573,9 @@ def found_include_markdown_tag(match: re.Match[str]) -> str: text_to_include += new_text_to_include # warn if expected start or ends haven't been found in included content - for i, argname in enumerate(['start', 'end']): + for i, delimiter_name in enumerate(['start', 'end']): if expected_but_any_found[i]: - value = locals()[argname] + delimiter_value = locals()[delimiter_name] readable_files_to_include = ', '.join([ os.path.relpath(fpath, docs_dir) for fpath in file_paths_to_include @@ -565,10 +586,25 @@ def found_include_markdown_tag(match: re.Match[str]) -> str: directive_match_start, ) logger.warning( - f"Delimiter {argname} '{value}' of 'include-markdown'" - f' directive at {os.path.relpath(page_src_path, docs_dir)}' - f':{lineno} not detected in the file{plural_suffix}' - f' {readable_files_to_include}', + ( + "Delimiter {delimiter_name} '{delimiter_value}' of" + " '{directive}' directive at" + ' {relative_path}:{line_number}' + ' not detected in the file{plural_suffix}' + ' {readable_files_to_include}' + ), + extra={ + 'delimiter_name': delimiter_name, + 'delimiter_value': delimiter_value, + 'directive': 'include-markdown', + 'relative_path': os.path.relpath( + page_src_path, + docs_dir, + ), + 'line_number': lineno, + 'plural_suffix': plural_suffix, + 'readable_files_to_include': readable_files_to_include, + }, ) return text_to_include @@ -577,11 +613,10 @@ def found_include_markdown_tag(match: re.Match[str]) -> str: found_include_tag, markdown, ) - markdown = tags['include-markdown'].sub( + return tags['include-markdown'].sub( found_include_markdown_tag, markdown, ) - return markdown def on_page_markdown( @@ -589,12 +624,15 @@ def on_page_markdown( page: Page, docs_dir: str, config: MutableMapping[str, Any] | None = None, - files_watcher: FilesWatcher | None = None, http_cache: Cache | None = None, ) -> str: """Process markdown content of a page.""" if config is None: - config = {} + config = { + '_files_watcher': None, + } + elif '_files_watcher' not in config: + config['_files_watcher'] = None return get_file_content( markdown, @@ -643,6 +681,6 @@ def on_page_markdown( { 'exclude': config.get('exclude', CONFIG_DEFAULTS['exclude']), }, - files_watcher=files_watcher, + files_watcher=config['_files_watcher'], http_cache=config.get('_cache', http_cache), ) diff --git a/src/mkdocs_include_markdown_plugin/files_watcher.py b/src/mkdocs_include_markdown_plugin/files_watcher.py index 25bcfab..555b371 100644 --- a/src/mkdocs_include_markdown_plugin/files_watcher.py +++ b/src/mkdocs_include_markdown_plugin/files_watcher.py @@ -4,6 +4,6 @@ class FilesWatcher: # noqa: D101 - def __init__(self) -> None: # pragma: no cover + def __init__(self) -> None: # noqa: D107 pragma: no cover self.prev_included_files: list[str] = [] self.included_files: list[str] = [] diff --git a/src/mkdocs_include_markdown_plugin/plugin.py b/src/mkdocs_include_markdown_plugin/plugin.py index 61b7d0a..db90e25 100644 --- a/src/mkdocs_include_markdown_plugin/plugin.py +++ b/src/mkdocs_include_markdown_plugin/plugin.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Callable -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any from mkdocs.exceptions import PluginError from mkdocs.livereload import LiveReloadServer @@ -24,14 +24,10 @@ from mkdocs_include_markdown_plugin.files_watcher import FilesWatcher -SERVER: LiveReloadServer | None = None -FILES_WATCHER: FilesWatcher | None = None - - class IncludeMarkdownPlugin(BasePlugin): config_scheme = CONFIG_SCHEME - def on_config(self, config: MkDocsConfig, **kwargs: Any) -> MkDocsConfig: + def on_config(self, config: MkDocsConfig) -> MkDocsConfig: self.config['_include_tag'] = create_include_tag( self.config['opening_tag'], self.config['closing_tag'], @@ -52,50 +48,47 @@ def on_config(self, config: MkDocsConfig, **kwargs: Any) -> MkDocsConfig: ' mkdocs-include-markdown-plugin with the "cache"' ' extra to install it.', ) - else: - cache.clean() - self.config['_cache'] = cache + cache.clean() + self.config['_cache'] = cache + + self.config['_files_watcher'] = None + self.config['_server'] = None return config def _watch_included_files(self) -> None: # pragma: no cover - global FILES_WATCHER, SERVER - SERVER = cast(LiveReloadServer, SERVER) - FILES_WATCHER = cast(FilesWatcher, FILES_WATCHER) - # unwatch previous watched files not needed anymore - for filepath in FILES_WATCHER.prev_included_files: - if filepath not in FILES_WATCHER.included_files: - SERVER.unwatch(filepath) - FILES_WATCHER.prev_included_files = ( - FILES_WATCHER.included_files[:] + for filepath in self.config['_files_watcher'].prev_included_files: + if filepath not in self.config['_files_watcher'].included_files: + self.config['_server'].unwatch(filepath) + self.config['_files_watcher'].prev_included_files = ( + self.config['_files_watcher'].included_files[:] ) # watch new included files - for filepath in FILES_WATCHER.included_files: - SERVER.watch(filepath, recursive=False) - FILES_WATCHER.included_files = [] + for filepath in self.config['_files_watcher'].included_files: + self.config['_server'].watch(filepath, recursive=False) + self.config['_files_watcher'].included_files = [] def on_page_content( self, html: str, - page: Page, - config: MkDocsConfig, - files: Files, + page: Page, # noqa: ARG002 + config: MkDocsConfig, # noqa: ARG002 + files: Files, # noqa: ARG002 ) -> str: - if SERVER is not None: # pragma: no cover + if self.config['_server'] is not None: # pragma: no cover self._watch_included_files() return html def on_serve( self, server: LiveReloadServer, - config: MkDocsConfig, - builder: Callable[[Any], Any], + config: MkDocsConfig, # noqa: ARG002 + builder: Callable[[Any], Any], # noqa: ARG002 ) -> None: - global SERVER - if SERVER is None: # pragma: no cover - SERVER = server + if self.config['_server'] is None: # pragma: no cover + self.config['_server'] = server self._watch_included_files() @event_priority(100) @@ -104,15 +97,13 @@ def on_page_markdown( markdown: str, page: Page, config: MkDocsConfig, - files: Files, + files: Files, # noqa: ARG002 ) -> str: - global FILES_WATCHER - if FILES_WATCHER is None: - FILES_WATCHER = FilesWatcher() + if self.config['_files_watcher'] is None: + self.config['_files_watcher'] = FilesWatcher() return _on_page_markdown( markdown, page, config['docs_dir'], config=self.config, - files_watcher=FILES_WATCHER, ) diff --git a/src/mkdocs_include_markdown_plugin/process.py b/src/mkdocs_include_markdown_plugin/process.py index 2102726..6b471c0 100644 --- a/src/mkdocs_include_markdown_plugin/process.py +++ b/src/mkdocs_include_markdown_plugin/process.py @@ -33,7 +33,7 @@ # Perl implementation but this doesn't seem possible in Python, the current # implementation only reaches two levels. MARKDOWN_LINK_REGEX = re.compile( # noqa: DUO138 - r""" + r''' ( # wrap whole match in $1 (? None: if _current_fcodeblock_delimiter: if line.lstrip().startswith(_current_fcodeblock_delimiter): _current_fcodeblock_delimiter = '' - else: - if not line.startswith(' ') and not line.startswith('\t'): - _inside_icodeblock = False + elif not line.startswith(' ') and not line.startswith('\t'): + _inside_icodeblock = False process_current_paragraph() @@ -196,7 +195,7 @@ def transform_line_by_line_skipping_codeblocks( ): _current_fcodeblock_delimiter = lstripped_line[:3] else: - line = func(line) + line = func(line) # noqa: PLW2901 elif line.lstrip().startswith(_current_fcodeblock_delimiter): _current_fcodeblock_delimiter = '' lines.append(line) @@ -283,7 +282,7 @@ def interpret_escapes(value: str) -> str: return value.encode('latin-1', 'backslashreplace').decode('unicode_escape') -def filter_inclusions( +def filter_inclusions( # noqa: PLR0912 start: str | None, end: str | None, text_to_include: str, @@ -442,7 +441,7 @@ def read_file(file_path: str, encoding: str) -> str: return f.read() -def read_url(url: str, http_cache: Cache | None) -> Any: # noqa: U100 +def read_url(url: str, http_cache: Cache | None) -> Any: """Read an HTTP location and return its content.""" if http_cache is not None: cached_content = http_cache.get_(url) diff --git a/tests/test_integration/test_cache_integration.py b/tests/test_integration/test_cache_integration.py index 291e380..862d26d 100644 --- a/tests/test_integration/test_cache_integration.py +++ b/tests/test_integration/test_cache_integration.py @@ -1,8 +1,8 @@ +from __future__ import annotations + import os import pytest -from testing_helpers import parametrize_directives - from mkdocs_include_markdown_plugin.cache import ( CACHE_AVAILABLE, Cache, @@ -10,6 +10,7 @@ initialize_cache, ) from mkdocs_include_markdown_plugin.event import on_page_markdown +from testing_helpers import parametrize_directives @pytest.mark.parametrize( diff --git a/tests/test_integration/test_examples.py b/tests/test_integration/test_examples.py index 1d78274..3127ba1 100644 --- a/tests/test_integration/test_examples.py +++ b/tests/test_integration/test_examples.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import subprocess import sys @@ -6,9 +8,8 @@ from mkdocs import config from mkdocs.commands.build import build from mkdocs.exceptions import Abort -from testing_helpers import rootdir - from mkdocs_include_markdown_plugin.cache import CACHE_AVAILABLE +from testing_helpers import rootdir EXAMPLES_DIR = os.path.join(rootdir, 'examples') diff --git a/tests/test_unit/test_arguments.py b/tests/test_unit/test_arguments.py index 03b1cd9..a8234b4 100644 --- a/tests/test_unit/test_arguments.py +++ b/tests/test_unit/test_arguments.py @@ -4,9 +4,8 @@ import pytest from mkdocs.exceptions import PluginError -from testing_helpers import parametrize_directives, unix_only - from mkdocs_include_markdown_plugin.event import on_page_markdown +from testing_helpers import parametrize_directives, unix_only @pytest.mark.parametrize( diff --git a/tests/test_unit/test_config.py b/tests/test_unit/test_config.py index f626b95..54e7079 100644 --- a/tests/test_unit/test_config.py +++ b/tests/test_unit/test_config.py @@ -1,9 +1,8 @@ """``include`` directive tests.""" import pytest -from testing_helpers import unix_only - from mkdocs_include_markdown_plugin.event import on_page_markdown +from testing_helpers import unix_only TESTS_ARGUMENTS = ( diff --git a/tests/test_unit/test_encoding.py b/tests/test_unit/test_encoding.py index e9d22b1..932c4a9 100644 --- a/tests/test_unit/test_encoding.py +++ b/tests/test_unit/test_encoding.py @@ -1,7 +1,6 @@ import pytest -from testing_helpers import parametrize_directives, unix_only - from mkdocs_include_markdown_plugin.event import on_page_markdown +from testing_helpers import parametrize_directives, unix_only @parametrize_directives diff --git a/tests/test_unit/test_exclude.py b/tests/test_unit/test_exclude.py index 16f583d..398d2ee 100644 --- a/tests/test_unit/test_exclude.py +++ b/tests/test_unit/test_exclude.py @@ -6,9 +6,8 @@ import pytest from mkdocs.exceptions import PluginError -from testing_helpers import parametrize_directives, unix_only - from mkdocs_include_markdown_plugin.event import on_page_markdown +from testing_helpers import parametrize_directives, unix_only @unix_only diff --git a/tests/test_unit/test_glob_include.py b/tests/test_unit/test_glob_include.py index dd57799..c6299d2 100644 --- a/tests/test_unit/test_glob_include.py +++ b/tests/test_unit/test_glob_include.py @@ -3,9 +3,8 @@ import os import pytest -from testing_helpers import parametrize_directives, unix_only - from mkdocs_include_markdown_plugin.event import on_page_markdown +from testing_helpers import parametrize_directives, unix_only @unix_only @@ -99,30 +98,46 @@ def test_glob_include_absolute(page, tmp_path): %} ''', [ - ( - "Delimiter end ''" - " of '{directive}' directive" - ' at {includer_file}:10 not detected in' - ' the files {included_file_01}, {included_file_02}' - ), - ( - "Delimiter end ''" - " of '{directive}' directive" - ' at {includer_file}:3 not detected in' - ' the files {included_file_01}, {included_file_02}' - ), - ( - "Delimiter start ''" - " of '{directive}' directive" - ' at {includer_file}:10 not detected in' - ' the files {included_file_01}, {included_file_02}' - ), - ( - "Delimiter start ''" - " of '{directive}' directive" - ' at {includer_file}:3 not detected in' - ' the files {included_file_01}, {included_file_02}' - ), + { + 'delimiter_name': 'start', + 'delimiter_value': '', + 'relative_path': '{includer_file}', + 'line_number': 3, + 'plural_suffix': 's', + 'readable_files_to_include': ( + '{included_file_01}, {included_file_02}' + ), + }, + { + 'delimiter_name': 'end', + 'delimiter_value': '', + 'relative_path': '{includer_file}', + 'line_number': 3, + 'plural_suffix': 's', + 'readable_files_to_include': ( + '{included_file_01}, {included_file_02}' + ), + }, + { + 'delimiter_name': 'start', + 'delimiter_value': '', + 'relative_path': '{includer_file}', + 'line_number': 10, + 'plural_suffix': 's', + 'readable_files_to_include': ( + '{included_file_01}, {included_file_02}' + ), + }, + { + 'delimiter_name': 'end', + 'delimiter_value': '', + 'relative_path': '{includer_file}', + 'line_number': 10, + 'plural_suffix': 's', + 'readable_files_to_include': ( + '{included_file_01}, {included_file_02}' + ), + }, ], id='start-end-not-found', ), @@ -168,20 +183,22 @@ def test_glob_include( # assert warnings expected_warnings_schemas = expected_warnings_schemas or [] - expected_warnings = [ - msg_schema.replace( - '{includer_file}', - str(includer_file.relative_to(tmp_path)), - ).replace( + for warning in expected_warnings_schemas: + warning['directive'] = directive + warning['relative_path'] = warning['relative_path'].replace( + '{includer_file}', str(includer_file.relative_to(tmp_path)), + ) + warning['readable_files_to_include'] = warning[ + 'readable_files_to_include' + ].replace( '{included_file_01}', str(included_01_file.relative_to(tmp_path)), ).replace( '{included_file_02}', str(included_02_file.relative_to(tmp_path)), - ).replace('{directive}', directive) - for msg_schema in expected_warnings_schemas - ] + ) - for record in caplog.records: - assert record.msg in expected_warnings + for i, warning in enumerate(expected_warnings_schemas): + for key in warning: + assert getattr(caplog.records[i], key) == warning[key] assert len(expected_warnings_schemas) == len(caplog.records) diff --git a/tests/test_unit/test_include.py b/tests/test_unit/test_include.py index 022e360..ce69ae0 100644 --- a/tests/test_unit/test_include.py +++ b/tests/test_unit/test_include.py @@ -1,7 +1,6 @@ """``include`` directive tests.""" import pytest - from mkdocs_include_markdown_plugin.event import on_page_markdown @@ -192,11 +191,13 @@ ''', [ - ( - "Delimiter start '' of 'include'" - ' directive at {filepath}:3' - ' not detected in the file {included_file}' - ), + { + 'delimiter_name': 'start', + 'delimiter_value': '', + 'relative_path': '{includer_file}', + 'line_number': 3, + 'readable_files_to_include': '{included_file}', + }, ], id='start=foo (not found)-end=None', ), @@ -217,11 +218,14 @@ Some text ''', [ - ( - "Delimiter end '' of 'include'" - ' directive at {filepath}:2' - ' not detected in the file {included_file}' - ), + { + 'delimiter_name': 'end', + 'delimiter_value': '', + 'relative_path': '{includer_file}', + 'line_number': 2, + 'readable_files_to_include': '{included_file}', + 'directive': 'include', + }, ], id='start=None-end=foo (not found)', ), @@ -388,16 +392,17 @@ def test_include( # assert warnings expected_warnings_schemas = expected_warnings_schemas or [] - expected_warnings = [ - msg_schema.replace( - '{filepath}', - str(includer_file.relative_to(tmp_path)), - ).replace( + for warning in expected_warnings_schemas: + warning['directive'] = 'include' + warning['relative_path'] = str(includer_file.relative_to(tmp_path)) + warning['readable_files_to_include'] = warning[ + 'readable_files_to_include' + ].replace( '{included_file}', str(included_file.relative_to(tmp_path)), - ) for msg_schema in expected_warnings_schemas - ] + ) - for record in caplog.records: - assert record.msg in expected_warnings + for i, warning in enumerate(expected_warnings_schemas): + for key in warning: + assert getattr(caplog.records[i], key) == warning[key] assert len(expected_warnings_schemas) == len(caplog.records) diff --git a/tests/test_unit/test_include_markdown.py b/tests/test_unit/test_include_markdown.py index 6064553..3302878 100644 --- a/tests/test_unit/test_include_markdown.py +++ b/tests/test_unit/test_include_markdown.py @@ -1,7 +1,6 @@ """``include-markdown`` directive tests""" import pytest - from mkdocs_include_markdown_plugin.event import on_page_markdown @@ -281,11 +280,13 @@ '''# Header ''', [ - ( - "Delimiter start '' of 'include-markdown'" - ' directive at {filepath}:2 not detected in the file' - ' {included_file}' - ), + { + 'delimiter_name': 'start', + 'delimiter_value': '', + 'relative_path': '{filepath}', + 'readable_files_to_include': '{included_file}', + 'line_number': 2, + }, ], id='start=foo (not found)-end=None', ), @@ -306,11 +307,13 @@ Some text ''', [ - ( - "Delimiter end '' of 'include-markdown'" - ' directive at {filepath}:2' - ' not detected in the file {included_file}' - ), + { + 'delimiter_name': 'end', + 'delimiter_value': '', + 'relative_path': '{filepath}', + 'readable_files_to_include': '{included_file}', + 'line_number': 2, + }, ], id='start=None-end=foo (not found)', ), @@ -707,11 +710,11 @@ "{% include-markdown \"{filepath}\"" " start='' %}" ), - """Ignored content + '''Ignored content Content to include -""", +''', r''' Content to include @@ -739,6 +742,7 @@ With some text after it ''', + # ruff: noqa: ISC003 TODO: error in ruff '''1. This is the first number line 1. @@ -754,7 +758,7 @@ 1. If everything works as expected this should be number 3 -''', # noqa: ISC003 +''', [], id='include-code-block-to-list-item (#123)', ), @@ -796,18 +800,22 @@ def test_include_markdown( # assert warnings expected_warnings_schemas = expected_warnings_schemas or [] - expected_warnings = [ - msg_schema.replace( + for warning in expected_warnings_schemas: + warning['directive'] = 'include-markdown' + warning['relative_path'] = warning['relative_path'].replace( '{filepath}', str(includer_file.relative_to(tmp_path)), - ).replace( - '{included_file}', - str(included_file.relative_to(tmp_path)), - ) for msg_schema in expected_warnings_schemas - ] - - for record in caplog.records: - assert record.msg in expected_warnings + ) + warning['readable_files_to_include'] = ( + warning['readable_files_to_include'].replace( + '{included_file}', + str(included_file.relative_to(tmp_path)), + ) + ) + + for i, warning in enumerate(expected_warnings_schemas): + for key in warning: + assert getattr(caplog.records[i], key) == warning[key] assert len(expected_warnings_schemas) == len(caplog.records) diff --git a/tests/test_unit/test_logging.py b/tests/test_unit/test_logging.py index c92826a..297acd0 100644 --- a/tests/test_unit/test_logging.py +++ b/tests/test_unit/test_logging.py @@ -1,7 +1,8 @@ -import pytest -from testing_helpers import parametrize_directives +"""Logging tests.""" +import pytest from mkdocs_include_markdown_plugin.event import on_page_markdown +from testing_helpers import parametrize_directives @parametrize_directives @@ -44,8 +45,10 @@ def test_start_end_arguments_not_found( includer_content, page(includer_file), tmp_path, ) == expected_result - assert ( - f"Delimiter {missing_argument} '' of" - f" '{directive}' directive at {includer_file_name}:3" - f' not detected in the file {included_file_name}' - ) in caplog.text + rec = caplog.records[0] + assert rec.delimiter_name == missing_argument + assert rec.delimiter_value == f'' + assert rec.directive == directive + assert rec.relative_path == includer_file_name + assert rec.line_number == 3 + assert rec.readable_files_to_include == included_file_name diff --git a/tests/test_unit/test_nested_includes.py b/tests/test_unit/test_nested_includes.py index 0e81e99..eac0af0 100644 --- a/tests/test_unit/test_nested_includes.py +++ b/tests/test_unit/test_nested_includes.py @@ -1,7 +1,6 @@ """Nested inclusion tests.""" import pytest - from mkdocs_include_markdown_plugin.event import on_page_markdown @@ -171,16 +170,22 @@ ''', [ - ( - "Delimiter start '' of 'include-markdown'" - ' directive at {first_includer_file}:3 not detected' - ' in the file {second_includer_file}' - ), - ( - "Delimiter end '' of 'include-markdown'" - ' directive at {first_includer_file}:3 not detected' - ' in the file {second_includer_file}' - ), + { + 'delimiter_name': 'start', + 'delimiter_value': '', + 'relative_path': '{first_includer_file}', + 'line_number': 3, + 'readable_files_to_include': '{second_includer_file}', + 'directive': 'include-markdown', + }, + { + 'delimiter_name': 'end', + 'delimiter_value': '', + 'relative_path': '{first_includer_file}', + 'line_number': 3, + 'readable_files_to_include': '{second_includer_file}', + 'directive': 'include-markdown', + }, ], id='start-end-not-found (first-level)', ), @@ -213,16 +218,22 @@ Included content ''', [ - ( - "Delimiter start '' of 'include-markdown'" - ' directive at {second_includer_file}:3 not detected' - ' in the file {included_file}' - ), - ( - "Delimiter end '' of 'include-markdown'" - ' directive at {second_includer_file}:3 not detected' - ' in the file {included_file}' - ), + { + 'delimiter_name': 'start', + 'delimiter_value': '', + 'relative_path': '{second_includer_file}', + 'line_number': 3, + 'readable_files_to_include': '{included_file}', + 'directive': 'include-markdown', + }, + { + 'delimiter_name': 'end', + 'delimiter_value': '', + 'relative_path': '{second_includer_file}', + 'line_number': 3, + 'readable_files_to_include': '{included_file}', + 'directive': 'include-markdown', + }, ], id='start-end-not-found (second-level)', ), @@ -260,21 +271,24 @@ def test_nested_include( # assert warnings expected_warnings_schemas = expected_warnings_schemas or [] - expected_warnings = [ - msg_schema.replace( + for warning in expected_warnings_schemas: + warning['relative_path'] = warning['relative_path'].replace( '{first_includer_file}', str(first_includer_file.relative_to(tmp_path)), ).replace( '{second_includer_file}', str(second_includer_file.relative_to(tmp_path)), - ).replace( - '{included_file}', - str(included_file.relative_to(tmp_path)), - ) for msg_schema in expected_warnings_schemas - ] + ) + warning['readable_files_to_include'] = warning[ + 'readable_files_to_include' + ].replace( + '{second_includer_file}', + str(second_includer_file.relative_to(tmp_path)), + ).replace('{included_file}', str(included_file.relative_to(tmp_path))) - for record in caplog.records: - assert record.msg in expected_warnings + for i, warning in enumerate(expected_warnings_schemas): + for key in warning: + assert getattr(caplog.records[i], key) == warning[key] assert len(expected_warnings_schemas) == len(caplog.records) diff --git a/tests/test_unit/test_process.py b/tests/test_unit/test_process.py index a85113c..e1b2205 100644 --- a/tests/test_unit/test_process.py +++ b/tests/test_unit/test_process.py @@ -1,7 +1,6 @@ """String processing tests.""" import pytest - from mkdocs_include_markdown_plugin.cache import Cache from mkdocs_include_markdown_plugin.process import ( increase_headings_offset,