Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support arguments to scripts #33

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ Or something a bit more interesting:

This adds programmatically generated pages to our site. This example script has been applied to this very site, you can see [sample/99-bottles.md](sample/99-bottles.md) etc.

### Specifying Arguments

You may specify arguments to the script by specifying the path to the script with `path` and the arguments with `args`.

```yaml
plugins:
- search
- gen-files:
scripts:
- path: gen_pages.py # or any other name or path
args: "--foo bar" # the full set of arguments
```

### Use cases

You might be wondering, what's the point of this?
Expand Down
51 changes: 48 additions & 3 deletions mkdocs_gen_files/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import logging
import runpy
import shlex
import sys
import tempfile
import urllib.parse
from typing import TYPE_CHECKING, TypeVar
from typing import TYPE_CHECKING, Any, TypeVar, Union

from mkdocs.config import Config
from mkdocs.config import config_options as opt
from mkdocs.config.base import BaseConfigOption, ValidationError
from mkdocs.exceptions import PluginError
from mkdocs.plugins import BasePlugin, event_priority

Expand All @@ -24,21 +27,63 @@
log = logging.getLogger(f"mkdocs.plugins.{__name__}")


class _ScriptValue(Config):
"""Config value to store a path to a script to execute and its arguments"""

path = opt.File(exists=True)
args = opt.Optional(opt.Type(str))


class _Script(BaseConfigOption[Union[_ScriptValue, str]]):
"""Config option that stores either a `_ScriptValue` or `str`, where the
latter contains a path to a script to execute"""

def run_validation(self, value: object) -> dict[Any, Any] | str:
if not isinstance(value, (dict, str)):
raise ValidationError(
f"Invalid configuration. Expected a dict or str. Found '{type(value)}'"
)
return value


class PluginConfig(Config):
scripts = opt.ListOfItems(opt.File(exists=True))
"""Base plugin configuration"""

scripts = opt.ListOfItems(_Script())


class GenFilesPlugin(BasePlugin[PluginConfig]):
def on_files(self, files: Files, config: MkDocsConfig) -> Files:
self._dir = tempfile.TemporaryDirectory(prefix="mkdocs_gen_files_")

with FilesEditor(files, config, self._dir.name) as ed:
for file_name in self.config.scripts:
for sub_config in self.config.scripts:
"""Parse the script configuration. The sub_config may just be a
string, in which case it's treated as a path ane executed
directly. Otherwise, the sub_config is a dictionary, with keys
path and optionally argv, where path is the path to the executable,
and argv is the arguments to the executable. The arguments to the
scripts are _always_ overridden, i.e. sys.argv is always modified.
"""
# get the path to the executable and optionally arguments
argv: list[str] = []
if isinstance(sub_config, str): # treat it as a path
file_name = sub_config
else: # treat it as a `_ScriptValue`
file_name = sub_config["path"]
if "argv" in sub_config: # optionally add argv
argv = shlex.split(sub_config["argv"])
# override sys.argv, but save it for later to restore
old_sys_argv = sys.argv
sys.argv = [file_name, *argv]
# run the script
try:
runpy.run_path(file_name)
except SystemExit as e:
if e.code:
raise PluginError(f"Script {file_name!r} caused {e!r}")
finally: # restore sys.argv
sys.argv = old_sys_argv

self._edit_paths = dict(ed.edit_paths)
return ed.files
Expand Down
Loading