Skip to content

Commit

Permalink
modularizing binder building
Browse files Browse the repository at this point in the history
  • Loading branch information
choldgraf committed May 5, 2018
1 parent 69c8639 commit 8409a74
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 31 deletions.
12 changes: 10 additions & 2 deletions doc/advanced_configuration.rst
Expand Up @@ -442,19 +442,27 @@ dictionary following the pattern below::
sphinx_gallery_conf = {
...
'binder': {
# Required keys
'org': '<github_org>',
'repo': '<github_repo>',
'url': '<binder_url>', # Any URL of a binder server. Must be full URL (e.g. https://mybinder.org).
'branch': '<branch-for-documentation>', # Can be any branch, tag, or commit hash. Use a branch that hosts your docs.
'dependencies': '<list_of_paths_to_dependency_files>',
'filepath_prefix': '<prefix>' # Optional, a prefix to append to any filepaths in Binder links.
use this if you move your built documentation to a sub-folder of your repository (e.g., "v2.1")
# Optional keys
'filepath_prefix': '<prefix>' # A prefix to append to any filepaths in Binder links.
use this if you move your built documentation to a sub-folder of your repository (e.g., "v2.1").
'notebooks_folder': '<notebooks-folder-name>' # A folder name where jupyter notebooks that Binder links point to will be moved.
Defaults to the name "notebooks".
'use_lab': <bool> # Whether Binder links should start Jupyter Lab instead of the Jupyter Notebook interface.
}
}

Note that ``branch:`` should be the branch on which your documentation is hosted.
If you host your documentation on GitHub, this is usually ``gh-pages`` or ``master``.

A copy of each generated Jupyter Notebook will be copied to the folder
specified in ``notebooks_folder``. Binder links will point to these notebooks.

.. important::

``dependencies`` should be a list of paths to Binder configuration files that
Expand Down
58 changes: 52 additions & 6 deletions sphinx_gallery/binder.py
Expand Up @@ -25,8 +25,11 @@
unicode = str

from .utils import replace_py_ipynb, _get_gallery_dir_path
from . import sphinx_compatibility


logger = sphinx_compatibility.getLogger('sphinx-gallery')

def gen_binder_url(fname, binder_conf, gallery_conf):
"""Generate a Binder URL according to the configuration in conf.py.
Expand Down Expand Up @@ -114,17 +117,58 @@ def gen_binder_rst(fname, binder_conf, gallery_conf):
return rst


def copy_binder_reqs(app):
def copy_binder_files(app, exception):
"""Copy all Binder requirements and notebooks files."""
if exception is not None:
return

if app.builder.name not in ['html', 'readthedocs']:
return

gallery_conf = app.config.sphinx_gallery_conf
binder_conf = check_binder_conf(gallery_conf.get('binder'))

# Copy the requirements files for binder
if not len(binder_conf) > 0:
return

logger.info('copying binder requirements...', color='white')
_copy_binder_reqs(app, gallery_conf, binder_conf)
logger.info('copying binder notebooks...', color='white')
_copy_binder_notebooks(app, gallery_conf, binder_conf)


def _copy_binder_reqs(app, gallery_conf, binder_conf):
"""Copy Binder requirements files to a "binder" folder in the docs."""
binder_conf = app.config.sphinx_gallery_conf['binder']
path_reqs = binder_conf.get('dependencies')

binder_folder = os.path.join(app.builder.outdir, 'binder')
binder_folder = os.path.join(app.outdir, 'binder')
if not os.path.isdir(binder_folder):
os.makedirs(binder_folder)
for path in path_reqs:
sh.copy(os.path.join(app.builder.srcdir, path),
binder_folder)
sh.copy(os.path.join(app.srcdir, path), binder_folder)


def _copy_binder_notebooks(app, gallery_conf, binder_conf):
"""Copy Jupyter notebooks to the output directory.
Walk through each output gallery directory
For each one, check if the file ends in ipynb
If it does, copy it to `notebooks_folder` with the same folder structure.
"""

# Copy ipynb files to the notebooks folder, preserving folder structure
notebooks_folder = binder_conf.get('notebooks_folder')
for i_folder in gallery_conf.get('gallery_dirs'):
for i_path, _, i_files in os.walk(i_folder):
for i_file in i_files:
if len(i_file) > 0 and i_file.endswith('.ipynb'):
path_out_dir = os.path.join(
app.outdir, notebooks_folder, i_path)
if not os.path.exists(path_out_dir):
os.makedirs(path_out_dir)
sh.copyfile(os.path.join(i_path, i_file),
os.path.join(path_out_dir, i_file))


def check_binder_conf(binder_conf):
Expand Down Expand Up @@ -168,7 +212,9 @@ def check_binder_conf(binder_conf):
elif not isinstance(path_reqs, (list, tuple)):
raise ValueError("`dependencies` value should be a list of strings. "
"Got type {}.".format(type(path_reqs)))

# Default to "notebooks" if it's not given
binder_conf['notebooks_dir'] = binder_conf.get('notebooks_dir',
'notebooks')
path_reqs_filenames = [os.path.basename(ii) for ii in path_reqs]
if not any(ii in path_reqs_filenames for ii in required_reqs_files):
raise ValueError(
Expand Down
13 changes: 4 additions & 9 deletions sphinx_gallery/gen_gallery.py
Expand Up @@ -21,7 +21,7 @@
from .docs_resolv import embed_code_links
from .downloads import generate_zipfiles
from .sorting import NumberOfCodeLinesSortKey
from .binder import copy_binder_reqs, check_binder_conf
from .binder import copy_binder_files, check_binder_conf

try:
FileNotFoundError
Expand Down Expand Up @@ -203,7 +203,7 @@ def generate_gallery_rst(app):
# better than nested.

this_fhindex, this_computation_times = generate_dir_rst(
examples_dir, gallery_dir, gallery_conf, seen_backrefs, app)
examples_dir, gallery_dir, gallery_conf, seen_backrefs)

computation_times += this_computation_times

Expand All @@ -217,7 +217,7 @@ def generate_gallery_rst(app):
src_dir = os.path.join(examples_dir, subsection)
target_dir = os.path.join(gallery_dir, subsection)
this_fhindex, this_computation_times = generate_dir_rst(src_dir, target_dir, gallery_conf,
seen_backrefs, app)
seen_backrefs)
fhindex.write(this_fhindex)
computation_times += this_computation_times

Expand All @@ -236,12 +236,6 @@ def generate_gallery_rst(app):
else:
logger.info("\t- %s: not run", fname)

# Copy the requirements files for binder
binder_conf = check_binder_conf(gallery_conf.get('binder'))
if len(binder_conf) > 0:
logger.info("copying binder requirements...")
copy_binder_reqs(app)


def touch_empty_backreferences(app, what, name, obj, options, lines):
"""Generate empty back-reference example files
Expand Down Expand Up @@ -367,6 +361,7 @@ def setup(app):

