mirror of
https://github.com/mkdocs/mkdocs.git
synced 2026-03-27 09:58:31 +07:00
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:
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user