Skip to content

Commit

Permalink
Add File.generated factory method for files
Browse files Browse the repository at this point in the history
-it populates `generated_by` automatically as well, based on the currently running plugin event.
  • Loading branch information
oprypin committed Dec 15, 2023
1 parent 680e3f6 commit 666d764
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 4 deletions.
7 changes: 5 additions & 2 deletions mkdocs/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,8 @@ class PluginCollection(dict, MutableMapping[str, BasePlugin]):
by calling `run_event`.
"""

_current_plugin: str | None

def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.events: dict[str, list[Callable]] = {k: [] for k in EVENTS}
Expand Down Expand Up @@ -553,16 +555,17 @@ def run_event(self, name: str, item=None, **kwargs):
"""
pass_item = item is not None
for method in self.events[name]:
self._current_plugin = self._event_origins.get(method, '<unknown>')
if log.getEffectiveLevel() <= logging.DEBUG:
plugin_name = self._event_origins.get(method, '<unknown>')
log.debug(f"Running `{name}` event from plugin '{plugin_name}'")
log.debug(f"Running `{name}` event from plugin '{self._current_plugin}'")
if pass_item:
result = method(item, **kwargs)
else:
result = method(**kwargs)
# keep item if method returned `None`
if result is not None:
item = result
self._current_plugin = None
return item

def on_startup(self, *, command: Literal['build', 'gh-deploy', 'serve'], dirty: bool) -> None:
Expand Down
67 changes: 65 additions & 2 deletions mkdocs/structure/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import warnings
from functools import cached_property
from pathlib import PurePath
from typing import TYPE_CHECKING, Callable, Iterable, Iterator, Sequence
from typing import TYPE_CHECKING, Callable, Iterable, Iterator, Sequence, overload
from urllib.parse import quote as urlquote

import pathspec
Expand Down Expand Up @@ -224,7 +224,9 @@ class File:
"""Whether the file will be excluded from the built site."""

generated_by: str | None = None
"""If not None, indicates that a plugin generated this file on the fly."""
"""If not None, indicates that a plugin generated this file on the fly.
The value is the plugin's entrypoint name and can be used to find the plugin by key in the PluginCollection."""

_content: str | bytes | None = None
"""If set, the file's content will be read from here.
Expand Down Expand Up @@ -252,6 +254,67 @@ def dest_path(self, value):

page: Page | None = None

@overload
@classmethod
def generated(
cls,
config: MkDocsConfig,
src_uri: str,
*,
content: str | bytes,
inclusion: InclusionLevel = InclusionLevel.UNDEFINED,
) -> File:
"""
Create a virtual file backed by in-memory content.
It will pretend to be a file in the docs dir at `src_uri`.
"""

@overload
@classmethod
def generated(
cls,
config: MkDocsConfig,
src_uri: str,
*,
abs_src_path: str,
inclusion: InclusionLevel = InclusionLevel.UNDEFINED,
) -> File:
"""
Create a virtual file backed by a physical temporary file at `abs_src_path`.
It will pretend to be a file in the docs dir at `src_uri`.
"""

@classmethod
def generated(
cls,
config: MkDocsConfig,
src_uri: str,
*,
content: str | bytes | None = None,
abs_src_path: str | None = None,
inclusion: InclusionLevel = InclusionLevel.UNDEFINED,
) -> File:
"""
Create a virtual file, backed either by in-memory `content` or by a file at `abs_src_path`.
It will pretend to be a file in the docs dir at `src_uri`.
"""
if (content is None) == (abs_src_path is None):
raise TypeError("File must have exactly one of 'content' or 'abs_src_path'")
f = cls(
src_uri,
src_dir=None,
dest_dir=config.site_dir,
use_directory_urls=config.use_directory_urls,
inclusion=inclusion,
)
f.generated_by = config.plugins._current_plugin or '<unknown>'
f.abs_src_path = abs_src_path
f._content = content
return f

def __init__(
self,
path: str,
Expand Down
17 changes: 17 additions & 0 deletions mkdocs/tests/structure/file_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,23 @@ def test_generated_file(self):
self.assertEqual(f.content_string, 'вміст')
self.assertEqual(f.edit_uri, None)

@tempdir(files={'x.md': 'вміст'})
def test_generated_file_constructor(self, tdir) -> None:
config = load_config(site_dir='/path/to/site', use_directory_urls=False)
config.plugins._current_plugin = 'foo'
for f in [
File.generated(config, 'foo/bar.md', content='вміст'),
File.generated(config, 'foo/bar.md', content='вміст'.encode()),
File.generated(config, 'foo/bar.md', abs_src_path=os.path.join(tdir, 'x.md')),
]:
self.assertEqual(f.src_uri, 'foo/bar.md')
self.assertIsNone(f.src_dir)
self.assertEqual(f.dest_uri, 'foo/bar.html')
self.assertPathsEqual(f.abs_dest_path, os.path.abspath('/path/to/site/foo/bar.html'))
self.assertEqual(f.content_string, 'вміст')
self.assertEqual(f.content_bytes, 'вміст'.encode())
self.assertEqual(f.edit_uri, None)

def test_files(self):
fs = [
File('index.md', '/path/to/docs', '/path/to/site', use_directory_urls=True),
Expand Down

0 comments on commit 666d764

Please sign in to comment.