Skip to content

Commit

Permalink
Added support for all options. Added a conf_script. Fixes #9
Browse files Browse the repository at this point in the history
  • Loading branch information
Sylvain MARIE committed Dec 11, 2021
1 parent d0790a6 commit 4c57af1
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 47 deletions.
4 changes: 3 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ Initial release with:
- gallery synthesis with proper icons and subgalleries
- auto inclusion in the ToC (nav) with support for the [section index pages feature](https://squidfunk.github.io/mkdocs-material/setup/setting-up-navigation/#section-index-pages)
- working `mkdocs serve`: correctly ignoring generated files to avoid infinite build loop

- working `mkdocs.yml` configuration for most options
- New option `conf_script` to configure via a script as in Sphinx-gallery.

- All gallery examples from Sphinx-Gallery successfully translated, in particular:
- LaTeX support works

Expand Down
15 changes: 15 additions & 0 deletions docs/gallery_conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import os
import sys

from mkdocs_gallery.gen_gallery import DefaultResetArgv

min_reported_time = 0
if 'SOURCE_DATE_EPOCH' in os.environ:
min_reported_time = sys.maxint if sys.version_info[0] == 2 else sys.maxsize

# To be used as the "base" config,
# this script is referenced in the mkdocs.yaml under `conf_script` option: docs/gallery_conf.py
conf = {
'reset_argv': DefaultResetArgv(),
'min_reported_time': min_reported_time,
}
24 changes: 24 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ docs/ # base mkdocs source directory

### 2. Configure mkdocs

#### a. Basics

Simply add the following configuration to you `mkdocs.yml`:

```yaml
Expand All @@ -66,6 +68,28 @@ Most [sphinx-gallery configuration options](https://sphinx-gallery.github.io/sta

See [mkdocs configuration](https://www.mkdocs.org/user-guide/configuration/) for general information about the `mkdocs.yml` file.

#### b. Advanced

You may wish to use the special `conf_script` option to create the base configuration using a python script, like what was done in Sphinx-gallery:

```yaml
plugins:
- gallery:
conf_script: docs/gallery_conf.py
# ... other options can still be added here
```

The python script should be executable without error, and at the end of execution should contain a `conf` variable defined at the module level. For example this is a valid script:

```python
from mkdocs_gallery.gen_gallery import DefaultResetArgv

conf = {
'reset_argv': DefaultResetArgv(),
}
```

You can set options both in the script and in the yaml. In case of duplicates, the yaml options override the script-defined ones.

### 3. Add gallery examples

Expand Down
19 changes: 10 additions & 9 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ nav:
plugins:
- search
- gallery:
conf_script: docs/gallery_conf.py # Base conf. Possibly modified by items below
examples_dirs:
- docs/examples # path to your example scripts, relative to mkdocs.yml
- docs/tutorials
Expand All @@ -24,28 +25,28 @@ plugins:
- docs/generated/gallery # where to save gallery generated output. Note that you may or may not include them in
- docs/generated/tutorials
# TODO tutorials and mayavi_examples
# backreferences_dir: gen_modules/backreferences # where to generate the back references summary
# doc_module: ['sphinx_gallery', 'numpy']

backreferences_dir: gen_modules/backreferences # where to generate the back references summary
# doc_module: ['sphinx_gallery', 'numpy'] TODO does not seem to work
# reference_url: {sphinx_gallery: None},
# image_scrapers: ['matplotlib']
# compress_images: ['images', 'thumbnails']
image_scrapers: matplotlib
compress_images: ['images', 'thumbnails']
# specify the order of examples to be according to filename
within_subsection_order: FileNameSortKey
expected_failing_examples:
- docs/examples/no_output/plot_raise.py
- docs/examples/no_output/plot_syntaxerror.py

# min_reported_time: min_reported_time,
# min_reported_time: min_reported_time, in conf file
# binder:
# org: sphinx-gallery
# repo: sphinx-gallery.github.io
# org: smarie
# repo: mkdocs-gallery.github.io
# branch: master
# binderhub_url: https://mybinder.org
# dependencies: ./binder/requirements.txt
# notebooks_dir: notebooks
# use_jupyter_lab: True
#
# show_memory: True,
show_memory: True
# junit: os.path.join('sphinx-gallery', 'junit-results.xml'),
# # capture raw HTML or, if not present, __repr__ of last expression in each code block
# capture_repr: ['_repr_html_', '__repr__']
Expand Down
10 changes: 4 additions & 6 deletions mkdocs_gallery/gen_data_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -821,12 +821,10 @@ def from_cfg(self, gallery_conf, mkdocs_conf):
if not isinstance(gallery_dirs, list):
gallery_dirs = [gallery_dirs]

# Back references ?
# TODO make sure we replicate this
if bool(gallery_conf['backreferences_dir']):
backreferences_dir = os.path.join(mkdocs_srcdir, gallery_conf['backreferences_dir'])
if not os.path.exists(backreferences_dir):
os.makedirs(backreferences_dir)
# Back references page
backreferences_dir = gallery_conf['backreferences_dir']
if backreferences_dir:
backreferences_dir.mkdir(parents=True, exist_ok=True)

# Create galleries
for e_dir, g_dir in zip(examples_dirs, gallery_dirs):
Expand Down
44 changes: 39 additions & 5 deletions mkdocs_gallery/gen_gallery.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

from __future__ import division, print_function, absolute_import

from importlib.util import spec_from_file_location, module_from_spec

from typing import Dict, Iterable, Tuple, List, Set

import codecs
Expand Down Expand Up @@ -114,11 +116,42 @@ def _bool_eval(x):
def parse_config(mkdocs_gallery_conf, mkdocs_conf, check_keys=True):
"""Process the Sphinx Gallery configuration."""

gallery_conf = _complete_gallery_conf(mkdocs_gallery_conf, mkdocs_conf=mkdocs_conf, check_keys=check_keys)
# Import the base configuration script
gallery_conf = load_base_conf(mkdocs_gallery_conf.pop("conf_script", None))
# Transform all strings to paths: not needed

# Merge configs
for opt_name, opt_value in mkdocs_gallery_conf.items():
# Did the user override the option in mkdocs.yml ? (for SubConfigswe do not receive None but {})
if opt_value is None or (opt_name in ('binder',) and len(opt_value) == 0):
continue # Not user-specified, skip

# User has overridden it. Use it
gallery_conf[opt_name] = opt_value

gallery_conf = _complete_gallery_conf(gallery_conf, mkdocs_conf=mkdocs_conf, check_keys=check_keys)

return gallery_conf


def load_base_conf(script: Path = None) -> Dict:
if script is None:
return dict()

try:
spec = spec_from_file_location("__mkdocs_gallery_conf", script)
foo = module_from_spec(spec)
spec.loader.exec_module(foo)
except ImportError:
raise ExtensionError(f"Error importing base configuration from `base_conf_py` {script}")

try:
return foo.conf
except AttributeError:
raise ExtensionError(f"Error loading base configuration from `base_conf_py` {script}, module does not contain "
f"a `conf` variable.")


def _complete_gallery_conf(mkdocs_gallery_conf, mkdocs_conf, lang='python',
builder_name='html', app=None, check_keys=True):
gallery_conf = copy.deepcopy(DEFAULT_GALLERY_CONF)
Expand Down Expand Up @@ -147,10 +180,12 @@ def _complete_gallery_conf(mkdocs_gallery_conf, mkdocs_conf, lang='python',
# Text to Class for sorting methods
_order = gallery_conf['subsection_order']
if isinstance(_order, str):
# the option was passed from the mkdocs.yml file
gallery_conf['subsection_order'] = str_to_sorting_method(_order)

_order = gallery_conf['within_subsection_order']
if isinstance(_order, str):
# the option was passed from the mkdocs.yml file
gallery_conf['within_subsection_order'] = str_to_sorting_method(_order)

# XXX anything that can only be a bool (rather than str) should probably be
Expand Down Expand Up @@ -335,10 +370,9 @@ def call_memory(func):
raise ConfigError("The 'backreferences_dir' parameter must be of type "
"str, Path or None, "
"found type %s" % type(backref))
# if 'backreferences_dir' is Path, make str for Python <=3.5
# compatibility
if isinstance(backref, Path):
gallery_conf['backreferences_dir'] = str(backref)
# if 'backreferences_dir' is str, make Path
if isinstance(backref, str):
gallery_conf['backreferences_dir'] = Path(backref)

# binder
gallery_conf['binder'] = check_binder_conf(gallery_conf['binder'])
Expand Down
101 changes: 75 additions & 26 deletions mkdocs_gallery/plugin.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
from mkdocs.config.base import ValidationError
from mkdocs.config.config_options import OptionallyRequired, BaseConfigOption

from pathlib import Path

from mkdocs.config.base import ValidationError
from mkdocs.config import config_options as co
from mkdocs.plugins import BasePlugin
from mkdocs.structure.files import Files

from typing import Dict, Any

import re

import os

from mkdocs.plugins import BasePlugin, log
from mkdocs.config import config_options

from . import glr_path_static
from .binder import copy_binder_files
# from .docs_resolv import embed_code_links
from .gen_gallery import parse_config, _KNOWN_CSS, generate_gallery_md, summarize_failing_examples, fill_mkdocs_nav


class ConfigList(OptionallyRequired):
class ConfigList(co.OptionallyRequired):
"""A list or single element of configuration matching a specific ConfigOption"""

def __init__(self, item_config: BaseConfigOption, single_elt_allowed: bool = True, **kwargs):
def __init__(self, item_config: co.BaseConfigOption, single_elt_allowed: bool = True, **kwargs):
super().__init__(**kwargs)
self.single_elt_allowed = single_elt_allowed
self.item_config = item_config
Expand All @@ -46,25 +41,79 @@ def run_validation(self, value):
return result


class MySubConfig(co.SubConfig):
"""Same as SubConfig except that it will be an empty dict when nothing is provided by user,
instead of a dict with all options containing their default values."""

def validate(self, value):
if value is None or len(value) == 0:
return None
else:
return super(MySubConfig, self).validate(value)


class GalleryPlugin(BasePlugin):
# # Mandatory to display plotly graph within the site
# import plotly.io as pio
# pio.renderers.default = "sphinx_gallery"

# TODO check more config options from mkdocs-gallery.gen_gallery.DEFAULT_GALLERY_CONF
config_scheme = (
('examples_dirs', ConfigList(config_options.Dir(exists=True), default="examples", required=True)),
('expected_failing_examples', ConfigList(config_options.File(exists=True))),
('gallery_dirs', ConfigList(config_options.Dir(exists=False), default="generated/gallery", required=True)),
('filename_pattern', config_options.Type(str, default=re.escape(os.sep) + 'plot')),
('subsection_order', config_options.Choice(choices=(None, "ExplicitOrder"), default=None)),
('within_subsection_order', config_options.Choice(choices=("FileNameSortKey", "NumberOfCodeLinesSortKey"),
default="FileNameSortKey")),
('conf_script', co.File(exists=True)),
('filename_pattern', co.Type(str)),
('ignore_pattern', co.Type(str)),
('examples_dirs', ConfigList(co.Dir(exists=True))),
# 'reset_argv': DefaultResetArgv(),
('subsection_order', co.Choice(choices=(None, "ExplicitOrder"))),
('within_subsection_order', co.Choice(choices=("FileNameSortKey", "NumberOfCodeLinesSortKey"))),

('gallery_dirs', ConfigList(co.Dir(exists=False))),
('backreferences_dir', co.Dir(exists=False)),
('doc_module', ConfigList(co.Type(str))),
# 'reference_url': {}, TODO how to link code to external functions?
('capture_repr', ConfigList(co.Type(str))),
('ignore_repr_types', co.Type(str)),
# Build options
('plot_gallery', config_options.Type(bool, default=True, required=True)),
('download_all_examples', config_options.Type(bool, default=True, required=True)),
('abort_on_example_error', config_options.Type(bool, default=False, required=True)),
('only_warn_on_example_error', config_options.Type(bool, default=False, required=True)),
('plot_gallery', co.Type(bool)),
('download_all_examples', co.Type(bool)),
('abort_on_example_error', co.Type(bool)),
('only_warn_on_example_error', co.Type(bool)),
# 'failing_examples': {}, # type: Set[str]
# 'passing_examples': [],
# 'stale_examples': [],
('run_stale_examples', co.Type(bool)),
('expected_failing_examples', ConfigList(co.File(exists=True))),
('thumbnail_size', ConfigList(co.Type(int), single_elt_allowed=False)),
('min_reported_time', co.Type(int)),
('binder', MySubConfig(
# Required keys
('org', co.Type(str, required=True)),
('repo', co.Type(str, required=True)),
('branch', co.Type(str, required=True)),
('binderhub_url', co.URL(required=True)),
('dependencies', ConfigList(co.File(exists=True), required=True)),
# Optional keys
('filepath_prefix', co.Type(str)),
('notebooks_dir', co.Type(str)),
('use_jupyter_lab', co.Type(bool)),
)),
('image_scrapers', ConfigList(co.Type(str))),
('compress_images', ConfigList(co.Type(str))),
('reset_modules', ConfigList(co.Type(str))),
('first_notebook_cell', co.Type(str)),
('last_notebook_cell', co.Type(str)),
('notebook_images', co.Type(bool)),
# # 'pypandoc': False,
('remove_config_comments', co.Type(bool)),
('show_memory', co.Type(bool)),
('show_signature', co.Type(bool)),
# 'junit': '',
# 'log_level': {'backreference_missing': 'warning'},
('inspect_global_variables', co.Type(bool)),
# 'css': _KNOWN_CSS,
('matplotlib_animations', co.Type(bool)),
('image_srcset', ConfigList(co.Type(str))),
('default_thumb_file', co.File(exists=True)),
('line_numbers', co.Type(bool)),
)

def on_config(self, config, **kwargs):
Expand Down Expand Up @@ -256,10 +305,10 @@ def merge_extra_config(extra_config: Dict[str, Any], config):
#
# config_scheme = (
# ('lang', LangOption()),
# ('separator', config_options.Type(str, default=r'[\s\-]+')),
# ('min_search_length', config_options.Type(int, default=3)),
# ('prebuild_index', config_options.Choice((False, True, 'node', 'python'), default=False)),
# ('indexing', config_options.Choice(('full', 'sections', 'titles'), default='full'))
# ('separator', co.Type(str, default=r'[\s\-]+')),
# ('min_search_length', co.Type(int, default=3)),
# ('prebuild_index', co.Choice((False, True, 'node', 'python'), default=False)),
# ('indexing', co.Choice(('full', 'sections', 'titles'), default='full'))
# )
#
# def on_config(self, config, **kwargs):
Expand Down

0 comments on commit 4c57af1

Please sign in to comment.