Skip to content

Commit

Permalink
Added media collection feature (#10)
Browse files Browse the repository at this point in the history
* commented 'html_static_path' out to get rid of the warning

* Added linked image to the notebook as a test case

* added vscode config to .gitignore

* added collected media to .gitignore

* added image in a sub directory, which is included in the notebook

* added "extra-media" key and paths to the .nblink file

* implemented media collection based on "extra-media" key 

in the .nblink file

* updated instructions on usage, to reflect the media collection feature

* fixed bug which did lead to wrong source path

* reverted formatting changes

* replaced python_logo.png with self drawn smile.png

* implemented changes marked as resolved

* updated the description of "extra-media" in README.rst

* rewrote the file copy function and added dependency registration

* refactored 'register_dependency' into 'copy_file'

* removed commented 'html_static_path' from conf.py

* moved 'os.makedirs(dest)' out of the loop

* Ensure copy_and_register_files handles subdirs

* added 'note_reread' for extra-media

* fixed wrong implementation of 'note_reread'

and fixed copying of files in sub-sub-directory's

* added media file in sub-sub-directory to the notebook

* Only note_reread if any args are dirs

* Cleanup docstring

* Add info note to readme about extra media

* Make reread code more readable
  • Loading branch information
s-weigand authored and vidartf committed Sep 27, 2019
1 parent d642886 commit 1ca10ef
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 13 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,10 @@ $RECYCLE.BIN/

# Windows shortcuts
*.lnk

# vscode config
.vscode

# collected media
docs/source/smile.png
docs/source/images
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ The .nblink file is a JSON file with the following structure::
"path": "relative/path/to/notebook"
}

Optionally the "extra-media" key can be added, if your notebook includes
any media, i.e. images. The value needs to be an array of strings,
which are paths to the media files or directories to include. Note that
this is not needed if the images are added as attachments to markdown
cells.

Further keys might be added in the future.

