Add File.generated factory method for files

-it populates `generated_by` automatically as well, based on the currently running plugin event.
This commit is contained in:
Oleh Prypin
2023-12-15 17:05:08 +01:00
parent 680e3f66d4
commit 666d764a3f
3 changed files with 87 additions and 4 deletions

View File

@@ -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}
@@ -553,9 +555,9 @@ class PluginCollection(dict, MutableMapping[str, BasePlugin]):
"""
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:
@@ -563,6 +565,7 @@ class PluginCollection(dict, MutableMapping[str, BasePlugin]):
# 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:

View File

@@ -9,7 +9,7 @@ import shutil
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
@@ -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.
@@ -252,6 +254,67 @@ class File:
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,

View File

@@ -293,6 +293,23 @@ class TestFiles(PathAssertionMixin, unittest.TestCase):
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),