├── .github ├── dependabot.yml ├── release.yml └── workflows │ ├── tests.yml │ └── tests_devdeps.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── codecov.yml ├── docs ├── _static │ ├── custom.css │ ├── logo-square.svg │ └── logo-wide.svg ├── authoring │ ├── basics.md │ ├── custom-formats.Rmd │ ├── index.md │ ├── jupyter-notebooks.md │ └── text-notebooks.md ├── computation │ ├── coconut-lang.md │ ├── execute.md │ └── index.md ├── conf.py ├── configuration.md ├── docutils.md ├── index.md ├── quickstart.md ├── reference │ ├── api.rst │ ├── changelog.md │ └── contributing.md ├── render │ ├── format_code_cells.md │ ├── glue.md │ ├── hiding.md │ ├── images │ │ └── fun-fish.png │ ├── index.md │ ├── inline.md │ ├── interactive.md │ └── orphaned_nb.ipynb └── requirements.txt ├── myst_nb ├── __init__.py ├── _compat.py ├── cli.py ├── core │ ├── __init__.py │ ├── config.py │ ├── execute │ │ ├── __init__.py │ │ ├── base.py │ │ ├── cache.py │ │ ├── direct.py │ │ └── inline.py │ ├── lexers.py │ ├── loggers.py │ ├── nb_to_tokens.py │ ├── read.py │ ├── render.py │ ├── utils.py │ └── variables.py ├── docutils_.py ├── ext │ ├── __init__.py │ ├── download.py │ ├── eval │ │ └── __init__.py │ ├── execution_tables.py │ ├── glue │ │ ├── __init__.py │ │ ├── crossref.py │ │ ├── directives.py │ │ ├── domain.py │ │ ├── roles.py │ │ └── utils.py │ └── utils.py ├── sphinx_.py ├── sphinx_ext.py ├── static │ ├── __init__.py │ └── mystnb.css └── warnings_.py ├── pyproject.toml ├── tests ├── conftest.py ├── nb_fixtures │ ├── basic.txt │ └── reporter_warnings.txt ├── notebooks │ ├── basic_failing.ipynb │ ├── basic_nometadata.md │ ├── basic_relative.ipynb │ ├── basic_run.ipynb │ ├── basic_run_intl.ipynb │ ├── basic_stderr.ipynb │ ├── basic_unrun.ipynb │ ├── basic_unrun.md │ ├── complex_outputs.ipynb │ ├── complex_outputs_unrun.ipynb │ ├── custom-formats.Rmd │ ├── custom-formats2.extra.exnt │ ├── example.jpg │ ├── file_level_config.ipynb │ ├── file_level_config.md │ ├── fun-fish.png │ ├── hide_cell_content.ipynb │ ├── ipywidgets.ipynb │ ├── kernel_alias.md │ ├── latex_build │ │ ├── index.ipynb │ │ └── other.ipynb │ ├── locale │ │ └── es │ │ │ └── LC_MESSAGES │ │ │ └── basic_run_intl.po │ ├── merge_streams.ipynb │ ├── merge_streams_parallel.ipynb │ ├── metadata_figure.ipynb │ ├── metadata_image.ipynb │ ├── metadata_image_output.ipynb │ ├── metadata_multiple_image.ipynb │ ├── mystnb_codecell_file.md │ ├── mystnb_codecell_file.py │ ├── mystnb_codecell_file_warnings.md │ ├── nb_exec_table.md │ ├── sleep_10.ipynb │ ├── sleep_10_metadata_timeout.ipynb │ ├── unknown_mimetype.ipynb │ ├── with_eval.md │ └── with_glue.ipynb ├── test_ansi_lexer.py ├── test_cli.py ├── test_codecell_file.py ├── test_codecell_file │ ├── test_codecell_file.ipynb │ ├── test_codecell_file.xml │ ├── test_codecell_file_warnings.ipynb │ └── test_codecell_file_warnings.xml ├── test_docutils.py ├── test_eval.py ├── test_eval │ └── test_sphinx.txt ├── test_execute.py ├── test_execute │ ├── test_allow_errors_auto.ipynb │ ├── test_allow_errors_auto.xml │ ├── test_allow_errors_cache.ipynb │ ├── test_allow_errors_cache.xml │ ├── test_basic_failing_auto.ipynb │ ├── test_basic_failing_auto.xml │ ├── test_basic_failing_cache.ipynb │ ├── test_basic_failing_cache.xml │ ├── test_basic_failing_inline.ipynb │ ├── test_basic_failing_inline.xml │ ├── test_basic_unrun_auto.ipynb │ ├── test_basic_unrun_auto.xml │ ├── test_basic_unrun_cache.ipynb │ ├── test_basic_unrun_cache.xml │ ├── test_basic_unrun_inline.ipynb │ ├── test_basic_unrun_inline.xml │ ├── test_complex_outputs_unrun_auto.ipynb │ ├── test_complex_outputs_unrun_auto.xml │ ├── test_complex_outputs_unrun_cache.ipynb │ ├── test_complex_outputs_unrun_cache.xml │ ├── test_custom_convert_auto.ipynb │ ├── test_custom_convert_auto.xml │ ├── test_custom_convert_cache.ipynb │ ├── test_custom_convert_cache.xml │ ├── test_custom_convert_multiple_extensions_auto.ipynb │ ├── test_custom_convert_multiple_extensions_auto.xml │ ├── test_custom_convert_multiple_extensions_cache.ipynb │ ├── test_custom_convert_multiple_extensions_cache.xml │ ├── test_exclude_path.xml │ ├── test_jupyter_cache_path.ipynb │ ├── test_jupyter_cache_path.xml │ ├── test_nb_exec_table.xml │ ├── test_no_execute.ipynb │ └── test_no_execute.xml ├── test_glue.py ├── test_glue │ └── test_parser.txt ├── test_parser.py ├── test_parser │ ├── test_basic_run.xml │ ├── test_basic_run_intl.xml │ ├── test_complex_outputs.xml │ └── test_toctree_in_ipynb.xml ├── test_render_outputs.py ├── test_render_outputs │ ├── test_basic_run.xml │ ├── test_complex_outputs.xml │ ├── test_complex_outputs_latex.xml │ ├── test_file_level_config_ipynb.xml │ ├── test_file_level_config_md.xml │ ├── test_hide_cell_content.xml │ ├── test_merge_streams.xml │ ├── test_merge_streams_parallel.xml │ ├── test_metadata_figure.xml │ ├── test_metadata_image.xml │ ├── test_metadata_image_output.xml │ ├── test_metadata_multiple_image.xml │ ├── test_stderr_remove.xml │ ├── test_stderr_tag.xml │ └── test_unknown_mimetype.xml ├── test_text_based.py └── test_text_based │ ├── test_basic_run.ipynb │ ├── test_basic_run.xml │ ├── test_basic_run_exec_off.ipynb │ └── test_basic_run_exec_off.xml └── tox.ini /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" 9 | directory: "/" 10 | schedule: 11 | interval: "monthly" 12 | 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "monthly" 17 | groups: 18 | actions: 19 | patterns: 20 | - "*" 21 | labels: 22 | - "github_actions" 23 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | authors: 4 | - dependabot 5 | - pre-commit-ci 6 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: continuous-integration 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | tags: 7 | - 'v*' 8 | pull_request: 9 | schedule: 10 | # run every Monday at 5am UTC 11 | - cron: '0 5 * * 1' 12 | workflow_dispatch: 13 | 14 | jobs: 15 | 16 | tests: 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [ubuntu-latest] 22 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 23 | sphinx: [""] # Newest Sphinx (any) 24 | myst-parser: [""] # Newest MyST Parser (any) 25 | include: 26 | # Just check the other platforms once 27 | - os: windows-latest 28 | python-version: "3.12" 29 | sphinx: "~=8.0" 30 | myst-parser: "~=4.0" 31 | pillow: "==11.0.0" 32 | - os: macos-latest 33 | python-version: "3.12" 34 | sphinx: "~=8.0" 35 | myst-parser: "~=4.0" 36 | pillow: "==11.0.0" 37 | - os: ubuntu-latest 38 | python-version: "3.12" 39 | sphinx: "~=8.0" 40 | myst-parser: "~=4.0" 41 | pillow: "==11.0.0" 42 | # Oldest known-compatible dependencies 43 | - os: ubuntu-latest 44 | python-version: "3.9" 45 | sphinx: "==5.0.0" 46 | myst-parser: "==1.0.0" 47 | pillow: "==11.0.0" 48 | # Mid-range dependencies 49 | - os: ubuntu-latest 50 | python-version: "3.11" 51 | sphinx: "==7.0.0" 52 | myst-parser: "==3.0.0" 53 | pillow: "==11.0.0" 54 | # Newest known-compatible dependencies 55 | - os: ubuntu-latest 56 | python-version: "3.12" 57 | sphinx: "==8.0.2" 58 | myst-parser: "==4.0.0" 59 | pillow: "==11.0.0" 60 | 61 | runs-on: ${{ matrix.os }} 62 | 63 | steps: 64 | - uses: actions/checkout@v4 65 | - name: Set up Python ${{ matrix.python-version }} 66 | uses: actions/setup-python@v5 67 | with: 68 | python-version: ${{ matrix.python-version }} 69 | allow-prereleases: true 70 | cache: pip 71 | - name: Install myst-nb with Sphinx ${{ matrix.sphinx }} 72 | shell: bash 73 | run: | 74 | pip install --upgrade "Sphinx${{ matrix.sphinx }}" "myst-parser${{ matrix.myst-parser }}" "pillow${{ matrix.pillow }}" -e .[testing] 75 | pip freeze 76 | 77 | - name: Run pytest 78 | run: pytest --durations=10 79 | 80 | coverage: 81 | needs: [tests] 82 | runs-on: ubuntu-latest 83 | 84 | steps: 85 | - uses: actions/checkout@v4 86 | - name: Set up Python ${{ matrix.python-version }} 87 | uses: actions/setup-python@v5 88 | with: 89 | python-version: "3.11" 90 | cache: pip 91 | - name: Install dependencies 92 | run: | 93 | pip install -e .[testing] 94 | pip freeze 95 | 96 | - name: Run pytest 97 | run: pytest --durations=10 --cov=myst_nb --cov-report=xml --cov-report=term-missing 98 | 99 | - name: Create cov 100 | run: coverage xml 101 | # for some reason the tests/conftest.py::check_nbs fixture breaks pytest-cov's cov-report outputting 102 | # this is why we run `coverage xml` afterwards (required by codecov) 103 | 104 | # TEMPORARY FIX: Disable codecov until we can get it working again 105 | - name: Upload to Codecov 106 | uses: codecov/codecov-action@v4 107 | if: false 108 | with: 109 | name: myst-nb-pytests 110 | flags: pytests 111 | files: ./coverage.xml 112 | 113 | publish: 114 | 115 | name: Publish to PyPi 116 | needs: [tests] 117 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') 118 | runs-on: ubuntu-latest 119 | steps: 120 | - name: Checkout source 121 | uses: actions/checkout@v4 122 | - name: Set up Python 123 | uses: actions/setup-python@v5 124 | with: 125 | python-version: "3.10" 126 | - name: install flit 127 | run: | 128 | pip install flit~=3.4 129 | - name: Build and publish 130 | run: | 131 | flit publish 132 | env: 133 | FLIT_USERNAME: __token__ 134 | FLIT_PASSWORD: ${{ secrets.PYPI_KEY }} 135 | -------------------------------------------------------------------------------- /.github/workflows/tests_devdeps.yml: -------------------------------------------------------------------------------- 1 | name: continuous-integration-devdeps 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | schedule: 7 | # run every Monday at 5am UTC 8 | - cron: '0 5 * * 1' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | 13 | tests: 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | sphinx: [""] # Newest Sphinx (any) 19 | myst-parser: [""] # Newest MyST Parser (any) 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up Python 3.12 26 | uses: actions/setup-python@v5 27 | with: 28 | python-version: "3.12" 29 | cache: pip 30 | - name: Install myst-nb with development Sphinx and myst-parser versions 31 | run: | 32 | pip install --upgrade pip 33 | pip install --upgrade git+https://github.com/executablebooks/MyST-Parser.git#egg=myst-parser git+https://github.com/sphinx-doc/sphinx.git#egg=sphinx -e .[testing] 34 | 35 | - name: Run pytest 36 | run: pytest --durations=10 37 | -------------------------------------------------------------------------------- /.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 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | pip-wheel-metadata/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | (PosixPath('/* 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Jupyter Cache 132 | .jupyter_cache 133 | 134 | # OSX 135 | .DS_Store 136 | 137 | .vscode/ 138 | 139 | todos.md 140 | _archive/ 141 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Install pre-commit hooks via 2 | # pre-commit install 3 | 4 | exclude: > 5 | (?x)^( 6 | \.vscode/settings\.json| 7 | tests/commonmark/commonmark\.json| 8 | .*\.xml| 9 | tests/.*\.txt 10 | )$ 11 | 12 | ci: 13 | autoupdate_schedule: 'monthly' 14 | 15 | repos: 16 | 17 | - repo: https://github.com/pre-commit/pre-commit-hooks 18 | rev: v5.0.0 19 | hooks: 20 | - id: check-json 21 | - id: check-yaml 22 | - id: end-of-file-fixer 23 | - id: trailing-whitespace 24 | - id: check-added-large-files 25 | - id: check-case-conflict 26 | - id: check-merge-conflict 27 | - id: mixed-line-ending 28 | - id: trailing-whitespace 29 | 30 | - repo: https://github.com/astral-sh/ruff-pre-commit 31 | rev: v0.11.12 32 | hooks: 33 | - id: ruff 34 | args: ["--fix", "--show-fixes"] 35 | - id: ruff-format 36 | 37 | - repo: https://github.com/pre-commit/mirrors-mypy 38 | rev: v1.16.0 39 | hooks: 40 | - id: mypy 41 | args: [--config-file=pyproject.toml] 42 | additional_dependencies: 43 | - importlib_metadata 44 | - myst-parser~=2.0.0 45 | - "sphinx~=7.3.7" 46 | - nbclient 47 | - types-PyYAML 48 | files: > 49 | (?x)^( 50 | myst_nb/.+\.py| 51 | )$ 52 | 53 | - repo: https://github.com/codespell-project/codespell 54 | rev: v2.4.1 55 | hooks: 56 | - id: codespell 57 | args: ["-S", "*.ipynb"] 58 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.11" 7 | 8 | python: 9 | install: 10 | - method: pip 11 | path: . 12 | extra_requirements: 13 | - rtd 14 | - requirements: docs/requirements.txt 15 | 16 | sphinx: 17 | configuration: docs/conf.py 18 | builder: html 19 | fail_on_warning: true 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, ExecutableBookProject 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 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. 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 | 3. 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MyST-NB 2 | 3 | [![Github-CI][github-ci]][github-link] 4 | [![Coverage Status][codecov-badge]][codecov-link] 5 | [![Documentation Status][rtd-badge]][rtd-link] 6 | [![PyPI][pypi-badge]][pypi-link] 7 | [![Conda Version][conda-badge]][conda-link] 8 | 9 | A collection of tools for working with Jupyter Notebooks in Sphinx. 10 | 11 | The primary tool this package provides is a Sphinx parser for `ipynb` files. 12 | This allows you to directly convert Jupyter Notebooks into Sphinx documents. 13 | It relies heavily on the [`MyST` parser](https://github.com/executablebooks/myst-parser). 14 | 15 | For more information, [see the MyST-NB documentation](https://myst-nb.readthedocs.io/en/latest/) 16 | 17 | ## Contributing 18 | 19 | We welcome all contributions! 20 | See the [Contributing Guide](https://myst-nb.readthedocs.io/en/latest/reference/contributing.html) for more details. 21 | 22 | [github-ci]: https://github.com/executablebooks/MyST-NB/workflows/continuous-integration/badge.svg?branch=master 23 | [github-link]: https://github.com/executablebooks/MyST-NB 24 | [rtd-badge]: https://readthedocs.org/projects/myst-nb/badge/?version=latest 25 | [rtd-link]: https://myst-nb.readthedocs.io/en/latest/?badge=latest 26 | [codecov-badge]: https://codecov.io/gh/executablebooks/MyST-NB/branch/master/graph/badge.svg 27 | [codecov-link]: https://codecov.io/gh/executablebooks/MyST-NB 28 | [pypi-badge]: https://img.shields.io/pypi/v/myst-nb.svg 29 | [pypi-link]: https://pypi.org/project/myst-nb 30 | [conda-badge]: https://img.shields.io/conda/vn/conda-forge/myst-nb.svg 31 | [conda-link]: https://anaconda.org/conda-forge/myst-nb 32 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: 80% 6 | threshold: 0.5% 7 | patch: 8 | default: 9 | target: 80% 10 | threshold: 0.5% 11 | -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | /** Add a counter before subsections **/ 2 | h1 { 3 | counter-reset: subsection; 4 | } 5 | h2 { 6 | counter-reset: subsubsection; 7 | } 8 | h2::before { 9 | counter-increment: subsection; 10 | content: counter(subsection) ". "; 11 | } 12 | h3::before { 13 | counter-increment: subsubsection; 14 | content: counter(subsection) "." counter(subsubsection) ". "; 15 | } 16 | -------------------------------------------------------------------------------- /docs/_static/logo-square.svg: -------------------------------------------------------------------------------- 1 | logo-square 2 | -------------------------------------------------------------------------------- /docs/_static/logo-wide.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/authoring/basics.md: -------------------------------------------------------------------------------- 1 | 2 | (authoring/intro)= 3 | # The basics 4 | 5 | ## Default file formats 6 | 7 | As well as writing in the Sphinx default format, [RestructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) (`.rst`), loading `myst_nb` will also parse: 8 | 9 | - Markdown files (`.md`) 10 | - Jupyter notebooks (`.ipynb`) 11 | - MyST-NB text-based notebooks (`.md` + top-matter) 12 | 13 | ## Custom file extensions 14 | 15 | You can change which file extensions are parsed by MyST-NB using 16 | the [source_suffix](https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-source_suffix) option in your `conf.py`, e.g.: 17 | 18 | ```python 19 | extensions = ["myst_nb"] 20 | source_suffix = { 21 | '.rst': 'restructuredtext', 22 | '.ipynb': 'myst-nb', 23 | '.myst': 'myst-nb', 24 | } 25 | ``` 26 | 27 | ## Other notebook formats 28 | 29 | See the [](write/custom_formats) section, for how to integrate other Notebook formats into your build, and integration with [jupytext](https://github.com/mwouts/jupytext). 30 | 31 | ## MyST Markdown 32 | 33 | For all file formats, Markdown authoring is the backbone of MyST-NB. 34 | By default, the MyST flavour of Markdown is enabled, 35 | which extends [CommonMark](https://commonmark.org/) with RST inspired syntaxes to provide the additional functionality required for technical writing. 36 | 37 | In particular MyST adds targets, roles and directives syntax, allowing you to utilise all available Docutils/Sphinx features: 38 | 39 | ::::{grid} 2 40 | :gutter: 1 41 | 42 | :::{grid-item-card} RestructuredText 43 | 44 | ``` 45 | .. _target: 46 | Header 47 | ------ 48 | 49 | :role-name:`content` 50 | 51 | .. directive-name:: argument 52 | :parameter: value 53 | 54 | content 55 | ``` 56 | ::: 57 | 58 | :::{grid-item-card} MyST Markdown 59 | 60 | ```` 61 | (target)= 62 | # Header 63 | 64 | {role-name}`content` 65 | 66 | ```{directive-name} argument 67 | :parameter: value 68 | 69 | content 70 | ``` 71 | ```` 72 | ::: 73 | :::: 74 | 75 | :::{seealso} 76 | See the [](authoring/jupyter-notebooks) section, for more details on how to author Jupyter notebooks. 77 | ::: 78 | 79 | ## Text-based notebooks 80 | 81 | MyST-NB text-based notebooks are a special format for storing Jupyter notebooks in a text file. 82 | They map directly to a Notebook file, without directly storing the code execution outputs. 83 | 84 | To designate a Markdown file as a text-based notebook, add the following top-matter to the start of the file: 85 | 86 | ```yaml 87 | --- 88 | file_format: mystnb 89 | kernelspec: 90 | name: python3 91 | --- 92 | ``` 93 | 94 | The `kernelspec.name` should relate to a [Jupyter kernel](https://github.com/jupyter/jupyter/wiki/Jupyter-kernels) installed in your environment. 95 | 96 | MyST-NB will also recognise [jupytext](https://jupytext.readthedocs.io) top-matter, such as: 97 | 98 | ```yaml 99 | --- 100 | kernelspec: 101 | name: python3 102 | display_name: python3 103 | jupytext: 104 | text_representation: 105 | extension: .md 106 | format_name: myst 107 | format_version: '0.13' 108 | jupytext_version: 1.13.8 109 | --- 110 | ``` 111 | 112 | Code cells are then designated by the `code-cell` directive: 113 | 114 | ````markdown 115 | ```{code-cell} 116 | :tags: [my-tag] 117 | 118 | print("Hello world!") 119 | ``` 120 | ```` 121 | 122 | and Markdown can be split into cells by the `+++` break syntax: 123 | 124 | ```markdown 125 | Markdown cell 1 126 | 127 | +++ {"tags": ["my-tag"]} 128 | 129 | Markdown cell 2, with metadata tags 130 | ``` 131 | 132 | :::{seealso} 133 | See the [](authoring/text-notebooks) section, for more details on text-based notebooks, and integration with [jupytext](https://jupytext.readthedocs.io). 134 | ::: 135 | 136 | ## Configuration 137 | 138 | MyST-NB parsing, execution and rendering can be configured at three levels of specificity; globally, per file, and per notebook cell, with the most specific configuration taking precedence. 139 | 140 | See the [](config/intro) section, for more details on how to configure MyST-NB. 141 | -------------------------------------------------------------------------------- /docs/authoring/custom-formats.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | author: "Marc Wouts" 3 | date: "June 16, 2018" 4 | jupyter: 5 | kernelspec: 6 | display_name: Python 7 | language: python 8 | name: python3 9 | --- 10 | 11 | (write/custom_formats)= 12 | 13 | # Custom Formats 14 | 15 | You can designate additional file types to be converted to Notebooks, and then executed / parsed in the same manner as regular Notebooks, by adding the following configuration to your `conf.py`: 16 | 17 | ```python 18 | nb_custom_formats = { 19 | ".mysuffix": "mylibrary.converter_function" 20 | } 21 | ``` 22 | 23 | - The string should be a Python function that will be loaded by `import mylibrary.converter_function` 24 | - The function should take a file's contents (as a `str`) and return an [nbformat.NotebookNode](inv:nbformat#api) 25 | 26 | If the function takes additional keyword arguments, then you can specify these as dictionary in a second argument. 27 | For example this is what the default conversion would look like: 28 | 29 | ```python 30 | nb_custom_formats = { 31 | '.ipynb': ['nbformat.reads', {'as_version': 4}], 32 | } 33 | ``` 34 | 35 | :::{important} 36 | 37 | By default, Markdown cells in the Notebook will be parsed using the same MyST parser configuration as for other Markdown files ([see available configuration options](config/intro)). 38 | 39 | But, if this is incompatible with your file format, then you can specify for the Markdown to be parsed as **strictly CommonMark**, using a third argument: 40 | 41 | ```python 42 | nb_custom_formats = { 43 | '.ipynb': ['nbformat.reads', {'as_version': 4}, True], 44 | } 45 | ``` 46 | 47 | ::: 48 | 49 | Finally, for text-based formats, MyST-NB also searches for an optional `source_map` key in the output Notebook's metadata. 50 | This key should be a list mapping each cell to the starting line number in the original source file, for example for a notebook with three cells: 51 | 52 | ```json 53 | { 54 | "metadata": { 55 | "source_map": [10, 21, 53] 56 | } 57 | } 58 | ``` 59 | 60 | This mapping allows for "true" error reporting, as described in [](myst/error-reporting). 61 | 62 | ## Using Jupytext 63 | 64 | A common conversion tool is [jupytext](https://github.com/mwouts/jupytext), which has been used to convert this very `.Rmd` file to a notebook! 65 | 66 | The configuration looks like: 67 | 68 | ```python 69 | nb_custom_formats = { 70 | ".Rmd": ["jupytext.reads", {"fmt": "Rmd"}] 71 | } 72 | ``` 73 | 74 | :::{important} 75 | For full compatibility with `myst-nb`, `jupytext>=1.11.2` should be used. 76 | ::: 77 | 78 | For example: 79 | 80 | ``` 81 | \```{python echo=TRUE} 82 | import pandas as pd 83 | series = pd.Series({'A':1, 'B':3, 'C':2}) 84 | pd.DataFrame({"Columne A": series}) 85 | \``` 86 | ``` 87 | 88 | ```{python echo=TRUE} 89 | import pandas as pd 90 | series = pd.Series({'A':1, 'B':3, 'C':2}) 91 | pd.DataFrame({"Columne A": series}) 92 | ``` 93 | 94 | ``` 95 | \```{python bar_plot, echo=FALSE, fig.height=5, fig.width=8} 96 | series.plot(kind='bar', title='Sample plot') 97 | \``` 98 | ``` 99 | 100 | ```{python bar_plot, echo=FALSE, fig.height=5, fig.width=8} 101 | series.plot(kind='bar', title='Sample plot') 102 | ``` 103 | 104 | ## Acknowledgements 105 | 106 | Thanks to [nbsphinx](https://nbsphinx.readthedocs.io), for providing the initial implementation which this was built on. 107 | -------------------------------------------------------------------------------- /docs/authoring/index.md: -------------------------------------------------------------------------------- 1 | # Authoring 2 | 3 | Create content in one or more formats. 4 | 5 | ```{toctree} 6 | :maxdepth: 1 7 | 8 | basics 9 | jupyter-notebooks 10 | text-notebooks 11 | custom-formats 12 | ``` 13 | -------------------------------------------------------------------------------- /docs/authoring/jupyter-notebooks.md: -------------------------------------------------------------------------------- 1 | --- 2 | file_format: mystnb 3 | kernelspec: 4 | name: python3 5 | --- 6 | 7 | (authoring/jupyter-notebooks)= 8 | # Jupyter Notebooks 9 | 10 | This notebook is a demonstration of directly-parsing Jupyter Notebooks into 11 | Sphinx using the MyST parser.[^download] 12 | 13 | [^download]: This notebook can be downloaded as **{nb-download}`jupyter-notebooks.ipynb`** and {download}`jupyter-notebooks.md` 14 | 15 | ## Markdown 16 | 17 | :::{seealso} 18 | For more information about what you can write with MyST Markdown, see the 19 | [MyST Parser documentation](inv:myst#intro/get-started). 20 | ::: 21 | 22 | ### Configuration 23 | 24 | The MyST-NB parser derives from [the base MyST-Parser](inv:myst#intro/get-started), and so all the same configuration options are available. 25 | See the [MyST configuration options](inv:myst#sphinx/config-options) for the full set of options, and [MyST syntax guide](inv:myst#syntax/core) for all the syntax options. 26 | 27 | To build documentation from this notebook, the following options are set: 28 | 29 | ```python 30 | myst_enable_extensions = [ 31 | "amsmath", 32 | "colon_fence", 33 | "deflist", 34 | "dollarmath", 35 | "html_image", 36 | ] 37 | myst_url_schemes = ("http", "https", "mailto") 38 | ``` 39 | 40 | :::{note} 41 | Loading the `myst_nb` extension also activates the [`myst_parser`](inv:myst#index) extension, for enabling the MyST flavour of Markdown. 42 | It is not required to add this explicitly in the list of `extensions`. 43 | ::: 44 | 45 | ### Syntax 46 | 47 | As you can see, markdown is parsed as expected. Embedding images should work as expected. 48 | For example, here's the MyST-NB logo: 49 | 50 | ```md 51 | ![myst-nb logo](../_static/logo-wide.svg) 52 | ``` 53 | 54 | ![myst-nb logo](../_static/logo-wide.svg) 55 | 56 | By adding `"html_image"` to the `myst_enable_extensions` list in the sphinx configuration ([see here](inv:myst#syntax/images)), you can even add HTML `img` tags with attributes: 57 | 58 | ```html 59 | logo 60 | ``` 61 | 62 | logo 63 | 64 | Because MyST-NB is using the MyST-markdown parser, you can include rich markdown with Sphinx in your notebook. 65 | For example, here's a note admonition block: 66 | 67 | :::::{note} 68 | **Wow**, a note! 69 | It was generated with this code ([as explained here](inv:myst:std:label#syntax/admonitions)): 70 | 71 | ````md 72 | :::{note} 73 | **Wow**, a note! 74 | ::: 75 | ```` 76 | 77 | ::::: 78 | 79 | If you wish to use "bare" LaTeX equations, then you should add `"amsmath"` to the `myst_enable_extensions` list in the sphinx configuration. 80 | This is [explained here](inv:myst:std:label#syntax/amsmath), and works as such: 81 | 82 | ```latex 83 | \begin{equation} 84 | \frac {\partial u}{\partial x} + \frac{\partial v}{\partial y} = - \, \frac{\partial w}{\partial z} 85 | \end{equation} 86 | 87 | \begin{align*} 88 | 2x - 5y &= 8 \\ 89 | 3x + 9y &= -12 90 | \end{align*} 91 | ``` 92 | 93 | \begin{equation} 94 | \frac {\partial u}{\partial x} + \frac{\partial v}{\partial y} = - \, \frac{\partial w}{\partial z} 95 | \end{equation} 96 | 97 | \begin{align*} 98 | 2x - 5y &= 8 \\ 99 | 3x + 9y &= -12 100 | \end{align*} 101 | 102 | Also you can use features like **equation numbering** and referencing in the notebooks: 103 | 104 | ```md 105 | $$e^{i\pi} + 1 = 0$$ (euler) 106 | ``` 107 | 108 | $$e^{i\pi} + 1 = 0$$ (euler) 109 | 110 | Euler's identity, equation {math:numref}`euler`, was elected one of the 111 | most beautiful mathematical formulas. 112 | 113 | You can see the syntax used for this example [here in the MyST documentation](inv:myst:std:label#syntax/math). 114 | 115 | ## Code cells and outputs 116 | 117 | You can run cells, and the cell outputs will be captured and inserted into 118 | the resulting Sphinx site. 119 | 120 | ### `__repr__` and HTML outputs 121 | 122 | For example, here's some simple Python: 123 | 124 | ```{code-cell} ipython3 125 | import matplotlib.pyplot as plt 126 | import numpy as np 127 | data = np.random.rand(3, 100) * 100 128 | data[:, :10] 129 | ``` 130 | 131 | This will also work with HTML outputs 132 | 133 | ```{code-cell} ipython3 134 | import pandas as pd 135 | df = pd.DataFrame(data.T, columns=['a', 'b', 'c']) 136 | df.head() 137 | ``` 138 | 139 | as well as math outputs 140 | 141 | ```{code-cell} ipython3 142 | from IPython.display import Math 143 | Math(r"\sum_{i=0}^n i^2 = \frac{(n^2+n)(2n+1)}{6}") 144 | ``` 145 | 146 | This works for error messages as well: 147 | 148 | ```{code-cell} ipython3 149 | :tags: [raises-exception] 150 | 151 | print("This will be properly printed...") 152 | print(thiswont) 153 | ``` 154 | 155 | ### Images 156 | 157 | Images that are generated from your code (e.g., with Matplotlib) will also 158 | be embedded. 159 | 160 | ```{code-cell} ipython3 161 | fig, ax = plt.subplots() 162 | ax.scatter(*data, c=data[2]) 163 | ``` 164 | 165 | ## Raw cells 166 | 167 | The [raw cell type](https://nbformat.readthedocs.io/en/latest/format_description.html#raw-nbconvert-cells) can be used to specifically render the content as a specific [MIME media type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types). 168 | 169 | ````markdown 170 | ```{raw-cell} 171 | :format: text/html 172 | 173 |

My cat is very grumpy.

174 | ``` 175 | ```` 176 | 177 | ```{raw-cell} 178 | :format: text/html 179 | 180 |

My cat is very grumpy.

181 | ``` 182 | -------------------------------------------------------------------------------- /docs/computation/coconut-lang.md: -------------------------------------------------------------------------------- 1 | --- 2 | file_format: mystnb 3 | kernelspec: 4 | name: coconut 5 | --- 6 | 7 | # Jupyter kernels 8 | 9 | A Jupyter Notebook can utilise any program kernel that implements the [Jupyter messaging protocol](http://jupyter-client.readthedocs.io/en/latest/messaging.html) for executing code. 10 | There are kernels available for [Python](http://ipython.org/notebook.html), [Julia](https://github.com/JuliaLang/IJulia.jl), [Ruby](https://github.com/minad/iruby), [Haskell](https://github.com/gibiansky/IHaskell) and [many other languages](https://github.com/jupyter/jupyter/wiki/Jupyter-kernels). 11 | 12 | In this notebook we demonstrate executing code with the [Coconut Programming Language](http://coconut-lang.org), a variant of Python built for *simple, elegant, Pythonic functional programming*. 13 | 14 | In the first example we will define a recursive `factorial` function, a fundamentally functional approach that doesn’t involve any state changes or loops: 15 | 16 | ```{code-cell} coconut 17 | def factorial(n): 18 | """Compute n! where n is an integer >= 0.""" 19 | case n: 20 | match 0: 21 | return 1 22 | match x is int if x > 0: 23 | return x * factorial(x-1) 24 | else: 25 | raise TypeError("the argument to factorial must be an integer >= 0") 26 | 27 | 3 |> factorial |> print 28 | ``` 29 | 30 | Although this example is very basic, pattern-matching is both one of Coconut’s most powerful and most complicated features. 31 | 32 | In the second example, we implement the quick sort algorithm. 33 | This quick_sort algorithm works using a bunch of new constructs: 34 | 35 | ```{code-cell} coconut 36 | def quick_sort(l): 37 | """Sort the input iterator using the quick sort algorithm.""" 38 | match [head] :: tail in l: 39 | tail = reiterable(tail) 40 | yield from quick_sort(left) :: [head] :: quick_sort(right) where: 41 | left = (x for x in tail if x < head) 42 | right = (x for x in tail if x >= head) 43 | # By yielding nothing if the match falls through, we implicitly return an empty iterator. 44 | 45 | [3,0,4,2,1] |> quick_sort |> list |> print 46 | ``` 47 | 48 | Finally, we see that exceptions are raised as one would expect: 49 | 50 | ```{code-cell} coconut 51 | :tags: [raises-exception] 52 | x 53 | ``` 54 | -------------------------------------------------------------------------------- /docs/computation/index.md: -------------------------------------------------------------------------------- 1 | # Computations 2 | 3 | Execute code and cache its output. 4 | 5 | ```{toctree} 6 | execute 7 | coconut-lang 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/docutils.md: -------------------------------------------------------------------------------- 1 | (render/single-page)= 2 | # Single page builds 3 | 4 | ```{versionadded} 0.14.0 5 | ``` 6 | 7 | Sphinx, and thus MyST-NB, is built on top of the [Docutils](https://docutils.sourceforge.io/docs/) package. 8 | MyST-NB offers a renderer, parser and CLI-interface for working directly with Docutils, independent of Sphinx, as described below. 9 | 10 | :::{note} 11 | Since these tools are independent of Sphinx, this means they cannot parse any Sphinx or Sphinx extensions specific roles or directives. 12 | ::: 13 | 14 | On installing MyST-NB, the following CLI-commands are made available: 15 | 16 | - `mystnb-docutils-html`: converts notebooks to HTML 17 | - `mystnb-docutils-html5`: converts notebooks to HTML5 18 | - `mystnb-docutils-latex`: converts notebooks to LaTeX 19 | - `mystnb-docutils-xml`: converts notebooks to docutils-native XML 20 | - `mystnb-docutils-pseudoxml`: converts notebooks to pseudo-XML (to visualise the AST structure) 21 | 22 | Each command can be piped stdin or take a file path as an argument: 23 | 24 | ```console 25 | $ mystnb-docutils-html --help 26 | $ mystnb-docutils-html --nb-execution-mode="off" hello-world.ipynb 27 | $ mystnb-docutils-html --nb-read-as-md="yes" hello-world.md 28 | ``` 29 | 30 | The commands are based on the [Docutils Front-End Tools](https://docutils.sourceforge.io/docs/user/tools.html), and so follow the same argument and options structure, included many of the MyST NB specific options detailed in the [](config/intro) section. 31 | 32 | :::{dropdown} Shared Docutils CLI Options 33 | ```{docutils-cli-help} 34 | ``` 35 | ::: 36 | 37 | The CLI commands can also utilise the [`docutils.conf` configuration file](https://docutils.sourceforge.io/docs/user/config.html) to configure the behaviour of the CLI commands. For example: 38 | 39 | ```ini 40 | # These entries affect all processing: 41 | [general] 42 | nb_execution_mode: off 43 | 44 | # These entries affect specific HTML output: 45 | [html writers] 46 | embed-stylesheet: no 47 | 48 | [html5 writer] 49 | stylesheet-dirs: path/to/html5_polyglot/ 50 | stylesheet-path: minimal.css, responsive.css 51 | ``` 52 | 53 | You can also use the {py:class}`myst_nb.docutils_.Parser` class programmatically with the [Docutils publisher API](https://docutils.sourceforge.io/docs/api/publisher.html): 54 | 55 | ```python 56 | from docutils.core import publish_string 57 | from nbformat import writes 58 | from nbformat.v4 import new_notebook 59 | from myst_nb.docutils_ import Parser 60 | 61 | source = writes(new_notebook()) 62 | output = publish_string( 63 | source=source, 64 | writer_name="html5", 65 | settings_overrides={ 66 | "nb_execution_mode": "off", 67 | "embed_stylesheet": False, 68 | }, 69 | parser=Parser(), 70 | ) 71 | ``` 72 | 73 | Finally, you can include MyST Markdown files within a RestructuredText file, using the [`include` directive](https://docutils.sourceforge.io/docs/ref/rst/directives.html#include): 74 | 75 | ```rst 76 | .. include:: include.ipynb 77 | :parser: myst_nb.docutils_ 78 | ``` 79 | 80 | ```{important} 81 | The `parser` option requires `docutils>=0.17` 82 | ``` 83 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | sd_hide_title: true 3 | --- 4 | 5 | # Overview 6 | 7 | ::::{grid} 8 | :reverse: 9 | :gutter: 3 4 4 4 10 | :margin: 1 2 1 2 11 | 12 | :::{grid-item} 13 | :columns: 12 4 4 4 14 | 15 | ```{image} _static/logo-square.svg 16 | :width: 200px 17 | :class: sd-m-auto 18 | ``` 19 | 20 | ::: 21 | 22 | :::{grid-item} 23 | :columns: 12 8 8 8 24 | :child-align: justify 25 | :class: sd-fs-5 26 | 27 | ```{rubric} Jupyter Notebook Publishing 28 | ``` 29 | 30 | A Sphinx and Docutils extension for compiling Jupyter Notebooks into high quality documentation formats. 31 | 32 | ```{button-ref} quickstart 33 | :ref-type: doc 34 | :color: primary 35 | :class: sd-rounded-pill 36 | 37 | Get Started 38 | ``` 39 | 40 | ::: 41 | 42 | :::: 43 | 44 | ---------------- 45 | 46 | ::::{grid} 1 2 2 3 47 | :gutter: 1 1 1 2 48 | 49 | :::{grid-item-card} {material-regular}`edit_note;2em` Write 50 | :link: authoring/intro 51 | :link-type: ref 52 | 53 | Mix Jupyter notebooks with text-based notebooks, Markdown and RST documents.\ 54 | Use MyST Markdown syntax to support technical authoring features such as cross-referencing, figures, and admonitions. 55 | 56 | +++ 57 | [Learn more »](authoring/intro) 58 | ::: 59 | 60 | :::{grid-item-card} {material-regular}`published_with_changes;2em` Compute 61 | :link: execute/intro 62 | :link-type: ref 63 | 64 | Generate dynamic outputs using Jupyter kernels, with configurable execution handling.\ 65 | Cache execution outputs, for fast re-builds. 66 | 67 | +++ 68 | [Learn more »](execute/intro) 69 | ::: 70 | 71 | :::{grid-item-card} {material-regular}`preview;2em` Render 72 | :link: render/code-cells 73 | :link-type: ref 74 | 75 | Convert Jupyter execution outputs to rich embedded content.\ 76 | Insert computed variables within the document flow.\ 77 | Build single or collections of documents into multiple formats (HTML, PDF, ...). 78 | 79 | +++ 80 | [Learn more »](render/code-cells) 81 | ::: 82 | 83 | :::: 84 | 85 | ---------------- 86 | 87 | MyST-NB is a module within the [Executable Books Project](https://executablebooks.org), 88 | an international collaboration to build open source tools that facilitate publishing computational narratives using the Jupyter ecosystem. 89 | It is also a core component of [Jupyter Book](inv:jb#intro). 90 | 91 | Check out the [Gallery of Jupyter Books](https://executablebooks.org/en/latest/gallery), 92 | for inspiration from across the community. 93 | 94 | See also, the [MyST-Markdown VS Code extension](https://marketplace.visualstudio.com/items?itemName=ExecutableBookProject.myst-highlight) 95 | and [jupyterlab-myst](https://github.com/executablebooks/jupyterlab-myst), for editor tools to author your notebooks. 96 | 97 | ```{toctree} 98 | :hidden: 99 | :maxdepth: 2 100 | 101 | quickstart 102 | ``` 103 | 104 | ```{toctree} 105 | :caption: Guides 106 | :hidden: 107 | :maxdepth: 2 108 | 109 | authoring/index 110 | computation/index 111 | render/index 112 | configuration 113 | docutils 114 | ``` 115 | 116 | ```{toctree} 117 | :caption: Reference 118 | :hidden: 119 | :titlesonly: 120 | :maxdepth: 1 121 | 122 | reference/api 123 | reference/changelog 124 | reference/contributing 125 | ``` 126 | -------------------------------------------------------------------------------- /docs/quickstart.md: -------------------------------------------------------------------------------- 1 | # Get Started 2 | 3 | `myst-nb` is distributed as a Python package and requires no non-Python dependencies. 4 | 5 | Use pip to install `myst-nb`: 6 | 7 | ```bash 8 | pip install myst-nb 9 | ``` 10 | 11 | You can use the `mystnb-quickstart` CLI to quickly create an example Sphinx + MyST-NB project: 12 | 13 | ```bash 14 | mystnb-quickstart my_project/docs/ 15 | ``` 16 | 17 | or simply add `myst_nb` to your existing Sphinx configuration: 18 | 19 | ```python 20 | extensions = [ 21 | ..., 22 | "myst_nb" 23 | ] 24 | ``` 25 | 26 | By default, MyST-NB will now parse both markdown (`.md`) and notebooks (`.ipynb`). 27 | 28 | If you used the `myst_parser` extension already, remove it from the extension list to avoid conflict — it is imported by `myst_nb` automatically; all its options, such as `myst_enable_extension`, will be processed. 29 | 30 | 31 | ```{button-ref} authoring/intro 32 | :ref-type: myst 33 | :color: primary 34 | 35 | Begin authoring your content {material-regular}`navigate_next;2em` 36 | ``` 37 | 38 | Once you have finished authoring your content, you can now use the [sphinx-build CLI](https://www.sphinx-doc.org/en/master/man/sphinx-build.html) to build your documentation, e.g. 39 | 40 | ```bash 41 | sphinx-build -nW --keep-going -b html docs/ docs/_build/html 42 | ``` 43 | 44 | 45 | :::{tip} 46 | MyST-NB is parallel-friendly, so you can also distribute the build (and execution of notebooks) over *N* processes with: `sphinx-build -j 4` 47 | ::: 48 | 49 | ```{admonition} The execution environment is the same as your Sphinx environment 50 | Your Sphinx build shares the same environment with the notebooks you execute during a build. 51 | Ensure that you call the correct `sphinx-build` command when building your documentation, or the environment needed to run the notebooks may not be correct. 52 | This often happens if you see an `Extension error` in the build log, or an error from `jupyter-cache`. 53 | ``` 54 | 55 | 56 | :::{seealso} 57 | Check out [Read the Docs](https://docs.readthedocs.io) for hosting and *continuous deployment* of documentation 58 | ::: 59 | -------------------------------------------------------------------------------- /docs/reference/api.rst: -------------------------------------------------------------------------------- 1 | .. _api/main: 2 | 3 | Python API 4 | ========== 5 | 6 | The parsing of a notebook consists of a number of stages, with each stage separated into a separate module: 7 | 8 | 1. The configuration is set (from a file or CLI) 9 | 2. The parser is called with an input string and source 10 | 3. The parser reads the input string to a notebook node 11 | 4. The notebook is converted to a Markdown-It tokens syntax tree 12 | 5. The notebook code outputs are potentially updated, via execution or from a cache 13 | 6. The syntax tree is transformed to a docutils document AST (calling the renderer plugin) 14 | 7. The docutils document is processed by docutils/sphinx, to create the desired output format(s) 15 | 16 | Configuration 17 | ------------- 18 | 19 | .. autoclass:: myst_nb.core.config.NbParserConfig 20 | :members: 21 | 22 | Parsers 23 | ------- 24 | 25 | .. autoclass:: myst_nb.docutils_.Parser 26 | :members: 27 | 28 | .. autoclass:: myst_nb.sphinx_.Parser 29 | :members: 30 | 31 | Read 32 | ---- 33 | 34 | .. autoclass:: myst_nb.core.read.NbReader 35 | :members: 36 | 37 | .. autofunction:: myst_nb.core.read.create_nb_reader 38 | 39 | .. autofunction:: myst_nb.core.read.is_myst_markdown_notebook 40 | 41 | .. autofunction:: myst_nb.core.read.read_myst_markdown_notebook 42 | 43 | Execute 44 | ------- 45 | 46 | .. autofunction:: myst_nb.core.execute.create_client 47 | 48 | .. autoclass:: myst_nb.core.execute.base.NotebookClientBase 49 | :members: 50 | 51 | .. autoclass:: myst_nb.core.execute.direct.NotebookClientDirect 52 | 53 | .. autoclass:: myst_nb.core.execute.cache.NotebookClientCache 54 | 55 | .. autoclass:: myst_nb.core.execute.inline.NotebookClientInline 56 | 57 | .. autoexception:: myst_nb.core.execute.base.ExecutionError 58 | 59 | .. autoexception:: myst_nb.core.execute.base.EvalNameError 60 | 61 | .. autoclass:: myst_nb.core.execute.base.ExecutionResult 62 | :members: 63 | 64 | Render plugin 65 | ------------- 66 | 67 | .. autoclass:: myst_nb.core.render.MimeData 68 | :members: 69 | 70 | .. autoclass:: myst_nb.core.render.NbElementRenderer 71 | :members: 72 | 73 | .. autoclass:: myst_nb.core.render.MimeRenderPlugin 74 | :members: 75 | :undoc-members: 76 | 77 | .. autoclass:: myst_nb.core.render.ExampleMimeRenderPlugin 78 | :members: 79 | :undoc-members: 80 | 81 | Lexers 82 | ------ 83 | 84 | .. autoclass:: myst_nb.core.lexers.AnsiColorLexer 85 | :members: 86 | :undoc-members: 87 | :show-inheritance: 88 | 89 | Loggers 90 | ------- 91 | 92 | .. autoclass:: myst_nb.core.loggers.DocutilsDocLogger 93 | :members: 94 | :undoc-members: 95 | :show-inheritance: 96 | 97 | 98 | .. autoclass:: myst_nb.core.loggers.SphinxDocLogger 99 | :members: 100 | :undoc-members: 101 | :show-inheritance: 102 | -------------------------------------------------------------------------------- /docs/reference/changelog.md: -------------------------------------------------------------------------------- 1 | ```{include} ../../CHANGELOG.md 2 | :relative-docs: docs/ 3 | ``` 4 | -------------------------------------------------------------------------------- /docs/render/hiding.md: -------------------------------------------------------------------------------- 1 | --- 2 | file_format: mystnb 3 | kernelspec: 4 | name: python3 5 | --- 6 | 7 | # Hide cell contents 8 | 9 | You can use Jupyter Notebook **cell tags** to control some of the behavior of 10 | the rendered notebook.[^download] 11 | If you are using cell tags for the first time, you can read more about them in this tutorial 12 | 13 | [^download]: This notebook can be downloaded as 14 | **{nb-download}`hiding.ipynb`** and {download}`hiding.md` 15 | 16 | (use/hiding/code)= 17 | 18 | ## Hide code cells 19 | 20 | You can use **cell tags** to control the content hidden with code cells at the cell level. 21 | Add the following tags to a cell's metadata to control 22 | what to hide in code cells: 23 | 24 | * **`hide-input`** tag to hide the cell inputs 25 | * **`hide-output`** to hide the cell outputs 26 | * **`hide-cell`** to hide the entire cell 27 | 28 | For example, we'll show cells with each below. 29 | 30 | ```{code-cell} ipython3 31 | :tags: [remove-cell] 32 | 33 | import matplotlib.pyplot as plt 34 | import numpy as np 35 | data = np.random.rand(2, 100) * 100 36 | ``` 37 | 38 | Here is a cell with a `hide-input` tag. 39 | 40 | ```{code-cell} ipython3 41 | :tags: [hide-input] 42 | 43 | # This cell has a hide-input tag 44 | fig, ax = plt.subplots() 45 | points =ax.scatter(*data, c=data[0], s=data[0]) 46 | ``` 47 | 48 | Here's a cell with a `hide-output` tag: 49 | 50 | ```{code-cell} ipython3 51 | :tags: [hide-output] 52 | 53 | # This cell has a hide-output tag 54 | fig, ax = plt.subplots() 55 | points =ax.scatter(*data, c=data[0], s=data[0]) 56 | ``` 57 | 58 | Here's a cell with both `hide-input` and `hide-output` tags: 59 | 60 | ```{code-cell} ipython3 61 | :tags: [hide-input, hide-output] 62 | 63 | # This cell has a hide-output tag 64 | fig, ax = plt.subplots() 65 | points =ax.scatter(*data, c=data[0], s=data[0]) 66 | ``` 67 | 68 | Here's a cell with a `hide-cell` tag: 69 | 70 | ```{code-cell} ipython3 71 | :tags: [hide-cell] 72 | 73 | # This cell has a hide-cell tag 74 | fig, ax = plt.subplots() 75 | points =ax.scatter(*data, c=data[0], s=data[0]) 76 | ``` 77 | 78 | Finally, a cell with both `remove-input` (see below) and `hide-output` tags: 79 | 80 | ```{code-cell} ipython3 81 | :tags: [remove-input, hide-output] 82 | 83 | fig, ax = plt.subplots() 84 | points = ax.scatter(*data, c=data[0], s=data[0]) 85 | ``` 86 | 87 | You can control the hide/show prompts by using the `code_prompt_show` and `code_prompt_hide` configuration options. 88 | The optional `{type}` placeholder will be replaced with `content`, `source`, or `outputs`, depending on the hide tag. 89 | See the {ref}`config/intro` section for more details. 90 | 91 | ````markdown 92 | 93 | ```{code-cell} ipython3 94 | :tags: [hide-cell] 95 | :mystnb: 96 | : code_prompt_show: "My show prompt for {type}" 97 | : code_prompt_hide: "My hide prompt for {type}" 98 | 99 | print("hallo world") 100 | ``` 101 | ```` 102 | 103 | ```{code-cell} ipython3 104 | :tags: [hide-cell] 105 | :mystnb: 106 | : code_prompt_show: "My show prompt for {type}" 107 | : code_prompt_hide: "My hide prompt for {type}" 108 | 109 | print("hallo world") 110 | ``` 111 | 112 | (use/hiding/markdown)= 113 | 114 | ## Hide markdown cells 115 | 116 | You cannot hide an entire markdown cell, but you can hide sections of markdown **content** by using roles and directives. 117 | 118 | For information on how to hide / toggle markdown content in Sphinx, see either [the `sphinx-togglebutton` documentation](https://sphinx-togglebutton.readthedocs.io/en/latest/) or the [`sphinx-design` dropdowns documentation](https://sphinx-design.readthedocs.io/en/latest/dropdowns.html). 119 | 120 | (use/removing)= 121 | 122 | ## Remove parts of cells 123 | 124 | Sometimes, you want to entirely remove parts of a cell so that it doesn't make it into the output at all. 125 | 126 | To do this at the global level, use the `nb_remove_code_source` or `nb_remove_code_outputs` configuration options, or at a per-file level, e.g. 127 | 128 | ```yaml 129 | --- 130 | mystnb: 131 | remove_code_source: true 132 | remove_code_outputs: true 133 | --- 134 | ``` 135 | 136 | See the [configuration section](config/intro) for more details. 137 | 138 | At a per-cell level you can use the same tag pattern described above, 139 | but with the word `remove_` instead of `hide_`. Use the following tags: 140 | 141 | * **`remove-input`** tag to remove the cell inputs 142 | * **`remove-output`** to remove the cell outputs 143 | * **`remove-cell`** to remove the entire cell 144 | 145 | +++ 146 | 147 | Here is a cell with a `remove-input` tag. The inputs will not make it into 148 | the page at all. 149 | 150 | ```{code-cell} ipython3 151 | :tags: [remove-input] 152 | 153 | fig, ax = plt.subplots() 154 | points =ax.scatter(*data, c=data[0], s=data[0]) 155 | ``` 156 | 157 | Here's a cell with a `remove-output` tag: 158 | 159 | ```{code-cell} ipython3 160 | :tags: [remove-output] 161 | 162 | fig, ax = plt.subplots() 163 | points = ax.scatter(*data, c=data[0], s=data[0]) 164 | ``` 165 | 166 | And the following cell has a `remove-cell` tag (there should be nothing 167 | below, since the cell will be gone). 168 | 169 | ```{code-cell} ipython3 170 | :tags: [remove-cell] 171 | 172 | fig, ax = plt.subplots() 173 | points = ax.scatter(*data, c=data[0], s=data[0]) 174 | ``` 175 | -------------------------------------------------------------------------------- /docs/render/images/fun-fish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/executablebooks/MyST-NB/201a48c2fff428ba15eaed096dec2f43b040ddc1/docs/render/images/fun-fish.png -------------------------------------------------------------------------------- /docs/render/index.md: -------------------------------------------------------------------------------- 1 | # Rendering 2 | 3 | Convert Jupyter execution outputs to embedded content, 4 | and build output formats. 5 | 6 | ```{toctree} 7 | :maxdepth: 1 8 | format_code_cells 9 | hiding 10 | interactive 11 | inline 12 | glue 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/render/inline.md: -------------------------------------------------------------------------------- 1 | --- 2 | file_format: mystnb 3 | kernelspec: 4 | name: python3 5 | mystnb: 6 | execution_mode: 'inline' 7 | --- 8 | 9 | (render/eval)= 10 | 11 | # Inline variable evaluation (eval) 12 | 13 | ```{versionadded} 0.15.0 14 | ``` 15 | 16 | The `eval` submodule allows you to insert code variables directly into the text flow of your documentation. 17 | 18 | Use of `eval` requires that the [notebook execution mode](execute/modes) is set to `inline`, since the variables are evaluated by the notebook kernel. 19 | For example, using the [top-matter](authoring/text-notebooks): 20 | 21 | ```yaml 22 | --- 23 | file_format: mystnb 24 | kernelspec: 25 | name: python3 26 | mystnb: 27 | execution_mode: 'inline' 28 | --- 29 | ``` 30 | 31 | ## Basic example 32 | 33 | Below we set a variable `v1` within a code cell. 34 | 35 | ```{code-cell} ipython3 36 | v1 = "My variable" 37 | ``` 38 | 39 | Using the `eval` role, we can insert the variable `v1` into the text of a paragraph: 40 | 41 | `` {eval}`v1` `` -> {eval}`v1` 42 | 43 | If we update the variable, we can see the change reflected in subsequent evaluation: 44 | 45 | ```{code-cell} ipython3 46 | v1 = "My new variable" 47 | ``` 48 | 49 | `` {eval}`v1` `` -> {eval}`v1` 50 | 51 | :::{important} 52 | Variable names must match the regex `[a-zA-Z][a-zA-Z0-9_]*` 53 | ::: 54 | 55 | ## Inserting different output types 56 | 57 | Any variable type can be inserted into the text flow using the `eval` role, 58 | and the most suitable output type will be used, based on the output format (see {ref}`render/output/priority` for more information). 59 | For example: 60 | 61 | ```{code-cell} ipython3 62 | import ipywidgets as widgets 63 | slider = widgets.IntSlider(value=5, min=0, max=10) 64 | ``` 65 | 66 | An inline slider (`` {eval}`slider` ``): {eval}`slider` 67 | 68 | You can also use the `eval` directive to insert variables as blocks: 69 | 70 | ```{code-cell} ipython3 71 | import matplotlib.pyplot as plt 72 | myplot, ax = plt.subplots(figsize=(6, 2)) 73 | mean = 2.0 74 | ax.plot([1,2,3]) 75 | ax.grid() 76 | plt.close() 77 | ``` 78 | 79 | using: 80 | 81 | ````markdown 82 | ```{eval} myplot 83 | ``` 84 | ```` 85 | 86 | gives: 87 | 88 | ```{eval} myplot 89 | ``` 90 | 91 | ## Embedding outputs in figures 92 | 93 | The `eval:figure` directive allows you to embed outputs in a figure, 94 | with an optional caption and other formatting options. 95 | 96 | For example, we can embed the output of the above plot in a figure: 97 | 98 | `````markdown 99 | ```{eval:figure} myplot 100 | :name: myplot 101 | My plot with a mean value of {eval}`mean`. 102 | ``` 103 | ````` 104 | 105 | which gives: 106 | 107 | ```{eval:figure} myplot 108 | :name: myplot 109 | My plot with a mean value of {eval}`mean`. 110 | ``` 111 | 112 | That can be referenced with `` {ref}`myplot` ``: {ref}`myplot` 113 | 114 | The following directive options are available: 115 | 116 | :::{table} `eval:figure` directive options 117 | | Option | Type | Description | 118 | | ------ | ---- | ----------- | 119 | | figwidth | length or percentage | The width of the figure | 120 | | figclass | text | A space-separated list of class names for the figure | 121 | | name | text | referenceable label for the figure | 122 | | alt | text | Alternate text of an image | 123 | | height | length | The desired height of an image | 124 | | width | length or percentage | The width of an image | 125 | | scale | percentage | The uniform scaling factor of an image | 126 | | class | text | A space-separated list of class names for the image | 127 | | align | text | left, center, or right | 128 | ::: 129 | -------------------------------------------------------------------------------- /docs/render/interactive.md: -------------------------------------------------------------------------------- 1 | --- 2 | file_format: mystnb 3 | kernelspec: 4 | name: python3 5 | --- 6 | 7 | # Widgets and interactive outputs 8 | 9 | Jupyter Notebooks have support for many kinds of interactive outputs. 10 | These should all be supported in MyST-NB by passing the output HTML through automatically. 11 | This page has a few common examples.[^download] 12 | 13 | [^download]: This notebook can be downloaded as **{nb-download}`interactive.ipynb`** and {download}`interactive.md` 14 | 15 | First off, we'll download a little bit of data and show its structure: 16 | 17 | ```{code-cell} ipython3 18 | import plotly.express as px 19 | data = px.data.iris() 20 | data.head() 21 | ``` 22 | 23 | ## Plotting libraries 24 | 25 | ### Altair 26 | 27 | Interactive outputs will work under the assumption that the outputs they produce have 28 | self-contained HTML that works without requiring any external dependencies to load. 29 | See the [`Altair` installation instructions](https://altair-viz.github.io/getting_started/installation.html#installation) to get set up with Altair. 30 | Below is some example output. 31 | 32 | ```{code-cell} ipython3 33 | import altair as alt 34 | alt.Chart(data=data).mark_point().encode( 35 | x="sepal_width", 36 | y="sepal_length", 37 | color="species", 38 | size='sepal_length' 39 | ) 40 | ``` 41 | 42 | ### Plotly 43 | 44 | Plotly is another interactive plotting library that provides a high-level API for visualization. 45 | See the [Plotly JupyterLab documentation](https://plotly.com/python/getting-started/#jupyterlab-support-python-35) to get started with Plotly in the notebook. 46 | 47 | Below is some example output. 48 | 49 | ```{code-cell} ipython3 50 | import plotly.io as pio 51 | import plotly.express as px 52 | import plotly.offline as py 53 | 54 | df = px.data.iris() 55 | fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species", size="sepal_length") 56 | fig 57 | ``` 58 | 59 | :::{important} 60 | 61 | You may need to supply the `require.js` for plotly to display; in your `conf.py`: 62 | 63 | ```python 64 | html_js_files = ["https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"] 65 | ``` 66 | 67 | ::: 68 | 69 | ### Bokeh 70 | 71 | Bokeh provides several options for interactive visualizations, and is part of the PyViz ecosystem. See 72 | [the Bokeh with Jupyter documentation](https://docs.bokeh.org/en/latest/docs/user_guide/jupyter.html#userguide-jupyter) to 73 | get started. 74 | 75 | Below is some example output. 76 | 77 | ```{code-cell} ipython3 78 | from bokeh.plotting import figure, show, output_notebook 79 | output_notebook() 80 | ``` 81 | 82 | ```{code-cell} ipython3 83 | from bokeh.plotting import figure, show, output_notebook 84 | output_notebook() 85 | 86 | p = figure() 87 | p.circle(data["sepal_width"], data["sepal_length"], fill_color=data["species"], size=data["sepal_length"]) 88 | show(p) 89 | ``` 90 | 91 | ## ipywidgets 92 | 93 | :::{note} 94 | IPyWidgets uses a special JS package `@jupyter-widgets/html-manager` for rendering Jupyter widgets outside notebooks. `myst-nb` loads a specific version of this package, which may be incompatible with your installation of IPyWidgets. If this is the case, you might need to specify the appropriate `nb_ipywidgets_js` config value, e.g. for `0.20.0` 95 | ```yaml 96 | 97 | sphinx: 98 | recursive_update: true 99 | config: 100 | nb_ipywidgets_js: 101 | # Load IPywidgets bundle for embedding. 102 | "https://cdn.jsdelivr.net/npm/@jupyter-widgets/html-manager@0.20.0/dist/embed-amd.js": 103 | "data-jupyter-widgets-cdn": "https://cdn.jsdelivr.net/npm/" 104 | "crossorigin": "anonymous" 105 | ``` 106 | To determine which version of `@jupyter-widgets/html-manager` is required, find the `html-manager` JS package in the [`ipywidgets` repo](https://github.com/jupyter-widgets/ipywidgets), and identify its version. 107 | ::: 108 | 109 | You may also run code for Jupyter Widgets in your document, and the interactive HTML 110 | outputs will embed themselves in your side. See [the ipywidgets documentation](https://ipywidgets.readthedocs.io/en/latest/user_install.html) 111 | for how to get set up in your own environment. 112 | 113 | ```{admonition} Widgets often need a kernel 114 | Note that `ipywidgets` tend to behave differently from other interactive viz libraries. 115 | They interact both with Javascript, and with Python. 116 | Some functionality in `ipywidgets` may not work in rendered pages (because no Python kernel is running). 117 | You may be able to get around this with tools for remote kernels, like [thebelab](https://thebelab.readthedocs.org). 118 | ``` 119 | 120 | Here are some simple widget elements rendered below. 121 | 122 | ```{code-cell} ipython3 123 | import ipywidgets as widgets 124 | widgets.IntSlider( 125 | value=7, 126 | min=0, 127 | max=10, 128 | step=1, 129 | description='Test:', 130 | disabled=False, 131 | continuous_update=False, 132 | orientation='horizontal', 133 | readout=True, 134 | readout_format='d' 135 | ) 136 | ``` 137 | 138 | ```{code-cell} ipython3 139 | tab_contents = ['P0', 'P1', 'P2', 'P3', 'P4'] 140 | children = [widgets.Text(description=name) for name in tab_contents] 141 | tab = widgets.Tab() 142 | tab.children = children 143 | for ii in range(len(children)): 144 | tab.set_title(ii, f"tab_{ii}") 145 | tab 146 | ``` 147 | 148 | You can find [a list of possible Jupyter Widgets](https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html) in the jupyter-widgets documentation. 149 | -------------------------------------------------------------------------------- /docs/render/orphaned_nb.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "(orphaned-nb)=\n", 8 | "\n", 9 | "# An orphaned notebook\n", 10 | "\n", 11 | "This defines a variable that we'll re-use in another notebook." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 1, 17 | "metadata": {}, 18 | "outputs": [ 19 | { 20 | "data": { 21 | "text/plain": [ 22 | "'My orphaned variable!'" 23 | ] 24 | }, 25 | "metadata": { 26 | "scrapbook": { 27 | "mime_prefix": "", 28 | "name": "orphaned_var" 29 | } 30 | }, 31 | "output_type": "display_data" 32 | } 33 | ], 34 | "source": [ 35 | "from myst_nb import glue\n", 36 | "\n", 37 | "glue(\"var_text\", \"My orphaned variable!\")\n", 38 | "glue(\"var_float\", 1.0 / 3.0)" 39 | ] 40 | } 41 | ], 42 | "metadata": { 43 | "kernelspec": { 44 | "display_name": "Python 3", 45 | "language": "python", 46 | "name": "python3" 47 | }, 48 | "language_info": { 49 | "codemirror_mode": { 50 | "name": "ipython", 51 | "version": 3 52 | }, 53 | "file_extension": ".py", 54 | "mimetype": "text/x-python", 55 | "name": "python", 56 | "nbconvert_exporter": "python", 57 | "pygments_lexer": "ipython3", 58 | "version": "3.7.6" 59 | }, 60 | "orphan": true 61 | }, 62 | "nbformat": 4, 63 | "nbformat_minor": 4 64 | } 65 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # this is only required by coconut kernel 2 | # Version limit is due to coconut and ipython inncompatibility as of 24/4/2025 3 | ipython<9 4 | -------------------------------------------------------------------------------- /myst_nb/__init__.py: -------------------------------------------------------------------------------- 1 | """A docutils/sphinx parser for Jupyter Notebooks.""" 2 | 3 | __version__ = "1.2.0" 4 | 5 | 6 | def setup(app): 7 | """Sphinx extension setup.""" 8 | # we import this locally, so sphinx is not automatically imported 9 | from .sphinx_ext import sphinx_setup 10 | 11 | return sphinx_setup(app) 12 | 13 | 14 | def glue(name: str, variable, display: bool = True) -> None: 15 | """Glue a variable into the notebook's cell metadata. 16 | 17 | Parameters 18 | ---------- 19 | name: string 20 | A unique name for the variable. You can use this name to refer to the variable 21 | later on. 22 | variable: Python object 23 | A variable in Python for which you'd like to store its display value. This is 24 | not quite the same as storing the object itself - the stored information is 25 | what is *displayed* when you print or show the object in a Jupyter Notebook. 26 | display: bool 27 | Display the object you are gluing. This is helpful in sanity-checking the 28 | state of the object at glue-time. 29 | """ 30 | # we import this locally, so IPython is not automatically imported 31 | from myst_nb.ext.glue import glue 32 | 33 | return glue(name, variable, display) 34 | -------------------------------------------------------------------------------- /myst_nb/_compat.py: -------------------------------------------------------------------------------- 1 | from docutils.nodes import Element 2 | 3 | 4 | def findall(node: Element): 5 | # findall replaces traverse in docutils v0.18 6 | # note a difference is that findall is an iterator 7 | return getattr(node, "findall", node.traverse) 8 | -------------------------------------------------------------------------------- /myst_nb/core/__init__.py: -------------------------------------------------------------------------------- 1 | """Modules for core functionality. 2 | 3 | The parsing of a notebook consists of a number of stages, 4 | with each stage separated into a separate module: 5 | 6 | 1. The configuration is set (from a file or CLI) 7 | 2. The parser is called with an input string and source 8 | 3. The parser reads the input string to a notebook node 9 | 4. The notebook is converted to a Markdown-It tokens syntax tree 10 | 5. The notebook code outputs are potentially updated, via execution or from a cache 11 | 6. The syntax tree is transformed to a docutils document AST (calling the renderer plugin) 12 | 7. The docutils document is processed by docutils/sphinx, to create the desired output format(s) 13 | """ 14 | -------------------------------------------------------------------------------- /myst_nb/core/execute/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from pathlib import Path, PurePosixPath 4 | from typing import TYPE_CHECKING 5 | 6 | from .base import ExecutionError, ExecutionResult, NotebookClientBase # noqa: F401 7 | from .cache import NotebookClientCache 8 | from .direct import NotebookClientDirect 9 | from .inline import NotebookClientInline 10 | 11 | if TYPE_CHECKING: 12 | from nbformat import NotebookNode 13 | 14 | from myst_nb.core.config import NbParserConfig 15 | from myst_nb.core.loggers import LoggerType 16 | 17 | 18 | def create_client( 19 | notebook: NotebookNode, 20 | source: str, 21 | nb_config: NbParserConfig, 22 | logger: LoggerType, 23 | read_fmt: None | dict = None, 24 | ) -> NotebookClientBase: 25 | """Create a notebook execution client, to update its outputs. 26 | 27 | This function may execute the notebook if necessary, to update its outputs, 28 | or populate from a cache. 29 | 30 | :param notebook: The notebook to update. 31 | :param source: Path to or description of the input source being processed. 32 | :param nb_config: The configuration for the notebook parser. 33 | :param logger: The logger to use. 34 | :param read_fmt: The format of the input source (to parse to jupyter cache) 35 | 36 | :returns: The updated notebook, and the (optional) execution metadata. 37 | """ 38 | # path should only be None when using docutils programmatically, 39 | # e.g. source="" 40 | try: 41 | path = Path(source) if Path(source).is_file() else None 42 | except OSError: 43 | path = None # occurs on Windows for `source=""` 44 | 45 | # check if the notebook is excluded from execution by pattern 46 | if path is not None and nb_config.execution_excludepatterns: 47 | posix_path = PurePosixPath(path.as_posix()) 48 | for pattern in nb_config.execution_excludepatterns: 49 | if posix_path.match(pattern): 50 | logger.info(f"Excluded from execution by pattern: {pattern!r}") 51 | return NotebookClientBase(notebook, path, nb_config, logger) 52 | 53 | # 'auto' mode only executes the notebook if it is missing at least one output 54 | missing_outputs = ( 55 | len(cell.outputs) == 0 for cell in notebook.cells if cell["cell_type"] == "code" 56 | ) 57 | if nb_config.execution_mode == "auto" and not any(missing_outputs): 58 | logger.info("Skipped execution in 'auto' mode (all outputs present)") 59 | return NotebookClientBase(notebook, path, nb_config, logger) 60 | 61 | if nb_config.execution_mode in ("auto", "force"): 62 | return NotebookClientDirect(notebook, path, nb_config, logger) 63 | 64 | if nb_config.execution_mode == "cache": 65 | return NotebookClientCache(notebook, path, nb_config, logger, read_fmt=read_fmt) 66 | 67 | if nb_config.execution_mode == "inline": 68 | return NotebookClientInline(notebook, path, nb_config, logger) 69 | 70 | return NotebookClientBase(notebook, path, nb_config, logger) 71 | -------------------------------------------------------------------------------- /myst_nb/core/execute/cache.py: -------------------------------------------------------------------------------- 1 | """Execute a notebook from the cache.""" 2 | 3 | from __future__ import annotations 4 | 5 | from contextlib import nullcontext, suppress 6 | from datetime import datetime 7 | import os 8 | from tempfile import TemporaryDirectory 9 | from typing import ContextManager 10 | 11 | from jupyter_cache import get_cache 12 | from jupyter_cache.base import CacheBundleIn 13 | from jupyter_cache.cache.db import NbProjectRecord 14 | from jupyter_cache.executors.utils import single_nb_execution 15 | 16 | from .base import ExecutionError, NotebookClientBase 17 | 18 | 19 | class NotebookClientCache(NotebookClientBase): 20 | """A notebook client that retrieves notebook outputs from the cache, 21 | or executes the notebook and adds them to the cache, on entrance of the context. 22 | """ 23 | 24 | def start_client(self): 25 | # setup the cache 26 | cache = get_cache(self.nb_config.execution_cache_path or ".jupyter_cache") 27 | # TODO config on what notebook/cell metadata to hash/merge 28 | 29 | # attempt to match the notebook to one in the cache 30 | cache_record = None 31 | with suppress(KeyError): 32 | cache_record = cache.match_cache_notebook(self.notebook) 33 | 34 | # use the cached notebook if it exists 35 | if cache_record is not None: 36 | self.logger.info(f"Using cached notebook: ID={cache_record.pk}") 37 | _, self._notebook = cache.merge_match_into_notebook(self.notebook) 38 | self.exec_metadata = { 39 | "mtime": cache_record.created.timestamp(), 40 | "runtime": cache_record.data.get("execution_seconds", None), 41 | "method": self.nb_config.execution_mode, 42 | "succeeded": True, 43 | "error": None, 44 | "traceback": None, 45 | } 46 | return 47 | 48 | if self.path is None: 49 | raise ValueError( 50 | "Input source must exist as file, if execution_mode is 'cache'" 51 | ) 52 | 53 | # attempt to execute the notebook 54 | read_fmt = self._kwargs.get("read_fmt", None) 55 | if read_fmt is not None: 56 | stage_record = cache.add_nb_to_project(str(self.path), read_data=read_fmt) 57 | else: 58 | stage_record = cache.add_nb_to_project(str(self.path)) 59 | # TODO do in try/except, in case of db write errors 60 | NbProjectRecord.remove_tracebacks([stage_record.pk], cache.db) 61 | cwd_context: ContextManager[str] = ( 62 | TemporaryDirectory() 63 | if self.nb_config.execution_in_temp 64 | else nullcontext(str(self.path.parent)) 65 | ) 66 | with cwd_context as cwd: 67 | cwd = os.path.abspath(cwd) 68 | self.logger.info( 69 | "Executing notebook using " 70 | + ("temporary" if self.nb_config.execution_in_temp else "local") 71 | + " CWD" 72 | ) 73 | result = single_nb_execution( 74 | self.notebook, 75 | cwd=cwd, 76 | allow_errors=self.nb_config.execution_allow_errors, 77 | timeout=self.nb_config.execution_timeout, 78 | meta_override=True, # TODO still support this? 79 | ) 80 | 81 | # handle success / failure cases 82 | # TODO do in try/except to be careful (in case of database write errors? 83 | if result.err is not None: 84 | msg = f"Executing notebook failed: {result.err.__class__.__name__}" 85 | if self.nb_config.execution_show_tb: 86 | msg += f"\n{result.exc_string}" 87 | self.logger.warning(msg, subtype="exec") 88 | if self.nb_config.execution_raise_on_error: 89 | raise ExecutionError(str(self.path)) from result.err 90 | NbProjectRecord.set_traceback(stage_record.uri, result.exc_string, cache.db) 91 | else: 92 | self.logger.info(f"Executed notebook in {result.time:.2f} seconds") 93 | cache_record = cache.cache_notebook_bundle( 94 | CacheBundleIn( 95 | self.notebook, 96 | stage_record.uri, 97 | data={"execution_seconds": result.time}, 98 | ), 99 | check_validity=False, 100 | overwrite=True, 101 | ) 102 | self.logger.info(f"Cached executed notebook: ID={cache_record.pk}") 103 | 104 | self.exec_metadata = { 105 | "mtime": datetime.now().timestamp(), 106 | "runtime": result.time, 107 | "method": self.nb_config.execution_mode, 108 | "succeeded": False if result.err else True, 109 | "error": f"{result.err.__class__.__name__}" if result.err else None, 110 | "traceback": result.exc_string if result.err else None, 111 | } 112 | -------------------------------------------------------------------------------- /myst_nb/core/execute/direct.py: -------------------------------------------------------------------------------- 1 | """Execute a notebook directly.""" 2 | 3 | from __future__ import annotations 4 | 5 | from contextlib import nullcontext 6 | from datetime import datetime 7 | import os 8 | from tempfile import TemporaryDirectory 9 | from typing import ContextManager 10 | 11 | from jupyter_cache.executors.utils import single_nb_execution 12 | 13 | from .base import ExecutionError, NotebookClientBase 14 | 15 | 16 | class NotebookClientDirect(NotebookClientBase): 17 | """A notebook client that executes the notebook directly, 18 | on entrance of the context. 19 | """ 20 | 21 | def start_client(self): 22 | # setup the execution current working directory 23 | cwd_context: ContextManager[str] 24 | if self.nb_config.execution_in_temp: 25 | cwd_context = TemporaryDirectory() 26 | else: 27 | if self.path is None: 28 | raise ValueError( 29 | "Input source must exist as file, if execution_in_temp=False" 30 | ) 31 | cwd_context = nullcontext(str(self.path.parent)) 32 | 33 | # execute in the context of the current working directory 34 | with cwd_context as cwd: 35 | cwd = os.path.abspath(cwd) 36 | self.logger.info( 37 | "Executing notebook using " 38 | + ("temporary" if self.nb_config.execution_in_temp else "local") 39 | + " CWD" 40 | ) 41 | result = single_nb_execution( 42 | self.notebook, 43 | cwd=cwd, 44 | allow_errors=self.nb_config.execution_allow_errors, 45 | timeout=self.nb_config.execution_timeout, 46 | meta_override=True, # TODO still support this? 47 | ) 48 | 49 | if result.err is not None: 50 | if self.nb_config.execution_raise_on_error: 51 | raise ExecutionError(str(self.path)) from result.err 52 | msg = f"Executing notebook failed: {result.err.__class__.__name__}" 53 | if self.nb_config.execution_show_tb: 54 | msg += f"\n{result.exc_string}" 55 | self.logger.warning(msg, subtype="exec") 56 | else: 57 | self.logger.info(f"Executed notebook in {result.time:.2f} seconds") 58 | 59 | self.exec_metadata = { 60 | "mtime": datetime.now().timestamp(), 61 | "runtime": result.time, 62 | "method": self.nb_config.execution_mode, 63 | "succeeded": False if result.err else True, 64 | "error": f"{result.err.__class__.__name__}" if result.err else None, 65 | "traceback": result.exc_string if result.err else None, 66 | } 67 | -------------------------------------------------------------------------------- /myst_nb/core/utils.py: -------------------------------------------------------------------------------- 1 | """Shared utilities.""" 2 | 3 | from __future__ import annotations 4 | 5 | import re 6 | 7 | from nbformat import NotebookNode 8 | 9 | _RGX_CARRIAGERETURN = re.compile(r".*\r(?=[^\n])") 10 | _RGX_BACKSPACE = re.compile(r"[^\n]\b") 11 | 12 | 13 | def coalesce_streams(outputs: list[NotebookNode]) -> list[NotebookNode]: 14 | """Merge all stream outputs with shared names into single streams. 15 | 16 | This ensure deterministic outputs. 17 | 18 | Adapted from: 19 | https://github.com/computationalmodelling/nbval/blob/master/nbval/plugin.py. 20 | """ 21 | if not outputs: 22 | return [] 23 | 24 | new_outputs = [] 25 | streams: dict[str, NotebookNode] = {} 26 | for output in outputs: 27 | if output["output_type"] == "stream": 28 | if output["name"] in streams: 29 | out = output["text"].rstrip() 30 | if out: 31 | streams[output["name"]]["text"] += f"{out}\n" 32 | else: 33 | output["text"] = output["text"].rstrip() + "\n" 34 | new_outputs.append(output) 35 | streams[output["name"]] = output 36 | else: 37 | new_outputs.append(output) 38 | 39 | # process \r and \b characters 40 | for output in streams.values(): 41 | old = output["text"] 42 | while len(output["text"]) < len(old): 43 | old = output["text"] 44 | # Cancel out anything-but-newline followed by backspace 45 | output["text"] = _RGX_BACKSPACE.sub("", output["text"]) 46 | # Replace all carriage returns not followed by newline 47 | output["text"] = _RGX_CARRIAGERETURN.sub("", output["text"]) 48 | 49 | # We also want to ensure stdout and stderr are always in the same consecutive order, 50 | # because they are asynchronous, so order isn't guaranteed. 51 | for i, output in enumerate(new_outputs): 52 | if output["output_type"] == "stream" and output["name"] == "stderr": 53 | if ( 54 | len(new_outputs) >= i + 2 55 | and new_outputs[i + 1]["output_type"] == "stream" 56 | and new_outputs[i + 1]["name"] == "stdout" 57 | ): 58 | stdout = new_outputs.pop(i + 1) 59 | new_outputs.insert(i, stdout) 60 | 61 | return new_outputs 62 | -------------------------------------------------------------------------------- /myst_nb/ext/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/executablebooks/MyST-NB/201a48c2fff428ba15eaed096dec2f43b040ddc1/myst_nb/ext/__init__.py -------------------------------------------------------------------------------- /myst_nb/ext/download.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | from docutils import nodes 5 | from sphinx.addnodes import download_reference 6 | from sphinx.util.docutils import ReferenceRole 7 | 8 | from myst_nb.sphinx_ import SphinxEnvType 9 | 10 | 11 | class NbDownloadRole(ReferenceRole): 12 | """Role to download an executed notebook.""" 13 | 14 | def run(self): 15 | """Run the role.""" 16 | # get a path relative to the current document 17 | self.env: SphinxEnvType 18 | path = Path(self.env.mystnb_config.output_folder).joinpath( 19 | *(self.env.docname.split("/")[:-1] + self.target.split("/")) 20 | ) 21 | reftarget = ( 22 | path.as_posix() 23 | if os.name == "nt" 24 | else ("/" + os.path.relpath(path, self.env.app.srcdir)) 25 | ) 26 | node = download_reference(self.rawtext, reftarget=reftarget) 27 | self.set_source_info(node) 28 | title = self.title if self.has_explicit_title else self.target 29 | node += nodes.literal( 30 | self.rawtext, title, classes=["xref", "download", "myst-nb"] 31 | ) 32 | return [node], [] 33 | -------------------------------------------------------------------------------- /myst_nb/ext/glue/__init__.py: -------------------------------------------------------------------------------- 1 | """Functionality for storing special data in notebook code cells, 2 | which can then be inserted into the document body. 3 | """ 4 | 5 | from __future__ import annotations 6 | 7 | from typing import TYPE_CHECKING, Any 8 | 9 | import IPython 10 | from IPython.display import display as ipy_display 11 | from nbformat import NotebookNode 12 | 13 | from myst_nb.core.loggers import LoggerType 14 | 15 | if TYPE_CHECKING: 16 | from sphinx.application import Sphinx 17 | 18 | from myst_nb.docutils_ import DocutilsApp 19 | 20 | GLUE_PREFIX = "application/papermill.record/" 21 | 22 | 23 | def load_glue_sphinx(app: Sphinx) -> None: 24 | """Load the eval domain.""" 25 | from .directives import PasteAnyDirective 26 | from .domain import NbGlueDomain 27 | from .roles import PasteRoleAny 28 | 29 | app.add_directive("glue", PasteAnyDirective, override=True) 30 | app.add_role("glue", PasteRoleAny(), override=True) 31 | app.add_domain(NbGlueDomain) 32 | 33 | 34 | def load_glue_docutils(app: DocutilsApp) -> None: 35 | from .directives import ( 36 | PasteAnyDirective, 37 | PasteFigureDirective, 38 | PasteMarkdownDirective, 39 | PasteMathDirective, 40 | ) 41 | from .roles import PasteMarkdownRole, PasteRoleAny, PasteTextRole 42 | 43 | for name, role in [ 44 | ("glue", PasteRoleAny()), 45 | ("glue:", PasteRoleAny()), 46 | ("glue:any", PasteRoleAny()), 47 | ("glue:text", PasteTextRole()), 48 | ("glue:md", PasteMarkdownRole()), 49 | ]: 50 | app.roles[name] = role 51 | 52 | for name, directive in [ 53 | ("glue", PasteAnyDirective), 54 | ("glue:", PasteAnyDirective), 55 | ("glue:any", PasteAnyDirective), 56 | ("glue:figure", PasteFigureDirective), 57 | ("glue:math", PasteMathDirective), 58 | ("glue:md", PasteMarkdownDirective), 59 | ]: 60 | app.directives[name] = directive 61 | 62 | 63 | def glue(name: str, variable: Any, display: bool = True) -> None: 64 | """Glue a variable into the notebook's cell metadata. 65 | 66 | Parameters 67 | ---------- 68 | name: string 69 | A unique name for the variable. You can use this name to refer to the variable 70 | later on. 71 | variable: Python object 72 | A variable in Python for which you'd like to store its display value. This is 73 | not quite the same as storing the object itself - the stored information is 74 | what is *displayed* when you print or show the object in a Jupyter Notebook. 75 | display: bool 76 | Display the object you are gluing. This is helpful in sanity-checking the 77 | state of the object at glue-time. 78 | """ 79 | mimebundle, metadata = IPython.core.formatters.format_display_data(variable) 80 | mime_prefix = "" if display else GLUE_PREFIX 81 | metadata["scrapbook"] = dict(name=name, mime_prefix=mime_prefix) 82 | ipy_display( 83 | {mime_prefix + k: v for k, v in mimebundle.items()}, raw=True, metadata=metadata 84 | ) 85 | 86 | 87 | def extract_glue_data( 88 | notebook: NotebookNode, 89 | source_map: list[int], 90 | logger: LoggerType, 91 | ) -> dict[str, NotebookNode]: 92 | """Extract all the glue data from the notebook.""" 93 | # note this assumes v4 notebook format 94 | data: dict[str, NotebookNode] = {} 95 | for index, cell in enumerate(notebook.cells): 96 | if cell.cell_type != "code": 97 | continue 98 | for key, cell_data in extract_glue_data_cell(cell): 99 | if key in data: 100 | logger.warning( 101 | f"glue key {key!r} duplicate", 102 | subtype="glue", 103 | line=source_map[index], 104 | ) 105 | data[key] = cell_data 106 | 107 | return data 108 | 109 | 110 | def extract_glue_data_cell(cell: NotebookNode) -> list[tuple[str, NotebookNode]]: 111 | """Extract glue data from a single cell.""" 112 | outputs = [] 113 | data = [] 114 | for output in cell.get("outputs", []): 115 | meta = output.get("metadata", {}) 116 | if "scrapbook" not in meta: 117 | outputs.append(output) 118 | continue 119 | key = meta["scrapbook"]["name"] 120 | mime_prefix = len(meta["scrapbook"].get("mime_prefix", "")) 121 | output["data"] = {k[mime_prefix:]: v for k, v in output["data"].items()} 122 | data.append((key, output)) 123 | if not mime_prefix: 124 | # assume that the output is a displayable object 125 | outputs.append(output) 126 | cell.outputs = outputs 127 | return data 128 | -------------------------------------------------------------------------------- /myst_nb/ext/glue/crossref.py: -------------------------------------------------------------------------------- 1 | """Sphinx only cross-document gluing. 2 | 3 | Note, we restrict this to a only a subset of mime-types and data -> nodes transforms, 4 | since adding these nodes in a post-transform will not apply any transforms to them. 5 | """ 6 | 7 | from __future__ import annotations 8 | 9 | from functools import lru_cache 10 | import json 11 | from pathlib import Path 12 | from typing import Any, Sequence 13 | 14 | from docutils import nodes 15 | from sphinx.transforms.post_transforms import SphinxPostTransform 16 | from sphinx.util import logging as sphinx_logging 17 | 18 | from myst_nb._compat import findall 19 | from myst_nb.core.loggers import DEFAULT_LOG_TYPE 20 | from myst_nb.core.render import get_mime_priority 21 | from myst_nb.core.variables import format_plain_text 22 | 23 | from .utils import PendingGlueReference 24 | 25 | SPHINX_LOGGER = sphinx_logging.getLogger(__name__) 26 | 27 | 28 | @lru_cache(maxsize=3) 29 | def read_glue_cache(folder: str, docname: str) -> dict[str, Any]: 30 | """Read a glue cache from the build folder, for a particular document.""" 31 | docpath = docname.split("/") 32 | path = Path(folder).joinpath(*docpath[:-1]).joinpath(f"{docpath[-1]}.glue.json") 33 | if not path.exists(): 34 | return {} 35 | with path.open("r") as f: 36 | return json.load(f) 37 | 38 | 39 | class ReplacePendingGlueReferences(SphinxPostTransform): 40 | """Sphinx only cross-document gluing. 41 | 42 | Note, we restrict this to a only a subset of mime-types and data -> nodes transforms, 43 | since adding these nodes in a post-transform will not apply any transforms to them. 44 | """ 45 | 46 | default_priority = 5 47 | 48 | def apply(self, **kwargs): 49 | """Apply the transform.""" 50 | cache_folder = self.env.mystnb_config.output_folder # type: ignore 51 | bname = self.app.builder.name 52 | priority_list = get_mime_priority( 53 | bname, self.config["nb_mime_priority_overrides"] 54 | ) 55 | node: PendingGlueReference 56 | for node in list(findall(self.document)(PendingGlueReference)): 57 | data = read_glue_cache(cache_folder, node.refdoc) 58 | if node.key not in data: 59 | SPHINX_LOGGER.warning( 60 | f"Glue reference {node.key!r} not found in doc {node.refdoc!r} " 61 | f"[{DEFAULT_LOG_TYPE}.glue_ref]", 62 | type=DEFAULT_LOG_TYPE, 63 | subtype="glue_ref", 64 | location=node, 65 | ) 66 | node.parent.remove(node) 67 | continue 68 | output = data[node.key] 69 | if node.gtype == "text": 70 | _nodes = generate_text_nodes(node, output) 71 | else: 72 | _nodes = generate_any_nodes(node, output, priority_list) 73 | 74 | if _nodes: 75 | node.replace_self(_nodes) 76 | else: 77 | node.parent.remove(node) 78 | 79 | 80 | def ref_warning(msg: str, node) -> None: 81 | """Log a warning for a reference.""" 82 | SPHINX_LOGGER.warning( 83 | f"{msg} [{DEFAULT_LOG_TYPE}.glue_ref]", 84 | type=DEFAULT_LOG_TYPE, 85 | subtype="glue_ref", 86 | location=node, 87 | ) 88 | 89 | 90 | def generate_any_nodes( 91 | node: PendingGlueReference, output: dict[str, Any], priority_list: Sequence[str] 92 | ) -> list[nodes.Element]: 93 | """Generate nodes for a cell, according to the highest priority mime type.""" 94 | data = output["data"] 95 | for mime_type in priority_list: 96 | if mime_type not in data: 97 | continue 98 | if mime_type == "text/plain": 99 | if node.inline: 100 | return [nodes.literal(data[mime_type], data[mime_type])] 101 | else: 102 | return [nodes.literal_block(data[mime_type], data[mime_type])] 103 | if mime_type == "text/html": 104 | return [ 105 | nodes.raw( 106 | text=data[mime_type], format="html", classes=["output", "text_html"] 107 | ) 108 | ] 109 | ref_warning( 110 | f"No allowed mime type found in {node.key!r}: {list(output['data'])}", node 111 | ) 112 | return [] 113 | 114 | 115 | def generate_text_nodes(node: PendingGlueReference, output: dict[str, Any]): 116 | """Generate nodes for a cell, for formatted text/plain.""" 117 | data = output["data"] 118 | if "text/plain" not in data: 119 | ref_warning(f"No text/plain found in {node.key!r}", node) 120 | return [] 121 | try: 122 | text = format_plain_text(data["text/plain"], node["fmt_spec"]) 123 | except Exception as exc: 124 | ref_warning(f"Failed to format text/plain: {exc}", node) 125 | return [] 126 | return [nodes.inline(text, text, classes=["pasted-text"])] 127 | -------------------------------------------------------------------------------- /myst_nb/ext/glue/domain.py: -------------------------------------------------------------------------------- 1 | """A domain to register in sphinx. 2 | 3 | This is required for any directive/role names using `:`. 4 | """ 5 | 6 | from sphinx.domains import Domain 7 | 8 | from .directives import ( 9 | PasteAnyDirective, 10 | PasteFigureDirective, 11 | PasteMarkdownDirective, 12 | PasteMathDirective, 13 | ) 14 | from .roles import PasteMarkdownRole, PasteRoleAny, PasteTextRole 15 | 16 | 17 | class NbGlueDomain(Domain): 18 | """A sphinx domain for defining glue roles and directives. 19 | 20 | Note, the only reason we use this, 21 | is that sphinx will not allow for `:` in a directive/role name, 22 | if it is part of a domain. 23 | """ 24 | 25 | name = "glue" 26 | label = "NotebookGlue" 27 | 28 | # data version, bump this when the format of self.data changes 29 | data_version = 1 30 | 31 | roles = { 32 | "": PasteRoleAny(), 33 | "any": PasteRoleAny(), 34 | "text": PasteTextRole(), 35 | "md": PasteMarkdownRole(), 36 | } 37 | directives = { 38 | "": PasteAnyDirective, 39 | "any": PasteAnyDirective, 40 | "figure": PasteFigureDirective, 41 | "math": PasteMathDirective, 42 | "md": PasteMarkdownDirective, 43 | } 44 | 45 | def merge_domaindata(self, *args, **kwargs): 46 | pass 47 | 48 | def resolve_any_xref(self, *args, **kwargs): 49 | return [] 50 | -------------------------------------------------------------------------------- /myst_nb/ext/glue/utils.py: -------------------------------------------------------------------------------- 1 | """Utilities for working with docutils and sphinx. 2 | 3 | We intentionally do no import sphinx in this module, 4 | in order to allow docutils-only use without sphinx installed. 5 | """ 6 | 7 | from __future__ import annotations 8 | 9 | from functools import partial 10 | from typing import TYPE_CHECKING, Any 11 | 12 | from docutils import nodes 13 | 14 | from myst_nb.core.render import NbElementRenderer 15 | from myst_nb.core.variables import ( 16 | RetrievalError, 17 | VariableOutput, 18 | create_warning, 19 | is_sphinx, 20 | ) 21 | 22 | if TYPE_CHECKING: 23 | from sphinx.environment import BuildEnvironment 24 | 25 | glue_warning = partial(create_warning, subtype="glue") 26 | 27 | 28 | class PendingGlueReference(nodes.Element): 29 | """A glue reference to another document.""" 30 | 31 | @property 32 | def refdoc(self) -> str: 33 | return self.attributes["refdoc"] 34 | 35 | @property 36 | def key(self) -> str: 37 | return self.attributes["key"] 38 | 39 | @property 40 | def inline(self) -> bool: 41 | return self.attributes.get("inline", False) 42 | 43 | @property 44 | def gtype(self) -> str | None: 45 | return self.attributes.get("gtype", None) 46 | 47 | 48 | class PendingGlueReferenceError(Exception): 49 | """An error occurred while resolving a pending glue reference.""" 50 | 51 | 52 | def create_pending_glue_ref( 53 | document: nodes.document, 54 | source: str, 55 | line: int, 56 | rel_doc: str, 57 | key: str, 58 | inline: bool = False, 59 | gtype: str | None = None, 60 | **kwargs: Any, 61 | ) -> PendingGlueReference: 62 | """Create a pending glue reference.""" 63 | if not is_sphinx(document): 64 | raise PendingGlueReferenceError( 65 | "Pending glue references are only supported in sphinx." 66 | ) 67 | env: BuildEnvironment = document.settings.env 68 | _, filepath = env.relfn2path(rel_doc, env.docname) 69 | refdoc = env.path2doc(filepath) 70 | if refdoc is None: 71 | raise PendingGlueReferenceError( 72 | f"Pending glue reference document not found: {filepath!r}." 73 | ) 74 | ref = PendingGlueReference( 75 | refdoc=refdoc, key=key, inline=inline, gtype=gtype, **kwargs 76 | ) 77 | ref.source = source 78 | ref.line = line 79 | return ref 80 | 81 | 82 | def retrieve_glue_data(document: nodes.document, key: str) -> VariableOutput: 83 | """Retrieve the glue data from a specific document.""" 84 | msg = f"No key {key!r} found in glue data for this document." 85 | if "nb_renderer" not in document: 86 | raise RetrievalError(msg) 87 | element: NbElementRenderer = document["nb_renderer"] 88 | glue_data = element.renderer.nb_client.glue_data 89 | 90 | if key not in glue_data: 91 | raise RetrievalError(msg) 92 | 93 | return VariableOutput( 94 | data=glue_data[key]["data"], 95 | metadata=glue_data[key].get("metadata", {}), 96 | nb_renderer=element, 97 | vtype="glue", 98 | ) 99 | -------------------------------------------------------------------------------- /myst_nb/ext/utils.py: -------------------------------------------------------------------------------- 1 | """Extension utilities for extensions. 2 | 3 | We intentionally do no import sphinx in this module, 4 | in order to allow docutils-only use without sphinx installed. 5 | """ 6 | 7 | from __future__ import annotations 8 | 9 | from typing import Any 10 | 11 | from docutils import nodes 12 | from docutils.parsers.rst import Directive 13 | from docutils.parsers.rst.states import Inliner 14 | from docutils.utils import unescape 15 | 16 | from myst_nb._compat import findall 17 | 18 | 19 | def set_source_info(node: nodes.Node, source: str, line: int) -> None: 20 | """Set the source info for a node and its descendants.""" 21 | for _node in findall(node)(include_self=True): 22 | _node.source = source 23 | _node.line = line 24 | 25 | 26 | class RoleBase: 27 | """A base class for creating a role.""" 28 | 29 | @property 30 | def document(self) -> nodes.document: 31 | """Get the document.""" 32 | return self.inliner.document 33 | 34 | def set_source_info(self, node: nodes.Node) -> None: 35 | """Set the source info for a node and its descendants.""" 36 | set_source_info(node, self.source, self.line) 37 | 38 | def __call__( 39 | self, 40 | name: str, 41 | rawtext: str, 42 | text: str, 43 | lineno: int, 44 | inliner: Inliner, 45 | options=None, 46 | content=(), 47 | ) -> tuple[list[nodes.Node], list[nodes.system_message]]: 48 | self.text: str = unescape(text) 49 | self.inliner = inliner 50 | self.rawtext = rawtext 51 | source, line = inliner.reporter.get_source_and_line(lineno) 52 | self.source: str = source 53 | self.line: int = line 54 | return self.run() 55 | 56 | def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]: 57 | """Run the role.""" 58 | raise NotImplementedError 59 | 60 | 61 | class DirectiveBase(Directive): 62 | """A base class for creating a directive.""" 63 | 64 | @property 65 | def document(self) -> nodes.document: 66 | return self.state.document 67 | 68 | def __init__(self, *args, **kwargs) -> None: 69 | self.arguments: list[str] 70 | self.options: dict[str, Any] 71 | self.content: str 72 | super().__init__(*args, **kwargs) 73 | source, line = self.state_machine.get_source_and_line(self.lineno) 74 | self.source: str = source 75 | self.line: int = line 76 | 77 | def set_source_info(self, node: nodes.Node) -> None: 78 | """Set source and line number to the node and its descendants.""" 79 | nodes = node if isinstance(node, (list, tuple)) else [node] 80 | for _node in nodes: 81 | set_source_info(_node, self.source, self.line) 82 | -------------------------------------------------------------------------------- /myst_nb/static/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/executablebooks/MyST-NB/201a48c2fff428ba15eaed096dec2f43b040ddc1/myst_nb/static/__init__.py -------------------------------------------------------------------------------- /myst_nb/warnings_.py: -------------------------------------------------------------------------------- 1 | """Central handling of warnings for the myst-nb extension.""" 2 | 3 | from __future__ import annotations 4 | 5 | from enum import Enum 6 | from typing import Sequence 7 | 8 | from docutils import nodes 9 | from myst_parser.warnings_ import MystWarnings 10 | from myst_parser.warnings_ import create_warning as myst_parser_create_warnings 11 | 12 | __all__ = [ 13 | "MystWarnings", 14 | "MystNBWarnings", 15 | "create_warning", 16 | ] 17 | 18 | 19 | class MystNBWarnings(Enum): 20 | """MySTNB warning types.""" 21 | 22 | LEXER = "lexer" 23 | """Issue resolving lexer""" 24 | 25 | FIG_CAPTION = "fig_caption" 26 | """Issue resoliving figure caption""" 27 | 28 | MIME_TYPE = "mime_type" 29 | """Issue resolving MIME type""" 30 | OUTPUT_TYPE = "output_type" 31 | """Issue resolving Output type""" 32 | 33 | CELL_METADATA_KEY = "cell_metadata_key" 34 | """Issue with a key in a cell's `metadata` dictionary.""" 35 | CELL_CONFIG = "cell_config" 36 | """Issue with a cell's configuration or metadata.""" 37 | 38 | 39 | def _is_suppressed_warning( 40 | type: str, subtype: str, suppress_warnings: Sequence[str] 41 | ) -> bool: 42 | """Check whether the warning is suppressed or not. 43 | 44 | Mirrors: 45 | https://github.com/sphinx-doc/sphinx/blob/47d9035bca9e83d6db30a0726a02dc9265bd66b1/sphinx/util/logging.py 46 | """ 47 | if type is None: 48 | return False 49 | 50 | subtarget: str | None 51 | 52 | for warning_type in suppress_warnings: 53 | if "." in warning_type: 54 | target, subtarget = warning_type.split(".", 1) 55 | else: 56 | target, subtarget = warning_type, None 57 | 58 | if target == type and subtarget in (None, subtype, "*"): 59 | return True 60 | 61 | return False 62 | 63 | 64 | def create_warning( 65 | document: nodes.document, 66 | message: str, 67 | subtype: MystNBWarnings | MystWarnings, 68 | *, 69 | line: int | None = None, 70 | append_to: nodes.Element | None = None, 71 | ) -> nodes.system_message | None: 72 | """Generate a warning, logging if it is necessary. 73 | 74 | If the warning type is listed in the ``suppress_warnings`` configuration, 75 | then ``None`` will be returned and no warning logged. 76 | """ 77 | # Pass off Myst Parser warnings to that package 78 | if isinstance(subtype, MystWarnings): 79 | myst_parser_create_warnings( 80 | document=document, 81 | message=message, 82 | subtype=subtype, 83 | line=line, 84 | append_to=append_to, 85 | ) 86 | 87 | wtype = "myst-nb" 88 | # figure out whether to suppress the warning, if sphinx is available, 89 | # it will have been set up by the Sphinx environment, 90 | # otherwise we will use the configuration set by docutils 91 | suppress_warnings: Sequence[str] = [] 92 | try: 93 | suppress_warnings = document.settings.env.app.config.suppress_warnings 94 | except AttributeError: 95 | suppress_warnings = document.settings.myst_suppress_warnings or [] 96 | if _is_suppressed_warning(wtype, subtype.value, suppress_warnings): 97 | return None 98 | 99 | kwargs = {"line": line} if line is not None else {} 100 | message = f"{message} [{wtype}.{subtype.value}]" 101 | msg_node = document.reporter.warning(message, **kwargs) 102 | if append_to is not None: 103 | append_to.append(msg_node) 104 | return msg_node 105 | -------------------------------------------------------------------------------- /tests/nb_fixtures/reporter_warnings.txt: -------------------------------------------------------------------------------- 1 | Unknown Role: 2 | . 3 | cells: 4 | - cell_type: markdown 5 | metadata: {} 6 | source: | 7 | a 8 | - cell_type: markdown 9 | metadata: {} 10 | source: | 11 | {unknown}`a` 12 | . 13 | :20002: (WARNING/2) Unknown interpreted text role "unknown". [myst.role_unknown] 14 | . 15 | 16 | Unknown directive: 17 | . 18 | cells: 19 | - cell_type: markdown 20 | metadata: {} 21 | source: | 22 | a 23 | ```{xyz} 24 | ``` 25 | . 26 | :10003: (WARNING/2) Unknown directive type: 'xyz' [myst.directive_unknown] 27 | . 28 | 29 | Directive parsing error: 30 | . 31 | cells: 32 | - cell_type: markdown 33 | metadata: {} 34 | source: | 35 | ```{class} 36 | ``` 37 | . 38 | :10002: (ERROR/3) Directive 'class': 1 argument(s) required, 0 supplied 39 | . 40 | 41 | Directive run error: 42 | . 43 | cells: 44 | - cell_type: markdown 45 | metadata: {} 46 | source: | 47 | ```{date} 48 | x 49 | ``` 50 | . 51 | :10002: (ERROR/3) Invalid context: the "date" directive can only be used within a substitution definition. 52 | . 53 | 54 | Duplicate reference definition: 55 | . 56 | cells: 57 | - cell_type: markdown 58 | metadata: {} 59 | source: | 60 | [a]: b 61 | - cell_type: markdown 62 | metadata: {} 63 | source: | 64 | d 65 | 66 | [a]: c 67 | . 68 | :20004: (WARNING/2) Duplicate reference definition: A [myst.duplicate_def] 69 | . 70 | -------------------------------------------------------------------------------- /tests/notebooks/basic_failing.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "raise Exception(\"oopsie!\")" 19 | ] 20 | } 21 | ], 22 | "metadata": { 23 | "kernelspec": { 24 | "display_name": "Python 3", 25 | "language": "python", 26 | "name": "python3" 27 | }, 28 | "language_info": { 29 | "codemirror_mode": { 30 | "name": "ipython", 31 | "version": 3 32 | }, 33 | "file_extension": ".py", 34 | "mimetype": "text/x-python", 35 | "name": "python", 36 | "nbconvert_exporter": "python", 37 | "pygments_lexer": "ipython3", 38 | "version": "3.6.1" 39 | }, 40 | "test_name": "notebook1" 41 | }, 42 | "nbformat": 4, 43 | "nbformat_minor": 2 44 | } 45 | -------------------------------------------------------------------------------- /tests/notebooks/basic_nometadata.md: -------------------------------------------------------------------------------- 1 | # a title 2 | 3 | this was created using `jupytext --to myst tests/notebooks/basic_unrun.ipynb` but 4 | with the jupytext metadata removed. 5 | 6 | ```{code-cell} ipython3 7 | a=1 8 | print(a) 9 | ``` 10 | -------------------------------------------------------------------------------- /tests/notebooks/basic_relative.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "# Test reading a relative file to make sure relative paths work\n", 19 | "from PIL import Image\n", 20 | "\n", 21 | "image = Image.open(\"./example.jpg\")" 22 | ] 23 | } 24 | ], 25 | "metadata": { 26 | "kernelspec": { 27 | "display_name": "Python 3", 28 | "language": "python", 29 | "name": "python3" 30 | }, 31 | "language_info": { 32 | "codemirror_mode": { 33 | "name": "ipython", 34 | "version": 3 35 | }, 36 | "file_extension": ".py", 37 | "mimetype": "text/x-python", 38 | "name": "python", 39 | "nbconvert_exporter": "python", 40 | "pygments_lexer": "ipython3", 41 | "version": "3.8.5" 42 | }, 43 | "test_name": "notebook1", 44 | "widgets": { 45 | "application/vnd.jupyter.widget-state+json": { 46 | "state": {}, 47 | "version_major": 2, 48 | "version_minor": 0 49 | } 50 | } 51 | }, 52 | "nbformat": 4, 53 | "nbformat_minor": 4 54 | } 55 | -------------------------------------------------------------------------------- /tests/notebooks/basic_run.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "1\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "a = 1\n", 27 | "print(a)" 28 | ] 29 | } 30 | ], 31 | "metadata": { 32 | "kernelspec": { 33 | "display_name": "Python 3", 34 | "language": "python", 35 | "name": "python3" 36 | }, 37 | "language_info": { 38 | "codemirror_mode": { 39 | "name": "ipython", 40 | "version": 3 41 | }, 42 | "file_extension": ".py", 43 | "mimetype": "text/x-python", 44 | "name": "python", 45 | "nbconvert_exporter": "python", 46 | "pygments_lexer": "ipython3", 47 | "version": "3.6.1" 48 | }, 49 | "test_name": "notebook1" 50 | }, 51 | "nbformat": 4, 52 | "nbformat_minor": 2 53 | } 54 | -------------------------------------------------------------------------------- /tests/notebooks/basic_run_intl.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "1\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "a = 1\n", 27 | "print(a)" 28 | ] 29 | } 30 | ], 31 | "metadata": { 32 | "kernelspec": { 33 | "display_name": "Python 3", 34 | "language": "python", 35 | "name": "python3" 36 | }, 37 | "language_info": { 38 | "codemirror_mode": { 39 | "name": "ipython", 40 | "version": 3 41 | }, 42 | "file_extension": ".py", 43 | "mimetype": "text/x-python", 44 | "name": "python", 45 | "nbconvert_exporter": "python", 46 | "pygments_lexer": "ipython3", 47 | "version": "3.6.1" 48 | }, 49 | "test_name": "notebook1" 50 | }, 51 | "nbformat": 4, 52 | "nbformat_minor": 2 53 | } 54 | -------------------------------------------------------------------------------- /tests/notebooks/basic_stderr.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stderr", 10 | "output_type": "stream", 11 | "text": [ 12 | "hallo\n" 13 | ] 14 | } 15 | ], 16 | "source": [ 17 | "import sys\n", 18 | "\n", 19 | "print(\"hallo\", file=sys.stderr)" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 1, 25 | "metadata": { 26 | "tags": [ 27 | "remove-stderr" 28 | ] 29 | }, 30 | "outputs": [ 31 | { 32 | "name": "stderr", 33 | "output_type": "stream", 34 | "text": [ 35 | "hallo\n" 36 | ] 37 | } 38 | ], 39 | "source": [ 40 | "import sys\n", 41 | "\n", 42 | "print(\"hallo\", file=sys.stderr)" 43 | ] 44 | } 45 | ], 46 | "metadata": { 47 | "kernelspec": { 48 | "display_name": "Python 3", 49 | "language": "python", 50 | "name": "python3" 51 | }, 52 | "language_info": { 53 | "codemirror_mode": { 54 | "name": "ipython", 55 | "version": 3 56 | }, 57 | "file_extension": ".py", 58 | "mimetype": "text/x-python", 59 | "name": "python", 60 | "nbconvert_exporter": "python", 61 | "pygments_lexer": "ipython3", 62 | "version": "3.6.1" 63 | } 64 | }, 65 | "nbformat": 4, 66 | "nbformat_minor": 2 67 | } 68 | -------------------------------------------------------------------------------- /tests/notebooks/basic_unrun.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "a = 1\n", 19 | "print(a)" 20 | ] 21 | } 22 | ], 23 | "metadata": { 24 | "kernelspec": { 25 | "display_name": "Python 3", 26 | "language": "python", 27 | "name": "python3" 28 | }, 29 | "language_info": { 30 | "codemirror_mode": { 31 | "name": "ipython", 32 | "version": 3 33 | }, 34 | "file_extension": ".py", 35 | "mimetype": "text/x-python", 36 | "name": "python", 37 | "nbconvert_exporter": "python", 38 | "pygments_lexer": "ipython3", 39 | "version": "3.6.1" 40 | }, 41 | "test_name": "notebook1" 42 | }, 43 | "nbformat": 4, 44 | "nbformat_minor": 2 45 | } 46 | -------------------------------------------------------------------------------- /tests/notebooks/basic_unrun.md: -------------------------------------------------------------------------------- 1 | --- 2 | file_format: mystnb 3 | kernelspec: 4 | display_name: Python 3 5 | language: python 6 | name: python3 7 | author: Chris 8 | --- 9 | 10 | # a title 11 | 12 | this was created using `jupytext --to myst tests/notebooks/basic_unrun.ipynb` 13 | 14 | ```{code-cell} ipython3 15 | a = 1 16 | print(a) 17 | ``` 18 | -------------------------------------------------------------------------------- /tests/notebooks/custom-formats.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Test chunk options in Rmd/Jupyter conversion" 3 | author: "Marc Wouts" 4 | date: "June 16, 2018" 5 | jupyter: 6 | kernelspec: 7 | display_name: Python 8 | language: python 9 | name: python3 10 | --- 11 | 12 | # Custom Formats 13 | 14 | ```{python echo=TRUE} 15 | import pandas as pd 16 | 17 | x = pd.Series({"A": 1, "B": 3, "C": 2}) 18 | ``` 19 | 20 | ```{python bar_plot, echo=FALSE, fig.height=5, fig.width=8} 21 | x.plot(kind="bar", title="Sample plot") 22 | ``` 23 | -------------------------------------------------------------------------------- /tests/notebooks/custom-formats2.extra.exnt: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Test chunk options in Rmd/Jupyter conversion" 3 | author: "Marc Wouts" 4 | date: "June 16, 2018" 5 | jupyter: 6 | kernelspec: 7 | display_name: Python 8 | language: python 9 | name: python3 10 | --- 11 | 12 | # Custom Formats 13 | 14 | ```{python echo=TRUE} 15 | import pandas as pd 16 | 17 | x = pd.Series({"A": 1, "B": 3, "C": 2}) 18 | ``` 19 | 20 | ```{python bar_plot, echo=FALSE, fig.height=5, fig.width=8} 21 | x.plot(kind="bar", title="Sample plot") 22 | ``` 23 | -------------------------------------------------------------------------------- /tests/notebooks/example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/executablebooks/MyST-NB/201a48c2fff428ba15eaed096dec2f43b040ddc1/tests/notebooks/example.jpg -------------------------------------------------------------------------------- /tests/notebooks/file_level_config.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "be9f2c4c", 6 | "metadata": {}, 7 | "source": [ 8 | "# Title\n", 9 | "\n", 10 | "name\n", 11 | ": definition" 12 | ] 13 | } 14 | ], 15 | "metadata": { 16 | "file_format": "mystnb", 17 | "kernelspec": { 18 | "display_name": "python3", 19 | "name": "python3" 20 | }, 21 | "myst": { 22 | "enable_extensions": [ 23 | "deflist" 24 | ] 25 | }, 26 | "mystnb": { 27 | "execution_mode": "off" 28 | } 29 | }, 30 | "nbformat": 4, 31 | "nbformat_minor": 5 32 | } 33 | -------------------------------------------------------------------------------- /tests/notebooks/file_level_config.md: -------------------------------------------------------------------------------- 1 | --- 2 | file_format: mystnb 3 | kernelspec: 4 | name: python3 5 | myst: 6 | enable_extensions: 7 | - "deflist" 8 | mystnb: 9 | execution_mode: "off" 10 | --- 11 | 12 | # Title 13 | 14 | name 15 | : definition 16 | -------------------------------------------------------------------------------- /tests/notebooks/fun-fish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/executablebooks/MyST-NB/201a48c2fff428ba15eaed096dec2f43b040ddc1/tests/notebooks/fun-fish.png -------------------------------------------------------------------------------- /tests/notebooks/hide_cell_content.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Hide Code Cell Content" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": { 14 | "tags": [ 15 | "hide-input" 16 | ] 17 | }, 18 | "outputs": [ 19 | { 20 | "name": "stdout", 21 | "output_type": "stream", 22 | "text": [ 23 | "hide-input\n" 24 | ] 25 | } 26 | ], 27 | "source": [ 28 | "print(\"hide-input\")" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 2, 34 | "metadata": { 35 | "tags": [ 36 | "hide-output" 37 | ] 38 | }, 39 | "outputs": [ 40 | { 41 | "name": "stdout", 42 | "output_type": "stream", 43 | "text": [ 44 | "hide-output\n" 45 | ] 46 | } 47 | ], 48 | "source": [ 49 | "print(\"hide-output\")" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 4, 55 | "metadata": { 56 | "tags": [ 57 | "hide-cell" 58 | ] 59 | }, 60 | "outputs": [ 61 | { 62 | "name": "stdout", 63 | "output_type": "stream", 64 | "text": [ 65 | "hide-cell\n" 66 | ] 67 | } 68 | ], 69 | "source": [ 70 | "print(\"hide-cell\")" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 5, 76 | "metadata": { 77 | "tags": [ 78 | "hide-cell" 79 | ], 80 | "mystnb": { 81 | "code_prompt_show": "My show message", 82 | "code_prompt_hide": "My hide message" 83 | } 84 | }, 85 | "outputs": [ 86 | { 87 | "name": "stdout", 88 | "output_type": "stream", 89 | "text": [ 90 | "hide-cell custom message\n" 91 | ] 92 | } 93 | ], 94 | "source": [ 95 | "print(\"hide-cell custom message\")" 96 | ] 97 | } 98 | ], 99 | "metadata": { 100 | "kernelspec": { 101 | "display_name": "Python 3.8.13", 102 | "language": "python", 103 | "name": "python3" 104 | }, 105 | "language_info": { 106 | "codemirror_mode": { 107 | "name": "ipython", 108 | "version": 3 109 | }, 110 | "file_extension": ".py", 111 | "mimetype": "text/x-python", 112 | "name": "python", 113 | "nbconvert_exporter": "python", 114 | "pygments_lexer": "ipython3", 115 | "version": "3.8.13" 116 | }, 117 | "orig_nbformat": 4, 118 | "vscode": { 119 | "interpreter": { 120 | "hash": "321f99720af1749431335326d75386e6232ab33d0a78426e9f427a66c2c329a4" 121 | } 122 | } 123 | }, 124 | "nbformat": 4, 125 | "nbformat_minor": 2 126 | } 127 | -------------------------------------------------------------------------------- /tests/notebooks/kernel_alias.md: -------------------------------------------------------------------------------- 1 | --- 2 | file_format: mystnb 3 | kernelspec: 4 | name: other 5 | --- 6 | 7 | # a title 8 | 9 | ```{code-cell} ipython3 10 | print("hi") 11 | ``` 12 | -------------------------------------------------------------------------------- /tests/notebooks/latex_build/index.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# MyST: An example project\n", 8 | "\n", 9 | "```{toctree}\n", 10 | "other\n", 11 | "```" 12 | ] 13 | } 14 | ], 15 | "metadata": { 16 | "kernelspec": { 17 | "display_name": "Python 3", 18 | "language": "python", 19 | "name": "python3" 20 | }, 21 | "language_info": { 22 | "codemirror_mode": { 23 | "name": "ipython", 24 | "version": 3 25 | }, 26 | "file_extension": ".py", 27 | "mimetype": "text/x-python", 28 | "name": "python", 29 | "nbconvert_exporter": "python", 30 | "pygments_lexer": "ipython3", 31 | "version": "3.7.6" 32 | } 33 | }, 34 | "nbformat": 4, 35 | "nbformat_minor": 4 36 | } 37 | -------------------------------------------------------------------------------- /tests/notebooks/latex_build/other.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "(title_ref)=\n", 8 | "# Title\n", 9 | "\n", 10 | "```{contents}\n", 11 | "---\n", 12 | "depth: 2\n", 13 | "---\n", 14 | "```\n", 15 | "\n", 16 | "Content\n" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 3, 22 | "metadata": {}, 23 | "outputs": [ 24 | { 25 | "name": "stdout", 26 | "output_type": "stream", 27 | "text": [ 28 | "1\n" 29 | ] 30 | } 31 | ], 32 | "source": [ 33 | "print(1)" 34 | ] 35 | } 36 | ], 37 | "metadata": { 38 | "kernelspec": { 39 | "display_name": "Python 3", 40 | "language": "python", 41 | "name": "python3" 42 | }, 43 | "language_info": { 44 | "codemirror_mode": { 45 | "name": "ipython", 46 | "version": 3 47 | }, 48 | "file_extension": ".py", 49 | "mimetype": "text/x-python", 50 | "name": "python", 51 | "nbconvert_exporter": "python", 52 | "pygments_lexer": "ipython3", 53 | "version": "3.8.1" 54 | } 55 | }, 56 | "nbformat": 4, 57 | "nbformat_minor": 4 58 | } 59 | -------------------------------------------------------------------------------- /tests/notebooks/locale/es/LC_MESSAGES/basic_run_intl.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 3 | # This file is distributed under the same license as the Project name not set package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Project name not set \n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2024-09-21 23:48+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | 19 | #: ../tests/notebooks/basic_run_intl.ipynb:10002 20 | msgid "a title" 21 | msgstr "un título" 22 | 23 | #: ../tests/notebooks/basic_run_intl.ipynb:10004 24 | msgid "some text" 25 | msgstr "algo de texto" 26 | -------------------------------------------------------------------------------- /tests/notebooks/merge_streams.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "stdout1\n", 13 | "stdout2\n" 14 | ] 15 | }, 16 | { 17 | "name": "stderr", 18 | "output_type": "stream", 19 | "text": [ 20 | "stderr1\n", 21 | "stderr2\n" 22 | ] 23 | }, 24 | { 25 | "name": "stdout", 26 | "output_type": "stream", 27 | "text": [ 28 | "stdout3\n" 29 | ] 30 | }, 31 | { 32 | "name": "stderr", 33 | "output_type": "stream", 34 | "text": [ 35 | "stderr3\n" 36 | ] 37 | }, 38 | { 39 | "data": { 40 | "text/plain": [ 41 | "1" 42 | ] 43 | }, 44 | "execution_count": 1, 45 | "metadata": {}, 46 | "output_type": "execute_result" 47 | } 48 | ], 49 | "source": [ 50 | "import sys\n", 51 | "\n", 52 | "print(\"stdout1\", file=sys.stdout)\n", 53 | "print(\"stdout2\", file=sys.stdout)\n", 54 | "print(\"stderr1\", file=sys.stderr)\n", 55 | "print(\"stderr2\", file=sys.stderr)\n", 56 | "print(\"stdout3\", file=sys.stdout)\n", 57 | "print(\"stderr3\", file=sys.stderr)\n", 58 | "1" 59 | ] 60 | } 61 | ], 62 | "metadata": { 63 | "kernelspec": { 64 | "display_name": "Python 3", 65 | "language": "python", 66 | "name": "python3" 67 | }, 68 | "language_info": { 69 | "codemirror_mode": { 70 | "name": "ipython", 71 | "version": 3 72 | }, 73 | "file_extension": ".py", 74 | "mimetype": "text/x-python", 75 | "name": "python", 76 | "nbconvert_exporter": "python", 77 | "pygments_lexer": "ipython3", 78 | "version": "3.6.1" 79 | } 80 | }, 81 | "nbformat": 4, 82 | "nbformat_minor": 2 83 | } 84 | -------------------------------------------------------------------------------- /tests/notebooks/merge_streams_parallel.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "execution": { 8 | "iopub.execute_input": "2024-09-19T21:44:29.809012Z", 9 | "iopub.status.busy": "2024-09-19T21:44:29.808809Z", 10 | "iopub.status.idle": "2024-09-19T21:44:29.978481Z", 11 | "shell.execute_reply": "2024-09-19T21:44:29.977891Z" 12 | } 13 | }, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "0" 20 | ] 21 | }, 22 | { 23 | "name": "stdout", 24 | "output_type": "stream", 25 | "text": [ 26 | "0" 27 | ] 28 | }, 29 | { 30 | "name": "stdout", 31 | "output_type": "stream", 32 | "text": [ 33 | "0" 34 | ] 35 | }, 36 | { 37 | "name": "stdout", 38 | "output_type": "stream", 39 | "text": [ 40 | "0" 41 | ] 42 | }, 43 | { 44 | "name": "stdout", 45 | "output_type": "stream", 46 | "text": [ 47 | "0" 48 | ] 49 | }, 50 | { 51 | "name": "stdout", 52 | "output_type": "stream", 53 | "text": [ 54 | "0" 55 | ] 56 | }, 57 | { 58 | "name": "stdout", 59 | "output_type": "stream", 60 | "text": [ 61 | "0" 62 | ] 63 | }, 64 | { 65 | "name": "stdout", 66 | "output_type": "stream", 67 | "text": [ 68 | "0" 69 | ] 70 | }, 71 | { 72 | "name": "stdout", 73 | "output_type": "stream", 74 | "text": [ 75 | "0" 76 | ] 77 | }, 78 | { 79 | "name": "stdout", 80 | "output_type": "stream", 81 | "text": [ 82 | "\n" 83 | ] 84 | }, 85 | { 86 | "name": "stdout", 87 | "output_type": "stream", 88 | "text": [ 89 | "0" 90 | ] 91 | }, 92 | { 93 | "name": "stdout", 94 | "output_type": "stream", 95 | "text": [ 96 | "\n" 97 | ] 98 | }, 99 | { 100 | "name": "stdout", 101 | "output_type": "stream", 102 | "text": [ 103 | "\n" 104 | ] 105 | }, 106 | { 107 | "name": "stdout", 108 | "output_type": "stream", 109 | "text": [ 110 | "\n" 111 | ] 112 | }, 113 | { 114 | "name": "stdout", 115 | "output_type": "stream", 116 | "text": [ 117 | "\n" 118 | ] 119 | }, 120 | { 121 | "name": "stdout", 122 | "output_type": "stream", 123 | "text": [ 124 | "\n" 125 | ] 126 | }, 127 | { 128 | "name": "stdout", 129 | "output_type": "stream", 130 | "text": [ 131 | "\n" 132 | ] 133 | }, 134 | { 135 | "name": "stdout", 136 | "output_type": "stream", 137 | "text": [ 138 | "\n" 139 | ] 140 | }, 141 | { 142 | "name": "stdout", 143 | "output_type": "stream", 144 | "text": [ 145 | "\n" 146 | ] 147 | }, 148 | { 149 | "name": "stdout", 150 | "output_type": "stream", 151 | "text": [ 152 | "\n" 153 | ] 154 | } 155 | ], 156 | "source": [ 157 | "from concurrent.futures import ProcessPoolExecutor\n", 158 | "\n", 159 | "with ProcessPoolExecutor() as executor:\n", 160 | " for i in executor.map(print, [0] * 10):\n", 161 | " pass" 162 | ] 163 | } 164 | ], 165 | "metadata": { 166 | "kernelspec": { 167 | "display_name": "Python 3", 168 | "language": "python", 169 | "name": "python3" 170 | }, 171 | "language_info": { 172 | "codemirror_mode": { 173 | "name": "ipython", 174 | "version": 3 175 | }, 176 | "file_extension": ".py", 177 | "mimetype": "text/x-python", 178 | "name": "python", 179 | "nbconvert_exporter": "python", 180 | "pygments_lexer": "ipython3", 181 | "version": "3.12.3" 182 | } 183 | }, 184 | "nbformat": 4, 185 | "nbformat_minor": 4 186 | } 187 | -------------------------------------------------------------------------------- /tests/notebooks/mystnb_codecell_file.md: -------------------------------------------------------------------------------- 1 | --- 2 | jupytext: 3 | text_representation: 4 | extension: .md 5 | format_name: myst 6 | kernelspec: 7 | display_name: Python 3 8 | language: python 9 | name: python3 10 | author: Matt 11 | --- 12 | 13 | # a title 14 | 15 | ```{code-cell} ipython3 16 | :load: mystnb_codecell_file.py 17 | ``` 18 | -------------------------------------------------------------------------------- /tests/notebooks/mystnb_codecell_file.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | import numpy as np 4 | -------------------------------------------------------------------------------- /tests/notebooks/mystnb_codecell_file_warnings.md: -------------------------------------------------------------------------------- 1 | --- 2 | jupytext: 3 | text_representation: 4 | extension: .md 5 | format_name: myst 6 | kernelspec: 7 | display_name: Python 3 8 | language: python 9 | name: python3 10 | author: Aakash 11 | --- 12 | 13 | # a title 14 | 15 | ```{code-cell} ipython3 16 | :load: mystnb_codecell_file.py 17 | i = 10 18 | print(i) 19 | ``` 20 | -------------------------------------------------------------------------------- /tests/notebooks/nb_exec_table.md: -------------------------------------------------------------------------------- 1 | --- 2 | jupytext: 3 | text_representation: 4 | extension: .md 5 | format_name: myst 6 | format_version: '0.8' 7 | jupytext_version: 1.4.1+dev 8 | kernelspec: 9 | display_name: Python 3 10 | language: python 11 | name: python3 12 | author: Chris 13 | --- 14 | 15 | # Test the `nb-exec-table` directive 16 | 17 | ```{code-cell} ipython3 18 | print("hi") 19 | ``` 20 | 21 | This directive should generate a table of executed notebook statistics. 22 | 23 | ```{nb-exec-table} 24 | ``` 25 | -------------------------------------------------------------------------------- /tests/notebooks/sleep_10.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "init_cell": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import time\n", 12 | "\n", 13 | "time.sleep(10)" 14 | ] 15 | } 16 | ], 17 | "metadata": { 18 | "celltoolbar": "Edit Metadata", 19 | "execution": {}, 20 | "hide_input": false, 21 | "jupytext": {}, 22 | "kernelspec": { 23 | "display_name": "Python 3", 24 | "language": "python", 25 | "name": "python3" 26 | }, 27 | "language_info": { 28 | "codemirror_mode": { 29 | "name": "ipython", 30 | "version": 3 31 | }, 32 | "file_extension": ".py", 33 | "mimetype": "text/x-python", 34 | "name": "python", 35 | "nbconvert_exporter": "python", 36 | "pygments_lexer": "ipython3", 37 | "version": "3.6.10" 38 | } 39 | }, 40 | "nbformat": 4, 41 | "nbformat_minor": 2 42 | } 43 | -------------------------------------------------------------------------------- /tests/notebooks/sleep_10_metadata_timeout.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "init_cell": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import time\n", 12 | "\n", 13 | "time.sleep(10)" 14 | ] 15 | } 16 | ], 17 | "metadata": { 18 | "celltoolbar": "Edit Metadata", 19 | "execution": { 20 | "timeout": 1 21 | }, 22 | "hide_input": false, 23 | "jupytext": {}, 24 | "kernelspec": { 25 | "display_name": "Python 3", 26 | "language": "python", 27 | "name": "python3" 28 | }, 29 | "language_info": { 30 | "codemirror_mode": { 31 | "name": "ipython", 32 | "version": 3 33 | }, 34 | "file_extension": ".py", 35 | "mimetype": "text/x-python", 36 | "name": "python", 37 | "nbconvert_exporter": "python", 38 | "pygments_lexer": "ipython3", 39 | "version": "3.6.10" 40 | } 41 | }, 42 | "nbformat": 4, 43 | "nbformat_minor": 2 44 | } 45 | -------------------------------------------------------------------------------- /tests/notebooks/unknown_mimetype.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "unknown": "" 11 | }, 12 | "metadata": {}, 13 | "output_type": "display_data" 14 | } 15 | ], 16 | "source": [ 17 | "a = 1\n", 18 | "print(a)" 19 | ] 20 | } 21 | ], 22 | "metadata": { 23 | "kernelspec": { 24 | "display_name": "Python 3", 25 | "language": "python", 26 | "name": "python3" 27 | }, 28 | "language_info": { 29 | "codemirror_mode": { 30 | "name": "ipython", 31 | "version": 3 32 | }, 33 | "file_extension": ".py", 34 | "mimetype": "text/x-python", 35 | "name": "python", 36 | "nbconvert_exporter": "python", 37 | "pygments_lexer": "ipython3", 38 | "version": "3.6.1" 39 | }, 40 | "test_name": "notebook1" 41 | }, 42 | "nbformat": 4, 43 | "nbformat_minor": 2 44 | } 45 | -------------------------------------------------------------------------------- /tests/notebooks/with_eval.md: -------------------------------------------------------------------------------- 1 | --- 2 | file_format: mystnb 3 | kernelspec: 4 | name: python3 5 | mystnb: 6 | execution_mode: 'inline' 7 | --- 8 | 9 | # Inline evaluation 10 | 11 | ```{code-cell} ipython3 12 | a = 1 13 | ``` 14 | 15 | Evaluated inline variable: {eval}`a` 16 | 17 | ```{eval} a 18 | ``` 19 | 20 | ```{code-cell} ipython3 21 | import base64 22 | from IPython.display import Image 23 | string = "iVBORw0KGgoAAAANSUhEUgAAAHQAAAB0CAYAAABUmhYnAAAEd0lEQVR4Xu2c0ZLjIAwEk///6GzVvZlspWtWksNRnVcwiGmNwHaS5+v1ej38HKPAU6DHsPy3EIGexVOgh/EUqEBPU+Cw9biHCvQwBQ5bjg4V6GEKHLYcHSrQwxQ4bDk6VKCHKXDYcnSoQA9T4LDllB36fD5vlWR9fUvz0+ve9fp0/O7FU7w0n0CXhBSoDiXTRO06FBKKBLLkLvlGgkTp+UvndPzu/ul46Xq7x2/fQ8kR0wtOBaL+1J6uZ+3fPb5Aw0PRtxOWEkigAr3mCJUMuk9cM45uG3ZvJwel8dN4byW8+r1cgWYPVgRaLIlpwqWCT1cgHbr8skOgYUqkgtHwVYfQKZTiTW8rdCgQFWjtt2Pjty3TGdztOB0aHlosuVcHpglJ+h3nUFow7bE6dDOHCjRN2fBty917qEAF+jEHaI+bTlhK0Nsf/aUBpXtYdXy6noDS9dTePf74oYgWRO3dC6b57k6o7vUJFAh3Cz6dMAIV6FWB9FCQlry1f/ejQXLgt9eX6tXu0DSAtL9APysm0OYHI2mCUgVKxxOoQNOcubc/7XnF5yj3LuYPs5Ud+oc5Ry8R6GEpK1CBjlaMuwcvl1xyBC2I8im9T0xva6pPbtL1V+MjPQW6KEQJRAlAggs0vK2oCibQ4g9+LbnXb96THlQBvl5y0yclqYNQAKgAVGIJQHWPpfjf4uv+bUsagECvClCCkL46VIdecyQtKZRhlKGW3OG3LekeQ0DSBOk+1VLCdbdTAqfzlUuuQFPJe/fM9kORQAV6UYBKJslF11NJS0s8xZO2U3zpeO0lNw2g2+HV8dLbKJov1aMKWKDFfyITKKRsegqmjE7H06FpTRHoRwUoQUnu9pJLh4z0EFMdjwRI46ESWwVC8VK7QMN/TRHookDqCB1Knry261AdmmXMdG86xabzd49H83fP1+5QWkB3e7sg4eu06nra46++4K4uqHp9uyACrSKpXS/Q5kMRnUJruN6vnr7Po/VMn9KrepX3UBKgGmD1UVw6P61HoKmi0F+HfhZIhy766NDhU2F66CEgzQXjQRUjjb8aX7tDaYFpwKkgAi0SSAUXaO0Pjkk/HUoKFQ9p0wm/hjcONC2B6W3B24KKv1ZLx0vzgfQoFsyHQJe3LQINHUEZrUNre6wO1aHLw+AvO5QOHdReLbE0/vSeedyhKBWUDh00XpoAAg2/EkIAqD0FlPYXqEDp3Pix/b8/FKUOIMem7fR6j8Yr0fvlYoEWK4JAw0dplOE6dLnrqH5JrCp4NcMFejPQ6h7RnTAUT/eTKkpYiidtH99D04C6bwvS+QX65W8sUMkVaKgAlcRwuLfuNL5Ah/fQKkC6Pi2JKXB6NEjxUTslKF1P7e17KE1YbRfoZwUFuuijQ4v/l5s6VocOOzQFYv9ZBcoldzY8R08VEGiq2Ob9Bbo5oDQ8gaaKbd5foJsDSsMTaKrY5v0FujmgNDyBpopt3l+gmwNKwxNoqtjm/QW6OaA0PIGmim3eX6CbA0rDE2iq2Ob9Bbo5oDS8H8eCMw7yCzx+AAAAAElFTkSuQmCC" 24 | img = Image(base64.b64decode(string)) 25 | ``` 26 | 27 | ```{eval:figure} img 28 | A caption 29 | ``` 30 | -------------------------------------------------------------------------------- /tests/test_ansi_lexer.py: -------------------------------------------------------------------------------- 1 | from pygments.token import Text, Token 2 | import pytest 3 | 4 | from myst_nb.core import lexers 5 | 6 | 7 | @pytest.mark.parametrize( 8 | ("bold", "faint", "fg_color", "bg_color", "expected"), 9 | ( 10 | (False, False, False, False, Text), 11 | (True, False, False, False, Token.Color.Bold), 12 | (True, False, "Red", False, Token.Color.Bold.Red), 13 | (True, False, "Red", "Blue", Token.Color.Bold.Red.BGBlue), 14 | (True, True, "Red", "Blue", Token.Color.Bold.Faint.Red.BGBlue), 15 | ), 16 | ) 17 | def test_token_from_lexer_state(bold, faint, fg_color, bg_color, expected): 18 | ret = lexers._token_from_lexer_state(bold, faint, fg_color, bg_color) 19 | assert ret == expected 20 | 21 | 22 | def _highlight(text): 23 | return tuple(lexers.AnsiColorLexer().get_tokens(text)) 24 | 25 | 26 | def test_plain_text(): 27 | assert _highlight("hello world\n") == ((Text, "hello world\n"),) 28 | 29 | 30 | def test_simple_colors(): 31 | assert _highlight( 32 | "plain text\n" 33 | "\x1b[31mred text\n" 34 | "\x1b[1;32mbold green text\n" 35 | "\x1b[39mfg color turned off\n" 36 | "\x1b[0mplain text after reset\n" 37 | "\x1b[1mbold text\n" 38 | "\x1b[43mbold from previous line with yellow bg\n" 39 | "\x1b[49mbg color turned off\n" 40 | "\x1b[2mfaint turned on\n" 41 | "\x1b[22mbold turned off\n" 42 | ) == ( 43 | (Text, "plain text\n"), 44 | (Token.Color.Red, "red text\n"), 45 | (Token.Color.Bold.Green, "bold green text\n"), 46 | (Token.Color.Bold, "fg color turned off\n"), 47 | (Text, "plain text after reset\n"), 48 | (Token.Color.Bold, "bold text\n"), 49 | (Token.Color.Bold.BGYellow, "bold from previous line with yellow bg\n"), 50 | (Token.Color.Bold, "bg color turned off\n"), 51 | (Token.Color.Bold.Faint, "faint turned on\n"), 52 | (Text, "bold turned off\n"), 53 | ) 54 | 55 | 56 | def test_highlight_empty_end_specifier(): 57 | ret = _highlight("plain\x1b[31mred\x1b[mplain\n") 58 | assert ret == ((Text, "plain"), (Token.Color.Red, "red"), (Text, "plain\n")) 59 | 60 | 61 | def test_ignores_unrecognized_ansi_color_codes(): 62 | """It should just strip and ignore any unrecognized color ANSI codes.""" 63 | assert _highlight( 64 | # unknown int code 65 | "\x1b[99m" 66 | "plain text\n" 67 | # invalid non-int code 68 | "\x1b[=m" 69 | "plain text\n" 70 | ) == ( 71 | (Text, "plain text\n"), 72 | (Text, "plain text\n"), 73 | ) 74 | 75 | 76 | def test_ignores_valid_ansi_non_color_codes(): 77 | """It should just strip and ignore any non-color ANSI codes. 78 | 79 | These include things like moving the cursor position, erasing lines, etc. 80 | """ 81 | assert _highlight( 82 | # restore cursor position 83 | "\x1b[u" 84 | "plain " 85 | # move cursor backwards 55 steps 86 | "\x1b[55C" 87 | "text\n" 88 | ) == ( 89 | # Ideally these would be just one token, but our regex isn't smart 90 | # enough yet. 91 | (Text, "plain "), 92 | (Text, "text\n"), 93 | ) 94 | 95 | 96 | def test_ignores_completely_invalid_escapes(): 97 | """It should strip and ignore invalid escapes. 98 | 99 | This shouldn't happen in valid ANSI text, but we could have an escape 100 | followed by garbage. 101 | """ 102 | assert _highlight("plain \x1b[%text\n") == ( 103 | (Text, "plain "), 104 | (Text, "%text\n"), 105 | ) 106 | -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | """Test the quickstart CLI""" 2 | 3 | import os 4 | from pathlib import Path 5 | 6 | import nbformat 7 | from sphinx import version_info as sphinx_version_info 8 | 9 | from myst_nb.cli import md_to_nb, quickstart 10 | 11 | 12 | def test_quickstart(tmp_path: Path, make_app): 13 | """Test the quickstart CLI builds a valid sphinx project.""" 14 | project_path = tmp_path / "project" 15 | quickstart([str(project_path)]) 16 | assert {p.name for p in project_path.iterdir()} == { 17 | ".gitignore", 18 | "conf.py", 19 | "index.md", 20 | "notebook1.ipynb", 21 | "notebook2.md", 22 | } 23 | 24 | # For compatibility with multiple versions of sphinx, convert pathlib.Path to 25 | # sphinx.testing.path.path here. 26 | if sphinx_version_info >= (7, 2): 27 | app_srcdir = project_path 28 | else: 29 | from sphinx.testing.path import path 30 | 31 | app_srcdir = path(os.fspath(project_path)) 32 | 33 | app = make_app(srcdir=app_srcdir, buildername="html") 34 | app.build() 35 | assert app._warning.getvalue().strip() == "" 36 | assert (project_path / "_build/html/index.html").exists() 37 | 38 | 39 | def test_md_to_nb(tmp_path: Path): 40 | """Test the md_to_nb CLI.""" 41 | path = tmp_path / "notebook.md" 42 | outpath = path.with_suffix(".ipynb") 43 | path.write_text( 44 | """\ 45 | --- 46 | kernelspec: 47 | name: python3 48 | --- 49 | # Title 50 | +++ 51 | next cell 52 | """, 53 | "utf-8", 54 | ) 55 | md_to_nb([str(path)]) 56 | assert path.exists() 57 | with outpath.open("r") as handle: 58 | nb = nbformat.read(handle, as_version=4) 59 | assert nb.metadata == {"kernelspec": {"display_name": "python3", "name": "python3"}} 60 | assert len(nb.cells) == 2 61 | -------------------------------------------------------------------------------- /tests/test_codecell_file.py: -------------------------------------------------------------------------------- 1 | """Test notebooks containing code cells with the `load` option.""" 2 | 3 | import pytest 4 | from sphinx.util.fileutil import copy_asset_file 5 | 6 | 7 | @pytest.mark.sphinx_params( 8 | "mystnb_codecell_file.md", 9 | conf={"nb_execution_mode": "cache", "source_suffix": {".md": "myst-nb"}}, 10 | ) 11 | def test_codecell_file(sphinx_run, file_regression, check_nbs, get_test_path): 12 | asset_path = get_test_path("mystnb_codecell_file.py") 13 | copy_asset_file(str(asset_path), str(sphinx_run.app.srcdir)) 14 | sphinx_run.build() 15 | assert sphinx_run.warnings() == "" 16 | assert set(sphinx_run.env.metadata["mystnb_codecell_file"].keys()) == { 17 | "jupytext", 18 | "author", 19 | "source_map", 20 | "wordcount", 21 | "kernelspec", 22 | "language_info", 23 | } 24 | assert set(sphinx_run.env.nb_metadata["mystnb_codecell_file"].keys()) == { 25 | "exec_data", 26 | } 27 | assert sphinx_run.env.metadata["mystnb_codecell_file"]["author"] == "Matt" 28 | assert sphinx_run.env.metadata["mystnb_codecell_file"]["kernelspec"] == { 29 | "display_name": "Python 3", 30 | "language": "python", 31 | "name": "python3", 32 | } 33 | try: 34 | file_regression.check( 35 | sphinx_run.get_nb(), 36 | check_fn=check_nbs, 37 | extension=".ipynb", 38 | encoding="utf-8", 39 | ) 40 | finally: 41 | file_regression.check( 42 | sphinx_run.get_doctree().pformat(), extension=".xml", encoding="utf-8" 43 | ) 44 | 45 | 46 | @pytest.mark.sphinx_params( 47 | "mystnb_codecell_file_warnings.md", 48 | conf={"nb_execution_mode": "force", "source_suffix": {".md": "myst-nb"}}, 49 | ) 50 | def test_codecell_file_warnings(sphinx_run, file_regression, check_nbs, get_test_path): 51 | asset_path = get_test_path("mystnb_codecell_file.py") 52 | copy_asset_file(str(asset_path), str(sphinx_run.app.srcdir)) 53 | sphinx_run.build() 54 | # assert ( 55 | # "mystnb_codecell_file_warnings.md:14 content of code-cell " 56 | # "is being overwritten by :load: mystnb_codecell_file.py" 57 | # in sphinx_run.warnings() 58 | # ) 59 | assert set(sphinx_run.env.metadata["mystnb_codecell_file_warnings"].keys()) == { 60 | "jupytext", 61 | "author", 62 | "source_map", 63 | "wordcount", 64 | "kernelspec", 65 | "language_info", 66 | } 67 | assert set(sphinx_run.env.nb_metadata["mystnb_codecell_file_warnings"].keys()) == { 68 | "exec_data", 69 | } 70 | assert ( 71 | sphinx_run.env.metadata["mystnb_codecell_file_warnings"]["author"] == "Aakash" 72 | ) 73 | assert sphinx_run.env.metadata["mystnb_codecell_file_warnings"]["kernelspec"] == { 74 | "display_name": "Python 3", 75 | "language": "python", 76 | "name": "python3", 77 | } 78 | try: 79 | file_regression.check( 80 | sphinx_run.get_nb(), 81 | check_fn=check_nbs, 82 | extension=".ipynb", 83 | encoding="utf-8", 84 | ) 85 | finally: 86 | file_regression.check( 87 | sphinx_run.get_doctree().pformat(), extension=".xml", encoding="utf-8" 88 | ) 89 | -------------------------------------------------------------------------------- /tests/test_codecell_file/test_codecell_file.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "77fd61dd", 6 | "metadata": {}, 7 | "source": [ 8 | "# a title" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 1, 14 | "id": "2e32aa7d", 15 | "metadata": { 16 | "load": "mystnb_codecell_file.py" 17 | }, 18 | "outputs": [], 19 | "source": [ 20 | "# flake8: noqa\n", 21 | "\n", 22 | "import numpy as np\n" 23 | ] 24 | } 25 | ], 26 | "metadata": { 27 | "author": "Matt", 28 | "jupytext": { 29 | "text_representation": { 30 | "extension": ".md", 31 | "format_name": "myst" 32 | } 33 | }, 34 | "kernelspec": { 35 | "display_name": "Python 3", 36 | "language": "python", 37 | "name": "python3" 38 | }, 39 | "language_info": { 40 | "codemirror_mode": { 41 | "name": "ipython", 42 | "version": 3 43 | }, 44 | "file_extension": ".py", 45 | "mimetype": "text/x-python", 46 | "name": "python", 47 | "nbconvert_exporter": "python", 48 | "pygments_lexer": "ipython3", 49 | "version": "3.8.10" 50 | }, 51 | "source_map": [ 52 | 11, 53 | 15 54 | ] 55 | }, 56 | "nbformat": 4, 57 | "nbformat_minor": 5 58 | } 59 | -------------------------------------------------------------------------------- /tests/test_codecell_file/test_codecell_file.xml: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | a title 5 | <container cell_index="1" cell_metadata="{'load': 'mystnb_codecell_file.py'}" classes="cell" exec_count="1" nb_element="cell_code"> 6 | <container classes="cell_input" nb_element="cell_code_source"> 7 | <literal_block language="ipython3" xml:space="preserve"> 8 | # flake8: noqa 9 | 10 | import numpy as np -------------------------------------------------------------------------------- /tests/test_codecell_file/test_codecell_file_warnings.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "daa53dc3", 6 | "metadata": {}, 7 | "source": [ 8 | "# a title" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 1, 14 | "id": "d218bd29", 15 | "metadata": { 16 | "load": "mystnb_codecell_file.py" 17 | }, 18 | "outputs": [], 19 | "source": [ 20 | "# flake8: noqa\n", 21 | "\n", 22 | "import numpy as np\n" 23 | ] 24 | } 25 | ], 26 | "metadata": { 27 | "author": "Aakash", 28 | "jupytext": { 29 | "text_representation": { 30 | "extension": ".md", 31 | "format_name": "myst" 32 | } 33 | }, 34 | "kernelspec": { 35 | "display_name": "Python 3", 36 | "language": "python", 37 | "name": "python3" 38 | }, 39 | "language_info": { 40 | "codemirror_mode": { 41 | "name": "ipython", 42 | "version": 3 43 | }, 44 | "file_extension": ".py", 45 | "mimetype": "text/x-python", 46 | "name": "python", 47 | "nbconvert_exporter": "python", 48 | "pygments_lexer": "ipython3", 49 | "version": "3.8.10" 50 | }, 51 | "source_map": [ 52 | 11, 53 | 15 54 | ] 55 | }, 56 | "nbformat": 4, 57 | "nbformat_minor": 5 58 | } 59 | -------------------------------------------------------------------------------- /tests/test_codecell_file/test_codecell_file_warnings.xml: -------------------------------------------------------------------------------- 1 | <document source="mystnb_codecell_file_warnings"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <container cell_index="1" cell_metadata="{'load': 'mystnb_codecell_file.py'}" classes="cell" exec_count="1" nb_element="cell_code"> 6 | <container classes="cell_input" nb_element="cell_code_source"> 7 | <literal_block language="ipython3" xml:space="preserve"> 8 | # flake8: noqa 9 | 10 | import numpy as np -------------------------------------------------------------------------------- /tests/test_docutils.py: -------------------------------------------------------------------------------- 1 | """Run parsing tests against the docutils parser.""" 2 | 3 | from io import StringIO 4 | import json 5 | from pathlib import Path 6 | 7 | from docutils.core import publish_doctree, publish_string 8 | import pytest 9 | import sphinx 10 | import yaml 11 | 12 | from myst_nb.docutils_ import Parser 13 | 14 | FIXTURE_PATH = Path(__file__).parent.joinpath("nb_fixtures") 15 | 16 | 17 | @pytest.mark.param_file(FIXTURE_PATH / "basic.txt") 18 | def test_basic(file_params): 19 | """Test basic parsing.""" 20 | if ( 21 | "Footnote definitions defined in different cells" in file_params.title 22 | and sphinx.version_info[0] < 5 23 | ): 24 | pytest.skip("footnote definition ids changes") 25 | dct = yaml.safe_load(file_params.content) 26 | dct.update({"nbformat": 4, "nbformat_minor": 4}) 27 | dct.setdefault("metadata", {}) 28 | dct["metadata"].setdefault( 29 | "kernelspec", {"name": "python3", "display_name": "Python 3", "language": ""} 30 | ) 31 | report_stream = StringIO() 32 | doctree = publish_doctree( 33 | json.dumps(dct), 34 | parser=Parser(), 35 | settings_overrides={ 36 | "nb_execution_mode": "off", 37 | "nb_output_folder": "", 38 | "myst_all_links_external": True, 39 | "warning_stream": report_stream, 40 | }, 41 | ) 42 | assert report_stream.getvalue().rstrip() == "" 43 | 44 | file_params.assert_expected(doctree.pformat(), rstrip=True) 45 | 46 | 47 | @pytest.mark.param_file(FIXTURE_PATH / "reporter_warnings.txt") 48 | def test_reporting(file_params): 49 | """Test that warnings and errors are reported as expected.""" 50 | dct = yaml.safe_load(file_params.content) 51 | dct.update({"metadata": {}, "nbformat": 4, "nbformat_minor": 4}) 52 | report_stream = StringIO() 53 | publish_doctree( 54 | json.dumps(dct), 55 | parser=Parser(), 56 | settings_overrides={ 57 | "nb_execution_mode": "off", 58 | "nb_output_folder": "", 59 | "warning_stream": report_stream, 60 | }, 61 | ) 62 | file_params.assert_expected(report_stream.getvalue(), rstrip=True) 63 | 64 | 65 | def test_html_resources(tmp_path): 66 | """Test HTML resources are correctly output.""" 67 | report_stream = StringIO() 68 | result = publish_string( 69 | json.dumps({"cells": [], "metadata": {}, "nbformat": 4, "nbformat_minor": 4}), 70 | parser=Parser(), 71 | writer_name="html", 72 | settings_overrides={ 73 | "nb_execution_mode": "off", 74 | "nb_output_folder": str(tmp_path), 75 | "warning_stream": report_stream, 76 | "output_encoding": "unicode", 77 | "embed_stylesheet": False, 78 | }, 79 | ) 80 | assert report_stream.getvalue().rstrip() == "" 81 | assert "mystnb.css" in result 82 | assert "pygments.css" in result 83 | assert tmp_path.joinpath("mystnb.css").is_file() 84 | assert tmp_path.joinpath("pygments.css").is_file() 85 | -------------------------------------------------------------------------------- /tests/test_eval.py: -------------------------------------------------------------------------------- 1 | """Test the `eval` directives and roles.""" 2 | 3 | import pytest 4 | 5 | 6 | @pytest.mark.sphinx_params("with_eval.md", conf={"nb_execution_mode": "inline"}) 7 | def test_sphinx(sphinx_run, clean_doctree, file_regression): 8 | """Test a sphinx build.""" 9 | sphinx_run.build() 10 | # print(sphinx_run.status()) 11 | # print(sphinx_run.warnings()) 12 | assert sphinx_run.warnings() == "" 13 | doctree = clean_doctree(sphinx_run.get_resolved_doctree("with_eval")) 14 | file_regression.check( 15 | doctree.pformat(), 16 | encoding="utf-8", 17 | ) 18 | -------------------------------------------------------------------------------- /tests/test_eval/test_sphinx.txt: -------------------------------------------------------------------------------- 1 | <document source="with_eval"> 2 | <section ids="inline-evaluation" names="inline\ evaluation"> 3 | <title> 4 | Inline evaluation 5 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 6 | <container classes="cell_input" nb_element="cell_code_source"> 7 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 8 | a = 1 9 | <paragraph> 10 | Evaluated inline variable: 11 | <inline classes="output text_plain"> 12 | 1 13 | <literal_block classes="output text_plain" language="myst-ansi" linenos="False" xml:space="preserve"> 14 | 1 15 | <container cell_index="3" cell_metadata="{}" classes="cell" exec_count="2" nb_element="cell_code"> 16 | <container classes="cell_input" nb_element="cell_code_source"> 17 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 18 | import base64 19 | from IPython.display import Image 20 | string = "iVBORw0KGgoAAAANSUhEUgAAAHQAAAB0CAYAAABUmhYnAAAEd0lEQVR4Xu2c0ZLjIAwEk///6GzVvZlspWtWksNRnVcwiGmNwHaS5+v1ej38HKPAU6DHsPy3EIGexVOgh/EUqEBPU+Cw9biHCvQwBQ5bjg4V6GEKHLYcHSrQwxQ4bDk6VKCHKXDYcnSoQA9T4LDllB36fD5vlWR9fUvz0+ve9fp0/O7FU7w0n0CXhBSoDiXTRO06FBKKBLLkLvlGgkTp+UvndPzu/ul46Xq7x2/fQ8kR0wtOBaL+1J6uZ+3fPb5Aw0PRtxOWEkigAr3mCJUMuk9cM45uG3ZvJwel8dN4byW8+r1cgWYPVgRaLIlpwqWCT1cgHbr8skOgYUqkgtHwVYfQKZTiTW8rdCgQFWjtt2Pjty3TGdztOB0aHlosuVcHpglJ+h3nUFow7bE6dDOHCjRN2fBty917qEAF+jEHaI+bTlhK0Nsf/aUBpXtYdXy6noDS9dTePf74oYgWRO3dC6b57k6o7vUJFAh3Cz6dMAIV6FWB9FCQlry1f/ejQXLgt9eX6tXu0DSAtL9APysm0OYHI2mCUgVKxxOoQNOcubc/7XnF5yj3LuYPs5Ud+oc5Ry8R6GEpK1CBjlaMuwcvl1xyBC2I8im9T0xva6pPbtL1V+MjPQW6KEQJRAlAggs0vK2oCibQ4g9+LbnXb96THlQBvl5y0yclqYNQAKgAVGIJQHWPpfjf4uv+bUsagECvClCCkL46VIdecyQtKZRhlKGW3OG3LekeQ0DSBOk+1VLCdbdTAqfzlUuuQFPJe/fM9kORQAV6UYBKJslF11NJS0s8xZO2U3zpeO0lNw2g2+HV8dLbKJov1aMKWKDFfyITKKRsegqmjE7H06FpTRHoRwUoQUnu9pJLh4z0EFMdjwRI46ESWwVC8VK7QMN/TRHookDqCB1Knry261AdmmXMdG86xabzd49H83fP1+5QWkB3e7sg4eu06nra46++4K4uqHp9uyACrSKpXS/Q5kMRnUJruN6vnr7Po/VMn9KrepX3UBKgGmD1UVw6P61HoKmi0F+HfhZIhy766NDhU2F66CEgzQXjQRUjjb8aX7tDaYFpwKkgAi0SSAUXaO0Pjkk/HUoKFQ9p0wm/hjcONC2B6W3B24KKv1ZLx0vzgfQoFsyHQJe3LQINHUEZrUNre6wO1aHLw+AvO5QOHdReLbE0/vSeedyhKBWUDh00XpoAAg2/EkIAqD0FlPYXqEDp3Pix/b8/FKUOIMem7fR6j8Yr0fvlYoEWK4JAw0dplOE6dLnrqH5JrCp4NcMFejPQ6h7RnTAUT/eTKkpYiidtH99D04C6bwvS+QX65W8sUMkVaKgAlcRwuLfuNL5Ah/fQKkC6Pi2JKXB6NEjxUTslKF1P7e17KE1YbRfoZwUFuuijQ4v/l5s6VocOOzQFYv9ZBcoldzY8R08VEGiq2Ob9Bbo5oDQ8gaaKbd5foJsDSsMTaKrY5v0FujmgNDyBpopt3l+gmwNKwxNoqtjm/QW6OaA0PIGmim3eX6CbA0rDE2iq2Ob9Bbo5oDS8H8eCMw7yCzx+AAAAAElFTkSuQmCC" 21 | img = Image(base64.b64decode(string)) 22 | <figure ids="id1"> 23 | <image candidates="{'*': '_build/jupyter_execute/20d976ca74932f09b2e705bb1f2c57e10e6117b273b301c48a4fbfd32dc1a69b.png'}" uri="_build/jupyter_execute/20d976ca74932f09b2e705bb1f2c57e10e6117b273b301c48a4fbfd32dc1a69b.png"> 24 | <caption> 25 | A caption 26 | -------------------------------------------------------------------------------- /tests/test_execute/test_allow_errors_auto.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "ename": "Exception", 19 | "evalue": "oopsie!", 20 | "output_type": "error", 21 | "traceback": [ 22 | "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", 23 | "\u001B[0;31mException\u001B[0m Traceback (most recent call last)", 24 | "Cell \u001B[0;32mIn[1], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mException\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124moopsie!\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", 25 | "\u001B[0;31mException\u001B[0m: oopsie!" 26 | ] 27 | } 28 | ], 29 | "source": [ 30 | "raise Exception(\"oopsie!\")" 31 | ] 32 | } 33 | ], 34 | "metadata": { 35 | "kernelspec": { 36 | "display_name": "Python 3", 37 | "language": "python", 38 | "name": "python3" 39 | }, 40 | "language_info": { 41 | "codemirror_mode": { 42 | "name": "ipython", 43 | "version": 3 44 | }, 45 | "file_extension": ".py", 46 | "mimetype": "text/x-python", 47 | "name": "python", 48 | "nbconvert_exporter": "python", 49 | "pygments_lexer": "ipython3", 50 | "version": "3.10.12" 51 | }, 52 | "test_name": "notebook1" 53 | }, 54 | "nbformat": 4, 55 | "nbformat_minor": 2 56 | } 57 | -------------------------------------------------------------------------------- /tests/test_execute/test_allow_errors_auto.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_failing"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | raise Exception("oopsie!") 11 | <container classes="cell_output" nb_element="cell_code_output"> 12 | <literal_block classes="output traceback" language="ipythontb" xml:space="preserve"> 13 | --------------------------------------------------------------------------- 14 | Exception Traceback (most recent call last) 15 | Cell In[1], line 1 16 | ----> 1 raise Exception("oopsie!") 17 | 18 | Exception: oopsie! 19 | -------------------------------------------------------------------------------- /tests/test_execute/test_allow_errors_cache.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "ename": "Exception", 19 | "evalue": "oopsie!", 20 | "output_type": "error", 21 | "traceback": [ 22 | "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", 23 | "\u001B[0;31mException\u001B[0m Traceback (most recent call last)", 24 | "Cell \u001B[0;32mIn[1], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mException\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124moopsie!\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", 25 | "\u001B[0;31mException\u001B[0m: oopsie!" 26 | ] 27 | } 28 | ], 29 | "source": [ 30 | "raise Exception(\"oopsie!\")" 31 | ] 32 | } 33 | ], 34 | "metadata": { 35 | "kernelspec": { 36 | "display_name": "Python 3", 37 | "language": "python", 38 | "name": "python3" 39 | }, 40 | "language_info": { 41 | "codemirror_mode": { 42 | "name": "ipython", 43 | "version": 3 44 | }, 45 | "file_extension": ".py", 46 | "mimetype": "text/x-python", 47 | "name": "python", 48 | "nbconvert_exporter": "python", 49 | "pygments_lexer": "ipython3", 50 | "version": "3.10.12" 51 | }, 52 | "test_name": "notebook1" 53 | }, 54 | "nbformat": 4, 55 | "nbformat_minor": 2 56 | } 57 | -------------------------------------------------------------------------------- /tests/test_execute/test_allow_errors_cache.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_failing"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | raise Exception("oopsie!") 11 | <container classes="cell_output" nb_element="cell_code_output"> 12 | <literal_block classes="output traceback" language="ipythontb" xml:space="preserve"> 13 | --------------------------------------------------------------------------- 14 | Exception Traceback (most recent call last) 15 | Cell In[1], line 1 16 | ----> 1 raise Exception("oopsie!") 17 | 18 | Exception: oopsie! 19 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_failing_auto.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "ename": "Exception", 19 | "evalue": "oopsie!", 20 | "output_type": "error", 21 | "traceback": [ 22 | "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", 23 | "\u001B[0;31mException\u001B[0m Traceback (most recent call last)", 24 | "Cell \u001B[0;32mIn[1], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mException\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124moopsie!\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", 25 | "\u001B[0;31mException\u001B[0m: oopsie!" 26 | ] 27 | } 28 | ], 29 | "source": [ 30 | "raise Exception(\"oopsie!\")" 31 | ] 32 | } 33 | ], 34 | "metadata": { 35 | "kernelspec": { 36 | "display_name": "Python 3", 37 | "language": "python", 38 | "name": "python3" 39 | }, 40 | "language_info": { 41 | "codemirror_mode": { 42 | "name": "ipython", 43 | "version": 3 44 | }, 45 | "file_extension": ".py", 46 | "mimetype": "text/x-python", 47 | "name": "python", 48 | "nbconvert_exporter": "python", 49 | "pygments_lexer": "ipython3", 50 | "version": "3.10.12" 51 | }, 52 | "test_name": "notebook1" 53 | }, 54 | "nbformat": 4, 55 | "nbformat_minor": 2 56 | } 57 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_failing_auto.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_failing"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | raise Exception("oopsie!") 11 | <container classes="cell_output" nb_element="cell_code_output"> 12 | <literal_block classes="output traceback" language="ipythontb" xml:space="preserve"> 13 | --------------------------------------------------------------------------- 14 | Exception Traceback (most recent call last) 15 | Cell In[1], line 1 16 | ----> 1 raise Exception("oopsie!") 17 | 18 | Exception: oopsie! 19 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_failing_cache.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "ename": "Exception", 19 | "evalue": "oopsie!", 20 | "output_type": "error", 21 | "traceback": [ 22 | "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", 23 | "\u001B[0;31mException\u001B[0m Traceback (most recent call last)", 24 | "Cell \u001B[0;32mIn[1], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mException\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124moopsie!\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", 25 | "\u001B[0;31mException\u001B[0m: oopsie!" 26 | ] 27 | } 28 | ], 29 | "source": [ 30 | "raise Exception(\"oopsie!\")" 31 | ] 32 | } 33 | ], 34 | "metadata": { 35 | "kernelspec": { 36 | "display_name": "Python 3", 37 | "language": "python", 38 | "name": "python3" 39 | }, 40 | "language_info": { 41 | "codemirror_mode": { 42 | "name": "ipython", 43 | "version": 3 44 | }, 45 | "file_extension": ".py", 46 | "mimetype": "text/x-python", 47 | "name": "python", 48 | "nbconvert_exporter": "python", 49 | "pygments_lexer": "ipython3", 50 | "version": "3.10.12" 51 | }, 52 | "test_name": "notebook1" 53 | }, 54 | "nbformat": 4, 55 | "nbformat_minor": 2 56 | } 57 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_failing_cache.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_failing"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | raise Exception("oopsie!") 11 | <container classes="cell_output" nb_element="cell_code_output"> 12 | <literal_block classes="output traceback" language="ipythontb" xml:space="preserve"> 13 | --------------------------------------------------------------------------- 14 | Exception Traceback (most recent call last) 15 | Cell In[1], line 1 16 | ----> 1 raise Exception("oopsie!") 17 | 18 | Exception: oopsie! 19 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_failing_inline.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "ename": "Exception", 19 | "evalue": "oopsie!", 20 | "output_type": "error", 21 | "traceback": [ 22 | "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", 23 | "\u001B[0;31mException\u001B[0m Traceback (most recent call last)", 24 | "Cell \u001B[0;32mIn[1], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mException\u001B[39;00m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124moopsie!\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n", 25 | "\u001B[0;31mException\u001B[0m: oopsie!" 26 | ] 27 | } 28 | ], 29 | "source": [ 30 | "raise Exception(\"oopsie!\")" 31 | ] 32 | } 33 | ], 34 | "metadata": { 35 | "kernelspec": { 36 | "display_name": "Python 3", 37 | "language": "python", 38 | "name": "python3" 39 | }, 40 | "language_info": { 41 | "codemirror_mode": { 42 | "name": "ipython", 43 | "version": 3 44 | }, 45 | "file_extension": ".py", 46 | "mimetype": "text/x-python", 47 | "name": "python", 48 | "nbconvert_exporter": "python", 49 | "pygments_lexer": "ipython3", 50 | "version": "3.10.12" 51 | }, 52 | "test_name": "notebook1" 53 | }, 54 | "nbformat": 4, 55 | "nbformat_minor": 2 56 | } 57 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_failing_inline.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_failing"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | raise Exception("oopsie!") 11 | <container classes="cell_output" nb_element="cell_code_output"> 12 | <literal_block classes="output traceback" language="ipythontb" xml:space="preserve"> 13 | --------------------------------------------------------------------------- 14 | Exception Traceback (most recent call last) 15 | Cell In[1], line 1 16 | ----> 1 raise Exception("oopsie!") 17 | 18 | Exception: oopsie! 19 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_unrun_auto.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "1\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "a = 1\n", 27 | "print(a)" 28 | ] 29 | } 30 | ], 31 | "metadata": { 32 | "kernelspec": { 33 | "display_name": "Python 3", 34 | "language": "python", 35 | "name": "python3" 36 | }, 37 | "language_info": { 38 | "codemirror_mode": { 39 | "name": "ipython", 40 | "version": 3 41 | }, 42 | "file_extension": ".py", 43 | "mimetype": "text/x-python", 44 | "name": "python", 45 | "nbconvert_exporter": "python", 46 | "pygments_lexer": "ipython3", 47 | "version": "3.10.12" 48 | }, 49 | "test_name": "notebook1" 50 | }, 51 | "nbformat": 4, 52 | "nbformat_minor": 2 53 | } 54 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_unrun_auto.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_unrun"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | a = 1 11 | print(a) 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <literal_block classes="output stream" language="myst-ansi" xml:space="preserve"> 14 | 1 15 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_unrun_cache.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "1\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "a = 1\n", 27 | "print(a)" 28 | ] 29 | } 30 | ], 31 | "metadata": { 32 | "kernelspec": { 33 | "display_name": "Python 3", 34 | "language": "python", 35 | "name": "python3" 36 | }, 37 | "language_info": { 38 | "codemirror_mode": { 39 | "name": "ipython", 40 | "version": 3 41 | }, 42 | "file_extension": ".py", 43 | "mimetype": "text/x-python", 44 | "name": "python", 45 | "nbconvert_exporter": "python", 46 | "pygments_lexer": "ipython3", 47 | "version": "3.10.12" 48 | }, 49 | "test_name": "notebook1" 50 | }, 51 | "nbformat": 4, 52 | "nbformat_minor": 2 53 | } 54 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_unrun_cache.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_unrun"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | a = 1 11 | print(a) 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <literal_block classes="output stream" language="myst-ansi" xml:space="preserve"> 14 | 1 15 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_unrun_inline.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "1\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "a = 1\n", 27 | "print(a)" 28 | ] 29 | } 30 | ], 31 | "metadata": { 32 | "kernelspec": { 33 | "display_name": "Python 3", 34 | "language": "python", 35 | "name": "python3" 36 | }, 37 | "language_info": { 38 | "codemirror_mode": { 39 | "name": "ipython", 40 | "version": 3 41 | }, 42 | "file_extension": ".py", 43 | "mimetype": "text/x-python", 44 | "name": "python", 45 | "nbconvert_exporter": "python", 46 | "pygments_lexer": "ipython3", 47 | "version": "3.10.12" 48 | }, 49 | "test_name": "notebook1" 50 | }, 51 | "nbformat": 4, 52 | "nbformat_minor": 2 53 | } 54 | -------------------------------------------------------------------------------- /tests/test_execute/test_basic_unrun_inline.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_unrun"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | a = 1 11 | print(a) 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <literal_block classes="output stream" language="myst-ansi" xml:space="preserve"> 14 | 1 15 | -------------------------------------------------------------------------------- /tests/test_execute/test_custom_convert_auto.xml: -------------------------------------------------------------------------------- 1 | <document source="custom-formats"> 2 | <section ids="custom-formats" names="custom\ formats"> 3 | <title> 4 | Custom Formats 5 | <container cell_index="2" cell_metadata="{'echo': True}" classes="cell" exec_count="1" nb_element="cell_code"> 6 | <container classes="cell_input" nb_element="cell_code_source"> 7 | <literal_block language="ipython3" xml:space="preserve"> 8 | import pandas as pd 9 | 10 | x = pd.Series({"A": 1, "B": 3, "C": 2}) 11 | <container cell_index="3" cell_metadata="{'name': 'bar_plot', 'tags': ['remove_input'], 'fig.height': 5, 'fig.width': 8}" classes="cell tag_remove_input" exec_count="2" nb_element="cell_code"> 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <container nb_element="mime_bundle"> 14 | <container mime_type="text/plain"> 15 | <literal_block classes="output text_plain" language="myst-ansi" xml:space="preserve"> 16 | <Axes: title={'center': 'Sample plot'}> 17 | <container nb_element="mime_bundle"> 18 | <container mime_type="text/plain"> 19 | <literal_block classes="output text_plain" language="myst-ansi" xml:space="preserve"> 20 | <Figure size 640x480 with 1 Axes> 21 | <container mime_type="image/png"> 22 | <image candidates="{'*': '_build/jupyter_execute/a2e637020dfe58f670ba2c942d7a55e49ba48bed09312569ee15a84f5ac680cb.png'}" uri="_build/jupyter_execute/a2e637020dfe58f670ba2c942d7a55e49ba48bed09312569ee15a84f5ac680cb.png"> 23 | -------------------------------------------------------------------------------- /tests/test_execute/test_custom_convert_cache.xml: -------------------------------------------------------------------------------- 1 | <document source="custom-formats"> 2 | <section ids="custom-formats" names="custom\ formats"> 3 | <title> 4 | Custom Formats 5 | <container cell_index="2" cell_metadata="{'echo': True}" classes="cell" exec_count="1" nb_element="cell_code"> 6 | <container classes="cell_input" nb_element="cell_code_source"> 7 | <literal_block language="ipython3" xml:space="preserve"> 8 | import pandas as pd 9 | 10 | x = pd.Series({"A": 1, "B": 3, "C": 2}) 11 | <container cell_index="3" cell_metadata="{'name': 'bar_plot', 'tags': ['remove_input'], 'fig.height': 5, 'fig.width': 8}" classes="cell tag_remove_input" exec_count="2" nb_element="cell_code"> 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <container nb_element="mime_bundle"> 14 | <container mime_type="text/plain"> 15 | <literal_block classes="output text_plain" language="myst-ansi" xml:space="preserve"> 16 | <Axes: title={'center': 'Sample plot'}> 17 | <container nb_element="mime_bundle"> 18 | <container mime_type="text/plain"> 19 | <literal_block classes="output text_plain" language="myst-ansi" xml:space="preserve"> 20 | <Figure size 640x480 with 1 Axes> 21 | <container mime_type="image/png"> 22 | <image candidates="{'*': '_build/jupyter_execute/a2e637020dfe58f670ba2c942d7a55e49ba48bed09312569ee15a84f5ac680cb.png'}" uri="_build/jupyter_execute/a2e637020dfe58f670ba2c942d7a55e49ba48bed09312569ee15a84f5ac680cb.png"> 23 | -------------------------------------------------------------------------------- /tests/test_execute/test_custom_convert_multiple_extensions_auto.xml: -------------------------------------------------------------------------------- 1 | <document source="custom-formats2"> 2 | <section ids="custom-formats" names="custom\ formats"> 3 | <title> 4 | Custom Formats 5 | <container cell_index="2" cell_metadata="{'echo': True}" classes="cell" exec_count="1" nb_element="cell_code"> 6 | <container classes="cell_input" nb_element="cell_code_source"> 7 | <literal_block language="ipython3" xml:space="preserve"> 8 | import pandas as pd 9 | 10 | x = pd.Series({"A": 1, "B": 3, "C": 2}) 11 | <container cell_index="3" cell_metadata="{'name': 'bar_plot', 'tags': ['remove_input'], 'fig.height': 5, 'fig.width': 8}" classes="cell tag_remove_input" exec_count="2" nb_element="cell_code"> 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <container nb_element="mime_bundle"> 14 | <container mime_type="text/plain"> 15 | <literal_block classes="output text_plain" language="myst-ansi" xml:space="preserve"> 16 | <Axes: title={'center': 'Sample plot'}> 17 | <container nb_element="mime_bundle"> 18 | <container mime_type="text/plain"> 19 | <literal_block classes="output text_plain" language="myst-ansi" xml:space="preserve"> 20 | <Figure size 640x480 with 1 Axes> 21 | <container mime_type="image/png"> 22 | <image candidates="{'*': '_build/jupyter_execute/a2e637020dfe58f670ba2c942d7a55e49ba48bed09312569ee15a84f5ac680cb.png'}" uri="_build/jupyter_execute/a2e637020dfe58f670ba2c942d7a55e49ba48bed09312569ee15a84f5ac680cb.png"> 23 | -------------------------------------------------------------------------------- /tests/test_execute/test_custom_convert_multiple_extensions_cache.xml: -------------------------------------------------------------------------------- 1 | <document source="custom-formats2"> 2 | <section ids="custom-formats" names="custom\ formats"> 3 | <title> 4 | Custom Formats 5 | <container cell_index="2" cell_metadata="{'echo': True}" classes="cell" exec_count="1" nb_element="cell_code"> 6 | <container classes="cell_input" nb_element="cell_code_source"> 7 | <literal_block language="ipython3" xml:space="preserve"> 8 | import pandas as pd 9 | 10 | x = pd.Series({"A": 1, "B": 3, "C": 2}) 11 | <container cell_index="3" cell_metadata="{'name': 'bar_plot', 'tags': ['remove_input'], 'fig.height': 5, 'fig.width': 8}" classes="cell tag_remove_input" exec_count="2" nb_element="cell_code"> 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <container nb_element="mime_bundle"> 14 | <container mime_type="text/plain"> 15 | <literal_block classes="output text_plain" language="myst-ansi" xml:space="preserve"> 16 | <Axes: title={'center': 'Sample plot'}> 17 | <container nb_element="mime_bundle"> 18 | <container mime_type="text/plain"> 19 | <literal_block classes="output text_plain" language="myst-ansi" xml:space="preserve"> 20 | <Figure size 640x480 with 1 Axes> 21 | <container mime_type="image/png"> 22 | <image candidates="{'*': '_build/jupyter_execute/a2e637020dfe58f670ba2c942d7a55e49ba48bed09312569ee15a84f5ac680cb.png'}" uri="_build/jupyter_execute/a2e637020dfe58f670ba2c942d7a55e49ba48bed09312569ee15a84f5ac680cb.png"> 23 | -------------------------------------------------------------------------------- /tests/test_execute/test_exclude_path.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_unrun"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="True" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | a = 1 11 | print(a) 12 | -------------------------------------------------------------------------------- /tests/test_execute/test_jupyter_cache_path.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "1\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "a = 1\n", 27 | "print(a)" 28 | ] 29 | } 30 | ], 31 | "metadata": { 32 | "kernelspec": { 33 | "display_name": "Python 3", 34 | "language": "python", 35 | "name": "python3" 36 | }, 37 | "language_info": { 38 | "codemirror_mode": { 39 | "name": "ipython", 40 | "version": 3 41 | }, 42 | "file_extension": ".py", 43 | "mimetype": "text/x-python", 44 | "name": "python", 45 | "nbconvert_exporter": "python", 46 | "pygments_lexer": "ipython3", 47 | "version": "3.10.12" 48 | }, 49 | "test_name": "notebook1" 50 | }, 51 | "nbformat": 4, 52 | "nbformat_minor": 2 53 | } 54 | -------------------------------------------------------------------------------- /tests/test_execute/test_jupyter_cache_path.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_unrun"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | a = 1 11 | print(a) 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <literal_block classes="output stream" language="myst-ansi" xml:space="preserve"> 14 | 1 15 | -------------------------------------------------------------------------------- /tests/test_execute/test_nb_exec_table.xml: -------------------------------------------------------------------------------- 1 | <document source="nb_exec_table"> 2 | <section ids="test-the-nb-exec-table-directive" names="test\ the\ nb-exec-table\ directive"> 3 | <title> 4 | Test the 5 | <literal> 6 | nb-exec-table 7 | directive 8 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 9 | <container classes="cell_input" nb_element="cell_code_source"> 10 | <literal_block language="ipython3" xml:space="preserve"> 11 | print("hi") 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <literal_block classes="output stream" language="myst-ansi" xml:space="preserve"> 14 | hi 15 | <paragraph> 16 | This directive should generate a table of executed notebook statistics. 17 | <ExecutionStatsNode> 18 | -------------------------------------------------------------------------------- /tests/test_execute/test_no_execute.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# a title\n", 8 | "\n", 9 | "some text\n" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "a = 1\n", 19 | "print(a)" 20 | ] 21 | } 22 | ], 23 | "metadata": { 24 | "kernelspec": { 25 | "display_name": "Python 3", 26 | "language": "python", 27 | "name": "python3" 28 | }, 29 | "language_info": { 30 | "codemirror_mode": { 31 | "name": "ipython", 32 | "version": 3 33 | }, 34 | "file_extension": ".py", 35 | "mimetype": "text/x-python", 36 | "name": "python", 37 | "nbconvert_exporter": "python", 38 | "pygments_lexer": "ipython3", 39 | "version": "3.6.1" 40 | }, 41 | "test_name": "notebook1" 42 | }, 43 | "nbformat": 4, 44 | "nbformat_minor": 2 45 | } 46 | -------------------------------------------------------------------------------- /tests/test_execute/test_no_execute.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_unrun"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="True" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | a = 1 11 | print(a) 12 | -------------------------------------------------------------------------------- /tests/test_glue.py: -------------------------------------------------------------------------------- 1 | """Test the `glue` directives and roles.""" 2 | 3 | from IPython.core.displaypub import DisplayPublisher 4 | from IPython.core.interactiveshell import InteractiveShell 5 | import nbformat 6 | import pytest 7 | 8 | from myst_nb.ext.glue import extract_glue_data, glue 9 | 10 | 11 | class MockDisplayPublisher(DisplayPublisher): 12 | def __init__(self, *args, **kwargs): 13 | super().__init__(*args, **kwargs) 14 | self.publish_calls = [] 15 | 16 | def publish(self, data, **kwargs): 17 | kwargs["data"] = data 18 | self.publish_calls.append(kwargs) 19 | 20 | 21 | @pytest.fixture() 22 | def mock_ipython(): 23 | """A mock IPython shell for testing notebook cell executions.""" 24 | shell = InteractiveShell.instance() # type: InteractiveShell 25 | shell.display_pub = MockDisplayPublisher() 26 | yield shell.display_pub 27 | InteractiveShell.clear_instance() 28 | 29 | 30 | def test_glue_func_text(mock_ipython): 31 | glue("a", "b") 32 | assert mock_ipython.publish_calls == [ 33 | { 34 | "metadata": {"scrapbook": {"name": "a", "mime_prefix": ""}}, 35 | "data": {"text/plain": "'b'"}, 36 | } 37 | ] 38 | 39 | 40 | def test_glue_func_obj(mock_ipython): 41 | class Obj: 42 | def __repr__(self): 43 | return "repr" 44 | 45 | def _repr_html_(self): 46 | return "<p>repr</p>" 47 | 48 | glue("a", Obj()) 49 | assert mock_ipython.publish_calls == [ 50 | { 51 | "metadata": {"scrapbook": {"name": "a", "mime_prefix": ""}}, 52 | "data": {"text/html": "<p>repr</p>", "text/plain": "repr"}, 53 | } 54 | ] 55 | 56 | 57 | def test_glue_func_obj_no_display(mock_ipython): 58 | class Obj: 59 | def __repr__(self): 60 | return "repr" 61 | 62 | def _repr_html_(self): 63 | return "<p>repr</p>" 64 | 65 | glue("a", Obj(), display=False) 66 | assert mock_ipython.publish_calls == [ 67 | { 68 | "metadata": { 69 | "scrapbook": { 70 | "name": "a", 71 | "mime_prefix": "application/papermill.record/", 72 | } 73 | }, 74 | "data": { 75 | "application/papermill.record/text/html": "<p>repr</p>", 76 | "application/papermill.record/text/plain": "repr", 77 | }, 78 | } 79 | ] 80 | 81 | 82 | def test_extract_glue_data(get_test_path): 83 | path = get_test_path("with_glue.ipynb") 84 | with open(path) as handle: 85 | notebook = nbformat.read(handle, as_version=4) 86 | data = extract_glue_data(notebook, [], None) 87 | assert set(data) == { 88 | "key_text1", 89 | "key_float", 90 | "key_undisplayed", 91 | "key_df", 92 | "key_plt", 93 | "sym_eq", 94 | } 95 | 96 | 97 | @pytest.mark.sphinx_params("with_glue.ipynb", conf={"nb_execution_mode": "off"}) 98 | def test_parser(sphinx_run, clean_doctree, file_regression): 99 | """Test a sphinx build.""" 100 | # TODO test duplicate warning in docutils 101 | sphinx_run.build() 102 | # print(sphinx_run.status()) 103 | # print(sphinx_run.warnings()) 104 | assert sphinx_run.warnings() == "" 105 | assert sphinx_run.env.nb_metadata["with_glue"]["glue"] == [ 106 | "key_text1", 107 | "key_float", 108 | "key_undisplayed", 109 | "key_df", 110 | "key_plt", 111 | "sym_eq", 112 | ] 113 | doctree = clean_doctree(sphinx_run.get_resolved_doctree("with_glue")) 114 | file_regression.check( 115 | doctree.pformat(), 116 | encoding="utf-8", 117 | ) 118 | -------------------------------------------------------------------------------- /tests/test_parser/test_basic_run.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_run"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | a = 1 11 | print(a) 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <literal_block classes="output stream" language="myst-ansi" xml:space="preserve"> 14 | 1 15 | -------------------------------------------------------------------------------- /tests/test_parser/test_basic_run_intl.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_run_intl"> 2 | <section ids="a-title" names="a\ title un\ título"> 3 | <title> 4 | un título 5 | <paragraph> 6 | algo de texto 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" xml:space="preserve"> 10 | a = 1 11 | print(a) 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <literal_block classes="output stream" language="myst-ansi" xml:space="preserve"> 14 | 1 15 | -------------------------------------------------------------------------------- /tests/test_parser/test_toctree_in_ipynb.xml: -------------------------------------------------------------------------------- 1 | <document source="latex_build/other"> 2 | <target refid="title-ref"> 3 | <section ids="title title-ref" names="title title_ref"> 4 | <title refid="id1"> 5 | Title 6 | <topic classes="contents" ids="contents" names="contents"> 7 | <title> 8 | Contents 9 | <bullet_list> 10 | <list_item> 11 | <paragraph> 12 | <reference ids="id1" refid="title"> 13 | Title 14 | <paragraph> 15 | Content 16 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="3" nb_element="cell_code"> 17 | <container classes="cell_input" nb_element="cell_code_source"> 18 | <literal_block language="ipython3" xml:space="preserve"> 19 | print(1) 20 | <container classes="cell_output" nb_element="cell_code_output"> 21 | <literal_block classes="output stream" language="myst-ansi" xml:space="preserve"> 22 | 1 23 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_basic_run.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_run"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | some text 7 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 10 | a = 1 11 | print(a) 12 | <container classes="cell_output" nb_element="cell_code_output"> 13 | <literal_block classes="output stream" language="myst-ansi" linenos="False" xml:space="preserve"> 14 | 1 15 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_file_level_config_ipynb.xml: -------------------------------------------------------------------------------- 1 | <document source="file_level_config"> 2 | <section ids="title" names="title"> 3 | <title> 4 | Title 5 | <definition_list classes="simple myst"> 6 | <definition_list_item> 7 | <term> 8 | name 9 | <definition> 10 | <paragraph> 11 | definition 12 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_file_level_config_md.xml: -------------------------------------------------------------------------------- 1 | <document source="file_level_config"> 2 | <section ids="title" names="title"> 3 | <title> 4 | Title 5 | <definition_list classes="simple myst"> 6 | <definition_list_item> 7 | <term> 8 | name 9 | <definition> 10 | <paragraph> 11 | definition 12 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_hide_cell_content.xml: -------------------------------------------------------------------------------- 1 | <document source="hide_cell_content"> 2 | <section ids="hide-code-cell-content" names="hide\ code\ cell\ content"> 3 | <title> 4 | Hide Code Cell Content 5 | <container cell_index="1" cell_metadata="{'tags': ['hide-input']}" classes="cell tag_hide-input" exec_count="1" hide_mode="input" nb_element="cell_code" prompt_hide="Hide code cell {type}" prompt_show="Show code cell {type}"> 6 | <HideCodeCellNode classes="above-input" prompt_hide="Hide code cell source" prompt_show="Show code cell source"> 7 | <container classes="cell_input" nb_element="cell_code_source"> 8 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 9 | print("hide-input") 10 | <container classes="cell_output" nb_element="cell_code_output"> 11 | <literal_block classes="output stream" language="myst-ansi" linenos="False" xml:space="preserve"> 12 | hide-input 13 | <container cell_index="2" cell_metadata="{'tags': ['hide-output']}" classes="cell tag_hide-output" exec_count="2" hide_mode="output" nb_element="cell_code" prompt_hide="Hide code cell {type}" prompt_show="Show code cell {type}"> 14 | <container classes="cell_input above-output-prompt" nb_element="cell_code_source"> 15 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 16 | print("hide-output") 17 | <HideCodeCellNode classes="below-input" prompt_hide="Hide code cell output" prompt_show="Show code cell output"> 18 | <container classes="cell_output" nb_element="cell_code_output"> 19 | <literal_block classes="output stream" language="myst-ansi" linenos="False" xml:space="preserve"> 20 | hide-output 21 | <container cell_index="3" cell_metadata="{'tags': ['hide-cell']}" classes="cell tag_hide-cell" exec_count="4" hide_mode="all" nb_element="cell_code" prompt_hide="Hide code cell {type}" prompt_show="Show code cell {type}"> 22 | <HideCodeCellNode classes="above-input" prompt_hide="Hide code cell content" prompt_show="Show code cell content"> 23 | <container classes="cell_input" nb_element="cell_code_source"> 24 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 25 | print("hide-cell") 26 | <container classes="cell_output" nb_element="cell_code_output"> 27 | <literal_block classes="output stream" language="myst-ansi" linenos="False" xml:space="preserve"> 28 | hide-cell 29 | <container cell_index="4" cell_metadata="{'tags': ['hide-cell'], 'mystnb': {'code_prompt_show': 'My show message', 'code_prompt_hide': 'My hide message'}}" classes="cell tag_hide-cell" exec_count="5" hide_mode="all" nb_element="cell_code" prompt_hide="My hide message" prompt_show="My show message"> 30 | <HideCodeCellNode classes="above-input" prompt_hide="My hide message" prompt_show="My show message"> 31 | <container classes="cell_input" nb_element="cell_code_source"> 32 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 33 | print("hide-cell custom message") 34 | <container classes="cell_output" nb_element="cell_code_output"> 35 | <literal_block classes="output stream" language="myst-ansi" linenos="False" xml:space="preserve"> 36 | hide-cell custom message 37 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_merge_streams.xml: -------------------------------------------------------------------------------- 1 | <document source="merge_streams"> 2 | <container cell_index="0" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 3 | <container classes="cell_input" nb_element="cell_code_source"> 4 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 5 | import sys 6 | 7 | print("stdout1", file=sys.stdout) 8 | print("stdout2", file=sys.stdout) 9 | print("stderr1", file=sys.stderr) 10 | print("stderr2", file=sys.stderr) 11 | print("stdout3", file=sys.stdout) 12 | print("stderr3", file=sys.stderr) 13 | 1 14 | <container classes="cell_output" nb_element="cell_code_output"> 15 | <literal_block classes="output stream" language="myst-ansi" linenos="False" xml:space="preserve"> 16 | stdout1 17 | stdout2 18 | stdout3 19 | <literal_block classes="output stderr" language="myst-ansi" linenos="False" xml:space="preserve"> 20 | stderr1 21 | stderr2 22 | stderr3 23 | <literal_block classes="output text_plain" language="myst-ansi" linenos="False" xml:space="preserve"> 24 | 1 25 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_merge_streams_parallel.xml: -------------------------------------------------------------------------------- 1 | <document source="merge_streams_parallel"> 2 | <container cell_index="0" cell_metadata="{'execution': {'iopub.execute_input': '2024-09-19T21:44:29.809012Z', 'iopub.status.busy': '2024-09-19T21:44:29.808809Z', 'iopub.status.idle': '2024-09-19T21:44:29.978481Z', 'shell.execute_reply': '2024-09-19T21:44:29.977891Z'}}" classes="cell" exec_count="1" nb_element="cell_code"> 3 | <container classes="cell_input" nb_element="cell_code_source"> 4 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 5 | from concurrent.futures import ProcessPoolExecutor 6 | 7 | with ProcessPoolExecutor() as executor: 8 | for i in executor.map(print, [0] * 10): 9 | pass 10 | <container classes="cell_output" nb_element="cell_code_output"> 11 | <literal_block classes="output stream" language="myst-ansi" linenos="False" xml:space="preserve"> 12 | 0 13 | 0 14 | 0 15 | 0 16 | 0 17 | 0 18 | 0 19 | 0 20 | 0 21 | 0 22 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_metadata_figure.xml: -------------------------------------------------------------------------------- 1 | <document source="metadata_figure"> 2 | <section ids="formatting-code-outputs" names="formatting\ code\ outputs"> 3 | <title> 4 | Formatting code outputs 5 | <container cell_index="1" cell_metadata="{'myst': {'figure': {'caption': 'Hey everyone its **party** time!\n', 'name': 'fun-fish'}}}" classes="cell" exec_count="1" nb_element="cell_code"> 6 | <container classes="cell_input" nb_element="cell_code_source"> 7 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 8 | from IPython.display import Image 9 | 10 | Image("fun-fish.png") 11 | <container classes="cell_output" nb_element="cell_code_output"> 12 | <figure align="default" ids="fun-fish" names="fun-fish"> 13 | <image candidates="{'*': '_build/jupyter_execute/3eacaf6adad1a4305807616181bbee897bb29177e79e2092ddd0264b848ddb4e.png'}" uri="_build/jupyter_execute/3eacaf6adad1a4305807616181bbee897bb29177e79e2092ddd0264b848ddb4e.png"> 14 | <caption> 15 | Hey everyone its 16 | <strong> 17 | party 18 | time! 19 | <paragraph> 20 | Link: 21 | <reference internal="True" refid="fun-fish"> 22 | <inline classes="std std-ref"> 23 | swim to the fish 24 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_metadata_image.xml: -------------------------------------------------------------------------------- 1 | <document source="metadata_image"> 2 | <section ids="formatting-code-outputs" names="formatting\ code\ outputs"> 3 | <title> 4 | Formatting code outputs 5 | <container cell_index="1" cell_metadata="{'myst': {'image': {'alt': 'fun-fish', 'classes': 'shadow bg-primary', 'width': '300px'}}}" classes="cell" exec_count="1" nb_element="cell_code"> 6 | <container classes="cell_input" nb_element="cell_code_source"> 7 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 8 | from IPython.display import Image 9 | 10 | Image("fun-fish.png") 11 | <container classes="cell_output" nb_element="cell_code_output"> 12 | <image alt="fun-fish" candidates="{'*': '_build/jupyter_execute/3eacaf6adad1a4305807616181bbee897bb29177e79e2092ddd0264b848ddb4e.png'}" classes="shadow bg-primary" uri="_build/jupyter_execute/3eacaf6adad1a4305807616181bbee897bb29177e79e2092ddd0264b848ddb4e.png" width="300px"> 13 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_metadata_image_output.xml: -------------------------------------------------------------------------------- 1 | <document source="metadata_image_output"> 2 | <section ids="output-metadata" names="output\ metadata"> 3 | <title> 4 | Output metadata 5 | <container cell_index="1" cell_metadata="{'tags': ['skip-execution']}" classes="cell tag_skip-execution" exec_count="1" nb_element="cell_code"> 6 | <container classes="cell_input" nb_element="cell_code_source"> 7 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 8 | # Outputs included with width/height in output metadata, 9 | # cell is not executed 10 | from IPython.display import Image 11 | 12 | Image(filename="./example.jpg", width=500, height=100) 13 | <container classes="cell_output" nb_element="cell_code_output"> 14 | <image candidates="{'*': '_build/jupyter_execute/a4c9580c74dacf6f3316a3bd2e2a347933aa4463834dcf1bb8f20b4fcb476ae1.jpg'}" height="100" uri="_build/jupyter_execute/a4c9580c74dacf6f3316a3bd2e2a347933aa4463834dcf1bb8f20b4fcb476ae1.jpg" width="500"> 15 | <container cell_index="2" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 16 | <container classes="cell_input" nb_element="cell_code_source"> 17 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 18 | # No outputs, cell is executed, image should have original size (370, 254) 19 | from IPython.display import Image 20 | 21 | Image(filename="./example.jpg") 22 | <container classes="cell_output" nb_element="cell_code_output"> 23 | <image candidates="{'*': '_build/jupyter_execute/a4c9580c74dacf6f3316a3bd2e2a347933aa4463834dcf1bb8f20b4fcb476ae1.jpg'}" uri="_build/jupyter_execute/a4c9580c74dacf6f3316a3bd2e2a347933aa4463834dcf1bb8f20b4fcb476ae1.jpg"> 24 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_metadata_multiple_image.xml: -------------------------------------------------------------------------------- 1 | <document source="metadata_multiple_image"> 2 | <section ids="formatting-code-outputs" names="formatting\ code\ outputs"> 3 | <title> 4 | Formatting code outputs 5 | <paragraph> 6 | This is a test case with multiple captioned and labelled images 7 | <container cell_index="1" cell_metadata="{'myst': {'figure': {'caption': 'Hey everyone its **party** time!\n', 'name': 'fun-fish'}, 'image': {'alt': 'fun-fish', 'classes': 'shadow bg-primary', 'width': '300px'}}}" classes="cell" exec_count="1" nb_element="cell_code"> 8 | <container classes="cell_input" nb_element="cell_code_source"> 9 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 10 | from IPython.display import Image 11 | 12 | Image("fun-fish.png") 13 | <container classes="cell_output" nb_element="cell_code_output"> 14 | <figure ids="fun-fish" names="fun-fish"> 15 | <image alt="fun-fish" candidates="{'*': '_build/jupyter_execute/3eacaf6adad1a4305807616181bbee897bb29177e79e2092ddd0264b848ddb4e.png'}" classes="shadow bg-primary" uri="_build/jupyter_execute/3eacaf6adad1a4305807616181bbee897bb29177e79e2092ddd0264b848ddb4e.png" width="300px"> 16 | <caption> 17 | Hey everyone its 18 | <strong> 19 | party 20 | time! 21 | <paragraph> 22 | Link: 23 | <reference internal="True" refid="fun-fish"> 24 | <inline classes="std std-ref"> 25 | swim to the fish 26 | <paragraph> 27 | Adding another party fish image to test for multiple images 28 | <container cell_index="4" cell_metadata="{'myst': {'figure': {'caption': 'Hey everyone its **party** time again!\n', 'name': 'fun-fish2'}, 'image': {'alt': 'fun-fish2', 'classes': 'shadow bg-primary', 'width': '200px'}}}" classes="cell" exec_count="2" nb_element="cell_code"> 29 | <container classes="cell_input" nb_element="cell_code_source"> 30 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 31 | from IPython.display import Image 32 | 33 | Image("fun-fish.png") 34 | <container classes="cell_output" nb_element="cell_code_output"> 35 | <figure ids="fun-fish2" names="fun-fish2"> 36 | <image alt="fun-fish2" candidates="{'*': '_build/jupyter_execute/3eacaf6adad1a4305807616181bbee897bb29177e79e2092ddd0264b848ddb4e.png'}" classes="shadow bg-primary" uri="_build/jupyter_execute/3eacaf6adad1a4305807616181bbee897bb29177e79e2092ddd0264b848ddb4e.png" width="200px"> 37 | <caption> 38 | Hey everyone its 39 | <strong> 40 | party 41 | time again! 42 | <paragraph> 43 | Link: 44 | <reference internal="True" refid="fun-fish"> 45 | <inline classes="std std-ref"> 46 | swim to the fish 47 | <paragraph> 48 | Link: 49 | <reference internal="True" refid="fun-fish2"> 50 | <inline classes="std std-ref"> 51 | swim to the fish again 52 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_stderr_remove.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_stderr"> 2 | <container cell_index="0" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 3 | <container classes="cell_input" nb_element="cell_code_source"> 4 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 5 | import sys 6 | 7 | print("hallo", file=sys.stderr) 8 | <container classes="cell_output" nb_element="cell_code_output"> 9 | <container cell_index="1" cell_metadata="{'tags': ['remove-stderr']}" classes="cell tag_remove-stderr" exec_count="1" nb_element="cell_code"> 10 | <container classes="cell_input" nb_element="cell_code_source"> 11 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 12 | import sys 13 | 14 | print("hallo", file=sys.stderr) 15 | <container classes="cell_output" nb_element="cell_code_output"> 16 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_stderr_tag.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_stderr"> 2 | <container cell_index="0" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 3 | <container classes="cell_input" nb_element="cell_code_source"> 4 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 5 | import sys 6 | 7 | print("hallo", file=sys.stderr) 8 | <container classes="cell_output" nb_element="cell_code_output"> 9 | <literal_block classes="output stderr" language="myst-ansi" linenos="False" xml:space="preserve"> 10 | hallo 11 | <container cell_index="1" cell_metadata="{'tags': ['remove-stderr']}" classes="cell tag_remove-stderr" exec_count="1" nb_element="cell_code"> 12 | <container classes="cell_input" nb_element="cell_code_source"> 13 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 14 | import sys 15 | 16 | print("hallo", file=sys.stderr) 17 | <container classes="cell_output" nb_element="cell_code_output"> 18 | -------------------------------------------------------------------------------- /tests/test_render_outputs/test_unknown_mimetype.xml: -------------------------------------------------------------------------------- 1 | <document source="unknown_mimetype"> 2 | <container cell_index="0" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 3 | <container classes="cell_input" nb_element="cell_code_source"> 4 | <literal_block language="ipython3" linenos="False" xml:space="preserve"> 5 | a = 1 6 | print(a) 7 | <container classes="cell_output" nb_element="cell_code_output"> 8 | -------------------------------------------------------------------------------- /tests/test_text_based.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.sphinx_params( 5 | "basic_unrun.md", 6 | conf={"nb_execution_mode": "cache", "source_suffix": {".md": "myst-nb"}}, 7 | ) 8 | def test_basic_run(sphinx_run, file_regression, check_nbs): 9 | sphinx_run.build() 10 | # print(sphinx_run.status()) 11 | assert sphinx_run.warnings() == "" 12 | assert set(sphinx_run.env.metadata["basic_unrun"].keys()) == { 13 | "file_format", 14 | "author", 15 | "source_map", 16 | "wordcount", 17 | "kernelspec", 18 | "language_info", 19 | } 20 | assert set(sphinx_run.env.nb_metadata["basic_unrun"].keys()) == { 21 | "exec_data", 22 | } 23 | assert sphinx_run.env.metadata["basic_unrun"]["author"] == "Chris" 24 | assert sphinx_run.env.metadata["basic_unrun"]["kernelspec"] == { 25 | "display_name": "Python 3", 26 | "language": "python", 27 | "name": "python3", 28 | } 29 | file_regression.check( 30 | sphinx_run.get_nb(), check_fn=check_nbs, extension=".ipynb", encoding="utf-8" 31 | ) 32 | file_regression.check( 33 | sphinx_run.get_doctree().pformat(), extension=".xml", encoding="utf-8" 34 | ) 35 | 36 | 37 | @pytest.mark.sphinx_params( 38 | "basic_unrun.md", 39 | conf={"nb_execution_mode": "off", "source_suffix": {".md": "myst-nb"}}, 40 | ) 41 | def test_basic_run_exec_off(sphinx_run, file_regression, check_nbs): 42 | sphinx_run.build() 43 | # print(sphinx_run.status()) 44 | assert set(sphinx_run.env.metadata["basic_unrun"].keys()) == { 45 | "file_format", 46 | "author", 47 | "source_map", 48 | "wordcount", 49 | "kernelspec", 50 | } 51 | assert set(sphinx_run.env.nb_metadata["basic_unrun"].keys()) == set() 52 | assert sphinx_run.env.metadata["basic_unrun"]["author"] == "Chris" 53 | 54 | file_regression.check( 55 | sphinx_run.get_nb(), check_fn=check_nbs, extension=".ipynb", encoding="utf-8" 56 | ) 57 | file_regression.check( 58 | sphinx_run.get_doctree().pformat(), extension=".xml", encoding="utf-8" 59 | ) 60 | 61 | 62 | @pytest.mark.sphinx_params( 63 | "basic_nometadata.md", 64 | conf={"nb_execution_mode": "off", "source_suffix": {".md": "myst-nb"}}, 65 | ) 66 | def test_basic_nometadata(sphinx_run): 67 | """A myst-markdown notebook with no jupytext metadata should raise a warning.""" 68 | sphinx_run.build() 69 | # print(sphinx_run.status()) 70 | assert "Found an unexpected `code-cell`" in sphinx_run.warnings() 71 | -------------------------------------------------------------------------------- /tests/test_text_based/test_basic_run.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "f0dc5a7d", 6 | "metadata": {}, 7 | "source": [ 8 | "# a title\n", 9 | "\n", 10 | "this was created using `jupytext --to myst tests/notebooks/basic_unrun.ipynb`" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 1, 16 | "id": "49a06520", 17 | "metadata": {}, 18 | "outputs": [ 19 | { 20 | "name": "stdout", 21 | "output_type": "stream", 22 | "text": [ 23 | "1\n" 24 | ] 25 | } 26 | ], 27 | "source": [ 28 | "a = 1\n", 29 | "print(a)" 30 | ] 31 | } 32 | ], 33 | "metadata": { 34 | "author": "Chris", 35 | "jupytext": { 36 | "text_representation": { 37 | "extension": ".md", 38 | "format_name": "myst", 39 | "format_version": "0.8", 40 | "jupytext_version": "1.4.1+dev" 41 | } 42 | }, 43 | "kernelspec": { 44 | "display_name": "Python 3", 45 | "language": "python", 46 | "name": "python3" 47 | }, 48 | "language_info": { 49 | "codemirror_mode": { 50 | "name": "ipython", 51 | "version": 3 52 | }, 53 | "file_extension": ".py", 54 | "mimetype": "text/x-python", 55 | "name": "python", 56 | "nbconvert_exporter": "python", 57 | "pygments_lexer": "ipython3", 58 | "version": "3.8.10" 59 | }, 60 | "source_map": [ 61 | 13, 62 | 19 63 | ] 64 | }, 65 | "nbformat": 4, 66 | "nbformat_minor": 5 67 | } 68 | -------------------------------------------------------------------------------- /tests/test_text_based/test_basic_run.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_unrun"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | this was created using 7 | <literal> 8 | jupytext --to myst tests/notebooks/basic_unrun.ipynb 9 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="1" nb_element="cell_code"> 10 | <container classes="cell_input" nb_element="cell_code_source"> 11 | <literal_block language="ipython3" xml:space="preserve"> 12 | a = 1 13 | print(a) 14 | <container classes="cell_output" nb_element="cell_code_output"> 15 | <literal_block classes="output stream" language="myst-ansi" xml:space="preserve"> 16 | 1 17 | -------------------------------------------------------------------------------- /tests/test_text_based/test_basic_run_exec_off.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "f1b10cf9", 6 | "metadata": {}, 7 | "source": [ 8 | "# a title\n", 9 | "\n", 10 | "this was created using `jupytext --to myst tests/notebooks/basic_unrun.ipynb`" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "id": "9daa25c2", 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "a = 1\n", 21 | "print(a)" 22 | ] 23 | } 24 | ], 25 | "metadata": { 26 | "author": "Chris", 27 | "jupytext": { 28 | "text_representation": { 29 | "extension": ".md", 30 | "format_name": "myst", 31 | "format_version": "0.8", 32 | "jupytext_version": "1.4.1+dev" 33 | } 34 | }, 35 | "kernelspec": { 36 | "display_name": "Python 3", 37 | "language": "python", 38 | "name": "python3" 39 | }, 40 | "source_map": [ 41 | 13, 42 | 19 43 | ] 44 | }, 45 | "nbformat": 4, 46 | "nbformat_minor": 5 47 | } 48 | -------------------------------------------------------------------------------- /tests/test_text_based/test_basic_run_exec_off.xml: -------------------------------------------------------------------------------- 1 | <document source="basic_unrun"> 2 | <section ids="a-title" names="a\ title"> 3 | <title> 4 | a title 5 | <paragraph> 6 | this was created using 7 | <literal> 8 | jupytext --to myst tests/notebooks/basic_unrun.ipynb 9 | <container cell_index="1" cell_metadata="{}" classes="cell" exec_count="True" nb_element="cell_code"> 10 | <container classes="cell_input" nb_element="cell_code_source"> 11 | <literal_block language="python" xml:space="preserve"> 12 | a = 1 13 | print(a) 14 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # To use tox, see https://tox.readthedocs.io 2 | # Simply pip or conda install tox 3 | # If you use conda, you may also want to install tox-conda 4 | # then run `tox` or `tox -- {pytest args}` 5 | # To run in parallel using `tox -p` (this does not appear to work for this repo) 6 | 7 | # To rebuild the tox environment, for example when dependencies change, use 8 | # `tox -r` 9 | 10 | # Note: if the following error is encountered: `ImportError while loading conftest` 11 | # then then deleting compiled files has been found to fix it: `find . -name \*.pyc -delete` 12 | 13 | [tox] 14 | envlist = py311-sphinx7 15 | 16 | [testenv] 17 | usedevelop = true 18 | 19 | [testenv:py{39,310,311,312,313}-sphinx{5,6,7,8}] 20 | extras = testing 21 | deps = 22 | sphinx5: sphinx>=5,<6 23 | sphinx6: sphinx>=6,<7 24 | sphinx7: sphinx>=7,<8 25 | sphinx8: sphinx>=8,<9 26 | commands = pytest {posargs} 27 | 28 | [testenv:docs-{update,clean}] 29 | extras = rtd 30 | deps = 31 | ipython 32 | setenv = 33 | BUILDER = {env:BUILDER:html} 34 | allowlist_externals = 35 | echo 36 | rm 37 | commands = 38 | pip freeze 39 | clean: rm -rf docs/_build 40 | sphinx-build {posargs} -nW --keep-going -b {env:BUILDER} docs/ docs/_build/{env:BUILDER} 41 | commands_post = echo "open file://{toxinidir}/docs/_build/{env:BUILDER}/index.html" 42 | 43 | 44 | [pytest] 45 | markers = 46 | sphinx_params: Specify parameters to pass to the sphinx_run fixture 47 | --------------------------------------------------------------------------------