├── notebooks ├── smile.png ├── images │ ├── smile.png │ └── subsubdir │ │ └── smile.png └── introduction.ipynb ├── nbsphinx_link ├── _version.py └── __init__.py ├── setup.cfg ├── docs ├── source │ ├── introduction.nblink │ ├── index.rst │ ├── installing.ipynb │ └── conf.py ├── Makefile └── make.bat ├── MANIFEST.in ├── .readthedocs.yml ├── .gitattributes ├── README.rst ├── LICENSE ├── setup.py └── .gitignore /notebooks/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vidartf/nbsphinx-link/HEAD/notebooks/smile.png -------------------------------------------------------------------------------- /nbsphinx_link/_version.py: -------------------------------------------------------------------------------- 1 | version_info = (1, 3, 2, "dev") 2 | __version__ = ".".join(map(str, version_info)) 3 | -------------------------------------------------------------------------------- /notebooks/images/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vidartf/nbsphinx-link/HEAD/notebooks/images/smile.png -------------------------------------------------------------------------------- /notebooks/images/subsubdir/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vidartf/nbsphinx-link/HEAD/notebooks/images/subsubdir/smile.png -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license_file = LICENSE 3 | 4 | [manifix] 5 | known_excludes = 6 | .git/** 7 | .git* 8 | **/__pycache__/** 9 | -------------------------------------------------------------------------------- /docs/source/introduction.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../notebooks/introduction.ipynb", 3 | "extra-media": [ 4 | "../../notebooks/images", 5 | "../../notebooks/smile.png", 6 | "not_a_path" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include *.rst 3 | include readthedocs.yml 4 | 5 | graft notebooks 6 | 7 | # Documentation 8 | graft docs 9 | exclude docs/\#* 10 | 11 | # docs subdirs we want to skip 12 | prune docs/build 13 | prune docs/gh-pages 14 | prune docs/dist 15 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # Required 2 | version: 2 3 | 4 | 5 | # Set the OS, Python version and other tools you might need 6 | build: 7 | os: ubuntu-22.04 8 | tools: 9 | python: "3.12" 10 | 11 | 12 | # Build documentation in the "docs/" directory with Sphinx 13 | sphinx: 14 | configuration: docs/source/conf.py 15 | 16 | python: 17 | install: 18 | - method: pip 19 | path: . 20 | extra_requirements: 21 | - docs 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = nbsphinx-link 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | 2 | nbsphinx-link 3 | ===================================== 4 | 5 | Version: |release| 6 | 7 | **nbsphinx-link** is a `sphinx`_ extension built on `nbsphinx`_ that 8 | allows you to include Jupyter notebooks that sit outside your sphinx source 9 | directory in your documentation. 10 | 11 | 12 | In this documentation, the Installation section is written as a 13 | notebook included with nbsphinx, while the Introduction section 14 | is written as a notebook included with nbsphinx-link. 15 | 16 | 17 | Contents 18 | -------- 19 | 20 | .. toctree:: 21 | :maxdepth: 2 22 | :caption: Installation and usage 23 | 24 | installing 25 | introduction 26 | 27 | 28 | 29 | .. links 30 | 31 | .. _`sphinx`: http://www.sphinx-doc.org 32 | 33 | .. _`nbsphinx`: https://nbsphinx.readthedocs.io 34 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | set SPHINXPROJ=nbsphinx-link 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | 2 | nbsphinx-link 3 | ============= 4 | 5 | A sphinx extension for including notebook files from outside the 6 | sphinx source root. 7 | 8 | Normally, Sphinx will only allow you to add files that are situated 9 | inside the source directory, but you might want to include files from 10 | another directory, for example a central 'examples' folder. For RST 11 | files these can be linked with `include` directives inside another 12 | RST file. For notebooks, there's nbsphinx-link! 13 | 14 | Usage 15 | ----- 16 | 17 | - Install the package. 18 | - Add 'nbsphinx_link' to extensions in Sphinx config 'conf.py' 19 | - Add a file with the '.nblink' extension where you want them included. 20 | 21 | The .nblink file is a JSON file with the following structure:: 22 | 23 | { 24 | "path": "relative/path/to/notebook" 25 | } 26 | 27 | Optionally the "extra-media" key can be added, if your notebook includes 28 | any media, i.e. images. The value needs to be an array of strings, 29 | which are paths to the media files or directories to include. Note that 30 | this is not needed if the images are added as attachments to markdown 31 | cells. 32 | 33 | Further keys might be added in the future. 34 | 35 | Note that the documentation of this project might serve as a 36 | further resource! 37 | -------------------------------------------------------------------------------- /docs/source/installing.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Installation" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "To install nbsphinx-link, run the following from a shell:\n", 15 | "\n", 16 | "```bash\n", 17 | "pip install nbsphinx-link\n", 18 | "```\n", 19 | "\n", 20 | "and add both `nbsphinx` and `nbsphinx_link` as extensions in your Sphinx build config, typicalyl in `conf.py`:\n", 21 | "\n", 22 | "```python\n", 23 | "\n", 24 | "extensions = [\n", 25 | " # ...\n", 26 | " # any other extensions you need,\n", 27 | " # ...\n", 28 | " 'nbsphinx',\n", 29 | " 'nbsphinx_link',\n", 30 | "]\n", 31 | "```" 32 | ] 33 | } 34 | ], 35 | "metadata": { 36 | "kernelspec": { 37 | "display_name": "Python 3", 38 | "language": "python", 39 | "name": "python3" 40 | }, 41 | "language_info": { 42 | "codemirror_mode": { 43 | "name": "ipython", 44 | "version": 3 45 | }, 46 | "file_extension": ".py", 47 | "mimetype": "text/x-python", 48 | "name": "python", 49 | "nbconvert_exporter": "python", 50 | "pygments_lexer": "ipython3", 51 | "version": "3.6.5" 52 | } 53 | }, 54 | "nbformat": 4, 55 | "nbformat_minor": 1 56 | } 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Simula Research Laboratory 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | import io 5 | import os 6 | 7 | from setuptools import setup, find_packages 8 | 9 | 10 | here = os.path.abspath(os.path.dirname(__file__)) 11 | 12 | # the name of the project 13 | name = 'nbsphinx-link' 14 | 15 | version_ns = {} 16 | with io.open(os.path.join(here, 'nbsphinx_link', '_version.py'), encoding="utf8") as f: 17 | exec(f.read(), {}, version_ns) 18 | 19 | 20 | setup_args = dict( 21 | name = name, 22 | description = "A sphinx extension for including notebook files outside sphinx source root", 23 | version = version_ns['__version__'], 24 | packages = find_packages(here), 25 | author = 'Vidar Tonaas Fauske', 26 | author_email = 'vidartf@gmail.com', 27 | url = 'https://github.com/vidartf/nbsphinx-link', 28 | license = 'BSD-3', 29 | platforms = "Linux, Mac OS X, Windows", 30 | keywords = ['Interactive', 'Interpreter', 'Shell', 'Web'], 31 | classifiers=[ 32 | 'Intended Audience :: Developers', 33 | 'License :: OSI Approved :: BSD License', 34 | 'Programming Language :: Python', 35 | "Programming Language :: Python :: 3", 36 | "Programming Language :: Python :: 3.7", 37 | "Programming Language :: Python :: 3.8", 38 | "Programming Language :: Python :: 3.9", 39 | "Programming Language :: Python :: 3.10", 40 | "Programming Language :: Python :: 3.11", 41 | "Programming Language :: Python :: 3.12", 42 | ], 43 | python_requires=">=3", 44 | install_requires = [ 45 | 'nbsphinx', 46 | 'sphinx>=1.8', 47 | ], 48 | extras_require={ 49 | 'docs': [ 50 | "sphinx_rtd_theme", 51 | "ipython", 52 | ] 53 | }, 54 | entry_points = { 55 | } 56 | ) 57 | 58 | 59 | if __name__ == '__main__': 60 | setup(**setup_args) 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask instance folder 57 | instance/ 58 | 59 | # Scrapy stuff: 60 | .scrapy 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | # PyBuilder 66 | target/ 67 | 68 | # IPython Notebook 69 | .ipynb_checkpoints 70 | 71 | # pyenv 72 | .python-version 73 | 74 | # celery beat schedule file 75 | celerybeat-schedule 76 | 77 | # dotenv 78 | .env 79 | 80 | # virtualenv 81 | venv/ 82 | ENV/ 83 | 84 | # Spyder project settings 85 | .spyderproject 86 | 87 | # Rope project settings 88 | .ropeproject 89 | 90 | # ========================= 91 | # Operating System Files 92 | # ========================= 93 | 94 | # OSX 95 | # ========================= 96 | 97 | .DS_Store 98 | .AppleDouble 99 | .LSOverride 100 | 101 | # Thumbnails 102 | ._* 103 | 104 | # Files that might appear in the root of a volume 105 | .DocumentRevisions-V100 106 | .fseventsd 107 | .Spotlight-V100 108 | .TemporaryItems 109 | .Trashes 110 | .VolumeIcon.icns 111 | 112 | # Directories potentially created on remote AFP share 113 | .AppleDB 114 | .AppleDesktop 115 | Network Trash Folder 116 | Temporary Items 117 | .apdisk 118 | 119 | # Windows 120 | # ========================= 121 | 122 | # Windows image file caches 123 | Thumbs.db 124 | ehthumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | # Recycle Bin used on file shares 130 | $RECYCLE.BIN/ 131 | 132 | # Windows Installer files 133 | *.cab 134 | *.msi 135 | *.msm 136 | *.msp 137 | 138 | # Windows shortcuts 139 | *.lnk 140 | 141 | # vscode config 142 | .vscode 143 | 144 | # collected media 145 | docs/source/smile.png 146 | docs/source/images 147 | -------------------------------------------------------------------------------- /notebooks/introduction.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "For a start, consider the documentation folder of the nbsphinx-link repository. This documentation page itself comes from a linked notebook:" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 1, 20 | "metadata": {}, 21 | "outputs": [ 22 | { 23 | "name": "stdout", 24 | "output_type": "stream", 25 | "text": [ 26 | "foobar\n" 27 | ] 28 | } 29 | ], 30 | "source": [ 31 | "def foo(a):\n", 32 | " return 'foo' + a\n", 33 | "\n", 34 | "print(foo('bar'))" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "After the installation steps (installing the package, and adding `nbsphinx_link` as an extension in the Sphinx config), you can link external notebooks by including `.nblink` files in your documentation source tree. The format of the link file is as follows:\n", 42 | "\n", 43 | "```json\n", 44 | "{\n", 45 | " \"path\": \"relative/path/to/notebook\"\n", 46 | "}\n", 47 | "```" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "It should then Just Work. For further information and examples, consider inspecting the Sphinx config file of the nbsphinx-link repository!" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "## Image including example\n", 62 | "Look at this happy smile, because the notebook made it in the docs.\n", 63 | "\n", 64 | "### Image path next to notebook\n", 65 | "![python logo 1](smile.png)\n", 66 | "\n", 67 | "### Image path in subdirectory notebook\n", 68 | "![python logo 2](images/smile.png)\n", 69 | "\n", 70 | "### Image path in sub-subdirectory notebook\n", 71 | "![python logo 3](images/subsubdir/smile.png)" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": null, 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [] 80 | } 81 | ], 82 | "metadata": { 83 | "kernelspec": { 84 | "display_name": "Python 3", 85 | "language": "python", 86 | "name": "python3" 87 | }, 88 | "language_info": { 89 | "codemirror_mode": { 90 | "name": "ipython", 91 | "version": 3 92 | }, 93 | "file_extension": ".py", 94 | "mimetype": "text/x-python", 95 | "name": "python", 96 | "nbconvert_exporter": "python", 97 | "pygments_lexer": "ipython3", 98 | "version": "3.7.3" 99 | } 100 | }, 101 | "nbformat": 4, 102 | "nbformat_minor": 4 103 | } 104 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/stable/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | # import os 16 | # import sys 17 | # sys.path.insert(0, os.path.abspath('.')) 18 | 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | project = 'nbsphinx-link' 23 | copyright = '2018, Vidar Tonaas Fauske' 24 | author = 'Vidar Tonaas Fauske' 25 | 26 | # The short X.Y version 27 | 28 | # get version from python package: 29 | import os 30 | here = os.path.dirname(__file__) 31 | repo = os.path.join(here, '..', '..') 32 | _version_py = os.path.join(repo, 'nbsphinx_link', '_version.py') 33 | version_ns = {} 34 | with open(_version_py) as f: 35 | exec(f.read(), version_ns) 36 | 37 | # The short X.Y version. 38 | version = '%i.%i' % version_ns['version_info'][:2] 39 | # The full version, including alpha/beta/rc tags. 40 | release = version_ns['__version__'] 41 | 42 | import subprocess 43 | try: 44 | git_rev = subprocess.check_output(['git', 'describe', '--exact-match', 'HEAD'], universal_newlines=True) 45 | except subprocess.CalledProcessError: 46 | try: 47 | git_rev = subprocess.check_output(['git', 'rev-parse', 'HEAD'], universal_newlines=True) 48 | except subprocess.CalledProcessError: 49 | git_rev = '' 50 | if git_rev: 51 | git_rev = git_rev.splitlines()[0] + '/' 52 | 53 | 54 | # -- General configuration --------------------------------------------------- 55 | 56 | # If your documentation needs a minimal Sphinx version, state it here. 57 | # 58 | # needs_sphinx = '1.0' 59 | 60 | # Add any Sphinx extension module names here, as strings. They can be 61 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 62 | # ones. 63 | extensions = [ 64 | 'nbsphinx', 65 | 'nbsphinx_link', 66 | "IPython.sphinxext.ipython_console_highlighting", 67 | ] 68 | 69 | # Add any paths that contain templates here, relative to this directory. 70 | templates_path = ['_templates'] 71 | 72 | # The suffix(es) of source filenames. 73 | # You can specify multiple suffix as a list of string: 74 | # 75 | # source_suffix = ['.rst', '.md'] 76 | source_suffix = '.rst' 77 | 78 | # The master toctree document. 79 | master_doc = 'index' 80 | 81 | # The language for content autogenerated by Sphinx. Refer to documentation 82 | # for a list of supported languages. 83 | # 84 | # This is also used if you do content translation via gettext catalogs. 85 | # Usually you set "language" from the command line for these cases. 86 | language = None 87 | 88 | # List of patterns, relative to source directory, that match files and 89 | # directories to ignore when looking for source files. 90 | # This pattern also affects html_static_path and html_extra_path . 91 | exclude_patterns = [] 92 | 93 | # The name of the Pygments (syntax highlighting) style to use. 94 | pygments_style = 'default' 95 | 96 | language = "en" 97 | 98 | 99 | # -- Options for HTML output ------------------------------------------------- 100 | 101 | 102 | # Read The Docs 103 | # on_rtd is whether we are on readthedocs.org, this line of code grabbed from 104 | # docs.readthedocs.org 105 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 106 | 107 | html_theme = 'sphinx_rtd_theme' 108 | 109 | if not on_rtd: # only import and set the theme if we're building docs locally 110 | import sphinx_rtd_theme 111 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 112 | 113 | # otherwise, readthedocs.org uses their theme by default, so no need to specify it 114 | 115 | # Theme options are theme-specific and customize the look and feel of a theme 116 | # further. For a list of options available for each theme, see the 117 | # documentation. 118 | # 119 | # html_theme_options = {} 120 | 121 | # Custom sidebar templates, must be a dictionary that maps document names 122 | # to template names. 123 | # 124 | # The default sidebars (for documents that don't match any pattern) are 125 | # defined by theme itself. Builtin themes are using these templates by 126 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 127 | # 'searchbox.html']``. 128 | # 129 | # html_sidebars = {} 130 | 131 | 132 | # -- Options for HTMLHelp output --------------------------------------------- 133 | 134 | # Output file base name for HTML help builder. 135 | htmlhelp_basename = 'nbsphinx-linkdoc' 136 | 137 | 138 | # -- Options for LaTeX output ------------------------------------------------ 139 | 140 | latex_elements = { 141 | # The paper size ('letterpaper' or 'a4paper'). 142 | # 143 | # 'papersize': 'letterpaper', 144 | 145 | # The font size ('10pt', '11pt' or '12pt'). 146 | # 147 | # 'pointsize': '10pt', 148 | 149 | # Additional stuff for the LaTeX preamble. 150 | # 151 | # 'preamble': '', 152 | 153 | # Latex figure (float) alignment 154 | # 155 | # 'figure_align': 'htbp', 156 | } 157 | 158 | # Grouping the document tree into LaTeX files. List of tuples 159 | # (source start file, target name, title, 160 | # author, documentclass [howto, manual, or own class]). 161 | latex_documents = [ 162 | (master_doc, 'nbsphinx-link.tex', 'nbsphinx-link Documentation', 163 | 'Vidar Tonaas Fauske', 'manual'), 164 | ] 165 | 166 | 167 | # -- Options for manual page output ------------------------------------------ 168 | 169 | # One entry per manual page. List of tuples 170 | # (source start file, name, description, authors, manual section). 171 | man_pages = [ 172 | (master_doc, 'nbsphinx-link', 'nbsphinx-link Documentation', 173 | [author], 1) 174 | ] 175 | 176 | 177 | # -- Options for Texinfo output ---------------------------------------------- 178 | 179 | # Grouping the document tree into Texinfo files. List of tuples 180 | # (source start file, target name, title, author, 181 | # dir menu entry, description, category) 182 | texinfo_documents = [ 183 | (master_doc, 'nbsphinx-link', 'nbsphinx-link Documentation', 184 | author, 'nbsphinx-link', 'One line description of project.', 185 | 'Miscellaneous'), 186 | ] 187 | 188 | 189 | # Ensure env.metadata[env.docname]['nbsphinx-link-target'] 190 | # points relative to repo root: 191 | nbsphinx_link_target_root = repo 192 | 193 | 194 | nbsphinx_prolog = ( 195 | r""" 196 | {% if env.metadata[env.docname]['nbsphinx-link-target'] %} 197 | {% set docpath = env.metadata[env.docname]['nbsphinx-link-target'] %} 198 | {% else %} 199 | {% set docpath = env.doc2path(env.docname, base='docs/source/') %} 200 | {% endif %} 201 | 202 | .. only:: html 203 | 204 | .. role:: raw-html(raw) 205 | :format: html 206 | 207 | .. nbinfo:: 208 | This page was generated from `{{ docpath }}`__. 209 | 210 | __ https://github.com/vidartf/nbsphinx-link/blob/ 211 | """ + 212 | git_rev + r"{{ docpath }}" 213 | ) 214 | -------------------------------------------------------------------------------- /nbsphinx_link/__init__.py: -------------------------------------------------------------------------------- 1 | """A sphinx extension for including notebook files from outside sphinx source root. 2 | 3 | Usage: 4 | - Install the package. 5 | - Add 'nbsphinx_link' to extensions in Sphinx config 'conf.py' 6 | - Add a file with the '.nblink' extension where you want them included. 7 | 8 | The .nblink file is a JSON file with the following structure: 9 | 10 | { 11 | "path": "relative/path/to/notebook" 12 | } 13 | 14 | 15 | Optionally the "extra-media" key can be added, if your notebook includes 16 | any media, i.e. images. The value needs to be an array of strings, 17 | which are paths to the media files or directories. 18 | 19 | Further keys might be added in the future. 20 | """ 21 | 22 | import json 23 | import os 24 | import shutil 25 | 26 | from docutils import io, nodes, utils 27 | from docutils.utils.error_reporting import SafeString, ErrorString 28 | import docutils # noqa: F401 29 | from nbsphinx import NotebookParser, NotebookError, _ipynbversion 30 | import nbformat 31 | from sphinx.util.logging import getLogger 32 | from ._version import __version__ 33 | 34 | 35 | def register_dependency(file_path, document): 36 | """ 37 | Registers files as dependency, so sphinx rebuilds the docs 38 | when they changed. 39 | 40 | Parameters 41 | ---------- 42 | file_path : str 43 | [description] 44 | document: docutils.nodes.document 45 | Parsed document instance. 46 | """ 47 | document.settings.record_dependencies.add(file_path) 48 | document.settings.env.note_dependency(file_path) 49 | 50 | 51 | def copy_file(src, dest, document): 52 | """ 53 | Copies a singe file from ``src`` to ``dest``. 54 | 55 | Parameters 56 | ---------- 57 | src : str 58 | Path to the source file. 59 | dest : str 60 | Path to the destination file or directory. 61 | document: docutils.nodes.document 62 | Parsed document instance. 63 | """ 64 | logger = getLogger(__name__) 65 | try: 66 | shutil.copy(src, dest) 67 | register_dependency(src, document) 68 | except (OSError) as e: 69 | logger.warning( 70 | "The the file {} couldn't be copied. " 71 | "Error:\n {}".format(src, e) 72 | ) 73 | 74 | 75 | def copy_and_register_files(src, dest, document): 76 | """ 77 | Copies a directory or file from the path ``src`` to ``dest`` 78 | and registers all files as dependency, 79 | so sphinx rebuilds the docs when they changed. 80 | 81 | Parameters 82 | ---------- 83 | src : str 84 | Path to the source directory or file 85 | dest : str 86 | Path to the destination directory or file 87 | document: docutils.nodes.document 88 | Parsed document instance. 89 | """ 90 | if os.path.isdir(src): 91 | for root, _, filenames in os.walk(src): 92 | dst_root = os.path.join(dest, os.path.relpath(root, src)) 93 | if filenames and not os.path.exists(dst_root): 94 | os.makedirs(dst_root) 95 | for filename in filenames: 96 | src_path = os.path.abspath(os.path.join(root, filename)) 97 | copy_file(src_path, dst_root, document) 98 | else: 99 | copy_file(src, dest, document) 100 | 101 | 102 | def collect_extra_media(extra_media, source_file, nb_path, document): 103 | """ 104 | Collects extra media defined in the .nblink file, with the key 105 | 'extra-media'. The extra media (i.e. images) need to be copied 106 | in order for nbsphinx to properly render the notebooks, since 107 | nbsphinx assumes that the files are relative to the .nblink. 108 | 109 | Parameters 110 | ---------- 111 | extra_media : list 112 | Paths to directories and/or files with extra media. 113 | source_file : str 114 | Path to the .nblink file. 115 | nb_path : str 116 | Path to the notebook defined in the .nblink file , with the key 'path'. 117 | document: docutils.nodes.document 118 | Parsed document instance. 119 | 120 | """ 121 | any_dirs = False 122 | logger = getLogger(__name__) 123 | source_dir = os.path.dirname(source_file) 124 | if not isinstance(extra_media, list): 125 | logger.warning( 126 | 'The "extra-media", defined in {} needs to be a list of paths. ' 127 | 'The current value is:\n{}'.format(source_file, extra_media) 128 | ) 129 | for extract_media_path in extra_media: 130 | if os.path.isabs(extract_media_path): 131 | src_path = extract_media_path 132 | else: 133 | extract_media_relpath = os.path.join( 134 | source_dir, extract_media_path 135 | ) 136 | src_path = os.path.normpath( 137 | os.path.join(source_dir, extract_media_relpath) 138 | ) 139 | 140 | dest_path = utils.relative_path(nb_path, src_path) 141 | dest_path = os.path.normpath(os.path.join(source_dir, dest_path)) 142 | if os.path.exists(src_path): 143 | any_dirs = any_dirs or os.path.isdir(src_path) 144 | copy_and_register_files(src_path, dest_path, document) 145 | else: 146 | logger.warning( 147 | 'The path "{}", defined in {} "extra-media", ' 148 | 'isn\'t a valid path.'.format( 149 | extract_media_path, source_file 150 | ) 151 | ) 152 | if any_dirs: 153 | document.settings.env.note_reread() 154 | 155 | 156 | class LinkedNotebookParser(NotebookParser): 157 | """A parser for .nblink files. 158 | 159 | The parser will replace the link file with the output from 160 | nbsphinx on the linked notebook. It will also add the linked 161 | file as a dependency, so that sphinx will take it into account 162 | when figuring out whether it should be rebuilt. 163 | 164 | The .nblink file is a JSON file with the following structure: 165 | 166 | { 167 | "path": "relative/path/to/notebook" 168 | } 169 | 170 | Optionally the "extra-media" key can be added, if your notebook includes 171 | any media, i.e. images. The value needs to be an array of strings, 172 | which are paths to the media files or directories. 173 | 174 | Further keys might be added in the future. 175 | """ 176 | 177 | supported = 'linked_jupyter_notebook', 178 | 179 | def parse(self, inputstring, document): 180 | """Parse the nblink file. 181 | 182 | Adds the linked file as a dependency, read the file, and 183 | pass the content to the nbshpinx.NotebookParser. 184 | """ 185 | link = json.loads(inputstring) 186 | env = document.settings.env 187 | source_dir = os.path.dirname(env.doc2path(env.docname)) 188 | 189 | abs_path = os.path.normpath(os.path.join(source_dir, link['path'])) 190 | path = utils.relative_path(None, abs_path) 191 | 192 | extra_media = link.get('extra-media', None) 193 | if extra_media: 194 | source_file = env.doc2path(env.docname) 195 | collect_extra_media(extra_media, source_file, path, document) 196 | 197 | register_dependency(path, document) 198 | 199 | target_root = env.config.nbsphinx_link_target_root 200 | target = utils.relative_path(target_root, abs_path).replace(os.path.sep, '/') 201 | env.metadata[env.docname]['nbsphinx-link-target'] = target 202 | 203 | # Copy parser from nbsphinx for our cutom format 204 | try: 205 | formats = env.config.nbsphinx_custom_formats 206 | except AttributeError: 207 | pass 208 | else: 209 | formats.setdefault( 210 | '.nblink', 211 | lambda s: nbformat.reads(s, as_version=_ipynbversion)) 212 | 213 | try: 214 | include_file = io.FileInput(source_path=path, encoding='utf8') 215 | except UnicodeEncodeError as error: 216 | raise NotebookError(u'Problems with linked notebook "%s" path:\n' 217 | 'Cannot encode input file path "%s" ' 218 | '(wrong locale?).' % 219 | (env.docname, SafeString(path))) 220 | except IOError as error: 221 | raise NotebookError(u'Problems with linked notebook "%s" path:\n%s.' % 222 | (env.docname, ErrorString(error))) 223 | 224 | try: 225 | rawtext = include_file.read() 226 | except UnicodeError as error: 227 | raise NotebookError(u'Problem with linked notebook "%s":\n%s' % 228 | (env.docname, ErrorString(error))) 229 | return super(LinkedNotebookParser, self).parse(rawtext, document) 230 | 231 | 232 | def setup(app): 233 | """Initialize Sphinx extension.""" 234 | app.setup_extension('nbsphinx') 235 | app.add_source_suffix('.nblink', 'linked_jupyter_notebook') 236 | app.add_source_parser(LinkedNotebookParser) 237 | app.add_config_value('nbsphinx_link_target_root', None, rebuild='env') 238 | 239 | return {'version': __version__, 'parallel_read_safe': True} 240 | --------------------------------------------------------------------------------