app.connect('builder-inited', generate_gallery_rst)

app.connect('build-finished', copy_binder_files)
app.connect('build-finished', sumarize_failing_examples)
app.connect('build-finished', embed_code_links)
metadata = {'parallel_read_safe': True,
Expand Down
17 changes: 3 additions & 14 deletions sphinx_gallery/gen_rst.py
Expand Up @@ -423,7 +423,7 @@ def save_thumbnail(image_path_template, src_file, file_conf, gallery_conf):
scale_image(img, thumb_file, *gallery_conf["thumbnail_size"])


def generate_dir_rst(src_dir, target_dir, gallery_conf, seen_backrefs, app):
def generate_dir_rst(src_dir, target_dir, gallery_conf, seen_backrefs):
"""Generate the gallery reStructuredText for an example directory"""

with codecs.open(os.path.join(src_dir, 'README.txt'), 'r',
Expand Down Expand Up @@ -457,8 +457,7 @@ def generate_dir_rst(src_dir, target_dir, gallery_conf, seen_backrefs, app):
fname,
target_dir,
src_dir,
gallery_conf,
app)
gallery_conf)
computation_times.append((time_elapsed, fname))
this_entry = _thumbnail_div(build_target_dir, fname, intro) + """
Expand Down Expand Up @@ -597,7 +596,7 @@ def clean_modules():
plt.rcdefaults()


def generate_file_rst(fname, target_dir, src_dir, gallery_conf, app):
def generate_file_rst(fname, target_dir, src_dir, gallery_conf):
"""Generate the rst file for a given example.
Returns
Expand Down Expand Up @@ -714,16 +713,6 @@ def generate_file_rst(fname, target_dir, src_dir, gallery_conf, app):
path_ipynb = replace_py_ipynb(example_file)
save_notebook(example_nb, path_ipynb)

# Copy the ipynb file to the static folder, preserving folder structure
if binder_conf.get('notebooks_folder'):
notebooks_folder = binder_conf.get('notebooks_folder')
path_ipynb_relative_gallery = _get_gallery_dir_path(path_ipynb,
gallery_conf)
path_notebooks_outdir = os.path.join(app.outdir, notebooks_folder,
path_ipynb_relative_gallery)
os.makedirs(os.path.dirname(path_notebooks_outdir), exist_ok=True)
shutil.copyfile(path_ipynb, path_notebooks_outdir)

with codecs.open(os.path.join(target_dir, base_image_name + '.rst'),
mode='w', encoding='utf-8') as f:
if time_elapsed >= gallery_conf["min_reported_time"]:
Expand Down

0 comments on commit 8409a74

Please sign in to comment.