Note that the documentation of this project might serve as a
Expand Down
5 changes: 0 additions & 5 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,6 @@
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
Expand Down
7 changes: 6 additions & 1 deletion docs/source/introduction.nblink
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
{
"path": "../../notebooks/introduction.ipynb"
"path": "../../notebooks/introduction.ipynb",
"extra-media": [
"../../notebooks/images",
"../../notebooks/smile.png",
"not_a_path"
]
}
145 changes: 140 additions & 5 deletions nbsphinx_link/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,155 @@
"path": "relative/path/to/notebook"
}
Optionally the "extra-media" key can be added, if your notebook includes
any media, i.e. images. The value needs to be an array of strings,
which are paths to the media files or directories.
Further keys might be added in the future.
"""


import json
import os
import shutil

from docutils import io, nodes, utils
from docutils.utils.error_reporting import SafeString, ErrorString
import docutils # noqa: F401
from nbsphinx import NotebookParser, NotebookError, _ipynbversion
import nbformat
from sphinx.util.logging import getLogger

from ._version import __version__


def register_dependency(file_path, document):
"""
Registers files as dependency, so sphinx rebuilds the docs
when they changed.
Parameters
----------
file_path : str
[description]
document: docutils.nodes.document
Parsed document instance.
"""
document.settings.record_dependencies.add(file_path)
document.settings.env.note_dependency(file_path)


def copy_file(src, dest, document):
"""
Copies a singe file from ``src`` to ``dest``.
Parameters
----------
src : str
Path to the source file.
dest : str
Path to the destination file or directory.
document: docutils.nodes.document
Parsed document instance.
"""
logger = getLogger(__name__)
try:
shutil.copy(src, dest)
register_dependency(src, document)
except (OSError) as e:
logger.warning(
"The the file {} couldn't be copied. "
"Error:\n {}".format(src, e)
)


def copy_and_register_files(src, dest, document):
"""
Copies a directory or file from the path ``src`` to ``dest``
and registers all files as dependency,
so sphinx rebuilds the docs when they changed.
Parameters
----------
src : str
Path to the source directory or file
dest : str
Path to the destination directory or file
document: docutils.nodes.document
Parsed document instance.
"""
if os.path.isdir(src):
for root, _, filenames in os.walk(src):
dst_root = os.path.join(dest, os.path.relpath(root, src))
if filenames and not os.path.exists(dst_root):
os.makedirs(dst_root)
for filename in filenames:
src_path = os.path.abspath(os.path.join(root, filename))
copy_file(src_path, dst_root, document)
else:
copy_file(src, dest, document)


def collect_extra_media(extra_media, source_file, nb_path, document):
"""
Collects extra media defined in the .nblink file, with the key
'extra-media'. The extra media (i.e. images) need to be copied
in order for nbsphinx to properly render the notebooks, since
nbsphinx assumes that the files are relative to the .nblink.
Parameters
----------
extra_media : list
Paths to directories and/or files with extra media.
source_file : str
Path to the .nblink file.
nb_path : str
Path to the notebook defined in the .nblink file , with the key 'path'.
document: docutils.nodes.document
Parsed document instance.
"""
any_dirs = False
logger = getLogger(__name__)
source_dir = os.path.dirname(source_file)
if not isinstance(extra_media, list):
logger.warning(
'The "extra-media", defined in {} needs to be a list of paths. '
'The current value is:\n{}'.format(source_file, extra_media)
)
for extract_media_path in extra_media:
if os.path.isabs(extract_media_path):
src_path = extract_media_path
else:
extract_media_relpath = os.path.join(
source_dir, extract_media_path
)
src_path = os.path.normpath(
os.path.join(source_dir, extract_media_relpath)
)

dest_path = utils.relative_path(nb_path, src_path)
dest_path = os.path.normpath(os.path.join(source_dir, dest_path))
if os.path.exists(src_path):
any_dirs = any_dirs or os.path.isdir(src_path)
copy_and_register_files(src_path, dest_path, document)
else:
logger.warning(
'The path "{}", defined in {} "extra-media", '
'isn\'t a valid path.'.format(
extract_media_path, source_file
)
)
if any_dirs:
document.settings.env.note_reread()


class LinkedNotebookParser(NotebookParser):
"""A parser for .nblink files.
The parser will replace the link file with the output from
nbsphinx on the linked notebook. It will also add the linked
file as a dependency, so that sphinx will take it into acount
file as a dependency, so that sphinx will take it into account
when figuring out whether it should be rebuilt.
The .nblink file is a JSON file with the following structure:
Expand All @@ -40,6 +168,10 @@ class LinkedNotebookParser(NotebookParser):
"path": "relative/path/to/notebook"
}
Optionally the "extra-media" key can be added, if your notebook includes
any media, i.e. images. The value needs to be an array of strings,
which are paths to the media files or directories.
Further keys might be added in the future.
"""

Expand All @@ -59,8 +191,12 @@ def parse(self, inputstring, document):
path = utils.relative_path(None, abs_path)
path = nodes.reprunicode(path)

document.settings.record_dependencies.add(path)
env.note_dependency(path)
extra_media = link.get('extra-media', None)
if extra_media:
source_file = env.doc2path(env.docname)
collect_extra_media(extra_media, source_file, path, document)

register_dependency(path, document)

target_root = env.config.nbsphinx_link_target_root
target = utils.relative_path(target_root, abs_path)
Expand Down Expand Up @@ -96,7 +232,6 @@ def parse(self, inputstring, document):
return super(LinkedNotebookParser, self).parse(rawtext, document)



def setup(app):
"""Initialize Sphinx extension."""
app.setup_extension('nbsphinx')
Expand Down
Binary file added notebooks/images/smile.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added notebooks/images/subsubdir/smile.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 26 additions & 2 deletions notebooks/introduction.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,30 @@
"source": [
"It should then Just Work. For further information and examples, consider inspecting the Sphinx config file of the nbsphinx-link repository!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Image including example\n",
"Look at this happy smile, because the notebook made it in the docs.\n",
"\n",
"### Image path next to notebook\n",
"![python logo 1](smile.png)\n",
"\n",
"### Image path in subdirectory notebook\n",
"![python logo 2](images/smile.png)\n",
"\n",
"### Image path in sub-subdirectory notebook\n",
"![python logo 3](images/subsubdir/smile.png)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
Expand All @@ -71,9 +95,9 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.5"
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 1
"nbformat_minor": 4
}
Binary file added notebooks/smile.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 1ca10ef

Please sign in to comment.