diff --git a/.ci/environment-ci.yml b/.ci/environment-ci.yml index 5243e0b6b..1582dcf5e 100644 --- a/.ci/environment-ci.yml +++ b/.ci/environment-ci.yml @@ -10,23 +10,6 @@ channels: - defaults - conda-forge dependencies: - - ipykernel - - nbconvert + - jupyterlab - nbformat>=5.1.2 - - pyyaml - - toml - - markdown-it-py - - mdit-py-plugins - - pip - - pytest - - pytest-randomly - - pytest-cov - - coverage - - flake8 - - autopep8 - - black - - isort - pandoc==2.16.2 - - sphinx-gallery<0.8 - - pre-commit - - gitpython diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 000000000..8e3dfbb9e --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,34 @@ +codecov: + notify: + after_n_builds: 15 + +comment: + after_n_builds: 15 + +coverage: + status: + project: + source: + paths: + - "src/jupytext/" + target: 97% + threshold: 0.002 + tests: + paths: + - "tests/" + target: 100% + unit-tests: + flags: + - unit + functional-tests: + flags: + - functional + integration-tests: + flags: + - integration + external-tests: + flags: + - external + patch: + default: + target: 80% # new contributions should have a coverage at least equal to target diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0dbbcbc98..bc508b0b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,10 @@ jobs: needs: [codeql] uses: ./.github/workflows/step_tests-pip.yml + test-unit-functional-integration: + needs: [codeql] + uses: ./.github/workflows/step_test_unit_functional.yml + test-conda: needs: [codeql] uses: ./.github/workflows/step_tests-conda.yml @@ -56,7 +60,7 @@ jobs: upload: ${{ inputs.upload-build-artifacts || false }} pass: - needs: [pre-commit, codeql, test-pip, test-conda, test-ui, build] + needs: [pre-commit, codeql, test-unit-functional-integration, test-pip, test-conda, test-ui, build] runs-on: ubuntu-latest steps: - name: Check jobs diff --git a/.github/workflows/step_build.yml b/.github/workflows/step_build.yml index abb0e9613..d5cb47807 100644 --- a/.github/workflows/step_build.yml +++ b/.github/workflows/step_build.yml @@ -29,7 +29,7 @@ jobs: python -m pip install build wheel # NOTE: These builds and verifications of the builds can be done more - # robustily with jupyter-releaser. + # robustly with jupyter-releaser. # # Removed the check on size of package as we are distributing tests/ with # sdist now and they are around 8MB. Seems like original check was to make diff --git a/.github/workflows/step_test_unit_functional.yml b/.github/workflows/step_test_unit_functional.yml new file mode 100644 index 000000000..1bf3c9ce6 --- /dev/null +++ b/.github/workflows/step_test_unit_functional.yml @@ -0,0 +1,45 @@ +name: test-categories +run-name: Run Unit/Functional/Integration and External tests using Pip + +on: + workflow_call: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-test_classification + cancel-in-progress: true + +jobs: + test-pip: + continue-on-error: ${{ matrix.experimental || false }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + python-version: [ "3.11" ] + coverage: [unit, functional, integration, external] + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Base Setup + uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 + with: + python_version: ${{ matrix.python-version }} + + - name: Install from source + run: HATCH_BUILD_HOOKS_ENABLE=false python -m pip install -e '.[test-cov,test-${{ matrix.coverage }}]' markdown-it-py~=3.0 + + - name: Install a Jupyter Kernel + run: python -m ipykernel install --name python_kernel --user + + - name: Run the tests + run: pytest tests/${{ matrix.coverage }} --cov + + - name: Upload the coverage + uses: codecov/codecov-action@v3 + with: + flags: ${{ matrix.coverage }} + fail_ci_if_error: true + verbose: true diff --git a/.github/workflows/step_tests-conda.yml b/.github/workflows/step_tests-conda.yml index a8577da6c..ce105fc6e 100644 --- a/.github/workflows/step_tests-conda.yml +++ b/.github/workflows/step_tests-conda.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: os: [ 'ubuntu-latest', 'macos-latest', 'windows-latest' ] - python-version: [ 3.9 ] + python-version: [ 3.11 ] runs-on: ${{ matrix.os }} steps: @@ -38,9 +38,8 @@ jobs: use-only-tar-bz2: true - name: Install from source - # This is required for the pre-commit tests shell: pwsh - run: python -m pip install -e '.[test-cov]' + run: python -m pip install -e '.[test-external,test-cov]' - name: Create kernel shell: pwsh @@ -61,8 +60,7 @@ jobs: - name: Test with pytest shell: pwsh - run: pytest --cov=src/jupytext --cov-report=xml + run: pytest --cov - name: Upload coverage uses: codecov/codecov-action@v3 - if: ${{ matrix.os != 'windows-latest' }} diff --git a/.github/workflows/step_tests-pip.yml b/.github/workflows/step_tests-pip.yml index ce18690af..2c95cec62 100644 --- a/.github/workflows/step_tests-pip.yml +++ b/.github/workflows/step_tests-pip.yml @@ -18,7 +18,6 @@ jobs: matrix: python-version: [ "3.8", "3.9", "3.10", "3.11" ] markdown-it-py-version: ["~=2.0"] - kernel: [true] include: - python-version: "3.12-dev" experimental: true @@ -28,14 +27,13 @@ jobs: markdown-it-py-version: "" - python-version: "3.x" markdown-it-py-version: "~=3.0" - - python-version: "3.11" + - python-version: "3.x" markdown-it-py-version: "~=4.0" experimental: true - python-version: "3.x" - kernel: false + no_kernel: true - python-version: "3.x" quarto: true - kernel: true steps: - name: Checkout @@ -46,11 +44,11 @@ jobs: with: python_version: ${{ matrix.python-version }} - - name: Install from source (required for the pre-commit tests) - run: python -m pip install -e '.[test-cov]' ${{ matrix.markdown-it-py-version && format('markdown-it-py{0}', matrix.markdown-it-py-version) }} + - name: Install from source + run: python -m pip install -e '.[test-cov,test-external]' ${{ matrix.markdown-it-py-version && format('markdown-it-py{0}', matrix.markdown-it-py-version) }} - name: Install a Jupyter Kernel - if: ${{ matrix.kernel }} + if: ${{ !matrix.no_kernel }} run: python -m ipykernel install --name python_kernel --user - name: Install Quarto @@ -66,12 +64,13 @@ jobs: - name: Test lab extension run: | # Check extension + pip install jupyterlab jupyter labextension list jupyter labextension list 2>&1 | grep -ie "jupyterlab-jupytext.*OK" python -m jupyterlab.browser_check - name: Test with pytest - run: pytest --cov=src/jupytext --cov-report=xml + run: pytest --cov - name: Upload coverage uses: codecov/codecov-action@v3 diff --git a/.github/workflows/step_tests-ui.yml b/.github/workflows/step_tests-ui.yml index 5cbb050c1..0579887c4 100644 --- a/.github/workflows/step_tests-ui.yml +++ b/.github/workflows/step_tests-ui.yml @@ -20,14 +20,16 @@ jobs: - name: Base Setup uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 - - name: Install from source (required for the pre-commit tests) - run: python -m pip install -e '.[test-cov]' + - name: Install from source + run: python -m pip install -e . - name: Install galata working-directory: jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests env: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - run: jlpm install + run: | + pip install jupyterlab + jlpm install - name: Install browser working-directory: jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e66eccbfe..998ed165b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ exclude: > (?x)^( demo/.*| - tests/notebooks/.*| + tests/data/notebooks/.*| )$ repos: diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index 0d1814fed..000000000 --- a/codecov.yml +++ /dev/null @@ -1,23 +0,0 @@ -codecov: - notify: - after_n_builds: 11 - -comment: - after_n_builds: 11 - -coverage: - status: - project: - default: false # disable the default status that measures entire project - tests: - paths: - - "tests/" - target: 100% - source: - paths: - - "jupytext/" - target: 97% - threshold: 0.002 - patch: - default: - target: 80% # new contributions should have a coverage at least equal to target diff --git a/docs/contributing.md b/docs/contributing.md index f8ea09834..198821d01 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -20,5 +20,5 @@ You want to submit an enhancement on Jupytext? Unless this is a small change, we A pull request for which you do not need to contact us in advance is the addition of a new language to Jupytext. In principle that should be easy - you would only have to: - document the language extension and comment by adding one line to `_SCRIPT_EXTENSIONS` in `jupytext/languages.py`. - add the language to `docs/languages.md` -- contribute a sample notebook in `tests/notebooks/ipynb_[language]`. -- run the tests suite (create a [development environment](developing.md), then execute `pytest` locally). The tests will generate various text representations corresponding to your notebook under `tests/notebooks/mirror/`. Please verify that these files are valid scripts, and include them in your PR. +- contribute a sample notebook in `tests/data/notebooks/inputs/ipynb_[language]`. +- run the tests suite (create a [development environment](developing.md), then execute `pytest` locally). The tests will generate various text representations corresponding to your notebook under `tests/data/notebooks/outputs/`. Please verify that these files are valid scripts, and include them in your PR. diff --git a/docs/using-pre-commit.md b/docs/using-pre-commit.md index db999ec2a..408f78877 100644 --- a/docs/using-pre-commit.md +++ b/docs/using-pre-commit.md @@ -62,5 +62,5 @@ repos: language_version: python3 ``` -Tested examples of how to use the pre-commit hook are available in our [tests](https://github.com/mwouts/jupytext/tree/main/tests) - -see for instance [test_pre_commit_1_sync_with_config.py](https://github.com/mwouts/jupytext/blob/main/tests/test_pre_commit_1_sync_with_config.py). +Tested examples of how to use the pre-commit hook are available in our [tests](https://github.com/mwouts/jupytext/tree/main/tests/functional/pre-commit) - +see for instance [test_pre_commit_1_sync_with_config.py](https://github.com/mwouts/jupytext/blob/main/tests/functional/pre-commit/test_pre_commit_1_sync_with_config.py). diff --git a/environment.yml b/environment.yml index 87959902c..3e4df5174 100644 --- a/environment.yml +++ b/environment.yml @@ -6,23 +6,6 @@ dependencies: - python>=3.8 - jupyterlab>=4.0.0 - nbformat>=5.1.2 - - pyyaml - - toml - - markdown-it-py>=1.0.0,<3.0.0 - - mdit-py-plugins - - nbconvert - - ipykernel - - pytest - - pytest-xdist - - pytest-randomly - - pytest-cov - pre-commit - - gitpython - - pylint - - flake8 - - black==23.11.0 - - isort==5.12.0 - - autopep8 - - sphinx-gallery<0.8 - - pandoc==2.16.2 - nodejs>=20 + - pandoc==2.16.2 diff --git a/pyproject.toml b/pyproject.toml index a60b0f94f..e9bc8d8bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,10 +32,11 @@ classifiers = [ ] dependencies = [ "nbformat", - "pyyaml", - "toml", "mdit_py_plugins", "markdown-it-py>=1.0.0", + "packaging", + "pyyaml", + "toml", ] dynamic = ["version"] @@ -46,31 +47,44 @@ Documentation = "https://jupytext.readthedocs.io" [project.optional-dependencies] # Test related dependencies -# TODO: Split them into unit, integration and functional tests groups test = [ - "autopep8", - "black", - "isort", - "ruff", - "flake8", - "pytest", - "pytest-randomly", - "gitpython", - "jupyterlab", - "notebook", - "nbconvert", - # jupyter-fs==0.4.0 is async, which is not supported by Jupytext ATM - "jupyter-fs<0.4.0", - "ipykernel", - "pre-commit", + "pytest", + "pytest-randomly" +] +test-functional = [ + "jupytext[test]", +] +test-integration = [ + "jupytext[test-functional]", + "jupyter-server", + # jupytext --execute + "nbconvert", + "ipykernel", +] +test-external = [ + "jupytext[test-integration]", + # jupytext --pipe and --check + "autopep8", + "black", + "isort", + "flake8", + # Sphinx gallery + "sphinx-gallery<0.8", + # Pre-commit tests + "gitpython", + "pre-commit", + # Interaction with other contents managers + # jupyter-fs==0.4.0 is async, which is not supported by Jupytext ATM + "jupyter-fs<0.4.0" ] # Coverage requirements test-cov = [ - "jupytext[test]", + "jupytext[test-integration]", "pytest-cov>=2.6.1", ] dev = [ "jupytext[test]", + "pre-commit" ] # Documentation dependencies docs = [ @@ -103,9 +117,7 @@ path = "src/jupytext/version.py" # Following config is related to JupyterLab extension [tool.hatch.build.targets.sdist] artifacts = ["jupyterlab/jupyterlab_jupytext/labextension"] -# Exclude tests (contains Notebooks that have a total size of ~8MB) to reduce sdist -# tarball. (See #1142) -exclude = ["tests", "demo", "**/.yarn", "**/node_modules"] +exclude = ["demo", "**/.yarn", "**/node_modules"] [tool.hatch.build.targets.wheel] packages = ["src/jupytext", "src/jupytext_config", "jupyterlab/jupyterlab_jupytext"] @@ -163,7 +175,7 @@ ignore = ["W002"] [tool.ruff] line-length = 127 exclude = [ - "tests/notebooks/*", + "tests/data/notebooks/*", ] # Seems like W503 is not implemented in ruff # ref: https://github.com/astral-sh/ruff/issues/4125 @@ -172,9 +184,40 @@ ignore = [ ] [tool.pytest.ini_options] +markers = [ + "requires_black", + "requires_isort", + "requires_flake8", + "requires_autopep8", + "requires_nbconvert", + "requires_myst", + "requires_no_myst", + "requires_quarto", + "requires_pandoc", + "requires_no_pandoc", + "requires_sphinx_gallery", + "requires_user_kernel_python3", + "requires_ir_kernel", + "skip_on_windows", + "pre_commit", +] filterwarnings = [ # Uncomment this "error" to turn all unfiltered warnings into errors # "error", + # Our cwd_tmpdir fixture + "ignore:pathlib.Path.__enter__\\(\\) is deprecated and scheduled for removal in Python 3.13:DeprecationWarning", + # Pre-commit + "ignore:read_text is deprecated. Use files\\(\\) instead:DeprecationWarning", + "ignore:open_text is deprecated. Use files\\(\\) instead:DeprecationWarning", + # Jupyter + "ignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning", + # Jupyter notebook + "ignore:Support for bleach <5 will be removed in a future version of nbconvert:DeprecationWarning", + # jupyterfs + "ignore:run_pre_save_hook is deprecated, use run_pre_save_hooks instead:DeprecationWarning", + "ignore:run_post_save_hook is deprecated, use run_post_save_hooks instead:DeprecationWarning", + "ignore:Deprecated call to `pkg_resources.declare_namespace:DeprecationWarning", + "ignore:pkg_resources is deprecated as an API:DeprecationWarning", # use single quote to denote raw strings in toml # (10 warnings) 'ignore:Passing unrecognized arguments to super\(KernelSpec\).__init__:DeprecationWarning', diff --git a/src/jupytext/cell_reader.py b/src/jupytext/cell_reader.py index 74aa45230..e4ea21c13 100644 --- a/src/jupytext/cell_reader.py +++ b/src/jupytext/cell_reader.py @@ -5,16 +5,16 @@ from copy import copy from nbformat.v4.nbbase import new_code_cell, new_markdown_cell, new_raw_cell +from packaging.version import parse from .doxygen import doxygen_to_markdown from .languages import _SCRIPT_EXTENSIONS -from .parse_version import parse_version # Sphinx Gallery is an optional dependency. And we intercept the SyntaxError for #301 try: from sphinx_gallery import __version__ as sg_version - if parse_version(sg_version) <= parse_version("0.7.0"): + if parse(sg_version) <= parse("0.7.0"): from sphinx_gallery.notebook import rst2md else: warnings.warn( diff --git a/src/jupytext/cli.py b/src/jupytext/cli.py index dcb1b9630..6ce98a033 100644 --- a/src/jupytext/cli.py +++ b/src/jupytext/cli.py @@ -50,6 +50,15 @@ def system(*args, **kwargs): return out.decode("utf-8") +def tool_version(tool): + try: + args = tool.split(" ") + args.append("--version") + return system(*args) + except (OSError, SystemExit): # pragma: no cover + return None + + def str2bool(value): """Parse Yes/No/Default string https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse""" diff --git a/src/jupytext/compare.py b/src/jupytext/compare.py index df3ea4739..5b3347c7e 100644 --- a/src/jupytext/compare.py +++ b/src/jupytext/compare.py @@ -2,13 +2,16 @@ import difflib import json +import os import re +from jupytext.paired_paths import full_path + from .cell_metadata import _IGNORE_CELL_METADATA from .combine import combine_inputs_with_outputs -from .formats import long_form_one_format +from .formats import check_auto_ext, long_form_one_format from .header import _DEFAULT_NOTEBOOK_METADATA -from .jupytext import reads, writes +from .jupytext import read, reads, write, writes from .metadata_filter import filter_metadata _BLANK_LINE = re.compile(r"^\s*$") @@ -388,3 +391,75 @@ def test_round_trip_conversion( allow_expected_differences, raise_on_first_difference=stop_on_first_error, ) + + +# The functions below are used in the Jupytext text collection +def create_mirror_file_if_missing(mirror_file, notebook, fmt): + if not os.path.isfile(mirror_file): + write(notebook, mirror_file, fmt=fmt) + + +def assert_conversion_same_as_mirror(nb_file, fmt, mirror_name, compare_notebook=False): + """This function is used in the tests""" + dirname, basename = os.path.split(nb_file) + file_name, org_ext = os.path.splitext(basename) + fmt = long_form_one_format(fmt) + notebook = read(nb_file, fmt=fmt) + fmt = check_auto_ext(fmt, notebook.metadata, "") + ext = fmt["extension"] + mirror_file = os.path.join( + dirname, "..", "..", "outputs", mirror_name, full_path(file_name, fmt) + ) + + # it's better not to have Jupytext metadata in test notebooks: + if fmt == "ipynb" and "jupytext" in notebook.metadata: # pragma: no cover + notebook.metadata.pop("jupytext") + write(nb_file, fmt=fmt) + + create_mirror_file_if_missing(mirror_file, notebook, fmt) + + # Compare the text representation of the two notebooks + if compare_notebook: + # Read and convert the mirror file to the latest nbformat version if necessary + nb_mirror = read(mirror_file, as_version=notebook.nbformat) + nb_mirror.nbformat_minor = notebook.nbformat_minor + compare_notebooks(nb_mirror, notebook) + return + elif ext == ".ipynb": + notebook = read(mirror_file) + fmt.update({"extension": org_ext}) + actual = writes(notebook, fmt) + with open(nb_file, encoding="utf-8") as fp: + expected = fp.read() + else: + actual = writes(notebook, fmt) + with open(mirror_file, encoding="utf-8") as fp: + expected = fp.read() + + if not actual.endswith("\n"): + actual = actual + "\n" + compare(actual, expected) + + # Compare the two notebooks + if ext != ".ipynb": + notebook = read(nb_file) + nb_mirror = read(mirror_file, fmt=fmt) + + if fmt.get("format_name") == "sphinx": + nb_mirror.cells = nb_mirror.cells[1:] + for cell in notebook.cells: + cell.metadata = {} + for cell in nb_mirror.cells: + cell.metadata = {} + + compare_notebooks(nb_mirror, notebook, fmt) + + nb_mirror = combine_inputs_with_outputs(nb_mirror, notebook) + compare_notebooks(nb_mirror, notebook, fmt, compare_outputs=True) + + +def notebook_model(nb): + """Return a notebook model, with content a + dictionary rather than a notebook object. + To be used in tests only.""" + return dict(type="notebook", content=json.loads(json.dumps(nb))) diff --git a/src/jupytext/formats.py b/src/jupytext/formats.py index d9a481768..efed28cd4 100644 --- a/src/jupytext/formats.py +++ b/src/jupytext/formats.py @@ -40,7 +40,7 @@ myst_extensions, myst_version, ) -from .pandoc import pandoc_version +from .pandoc import is_pandoc_available, pandoc_version from .stringparser import StringParser from .version import __version__ @@ -825,3 +825,13 @@ def check_auto_ext(fmt, metadata, option): option, short_form_one_format(fmt) ) ) + + +def formats_with_support_for_cell_metadata(): + for fmt in JUPYTEXT_FORMATS: + if fmt.format_name == "myst" and not is_myst_available(): + continue + if fmt.format_name == "pandoc" and not is_pandoc_available(): + continue + if fmt.format_name not in ["sphinx", "nomarker", "spin", "quarto"]: + yield f"{fmt.extension[1:]}:{fmt.format_name}" diff --git a/src/jupytext/pandoc.py b/src/jupytext/pandoc.py index 11b9b6e1b..41afce087 100644 --- a/src/jupytext/pandoc.py +++ b/src/jupytext/pandoc.py @@ -3,13 +3,11 @@ import os import subprocess import tempfile -from functools import partial # Copy nbformat reads and writes to avoid them being patched in the contents manager!! from nbformat import reads as ipynb_reads from nbformat import writes as ipynb_writes - -from .parse_version import parse_version as parse +from packaging.version import parse class PandocError(OSError): @@ -58,14 +56,13 @@ def raise_if_pandoc_is_not_available(min_version="2.7.2", max_version=None): "but pandoc was not found" ) - parse_version = partial(parse, custom_error=PandocError) - if parse_version(version) < parse_version(min_version): + if parse(version) < parse(min_version): raise PandocError( f"The Pandoc Markdown format requires 'pandoc>={min_version}', " f"but pandoc version {version} was found" ) - if max_version and parse_version(version) > parse_version(max_version): + if max_version and parse(version) > parse(max_version): raise PandocError( f"The Pandoc Markdown format requires 'pandoc<={max_version}', " f"but pandoc version {version} was found" @@ -89,8 +86,7 @@ def md_to_notebook(text): tmp_file.write(text.encode("utf-8")) tmp_file.close() - parse_version = partial(parse, custom_error=PandocError) - if parse_version(pandoc_version()) < parse_version("2.11.2"): + if parse(pandoc_version()) < parse("2.11.2"): pandoc_args = "--from markdown --to ipynb -s --atx-headers --wrap=preserve --preserve-tabs" else: pandoc_args = "--from markdown --to ipynb -s --markdown-headings=atx --wrap=preserve --preserve-tabs" @@ -114,8 +110,7 @@ def notebook_to_md(notebook): tmp_file.write(ipynb_writes(notebook).encode("utf-8")) tmp_file.close() - parse_version = partial(parse, custom_error=PandocError) - if parse_version(pandoc_version()) < parse_version("2.11.2"): + if parse(pandoc_version()) < parse("2.11.2"): pandoc_args = "--from ipynb --to markdown -s --atx-headers --wrap=preserve --preserve-tabs" else: pandoc_args = "--from ipynb --to markdown -s --markdown-headings=atx --wrap=preserve --preserve-tabs" diff --git a/src/jupytext/parse_version.py b/src/jupytext/parse_version.py deleted file mode 100644 index 86fbde04a..000000000 --- a/src/jupytext/parse_version.py +++ /dev/null @@ -1,11 +0,0 @@ -def parse_version(version, custom_error=ImportError): - try: - from pkg_resources import parse_version as parse - except ImportError: - try: - from packaging.version import parse - except ImportError: - raise custom_error("Please install either pkg_resources or packaging") - - print(version) - return parse(version) diff --git a/src/jupytext/quarto.py b/src/jupytext/quarto.py index e704b7682..0eec6b2dc 100644 --- a/src/jupytext/quarto.py +++ b/src/jupytext/quarto.py @@ -3,13 +3,11 @@ import os import subprocess import tempfile -from functools import partial # Copy nbformat reads and writes to avoid them being patched in the contents manager!! from nbformat import reads as ipynb_reads from nbformat import writes as ipynb_writes - -from .parse_version import parse_version as parse +from packaging.version import parse QUARTO_MIN_VERSION = "0.2.134" @@ -53,8 +51,7 @@ def raise_if_quarto_is_not_available(min_version=QUARTO_MIN_VERSION): "but quarto was not found" ) - parse_version = partial(parse, custom_error=QuartoError) - if parse_version(version) < parse_version(min_version): + if parse(version) < parse(min_version): raise QuartoError( f"The Quarto Markdown format requires 'quarto>={min_version}', " f"but quarto version {version} was not found" diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/conftest.py b/tests/conftest.py index 53f8ac53e..8fd89c621 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,12 @@ +import itertools +import os.path +import re +import sys import unittest.mock as mock from pathlib import Path import pytest -from git import Repo +from jupyter_client.kernelspec import find_kernel_specs, get_kernel_spec from nbformat.v4 import nbbase from nbformat.v4.nbbase import ( new_code_cell, @@ -12,14 +16,20 @@ ) import jupytext -from jupytext.cli import system +from jupytext.cell_reader import rst2md +from jupytext.cli import system, tool_version +from jupytext.formats import formats_with_support_for_cell_metadata +from jupytext.myst import is_myst_available +from jupytext.pandoc import is_pandoc_available +from jupytext.quarto import is_quarto_available -from .utils import formats_with_support_for_cell_metadata - -# Pytest's tmpdir is in /tmp (at least for me), so this helps avoiding interferences between +# Pytest's tmpdir is in /tmp (at least for me), so this helps to avoid interferences between # global configuration on HOME and the test collection jupytext.config.JUPYTEXT_CEILING_DIRECTORIES = ["/tmp/"] +SAMPLE_NOTEBOOK_PATH = Path(__file__).parent / "data" / "notebooks" / "inputs" +ROOT_PATH = Path(__file__).parent.parent + @pytest.fixture def no_jupytext_version_number(): @@ -27,12 +37,6 @@ def no_jupytext_version_number(): yield -@pytest.fixture -def tmp_repo(tmpdir): - repo = Repo.init(str(tmpdir)) - return repo - - @pytest.fixture def cwd_tmpdir(tmpdir): # Run the whole test from inside tmpdir @@ -71,7 +75,16 @@ def python_notebook(): "display_name": "Python 3", "language": "python", "name": "python_kernel", - } + }, + "language_info": { + "codemirror_mode": {"name": "ipython", "version": 3}, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4", + }, }, ) @@ -107,6 +120,296 @@ def fmt_with_cell_metadata(request): return request.param +def list_notebooks(path="ipynb", skip=""): + """All notebooks in the directory notebooks/path, + or in the package itself""" + if path == "ipynb": + return ( + list_notebooks("ipynb_julia", skip=skip) + + list_notebooks("ipynb_py", skip=skip) + + list_notebooks("ipynb_R", skip=skip) + ) + + nb_path = SAMPLE_NOTEBOOK_PATH + + if path == "ipynb_all": + return itertools.chain( + *( + list_notebooks(folder.name, skip=skip) + for folder in nb_path.iterdir() + if folder.name.startswith("ipynb_") + ) + ) + + if path == "all": + return itertools.chain( + *(list_notebooks(folder.name, skip=skip) for folder in nb_path.iterdir()) + ) + + if path.startswith("."): + nb_path = Path(__file__).parent / ".." / path + else: + nb_path = nb_path / path + + if skip: + skip_re = re.compile(".*" + skip + ".*") + return [ + str(nb_file) + for nb_file in nb_path.iterdir() + if nb_file.is_file() and not skip_re.match(nb_file.name) + ] + + return [str(nb_file) for nb_file in nb_path.iterdir() if nb_file.is_file()] + + +def notebook_id_func(nb_file): + nb_file = Path(nb_file) + if SAMPLE_NOTEBOOK_PATH in nb_file.parents: + return str(nb_file.relative_to(SAMPLE_NOTEBOOK_PATH)) + return str(nb_file.relative_to(ROOT_PATH)) + + +@pytest.fixture(params=list_notebooks("ipynb_all"), ids=notebook_id_func) +def ipynb_file(request): + return request.param + + +@pytest.fixture(params=list_notebooks("all"), ids=notebook_id_func) +def any_nb_file(request): + return request.param + + +@pytest.fixture +def ipynb_py_R_jl_files(): + return list_notebooks() + + +@pytest.fixture(params=list_notebooks(), ids=notebook_id_func) +def ipynb_py_R_jl_file(request): + return request.param + + +@pytest.fixture +def ipynb_py_R_jl_ext(ipynb_py_R_jl_file): + for language in "py", "R", "julia": + if f"{os.path.sep}ipynb_{language}{os.path.sep}" in ipynb_py_R_jl_file: + return ".jl" if language == "julia" else "." + language + + raise RuntimeError(f"language not found for {ipynb_py_R_jl_file}") + + +@pytest.fixture( + params=list_notebooks("ipynb") + list_notebooks("Rmd"), ids=notebook_id_func +) +def ipynb_or_rmd_file(request): + return request.param + + +@pytest.fixture( + params=list_notebooks("ipynb_py") + list_notebooks("ipynb_R"), ids=notebook_id_func +) +def ipynb_py_R_file(request): + return request.param + + +@pytest.fixture +def ipynb_py_files(): + return list_notebooks("ipynb_py") + + +@pytest.fixture(params=list_notebooks("ipynb_py"), ids=notebook_id_func) +def ipynb_py_file(request): + return request.param + + +@pytest.fixture(params=list_notebooks("ipynb_R"), ids=notebook_id_func) +def ipynb_R_file(request): + return request.param + + +@pytest.fixture(params=list_notebooks("ipynb_julia"), ids=notebook_id_func) +def ipynb_julia_file(request): + return request.param + + +@pytest.fixture(params=list_notebooks("ipynb_scheme"), ids=notebook_id_func) +def ipynb_scheme_file(request): + return request.param + + +@pytest.fixture(params=list_notebooks("ipynb_cpp"), ids=notebook_id_func) +def ipynb_cpp_file(request): + return request.param + + +@pytest.fixture( + params=list_notebooks("ipynb_all", skip="many hash"), ids=notebook_id_func +) +def ipynb_to_light(request): + return request.param + + +@pytest.fixture(params=list_notebooks("ipynb_all"), ids=notebook_id_func) +def ipynb_to_myst(request): + return request.param + + +@pytest.fixture( + params=[ + py_file + for py_file in list_notebooks("./src/jupytext") + if py_file.endswith(".py") and "folding_markers" not in py_file + ], + ids=notebook_id_func, +) +def py_file(request): + return request.param + + +@pytest.fixture( + params=list_notebooks("julia") + + list_notebooks("python") + + list_notebooks("R") + + list_notebooks("ps1"), + ids=notebook_id_func, +) +def script_to_ipynb(request): + return request.param + + +@pytest.fixture(params=list_notebooks("python"), ids=notebook_id_func) +def python_file(request): + return request.param + + +@pytest.fixture(params=list_notebooks("percent"), ids=notebook_id_func) +def percent_file(request): + return request.param + + +@pytest.fixture(params=list_notebooks("hydrogen"), ids=notebook_id_func) +def hydrogen_file(request): + return request.param + + +@pytest.fixture(params=list_notebooks("R"), ids=notebook_id_func) +def r_file(request): + return request.param + + +@pytest.fixture(params=list_notebooks("R_spin"), ids=notebook_id_func) +def r_spin_file(request): + return request.param + + +@pytest.fixture(params=list_notebooks("md"), ids=notebook_id_func) +def md_file(request): + return request.param + + +@pytest.fixture( + params=list_notebooks( + "ipynb", skip="(functional|Notebook with|flavors|invalid|305)" + ), + ids=notebook_id_func, +) +def ipynb_to_pandoc(request): + return request.param + + +@pytest.fixture( + params=list_notebooks( + "ipynb", + skip="(functional|Notebook with|plotly_graphs|flavors|complex_metadata|" + "update83|raw_cell|_66|nteract|LaTeX|invalid|305|text_outputs|ir_notebook|jupyter|with_R_magic)", + ), + ids=notebook_id_func, +) +def ipynb_to_quarto(request): + return request.param + + +@pytest.fixture( + params=list_notebooks("ipynb_py", skip="(raw|hash|frozen|magic|html|164|long)"), + ids=notebook_id_func, +) +def ipynb_to_sphinx(request): + return request.param + + +@pytest.fixture(params=list_notebooks("Rmd"), ids=notebook_id_func) +def rmd_file(request): + return request.param + + +@pytest.fixture(params=list_notebooks("sphinx"), ids=notebook_id_func) +def sphinx_file(request): + return request.param + + +def pytest_runtest_setup(item): + for mark in item.iter_markers(): + for tool in [ + "jupytext", + "black", + "isort", + "flake8", + "autopep8", + "jupyter nbconvert", + ]: + if mark.name == f"requires_{tool.replace(' ', '_')}": + if not tool_version(tool): + pytest.skip(f"{tool} is not installed") + if mark.name == "requires_sphinx_gallery": + if not rst2md: + pytest.skip("sphinx_gallery is not available") + if mark.name == "requires_pandoc": + # The mirror files changed slightly when Pandoc 2.11 was introduced + # https://github.com/mwouts/jupytext/commit/c07d919702999056ce47f92b74f63a15c8361c5d + # The mirror files changed again when Pandoc 2.16 was introduced + # https://github.com/mwouts/jupytext/pull/919/commits/1fa1451ecdaa6ad8d803bcb6fb0c0cf09e5371bf + if not is_pandoc_available(min_version="2.16.2", max_version="2.16.2"): + pytest.skip("pandoc==2.16.2 is not available") + if mark.name == "requires_quarto": + if not is_quarto_available(min_version="0.2.0"): + pytest.skip("quarto>=0.2 is not available") + if mark.name == "requires_no_pandoc": + if is_pandoc_available(): + pytest.skip("Pandoc is installed") + if mark.name == "requires_ir_kernel": + if not any( + get_kernel_spec(name).language == "R" for name in find_kernel_specs() + ): + pytest.skip("irkernel is not installed") + if mark.name == "requires_user_kernel_python3": + if "python_kernel" not in find_kernel_specs(): + pytest.skip( + "Please run 'python -m ipykernel install --name python_kernel --user'" + ) + if mark.name == "requires_myst": + if not is_myst_available(): + pytest.skip("myst_parser not found") + if mark.name == "requires_no_myst": + if is_myst_available(): + pytest.skip("myst is available") + if mark.name == "skip_on_windows": + if sys.platform.startswith("win"): + pytest.skip("Issue 489") + if mark.name == "pre_commit": + if sys.platform.startswith("win"): + pytest.skip( + "OSError: [WinError 193] %1 is not a valid Win32 application" + ) + if not (Path(__file__).parent.parent / ".git").is_dir(): + pytest.skip("Jupytext folder is not a git repository #814") + + +def pytest_collection_modifyitems(config, items): + for item in items: + if (config.rootdir / "tests" / "external" / "pre_commit") in item.path.parents: + item.add_marker(pytest.mark.pre_commit) + + """To make sure that cell ids are distinct we use a global counter. This solves https://github.com/mwouts/jupytext/issues/747""" global_cell_count = 0 diff --git a/tests/notebooks/R/simple_r_script.R b/tests/data/notebooks/inputs/R/simple_r_script.R similarity index 100% rename from tests/notebooks/R/simple_r_script.R rename to tests/data/notebooks/inputs/R/simple_r_script.R diff --git a/tests/notebooks/R_spin/knitr-spin.R b/tests/data/notebooks/inputs/R_spin/knitr-spin.R similarity index 100% rename from tests/notebooks/R_spin/knitr-spin.R rename to tests/data/notebooks/inputs/R_spin/knitr-spin.R diff --git a/tests/notebooks/Rmd/R_sample.Rmd b/tests/data/notebooks/inputs/Rmd/R_sample.Rmd similarity index 100% rename from tests/notebooks/Rmd/R_sample.Rmd rename to tests/data/notebooks/inputs/Rmd/R_sample.Rmd diff --git a/tests/notebooks/Rmd/chunk_options.Rmd b/tests/data/notebooks/inputs/Rmd/chunk_options.Rmd similarity index 100% rename from tests/notebooks/Rmd/chunk_options.Rmd rename to tests/data/notebooks/inputs/Rmd/chunk_options.Rmd diff --git a/tests/notebooks/Rmd/ioslides.Rmd b/tests/data/notebooks/inputs/Rmd/ioslides.Rmd similarity index 100% rename from tests/notebooks/Rmd/ioslides.Rmd rename to tests/data/notebooks/inputs/Rmd/ioslides.Rmd diff --git a/tests/notebooks/Rmd/knitr-spin.Rmd b/tests/data/notebooks/inputs/Rmd/knitr-spin.Rmd similarity index 100% rename from tests/notebooks/Rmd/knitr-spin.Rmd rename to tests/data/notebooks/inputs/Rmd/knitr-spin.Rmd diff --git a/tests/notebooks/Rmd/markdown.Rmd b/tests/data/notebooks/inputs/Rmd/markdown.Rmd similarity index 100% rename from tests/notebooks/Rmd/markdown.Rmd rename to tests/data/notebooks/inputs/Rmd/markdown.Rmd diff --git a/tests/notebooks/hydrogen/hydrogen_latex_html_R_magics.py b/tests/data/notebooks/inputs/hydrogen/hydrogen_latex_html_R_magics.py similarity index 100% rename from tests/notebooks/hydrogen/hydrogen_latex_html_R_magics.py rename to tests/data/notebooks/inputs/hydrogen/hydrogen_latex_html_R_magics.py diff --git a/tests/notebooks/ipynb_R/R notebook with invalid cell keys.ipynb b/tests/data/notebooks/inputs/ipynb_R/R notebook with invalid cell keys.ipynb similarity index 100% rename from tests/notebooks/ipynb_R/R notebook with invalid cell keys.ipynb rename to tests/data/notebooks/inputs/ipynb_R/R notebook with invalid cell keys.ipynb diff --git a/tests/notebooks/ipynb_R/ir_notebook.ipynb b/tests/data/notebooks/inputs/ipynb_R/ir_notebook.ipynb similarity index 100% rename from tests/notebooks/ipynb_R/ir_notebook.ipynb rename to tests/data/notebooks/inputs/ipynb_R/ir_notebook.ipynb diff --git a/tests/notebooks/ipynb_bash/sample_bash_notebook.ipynb b/tests/data/notebooks/inputs/ipynb_bash/sample_bash_notebook.ipynb similarity index 100% rename from tests/notebooks/ipynb_bash/sample_bash_notebook.ipynb rename to tests/data/notebooks/inputs/ipynb_bash/sample_bash_notebook.ipynb diff --git a/tests/notebooks/ipynb_clojure/html-demo.ipynb b/tests/data/notebooks/inputs/ipynb_clojure/html-demo.ipynb similarity index 100% rename from tests/notebooks/ipynb_clojure/html-demo.ipynb rename to tests/data/notebooks/inputs/ipynb_clojure/html-demo.ipynb diff --git a/tests/notebooks/ipynb_coconut/coconut_homepage_demo.ipynb b/tests/data/notebooks/inputs/ipynb_coconut/coconut_homepage_demo.ipynb similarity index 100% rename from tests/notebooks/ipynb_coconut/coconut_homepage_demo.ipynb rename to tests/data/notebooks/inputs/ipynb_coconut/coconut_homepage_demo.ipynb diff --git a/tests/notebooks/ipynb_cpp/xcpp_by_quantstack.ipynb b/tests/data/notebooks/inputs/ipynb_cpp/xcpp_by_quantstack.ipynb similarity index 100% rename from tests/notebooks/ipynb_cpp/xcpp_by_quantstack.ipynb rename to tests/data/notebooks/inputs/ipynb_cpp/xcpp_by_quantstack.ipynb diff --git a/tests/notebooks/ipynb_cs/csharp.ipynb b/tests/data/notebooks/inputs/ipynb_cs/csharp.ipynb similarity index 100% rename from tests/notebooks/ipynb_cs/csharp.ipynb rename to tests/data/notebooks/inputs/ipynb_cs/csharp.ipynb diff --git a/tests/notebooks/ipynb_fs/fsharp.ipynb b/tests/data/notebooks/inputs/ipynb_fs/fsharp.ipynb similarity index 100% rename from tests/notebooks/ipynb_fs/fsharp.ipynb rename to tests/data/notebooks/inputs/ipynb_fs/fsharp.ipynb diff --git a/tests/notebooks/ipynb_gnuplot/gnuplot_notebook.ipynb b/tests/data/notebooks/inputs/ipynb_gnuplot/gnuplot_notebook.ipynb similarity index 100% rename from tests/notebooks/ipynb_gnuplot/gnuplot_notebook.ipynb rename to tests/data/notebooks/inputs/ipynb_gnuplot/gnuplot_notebook.ipynb diff --git a/tests/notebooks/ipynb_groovy/tailrecursive-factorial.ipynb b/tests/data/notebooks/inputs/ipynb_groovy/tailrecursive-factorial.ipynb similarity index 100% rename from tests/notebooks/ipynb_groovy/tailrecursive-factorial.ipynb rename to tests/data/notebooks/inputs/ipynb_groovy/tailrecursive-factorial.ipynb diff --git a/tests/notebooks/ipynb_haskell/haskell_notebook.ipynb b/tests/data/notebooks/inputs/ipynb_haskell/haskell_notebook.ipynb similarity index 100% rename from tests/notebooks/ipynb_haskell/haskell_notebook.ipynb rename to tests/data/notebooks/inputs/ipynb_haskell/haskell_notebook.ipynb diff --git a/tests/notebooks/ipynb_idl/demo_gdl_fbp.ipynb b/tests/data/notebooks/inputs/ipynb_idl/demo_gdl_fbp.ipynb similarity index 100% rename from tests/notebooks/ipynb_idl/demo_gdl_fbp.ipynb rename to tests/data/notebooks/inputs/ipynb_idl/demo_gdl_fbp.ipynb diff --git a/tests/notebooks/ipynb_java/simple-helloworld.ipynb b/tests/data/notebooks/inputs/ipynb_java/simple-helloworld.ipynb similarity index 100% rename from tests/notebooks/ipynb_java/simple-helloworld.ipynb rename to tests/data/notebooks/inputs/ipynb_java/simple-helloworld.ipynb diff --git a/tests/notebooks/ipynb_js/ijavascript.ipynb b/tests/data/notebooks/inputs/ipynb_js/ijavascript.ipynb similarity index 100% rename from tests/notebooks/ipynb_js/ijavascript.ipynb rename to tests/data/notebooks/inputs/ipynb_js/ijavascript.ipynb diff --git a/tests/notebooks/ipynb_julia/julia_benchmark_plotly_barchart.ipynb b/tests/data/notebooks/inputs/ipynb_julia/julia_benchmark_plotly_barchart.ipynb similarity index 100% rename from tests/notebooks/ipynb_julia/julia_benchmark_plotly_barchart.ipynb rename to tests/data/notebooks/inputs/ipynb_julia/julia_benchmark_plotly_barchart.ipynb diff --git a/tests/notebooks/ipynb_julia/julia_functional_geometry.ipynb b/tests/data/notebooks/inputs/ipynb_julia/julia_functional_geometry.ipynb similarity index 100% rename from tests/notebooks/ipynb_julia/julia_functional_geometry.ipynb rename to tests/data/notebooks/inputs/ipynb_julia/julia_functional_geometry.ipynb diff --git a/tests/notebooks/ipynb_m/octave_notebook.ipynb b/tests/data/notebooks/inputs/ipynb_m/octave_notebook.ipynb similarity index 100% rename from tests/notebooks/ipynb_m/octave_notebook.ipynb rename to tests/data/notebooks/inputs/ipynb_m/octave_notebook.ipynb diff --git a/tests/notebooks/ipynb_maxima/maxima_example.ipynb b/tests/data/notebooks/inputs/ipynb_maxima/maxima_example.ipynb similarity index 100% rename from tests/notebooks/ipynb_maxima/maxima_example.ipynb rename to tests/data/notebooks/inputs/ipynb_maxima/maxima_example.ipynb diff --git a/tests/notebooks/ipynb_ocaml/ocaml_notebook.ipynb b/tests/data/notebooks/inputs/ipynb_ocaml/ocaml_notebook.ipynb similarity index 100% rename from tests/notebooks/ipynb_ocaml/ocaml_notebook.ipynb rename to tests/data/notebooks/inputs/ipynb_ocaml/ocaml_notebook.ipynb diff --git a/tests/notebooks/ipynb_ps1/powershell.ipynb b/tests/data/notebooks/inputs/ipynb_ps1/powershell.ipynb similarity index 100% rename from tests/notebooks/ipynb_ps1/powershell.ipynb rename to tests/data/notebooks/inputs/ipynb_ps1/powershell.ipynb diff --git a/tests/notebooks/ipynb_py/Line_breaks_in_LateX_305.ipynb b/tests/data/notebooks/inputs/ipynb_py/Line_breaks_in_LateX_305.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/Line_breaks_in_LateX_305.ipynb rename to tests/data/notebooks/inputs/ipynb_py/Line_breaks_in_LateX_305.ipynb diff --git a/tests/notebooks/ipynb_py/Notebook with function and cell metadata 164.ipynb b/tests/data/notebooks/inputs/ipynb_py/Notebook with function and cell metadata 164.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/Notebook with function and cell metadata 164.ipynb rename to tests/data/notebooks/inputs/ipynb_py/Notebook with function and cell metadata 164.ipynb diff --git a/tests/notebooks/ipynb_py/Notebook with html and latex cells.ipynb b/tests/data/notebooks/inputs/ipynb_py/Notebook with html and latex cells.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/Notebook with html and latex cells.ipynb rename to tests/data/notebooks/inputs/ipynb_py/Notebook with html and latex cells.ipynb diff --git a/tests/notebooks/ipynb_py/Notebook with many hash signs.ipynb b/tests/data/notebooks/inputs/ipynb_py/Notebook with many hash signs.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/Notebook with many hash signs.ipynb rename to tests/data/notebooks/inputs/ipynb_py/Notebook with many hash signs.ipynb diff --git a/tests/notebooks/ipynb_py/Notebook with metadata and long cells.ipynb b/tests/data/notebooks/inputs/ipynb_py/Notebook with metadata and long cells.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/Notebook with metadata and long cells.ipynb rename to tests/data/notebooks/inputs/ipynb_py/Notebook with metadata and long cells.ipynb diff --git a/tests/notebooks/ipynb_py/Notebook_with_R_magic.ipynb b/tests/data/notebooks/inputs/ipynb_py/Notebook_with_R_magic.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/Notebook_with_R_magic.ipynb rename to tests/data/notebooks/inputs/ipynb_py/Notebook_with_R_magic.ipynb diff --git a/tests/notebooks/ipynb_py/Notebook_with_more_R_magic_111.ipynb b/tests/data/notebooks/inputs/ipynb_py/Notebook_with_more_R_magic_111.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/Notebook_with_more_R_magic_111.ipynb rename to tests/data/notebooks/inputs/ipynb_py/Notebook_with_more_R_magic_111.ipynb diff --git a/tests/notebooks/ipynb_py/The flavors of raw cells.ipynb b/tests/data/notebooks/inputs/ipynb_py/The flavors of raw cells.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/The flavors of raw cells.ipynb rename to tests/data/notebooks/inputs/ipynb_py/The flavors of raw cells.ipynb diff --git a/tests/notebooks/ipynb_py/cat_variable.ipynb b/tests/data/notebooks/inputs/ipynb_py/cat_variable.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/cat_variable.ipynb rename to tests/data/notebooks/inputs/ipynb_py/cat_variable.ipynb diff --git a/tests/notebooks/ipynb_py/convert_to_py_then_test_with_update83.ipynb b/tests/data/notebooks/inputs/ipynb_py/convert_to_py_then_test_with_update83.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/convert_to_py_then_test_with_update83.ipynb rename to tests/data/notebooks/inputs/ipynb_py/convert_to_py_then_test_with_update83.ipynb diff --git a/tests/notebooks/ipynb_py/frozen_cell.ipynb b/tests/data/notebooks/inputs/ipynb_py/frozen_cell.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/frozen_cell.ipynb rename to tests/data/notebooks/inputs/ipynb_py/frozen_cell.ipynb diff --git a/tests/notebooks/ipynb_py/jupyter.ipynb b/tests/data/notebooks/inputs/ipynb_py/jupyter.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/jupyter.ipynb rename to tests/data/notebooks/inputs/ipynb_py/jupyter.ipynb diff --git a/tests/notebooks/ipynb_py/jupyter_again.ipynb b/tests/data/notebooks/inputs/ipynb_py/jupyter_again.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/jupyter_again.ipynb rename to tests/data/notebooks/inputs/ipynb_py/jupyter_again.ipynb diff --git a/tests/notebooks/ipynb_py/jupyter_with_raw_cell_in_body.ipynb b/tests/data/notebooks/inputs/ipynb_py/jupyter_with_raw_cell_in_body.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/jupyter_with_raw_cell_in_body.ipynb rename to tests/data/notebooks/inputs/ipynb_py/jupyter_with_raw_cell_in_body.ipynb diff --git a/tests/notebooks/ipynb_py/jupyter_with_raw_cell_on_top.ipynb b/tests/data/notebooks/inputs/ipynb_py/jupyter_with_raw_cell_on_top.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/jupyter_with_raw_cell_on_top.ipynb rename to tests/data/notebooks/inputs/ipynb_py/jupyter_with_raw_cell_on_top.ipynb diff --git a/tests/notebooks/ipynb_py/notebook_with_complex_metadata.ipynb b/tests/data/notebooks/inputs/ipynb_py/notebook_with_complex_metadata.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/notebook_with_complex_metadata.ipynb rename to tests/data/notebooks/inputs/ipynb_py/notebook_with_complex_metadata.ipynb diff --git a/tests/notebooks/ipynb_py/nteract_with_parameter.ipynb b/tests/data/notebooks/inputs/ipynb_py/nteract_with_parameter.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/nteract_with_parameter.ipynb rename to tests/data/notebooks/inputs/ipynb_py/nteract_with_parameter.ipynb diff --git a/tests/notebooks/ipynb_py/plotly_graphs.ipynb b/tests/data/notebooks/inputs/ipynb_py/plotly_graphs.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/plotly_graphs.ipynb rename to tests/data/notebooks/inputs/ipynb_py/plotly_graphs.ipynb diff --git a/tests/notebooks/ipynb_py/sample_rise_notebook_66.ipynb b/tests/data/notebooks/inputs/ipynb_py/sample_rise_notebook_66.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/sample_rise_notebook_66.ipynb rename to tests/data/notebooks/inputs/ipynb_py/sample_rise_notebook_66.ipynb diff --git a/tests/notebooks/ipynb_py/text_outputs_and_images.ipynb b/tests/data/notebooks/inputs/ipynb_py/text_outputs_and_images.ipynb similarity index 100% rename from tests/notebooks/ipynb_py/text_outputs_and_images.ipynb rename to tests/data/notebooks/inputs/ipynb_py/text_outputs_and_images.ipynb diff --git a/tests/notebooks/ipynb_q/kalman_filter_and_visualization.ipynb b/tests/data/notebooks/inputs/ipynb_q/kalman_filter_and_visualization.ipynb similarity index 100% rename from tests/notebooks/ipynb_q/kalman_filter_and_visualization.ipynb rename to tests/data/notebooks/inputs/ipynb_q/kalman_filter_and_visualization.ipynb diff --git a/tests/notebooks/ipynb_robot/simple_robot_notebook.ipynb b/tests/data/notebooks/inputs/ipynb_robot/simple_robot_notebook.ipynb similarity index 100% rename from tests/notebooks/ipynb_robot/simple_robot_notebook.ipynb rename to tests/data/notebooks/inputs/ipynb_robot/simple_robot_notebook.ipynb diff --git a/tests/notebooks/ipynb_rust/evcxr_jupyter_tour.ipynb b/tests/data/notebooks/inputs/ipynb_rust/evcxr_jupyter_tour.ipynb similarity index 100% rename from tests/notebooks/ipynb_rust/evcxr_jupyter_tour.ipynb rename to tests/data/notebooks/inputs/ipynb_rust/evcxr_jupyter_tour.ipynb diff --git a/tests/notebooks/ipynb_sage/sage_print_hello.ipynb b/tests/data/notebooks/inputs/ipynb_sage/sage_print_hello.ipynb similarity index 100% rename from tests/notebooks/ipynb_sage/sage_print_hello.ipynb rename to tests/data/notebooks/inputs/ipynb_sage/sage_print_hello.ipynb diff --git a/tests/notebooks/ipynb_sas/sas.ipynb b/tests/data/notebooks/inputs/ipynb_sas/sas.ipynb similarity index 100% rename from tests/notebooks/ipynb_sas/sas.ipynb rename to tests/data/notebooks/inputs/ipynb_sas/sas.ipynb diff --git a/tests/notebooks/ipynb_scala/simple_scala_notebook.ipynb b/tests/data/notebooks/inputs/ipynb_scala/simple_scala_notebook.ipynb similarity index 100% rename from tests/notebooks/ipynb_scala/simple_scala_notebook.ipynb rename to tests/data/notebooks/inputs/ipynb_scala/simple_scala_notebook.ipynb diff --git a/tests/notebooks/ipynb_scheme/Reference Guide for Calysto Scheme.ipynb b/tests/data/notebooks/inputs/ipynb_scheme/Reference Guide for Calysto Scheme.ipynb similarity index 100% rename from tests/notebooks/ipynb_scheme/Reference Guide for Calysto Scheme.ipynb rename to tests/data/notebooks/inputs/ipynb_scheme/Reference Guide for Calysto Scheme.ipynb diff --git a/tests/notebooks/ipynb_sos/jupytext_replication.ipynb b/tests/data/notebooks/inputs/ipynb_sos/jupytext_replication.ipynb similarity index 100% rename from tests/notebooks/ipynb_sos/jupytext_replication.ipynb rename to tests/data/notebooks/inputs/ipynb_sos/jupytext_replication.ipynb diff --git a/tests/notebooks/ipynb_stata/stata_notebook.ipynb b/tests/data/notebooks/inputs/ipynb_stata/stata_notebook.ipynb similarity index 100% rename from tests/notebooks/ipynb_stata/stata_notebook.ipynb rename to tests/data/notebooks/inputs/ipynb_stata/stata_notebook.ipynb diff --git a/tests/notebooks/ipynb_tcl/tcl_test.ipynb b/tests/data/notebooks/inputs/ipynb_tcl/tcl_test.ipynb similarity index 100% rename from tests/notebooks/ipynb_tcl/tcl_test.ipynb rename to tests/data/notebooks/inputs/ipynb_tcl/tcl_test.ipynb diff --git a/tests/notebooks/ipynb_ts/itypescript.ipynb b/tests/data/notebooks/inputs/ipynb_ts/itypescript.ipynb similarity index 100% rename from tests/notebooks/ipynb_ts/itypescript.ipynb rename to tests/data/notebooks/inputs/ipynb_ts/itypescript.ipynb diff --git a/tests/notebooks/ipynb_wolfram/wolfram.ipynb b/tests/data/notebooks/inputs/ipynb_wolfram/wolfram.ipynb similarity index 100% rename from tests/notebooks/ipynb_wolfram/wolfram.ipynb rename to tests/data/notebooks/inputs/ipynb_wolfram/wolfram.ipynb diff --git a/tests/notebooks/julia/julia_sample_script.jl b/tests/data/notebooks/inputs/julia/julia_sample_script.jl similarity index 100% rename from tests/notebooks/julia/julia_sample_script.jl rename to tests/data/notebooks/inputs/julia/julia_sample_script.jl diff --git a/tests/notebooks/md/jupytext_markdown.md b/tests/data/notebooks/inputs/md/jupytext_markdown.md similarity index 100% rename from tests/notebooks/md/jupytext_markdown.md rename to tests/data/notebooks/inputs/md/jupytext_markdown.md diff --git a/tests/notebooks/md/plain_markdown.md b/tests/data/notebooks/inputs/md/plain_markdown.md similarity index 100% rename from tests/notebooks/md/plain_markdown.md rename to tests/data/notebooks/inputs/md/plain_markdown.md diff --git a/tests/notebooks/percent/hydrogen.py b/tests/data/notebooks/inputs/percent/hydrogen.py similarity index 100% rename from tests/notebooks/percent/hydrogen.py rename to tests/data/notebooks/inputs/percent/hydrogen.py diff --git a/tests/notebooks/ps1/build.ps1 b/tests/data/notebooks/inputs/ps1/build.ps1 similarity index 100% rename from tests/notebooks/ps1/build.ps1 rename to tests/data/notebooks/inputs/ps1/build.ps1 diff --git a/tests/notebooks/python/light_sample.py b/tests/data/notebooks/inputs/python/light_sample.py similarity index 100% rename from tests/notebooks/python/light_sample.py rename to tests/data/notebooks/inputs/python/light_sample.py diff --git a/tests/notebooks/python/python_notebook_sample.py b/tests/data/notebooks/inputs/python/python_notebook_sample.py similarity index 100% rename from tests/notebooks/python/python_notebook_sample.py rename to tests/data/notebooks/inputs/python/python_notebook_sample.py diff --git a/tests/notebooks/sphinx/plot_notebook.py b/tests/data/notebooks/inputs/sphinx/plot_notebook.py similarity index 100% rename from tests/notebooks/sphinx/plot_notebook.py rename to tests/data/notebooks/inputs/sphinx/plot_notebook.py diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/R_sample.ipynb b/tests/data/notebooks/outputs/Rmd_to_ipynb/R_sample.ipynb similarity index 100% rename from tests/notebooks/mirror/Rmd_to_ipynb/R_sample.ipynb rename to tests/data/notebooks/outputs/Rmd_to_ipynb/R_sample.ipynb diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/chunk_options.ipynb b/tests/data/notebooks/outputs/Rmd_to_ipynb/chunk_options.ipynb similarity index 100% rename from tests/notebooks/mirror/Rmd_to_ipynb/chunk_options.ipynb rename to tests/data/notebooks/outputs/Rmd_to_ipynb/chunk_options.ipynb diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb b/tests/data/notebooks/outputs/Rmd_to_ipynb/ioslides.ipynb similarity index 100% rename from tests/notebooks/mirror/Rmd_to_ipynb/ioslides.ipynb rename to tests/data/notebooks/outputs/Rmd_to_ipynb/ioslides.ipynb diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb b/tests/data/notebooks/outputs/Rmd_to_ipynb/knitr-spin.ipynb similarity index 100% rename from tests/notebooks/mirror/Rmd_to_ipynb/knitr-spin.ipynb rename to tests/data/notebooks/outputs/Rmd_to_ipynb/knitr-spin.ipynb diff --git a/tests/notebooks/mirror/Rmd_to_ipynb/markdown.ipynb b/tests/data/notebooks/outputs/Rmd_to_ipynb/markdown.ipynb similarity index 100% rename from tests/notebooks/mirror/Rmd_to_ipynb/markdown.ipynb rename to tests/data/notebooks/outputs/Rmd_to_ipynb/markdown.ipynb diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/Line_breaks_in_LateX_305.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/Line_breaks_in_LateX_305.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/Line_breaks_in_LateX_305.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/Line_breaks_in_LateX_305.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/Notebook with function and cell metadata 164.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook with function and cell metadata 164.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/Notebook with function and cell metadata 164.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook with function and cell metadata 164.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/Notebook with html and latex cells.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook with html and latex cells.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/Notebook with html and latex cells.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook with html and latex cells.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/Notebook with many hash signs.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook with many hash signs.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/Notebook with many hash signs.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook with many hash signs.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/Notebook with metadata and long cells.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook with metadata and long cells.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/Notebook with metadata and long cells.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook with metadata and long cells.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/Notebook_with_R_magic.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook_with_R_magic.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/Notebook_with_R_magic.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook_with_R_magic.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/Notebook_with_more_R_magic_111.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook_with_more_R_magic_111.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/Notebook_with_more_R_magic_111.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/Notebook_with_more_R_magic_111.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/R notebook with invalid cell keys.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/R notebook with invalid cell keys.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/R notebook with invalid cell keys.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/R notebook with invalid cell keys.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/Reference Guide for Calysto Scheme.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/Reference Guide for Calysto Scheme.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/Reference Guide for Calysto Scheme.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/Reference Guide for Calysto Scheme.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/The flavors of raw cells.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/The flavors of raw cells.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/The flavors of raw cells.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/The flavors of raw cells.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/cat_variable.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/cat_variable.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/cat_variable.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/cat_variable.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/coconut_homepage_demo.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/coconut_homepage_demo.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/coconut_homepage_demo.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/coconut_homepage_demo.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/convert_to_py_then_test_with_update83.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/convert_to_py_then_test_with_update83.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/convert_to_py_then_test_with_update83.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/convert_to_py_then_test_with_update83.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/csharp.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/csharp.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/csharp.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/csharp.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/demo_gdl_fbp.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/demo_gdl_fbp.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/demo_gdl_fbp.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/demo_gdl_fbp.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/evcxr_jupyter_tour.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/evcxr_jupyter_tour.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/evcxr_jupyter_tour.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/evcxr_jupyter_tour.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/frozen_cell.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/frozen_cell.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/frozen_cell.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/frozen_cell.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/fsharp.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/fsharp.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/fsharp.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/fsharp.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/gnuplot_notebook.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/gnuplot_notebook.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/gnuplot_notebook.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/gnuplot_notebook.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/haskell_notebook.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/haskell_notebook.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/haskell_notebook.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/haskell_notebook.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/html-demo.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/html-demo.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/html-demo.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/html-demo.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/ijavascript.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/ijavascript.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/ijavascript.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/ijavascript.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/ir_notebook.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/ir_notebook.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/ir_notebook.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/ir_notebook.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/itypescript.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/itypescript.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/itypescript.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/itypescript.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/julia_benchmark_plotly_barchart.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/julia_benchmark_plotly_barchart.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/julia_benchmark_plotly_barchart.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/julia_benchmark_plotly_barchart.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/julia_functional_geometry.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/julia_functional_geometry.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/julia_functional_geometry.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/julia_functional_geometry.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/jupyter.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/jupyter.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/jupyter.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/jupyter.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/jupyter_again.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/jupyter_again.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/jupyter_again.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/jupyter_again.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/jupyter_with_raw_cell_in_body.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/jupyter_with_raw_cell_in_body.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/jupyter_with_raw_cell_in_body.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/jupyter_with_raw_cell_in_body.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/jupyter_with_raw_cell_on_top.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/jupyter_with_raw_cell_on_top.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/jupyter_with_raw_cell_on_top.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/jupyter_with_raw_cell_on_top.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/jupytext_replication.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/jupytext_replication.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/jupytext_replication.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/jupytext_replication.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/kalman_filter_and_visualization.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/kalman_filter_and_visualization.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/kalman_filter_and_visualization.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/kalman_filter_and_visualization.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/maxima_example.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/maxima_example.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/maxima_example.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/maxima_example.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/notebook_with_complex_metadata.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/notebook_with_complex_metadata.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/notebook_with_complex_metadata.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/notebook_with_complex_metadata.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/nteract_with_parameter.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/nteract_with_parameter.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/nteract_with_parameter.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/nteract_with_parameter.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/ocaml_notebook.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/ocaml_notebook.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/ocaml_notebook.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/ocaml_notebook.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/octave_notebook.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/octave_notebook.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/octave_notebook.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/octave_notebook.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/plotly_graphs.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/plotly_graphs.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/plotly_graphs.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/plotly_graphs.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/powershell.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/powershell.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/powershell.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/powershell.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/sage_print_hello.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/sage_print_hello.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/sage_print_hello.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/sage_print_hello.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/sample_bash_notebook.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/sample_bash_notebook.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/sample_bash_notebook.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/sample_bash_notebook.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/sample_rise_notebook_66.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/sample_rise_notebook_66.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/sample_rise_notebook_66.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/sample_rise_notebook_66.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/sas.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/sas.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/sas.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/sas.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/simple-helloworld.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/simple-helloworld.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/simple-helloworld.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/simple-helloworld.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/simple_robot_notebook.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/simple_robot_notebook.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/simple_robot_notebook.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/simple_robot_notebook.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/simple_scala_notebook.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/simple_scala_notebook.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/simple_scala_notebook.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/simple_scala_notebook.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/stata_notebook.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/stata_notebook.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/stata_notebook.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/stata_notebook.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/tailrecursive-factorial.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/tailrecursive-factorial.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/tailrecursive-factorial.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/tailrecursive-factorial.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/tcl_test.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/tcl_test.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/tcl_test.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/tcl_test.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/text_outputs_and_images.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/text_outputs_and_images.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/text_outputs_and_images.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/text_outputs_and_images.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/wolfram.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/wolfram.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/wolfram.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/wolfram.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/xcpp_by_quantstack.Rmd b/tests/data/notebooks/outputs/ipynb_to_Rmd/xcpp_by_quantstack.Rmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_Rmd/xcpp_by_quantstack.Rmd rename to tests/data/notebooks/outputs/ipynb_to_Rmd/xcpp_by_quantstack.Rmd diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/Line_breaks_in_LateX_305.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/Line_breaks_in_LateX_305.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/Line_breaks_in_LateX_305.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/Line_breaks_in_LateX_305.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/Notebook with function and cell metadata 164.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook with function and cell metadata 164.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/Notebook with function and cell metadata 164.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook with function and cell metadata 164.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/Notebook with html and latex cells.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook with html and latex cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/Notebook with html and latex cells.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook with html and latex cells.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/Notebook with many hash signs.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook with many hash signs.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/Notebook with many hash signs.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook with many hash signs.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/Notebook with metadata and long cells.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook with metadata and long cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/Notebook with metadata and long cells.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook with metadata and long cells.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/Notebook_with_R_magic.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook_with_R_magic.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/Notebook_with_R_magic.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook_with_R_magic.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/Notebook_with_more_R_magic_111.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook_with_more_R_magic_111.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/Notebook_with_more_R_magic_111.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/Notebook_with_more_R_magic_111.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/R notebook with invalid cell keys.R b/tests/data/notebooks/outputs/ipynb_to_hydrogen/R notebook with invalid cell keys.R similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/R notebook with invalid cell keys.R rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/R notebook with invalid cell keys.R diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/Reference Guide for Calysto Scheme.ss b/tests/data/notebooks/outputs/ipynb_to_hydrogen/Reference Guide for Calysto Scheme.ss similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/Reference Guide for Calysto Scheme.ss rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/Reference Guide for Calysto Scheme.ss diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/The flavors of raw cells.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/The flavors of raw cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/The flavors of raw cells.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/The flavors of raw cells.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/cat_variable.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/cat_variable.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/cat_variable.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/cat_variable.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/coconut_homepage_demo.coco b/tests/data/notebooks/outputs/ipynb_to_hydrogen/coconut_homepage_demo.coco similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/coconut_homepage_demo.coco rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/coconut_homepage_demo.coco diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/convert_to_py_then_test_with_update83.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/convert_to_py_then_test_with_update83.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/convert_to_py_then_test_with_update83.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/convert_to_py_then_test_with_update83.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/csharp.cs b/tests/data/notebooks/outputs/ipynb_to_hydrogen/csharp.cs similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/csharp.cs rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/csharp.cs diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/demo_gdl_fbp.pro b/tests/data/notebooks/outputs/ipynb_to_hydrogen/demo_gdl_fbp.pro similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/demo_gdl_fbp.pro rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/demo_gdl_fbp.pro diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/evcxr_jupyter_tour.rs b/tests/data/notebooks/outputs/ipynb_to_hydrogen/evcxr_jupyter_tour.rs similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/evcxr_jupyter_tour.rs rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/evcxr_jupyter_tour.rs diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/frozen_cell.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/frozen_cell.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/frozen_cell.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/frozen_cell.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/fsharp.fsx b/tests/data/notebooks/outputs/ipynb_to_hydrogen/fsharp.fsx similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/fsharp.fsx rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/fsharp.fsx diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/gnuplot_notebook.gp b/tests/data/notebooks/outputs/ipynb_to_hydrogen/gnuplot_notebook.gp similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/gnuplot_notebook.gp rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/gnuplot_notebook.gp diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/haskell_notebook.hs b/tests/data/notebooks/outputs/ipynb_to_hydrogen/haskell_notebook.hs similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/haskell_notebook.hs rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/haskell_notebook.hs diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/html-demo.clj b/tests/data/notebooks/outputs/ipynb_to_hydrogen/html-demo.clj similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/html-demo.clj rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/html-demo.clj diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/ijavascript.js b/tests/data/notebooks/outputs/ipynb_to_hydrogen/ijavascript.js similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/ijavascript.js rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/ijavascript.js diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/ir_notebook.R b/tests/data/notebooks/outputs/ipynb_to_hydrogen/ir_notebook.R similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/ir_notebook.R rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/ir_notebook.R diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/itypescript.ts b/tests/data/notebooks/outputs/ipynb_to_hydrogen/itypescript.ts similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/itypescript.ts rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/itypescript.ts diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/julia_benchmark_plotly_barchart.jl b/tests/data/notebooks/outputs/ipynb_to_hydrogen/julia_benchmark_plotly_barchart.jl similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/julia_benchmark_plotly_barchart.jl rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/julia_benchmark_plotly_barchart.jl diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/julia_functional_geometry.jl b/tests/data/notebooks/outputs/ipynb_to_hydrogen/julia_functional_geometry.jl similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/julia_functional_geometry.jl rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/julia_functional_geometry.jl diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/jupyter.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/jupyter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/jupyter.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/jupyter.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/jupyter_again.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/jupyter_again.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/jupyter_again.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/jupyter_again.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/jupyter_with_raw_cell_in_body.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/jupyter_with_raw_cell_in_body.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/jupyter_with_raw_cell_in_body.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/jupyter_with_raw_cell_in_body.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/jupyter_with_raw_cell_on_top.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/jupyter_with_raw_cell_on_top.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/jupyter_with_raw_cell_on_top.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/jupyter_with_raw_cell_on_top.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/jupytext_replication.sos b/tests/data/notebooks/outputs/ipynb_to_hydrogen/jupytext_replication.sos similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/jupytext_replication.sos rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/jupytext_replication.sos diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/kalman_filter_and_visualization.q b/tests/data/notebooks/outputs/ipynb_to_hydrogen/kalman_filter_and_visualization.q similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/kalman_filter_and_visualization.q rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/kalman_filter_and_visualization.q diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/maxima_example.mac b/tests/data/notebooks/outputs/ipynb_to_hydrogen/maxima_example.mac similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/maxima_example.mac rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/maxima_example.mac diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/notebook_with_complex_metadata.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/notebook_with_complex_metadata.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/notebook_with_complex_metadata.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/notebook_with_complex_metadata.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/nteract_with_parameter.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/nteract_with_parameter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/nteract_with_parameter.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/nteract_with_parameter.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/ocaml_notebook.ml b/tests/data/notebooks/outputs/ipynb_to_hydrogen/ocaml_notebook.ml similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/ocaml_notebook.ml rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/ocaml_notebook.ml diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/octave_notebook.m b/tests/data/notebooks/outputs/ipynb_to_hydrogen/octave_notebook.m similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/octave_notebook.m rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/octave_notebook.m diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/plotly_graphs.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/plotly_graphs.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/plotly_graphs.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/plotly_graphs.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/powershell.ps1 b/tests/data/notebooks/outputs/ipynb_to_hydrogen/powershell.ps1 similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/powershell.ps1 rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/powershell.ps1 diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/sage_print_hello.sage b/tests/data/notebooks/outputs/ipynb_to_hydrogen/sage_print_hello.sage similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/sage_print_hello.sage rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/sage_print_hello.sage diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/sample_bash_notebook.sh b/tests/data/notebooks/outputs/ipynb_to_hydrogen/sample_bash_notebook.sh similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/sample_bash_notebook.sh rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/sample_bash_notebook.sh diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/sample_rise_notebook_66.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/sample_rise_notebook_66.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/sample_rise_notebook_66.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/sample_rise_notebook_66.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/sas.sas b/tests/data/notebooks/outputs/ipynb_to_hydrogen/sas.sas similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/sas.sas rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/sas.sas diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/simple-helloworld.java b/tests/data/notebooks/outputs/ipynb_to_hydrogen/simple-helloworld.java similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/simple-helloworld.java rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/simple-helloworld.java diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/simple_robot_notebook.robot b/tests/data/notebooks/outputs/ipynb_to_hydrogen/simple_robot_notebook.robot similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/simple_robot_notebook.robot rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/simple_robot_notebook.robot diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/simple_scala_notebook.scala b/tests/data/notebooks/outputs/ipynb_to_hydrogen/simple_scala_notebook.scala similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/simple_scala_notebook.scala rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/simple_scala_notebook.scala diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/stata_notebook.do b/tests/data/notebooks/outputs/ipynb_to_hydrogen/stata_notebook.do similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/stata_notebook.do rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/stata_notebook.do diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/tailrecursive-factorial.groovy b/tests/data/notebooks/outputs/ipynb_to_hydrogen/tailrecursive-factorial.groovy similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/tailrecursive-factorial.groovy rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/tailrecursive-factorial.groovy diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/tcl_test.tcl b/tests/data/notebooks/outputs/ipynb_to_hydrogen/tcl_test.tcl similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/tcl_test.tcl rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/tcl_test.tcl diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/text_outputs_and_images.py b/tests/data/notebooks/outputs/ipynb_to_hydrogen/text_outputs_and_images.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/text_outputs_and_images.py rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/text_outputs_and_images.py diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/wolfram.wolfram b/tests/data/notebooks/outputs/ipynb_to_hydrogen/wolfram.wolfram similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/wolfram.wolfram rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/wolfram.wolfram diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/xcpp_by_quantstack.cpp b/tests/data/notebooks/outputs/ipynb_to_hydrogen/xcpp_by_quantstack.cpp similarity index 100% rename from tests/notebooks/mirror/ipynb_to_hydrogen/xcpp_by_quantstack.cpp rename to tests/data/notebooks/outputs/ipynb_to_hydrogen/xcpp_by_quantstack.cpp diff --git a/tests/notebooks/mirror/ipynb_to_md/Line_breaks_in_LateX_305.md b/tests/data/notebooks/outputs/ipynb_to_md/Line_breaks_in_LateX_305.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/Line_breaks_in_LateX_305.md rename to tests/data/notebooks/outputs/ipynb_to_md/Line_breaks_in_LateX_305.md diff --git a/tests/notebooks/mirror/ipynb_to_md/Notebook with function and cell metadata 164.md b/tests/data/notebooks/outputs/ipynb_to_md/Notebook with function and cell metadata 164.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/Notebook with function and cell metadata 164.md rename to tests/data/notebooks/outputs/ipynb_to_md/Notebook with function and cell metadata 164.md diff --git a/tests/notebooks/mirror/ipynb_to_md/Notebook with html and latex cells.md b/tests/data/notebooks/outputs/ipynb_to_md/Notebook with html and latex cells.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/Notebook with html and latex cells.md rename to tests/data/notebooks/outputs/ipynb_to_md/Notebook with html and latex cells.md diff --git a/tests/notebooks/mirror/ipynb_to_md/Notebook with many hash signs.md b/tests/data/notebooks/outputs/ipynb_to_md/Notebook with many hash signs.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/Notebook with many hash signs.md rename to tests/data/notebooks/outputs/ipynb_to_md/Notebook with many hash signs.md diff --git a/tests/notebooks/mirror/ipynb_to_md/Notebook with metadata and long cells.md b/tests/data/notebooks/outputs/ipynb_to_md/Notebook with metadata and long cells.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/Notebook with metadata and long cells.md rename to tests/data/notebooks/outputs/ipynb_to_md/Notebook with metadata and long cells.md diff --git a/tests/notebooks/mirror/ipynb_to_md/Notebook_with_R_magic.md b/tests/data/notebooks/outputs/ipynb_to_md/Notebook_with_R_magic.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/Notebook_with_R_magic.md rename to tests/data/notebooks/outputs/ipynb_to_md/Notebook_with_R_magic.md diff --git a/tests/notebooks/mirror/ipynb_to_md/Notebook_with_more_R_magic_111.md b/tests/data/notebooks/outputs/ipynb_to_md/Notebook_with_more_R_magic_111.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/Notebook_with_more_R_magic_111.md rename to tests/data/notebooks/outputs/ipynb_to_md/Notebook_with_more_R_magic_111.md diff --git a/tests/notebooks/mirror/ipynb_to_md/R notebook with invalid cell keys.md b/tests/data/notebooks/outputs/ipynb_to_md/R notebook with invalid cell keys.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/R notebook with invalid cell keys.md rename to tests/data/notebooks/outputs/ipynb_to_md/R notebook with invalid cell keys.md diff --git a/tests/notebooks/mirror/ipynb_to_md/Reference Guide for Calysto Scheme.md b/tests/data/notebooks/outputs/ipynb_to_md/Reference Guide for Calysto Scheme.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/Reference Guide for Calysto Scheme.md rename to tests/data/notebooks/outputs/ipynb_to_md/Reference Guide for Calysto Scheme.md diff --git a/tests/notebooks/mirror/ipynb_to_md/The flavors of raw cells.md b/tests/data/notebooks/outputs/ipynb_to_md/The flavors of raw cells.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/The flavors of raw cells.md rename to tests/data/notebooks/outputs/ipynb_to_md/The flavors of raw cells.md diff --git a/tests/notebooks/mirror/ipynb_to_md/cat_variable.md b/tests/data/notebooks/outputs/ipynb_to_md/cat_variable.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/cat_variable.md rename to tests/data/notebooks/outputs/ipynb_to_md/cat_variable.md diff --git a/tests/notebooks/mirror/ipynb_to_md/coconut_homepage_demo.md b/tests/data/notebooks/outputs/ipynb_to_md/coconut_homepage_demo.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/coconut_homepage_demo.md rename to tests/data/notebooks/outputs/ipynb_to_md/coconut_homepage_demo.md diff --git a/tests/notebooks/mirror/ipynb_to_md/convert_to_py_then_test_with_update83.md b/tests/data/notebooks/outputs/ipynb_to_md/convert_to_py_then_test_with_update83.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/convert_to_py_then_test_with_update83.md rename to tests/data/notebooks/outputs/ipynb_to_md/convert_to_py_then_test_with_update83.md diff --git a/tests/notebooks/mirror/ipynb_to_md/csharp.md b/tests/data/notebooks/outputs/ipynb_to_md/csharp.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/csharp.md rename to tests/data/notebooks/outputs/ipynb_to_md/csharp.md diff --git a/tests/notebooks/mirror/ipynb_to_md/demo_gdl_fbp.md b/tests/data/notebooks/outputs/ipynb_to_md/demo_gdl_fbp.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/demo_gdl_fbp.md rename to tests/data/notebooks/outputs/ipynb_to_md/demo_gdl_fbp.md diff --git a/tests/notebooks/mirror/ipynb_to_md/evcxr_jupyter_tour.md b/tests/data/notebooks/outputs/ipynb_to_md/evcxr_jupyter_tour.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/evcxr_jupyter_tour.md rename to tests/data/notebooks/outputs/ipynb_to_md/evcxr_jupyter_tour.md diff --git a/tests/notebooks/mirror/ipynb_to_md/frozen_cell.md b/tests/data/notebooks/outputs/ipynb_to_md/frozen_cell.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/frozen_cell.md rename to tests/data/notebooks/outputs/ipynb_to_md/frozen_cell.md diff --git a/tests/notebooks/mirror/ipynb_to_md/fsharp.md b/tests/data/notebooks/outputs/ipynb_to_md/fsharp.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/fsharp.md rename to tests/data/notebooks/outputs/ipynb_to_md/fsharp.md diff --git a/tests/notebooks/mirror/ipynb_to_md/gnuplot_notebook.md b/tests/data/notebooks/outputs/ipynb_to_md/gnuplot_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/gnuplot_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_md/gnuplot_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_md/haskell_notebook.md b/tests/data/notebooks/outputs/ipynb_to_md/haskell_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/haskell_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_md/haskell_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_md/html-demo.md b/tests/data/notebooks/outputs/ipynb_to_md/html-demo.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/html-demo.md rename to tests/data/notebooks/outputs/ipynb_to_md/html-demo.md diff --git a/tests/notebooks/mirror/ipynb_to_md/ijavascript.md b/tests/data/notebooks/outputs/ipynb_to_md/ijavascript.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/ijavascript.md rename to tests/data/notebooks/outputs/ipynb_to_md/ijavascript.md diff --git a/tests/notebooks/mirror/ipynb_to_md/ir_notebook.md b/tests/data/notebooks/outputs/ipynb_to_md/ir_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/ir_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_md/ir_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_md/itypescript.md b/tests/data/notebooks/outputs/ipynb_to_md/itypescript.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/itypescript.md rename to tests/data/notebooks/outputs/ipynb_to_md/itypescript.md diff --git a/tests/notebooks/mirror/ipynb_to_md/julia_benchmark_plotly_barchart.md b/tests/data/notebooks/outputs/ipynb_to_md/julia_benchmark_plotly_barchart.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/julia_benchmark_plotly_barchart.md rename to tests/data/notebooks/outputs/ipynb_to_md/julia_benchmark_plotly_barchart.md diff --git a/tests/notebooks/mirror/ipynb_to_md/julia_functional_geometry.md b/tests/data/notebooks/outputs/ipynb_to_md/julia_functional_geometry.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/julia_functional_geometry.md rename to tests/data/notebooks/outputs/ipynb_to_md/julia_functional_geometry.md diff --git a/tests/notebooks/mirror/ipynb_to_md/jupyter.md b/tests/data/notebooks/outputs/ipynb_to_md/jupyter.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/jupyter.md rename to tests/data/notebooks/outputs/ipynb_to_md/jupyter.md diff --git a/tests/notebooks/mirror/ipynb_to_md/jupyter_again.md b/tests/data/notebooks/outputs/ipynb_to_md/jupyter_again.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/jupyter_again.md rename to tests/data/notebooks/outputs/ipynb_to_md/jupyter_again.md diff --git a/tests/notebooks/mirror/ipynb_to_md/jupyter_with_raw_cell_in_body.md b/tests/data/notebooks/outputs/ipynb_to_md/jupyter_with_raw_cell_in_body.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/jupyter_with_raw_cell_in_body.md rename to tests/data/notebooks/outputs/ipynb_to_md/jupyter_with_raw_cell_in_body.md diff --git a/tests/notebooks/mirror/ipynb_to_md/jupyter_with_raw_cell_on_top.md b/tests/data/notebooks/outputs/ipynb_to_md/jupyter_with_raw_cell_on_top.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/jupyter_with_raw_cell_on_top.md rename to tests/data/notebooks/outputs/ipynb_to_md/jupyter_with_raw_cell_on_top.md diff --git a/tests/notebooks/mirror/ipynb_to_md/jupytext_replication.md b/tests/data/notebooks/outputs/ipynb_to_md/jupytext_replication.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/jupytext_replication.md rename to tests/data/notebooks/outputs/ipynb_to_md/jupytext_replication.md diff --git a/tests/notebooks/mirror/ipynb_to_md/kalman_filter_and_visualization.md b/tests/data/notebooks/outputs/ipynb_to_md/kalman_filter_and_visualization.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/kalman_filter_and_visualization.md rename to tests/data/notebooks/outputs/ipynb_to_md/kalman_filter_and_visualization.md diff --git a/tests/notebooks/mirror/ipynb_to_md/maxima_example.md b/tests/data/notebooks/outputs/ipynb_to_md/maxima_example.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/maxima_example.md rename to tests/data/notebooks/outputs/ipynb_to_md/maxima_example.md diff --git a/tests/notebooks/mirror/ipynb_to_md/notebook_with_complex_metadata.md b/tests/data/notebooks/outputs/ipynb_to_md/notebook_with_complex_metadata.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/notebook_with_complex_metadata.md rename to tests/data/notebooks/outputs/ipynb_to_md/notebook_with_complex_metadata.md diff --git a/tests/notebooks/mirror/ipynb_to_md/nteract_with_parameter.md b/tests/data/notebooks/outputs/ipynb_to_md/nteract_with_parameter.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/nteract_with_parameter.md rename to tests/data/notebooks/outputs/ipynb_to_md/nteract_with_parameter.md diff --git a/tests/notebooks/mirror/ipynb_to_md/ocaml_notebook.md b/tests/data/notebooks/outputs/ipynb_to_md/ocaml_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/ocaml_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_md/ocaml_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_md/octave_notebook.md b/tests/data/notebooks/outputs/ipynb_to_md/octave_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/octave_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_md/octave_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_md/plotly_graphs.md b/tests/data/notebooks/outputs/ipynb_to_md/plotly_graphs.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/plotly_graphs.md rename to tests/data/notebooks/outputs/ipynb_to_md/plotly_graphs.md diff --git a/tests/notebooks/mirror/ipynb_to_md/powershell.md b/tests/data/notebooks/outputs/ipynb_to_md/powershell.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/powershell.md rename to tests/data/notebooks/outputs/ipynb_to_md/powershell.md diff --git a/tests/notebooks/mirror/ipynb_to_md/sage_print_hello.md b/tests/data/notebooks/outputs/ipynb_to_md/sage_print_hello.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/sage_print_hello.md rename to tests/data/notebooks/outputs/ipynb_to_md/sage_print_hello.md diff --git a/tests/notebooks/mirror/ipynb_to_md/sample_bash_notebook.md b/tests/data/notebooks/outputs/ipynb_to_md/sample_bash_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/sample_bash_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_md/sample_bash_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_md/sample_rise_notebook_66.md b/tests/data/notebooks/outputs/ipynb_to_md/sample_rise_notebook_66.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/sample_rise_notebook_66.md rename to tests/data/notebooks/outputs/ipynb_to_md/sample_rise_notebook_66.md diff --git a/tests/notebooks/mirror/ipynb_to_md/sas.md b/tests/data/notebooks/outputs/ipynb_to_md/sas.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/sas.md rename to tests/data/notebooks/outputs/ipynb_to_md/sas.md diff --git a/tests/notebooks/mirror/ipynb_to_md/simple-helloworld.md b/tests/data/notebooks/outputs/ipynb_to_md/simple-helloworld.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/simple-helloworld.md rename to tests/data/notebooks/outputs/ipynb_to_md/simple-helloworld.md diff --git a/tests/notebooks/mirror/ipynb_to_md/simple_robot_notebook.md b/tests/data/notebooks/outputs/ipynb_to_md/simple_robot_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/simple_robot_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_md/simple_robot_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_md/simple_scala_notebook.md b/tests/data/notebooks/outputs/ipynb_to_md/simple_scala_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/simple_scala_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_md/simple_scala_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_md/stata_notebook.md b/tests/data/notebooks/outputs/ipynb_to_md/stata_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/stata_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_md/stata_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_md/tailrecursive-factorial.md b/tests/data/notebooks/outputs/ipynb_to_md/tailrecursive-factorial.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/tailrecursive-factorial.md rename to tests/data/notebooks/outputs/ipynb_to_md/tailrecursive-factorial.md diff --git a/tests/notebooks/mirror/ipynb_to_md/tcl_test.md b/tests/data/notebooks/outputs/ipynb_to_md/tcl_test.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/tcl_test.md rename to tests/data/notebooks/outputs/ipynb_to_md/tcl_test.md diff --git a/tests/notebooks/mirror/ipynb_to_md/text_outputs_and_images.md b/tests/data/notebooks/outputs/ipynb_to_md/text_outputs_and_images.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/text_outputs_and_images.md rename to tests/data/notebooks/outputs/ipynb_to_md/text_outputs_and_images.md diff --git a/tests/notebooks/mirror/ipynb_to_md/wolfram.md b/tests/data/notebooks/outputs/ipynb_to_md/wolfram.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/wolfram.md rename to tests/data/notebooks/outputs/ipynb_to_md/wolfram.md diff --git a/tests/notebooks/mirror/ipynb_to_md/xcpp_by_quantstack.md b/tests/data/notebooks/outputs/ipynb_to_md/xcpp_by_quantstack.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_md/xcpp_by_quantstack.md rename to tests/data/notebooks/outputs/ipynb_to_md/xcpp_by_quantstack.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/Line_breaks_in_LateX_305.md b/tests/data/notebooks/outputs/ipynb_to_myst/Line_breaks_in_LateX_305.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/Line_breaks_in_LateX_305.md rename to tests/data/notebooks/outputs/ipynb_to_myst/Line_breaks_in_LateX_305.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/Notebook with function and cell metadata 164.md b/tests/data/notebooks/outputs/ipynb_to_myst/Notebook with function and cell metadata 164.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/Notebook with function and cell metadata 164.md rename to tests/data/notebooks/outputs/ipynb_to_myst/Notebook with function and cell metadata 164.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/Notebook with html and latex cells.md b/tests/data/notebooks/outputs/ipynb_to_myst/Notebook with html and latex cells.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/Notebook with html and latex cells.md rename to tests/data/notebooks/outputs/ipynb_to_myst/Notebook with html and latex cells.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/Notebook with many hash signs.md b/tests/data/notebooks/outputs/ipynb_to_myst/Notebook with many hash signs.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/Notebook with many hash signs.md rename to tests/data/notebooks/outputs/ipynb_to_myst/Notebook with many hash signs.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/Notebook with metadata and long cells.md b/tests/data/notebooks/outputs/ipynb_to_myst/Notebook with metadata and long cells.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/Notebook with metadata and long cells.md rename to tests/data/notebooks/outputs/ipynb_to_myst/Notebook with metadata and long cells.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/Notebook_with_R_magic.md b/tests/data/notebooks/outputs/ipynb_to_myst/Notebook_with_R_magic.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/Notebook_with_R_magic.md rename to tests/data/notebooks/outputs/ipynb_to_myst/Notebook_with_R_magic.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/Notebook_with_more_R_magic_111.md b/tests/data/notebooks/outputs/ipynb_to_myst/Notebook_with_more_R_magic_111.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/Notebook_with_more_R_magic_111.md rename to tests/data/notebooks/outputs/ipynb_to_myst/Notebook_with_more_R_magic_111.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/R notebook with invalid cell keys.md b/tests/data/notebooks/outputs/ipynb_to_myst/R notebook with invalid cell keys.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/R notebook with invalid cell keys.md rename to tests/data/notebooks/outputs/ipynb_to_myst/R notebook with invalid cell keys.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/Reference Guide for Calysto Scheme.md b/tests/data/notebooks/outputs/ipynb_to_myst/Reference Guide for Calysto Scheme.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/Reference Guide for Calysto Scheme.md rename to tests/data/notebooks/outputs/ipynb_to_myst/Reference Guide for Calysto Scheme.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/The flavors of raw cells.md b/tests/data/notebooks/outputs/ipynb_to_myst/The flavors of raw cells.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/The flavors of raw cells.md rename to tests/data/notebooks/outputs/ipynb_to_myst/The flavors of raw cells.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/cat_variable.md b/tests/data/notebooks/outputs/ipynb_to_myst/cat_variable.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/cat_variable.md rename to tests/data/notebooks/outputs/ipynb_to_myst/cat_variable.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/coconut_homepage_demo.md b/tests/data/notebooks/outputs/ipynb_to_myst/coconut_homepage_demo.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/coconut_homepage_demo.md rename to tests/data/notebooks/outputs/ipynb_to_myst/coconut_homepage_demo.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/convert_to_py_then_test_with_update83.md b/tests/data/notebooks/outputs/ipynb_to_myst/convert_to_py_then_test_with_update83.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/convert_to_py_then_test_with_update83.md rename to tests/data/notebooks/outputs/ipynb_to_myst/convert_to_py_then_test_with_update83.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/csharp.md b/tests/data/notebooks/outputs/ipynb_to_myst/csharp.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/csharp.md rename to tests/data/notebooks/outputs/ipynb_to_myst/csharp.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/demo_gdl_fbp.md b/tests/data/notebooks/outputs/ipynb_to_myst/demo_gdl_fbp.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/demo_gdl_fbp.md rename to tests/data/notebooks/outputs/ipynb_to_myst/demo_gdl_fbp.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/evcxr_jupyter_tour.md b/tests/data/notebooks/outputs/ipynb_to_myst/evcxr_jupyter_tour.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/evcxr_jupyter_tour.md rename to tests/data/notebooks/outputs/ipynb_to_myst/evcxr_jupyter_tour.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/frozen_cell.md b/tests/data/notebooks/outputs/ipynb_to_myst/frozen_cell.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/frozen_cell.md rename to tests/data/notebooks/outputs/ipynb_to_myst/frozen_cell.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/fsharp.md b/tests/data/notebooks/outputs/ipynb_to_myst/fsharp.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/fsharp.md rename to tests/data/notebooks/outputs/ipynb_to_myst/fsharp.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/gnuplot_notebook.md b/tests/data/notebooks/outputs/ipynb_to_myst/gnuplot_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/gnuplot_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_myst/gnuplot_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/haskell_notebook.md b/tests/data/notebooks/outputs/ipynb_to_myst/haskell_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/haskell_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_myst/haskell_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/ijavascript.md b/tests/data/notebooks/outputs/ipynb_to_myst/ijavascript.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/ijavascript.md rename to tests/data/notebooks/outputs/ipynb_to_myst/ijavascript.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/ir_notebook.md b/tests/data/notebooks/outputs/ipynb_to_myst/ir_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/ir_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_myst/ir_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/itypescript.md b/tests/data/notebooks/outputs/ipynb_to_myst/itypescript.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/itypescript.md rename to tests/data/notebooks/outputs/ipynb_to_myst/itypescript.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/julia_benchmark_plotly_barchart.md b/tests/data/notebooks/outputs/ipynb_to_myst/julia_benchmark_plotly_barchart.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/julia_benchmark_plotly_barchart.md rename to tests/data/notebooks/outputs/ipynb_to_myst/julia_benchmark_plotly_barchart.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/jupyter.md b/tests/data/notebooks/outputs/ipynb_to_myst/jupyter.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/jupyter.md rename to tests/data/notebooks/outputs/ipynb_to_myst/jupyter.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/jupyter_again.md b/tests/data/notebooks/outputs/ipynb_to_myst/jupyter_again.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/jupyter_again.md rename to tests/data/notebooks/outputs/ipynb_to_myst/jupyter_again.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/jupyter_with_raw_cell_in_body.md b/tests/data/notebooks/outputs/ipynb_to_myst/jupyter_with_raw_cell_in_body.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/jupyter_with_raw_cell_in_body.md rename to tests/data/notebooks/outputs/ipynb_to_myst/jupyter_with_raw_cell_in_body.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/jupyter_with_raw_cell_on_top.md b/tests/data/notebooks/outputs/ipynb_to_myst/jupyter_with_raw_cell_on_top.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/jupyter_with_raw_cell_on_top.md rename to tests/data/notebooks/outputs/ipynb_to_myst/jupyter_with_raw_cell_on_top.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/jupytext_replication.md b/tests/data/notebooks/outputs/ipynb_to_myst/jupytext_replication.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/jupytext_replication.md rename to tests/data/notebooks/outputs/ipynb_to_myst/jupytext_replication.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/kalman_filter_and_visualization.md b/tests/data/notebooks/outputs/ipynb_to_myst/kalman_filter_and_visualization.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/kalman_filter_and_visualization.md rename to tests/data/notebooks/outputs/ipynb_to_myst/kalman_filter_and_visualization.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/maxima_example.md b/tests/data/notebooks/outputs/ipynb_to_myst/maxima_example.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/maxima_example.md rename to tests/data/notebooks/outputs/ipynb_to_myst/maxima_example.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/notebook_with_complex_metadata.md b/tests/data/notebooks/outputs/ipynb_to_myst/notebook_with_complex_metadata.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/notebook_with_complex_metadata.md rename to tests/data/notebooks/outputs/ipynb_to_myst/notebook_with_complex_metadata.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/nteract_with_parameter.md b/tests/data/notebooks/outputs/ipynb_to_myst/nteract_with_parameter.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/nteract_with_parameter.md rename to tests/data/notebooks/outputs/ipynb_to_myst/nteract_with_parameter.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/ocaml_notebook.md b/tests/data/notebooks/outputs/ipynb_to_myst/ocaml_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/ocaml_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_myst/ocaml_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/octave_notebook.md b/tests/data/notebooks/outputs/ipynb_to_myst/octave_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/octave_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_myst/octave_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/plotly_graphs.md b/tests/data/notebooks/outputs/ipynb_to_myst/plotly_graphs.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/plotly_graphs.md rename to tests/data/notebooks/outputs/ipynb_to_myst/plotly_graphs.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/powershell.md b/tests/data/notebooks/outputs/ipynb_to_myst/powershell.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/powershell.md rename to tests/data/notebooks/outputs/ipynb_to_myst/powershell.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/sage_print_hello.md b/tests/data/notebooks/outputs/ipynb_to_myst/sage_print_hello.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/sage_print_hello.md rename to tests/data/notebooks/outputs/ipynb_to_myst/sage_print_hello.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/sample_bash_notebook.md b/tests/data/notebooks/outputs/ipynb_to_myst/sample_bash_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/sample_bash_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_myst/sample_bash_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/sample_rise_notebook_66.md b/tests/data/notebooks/outputs/ipynb_to_myst/sample_rise_notebook_66.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/sample_rise_notebook_66.md rename to tests/data/notebooks/outputs/ipynb_to_myst/sample_rise_notebook_66.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/sas.md b/tests/data/notebooks/outputs/ipynb_to_myst/sas.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/sas.md rename to tests/data/notebooks/outputs/ipynb_to_myst/sas.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/simple-helloworld.md b/tests/data/notebooks/outputs/ipynb_to_myst/simple-helloworld.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/simple-helloworld.md rename to tests/data/notebooks/outputs/ipynb_to_myst/simple-helloworld.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/simple_robot_notebook.md b/tests/data/notebooks/outputs/ipynb_to_myst/simple_robot_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/simple_robot_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_myst/simple_robot_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/simple_scala_notebook.md b/tests/data/notebooks/outputs/ipynb_to_myst/simple_scala_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/simple_scala_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_myst/simple_scala_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/stata_notebook.md b/tests/data/notebooks/outputs/ipynb_to_myst/stata_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/stata_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_myst/stata_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/tailrecursive-factorial.md b/tests/data/notebooks/outputs/ipynb_to_myst/tailrecursive-factorial.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/tailrecursive-factorial.md rename to tests/data/notebooks/outputs/ipynb_to_myst/tailrecursive-factorial.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/tcl_test.md b/tests/data/notebooks/outputs/ipynb_to_myst/tcl_test.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/tcl_test.md rename to tests/data/notebooks/outputs/ipynb_to_myst/tcl_test.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/text_outputs_and_images.md b/tests/data/notebooks/outputs/ipynb_to_myst/text_outputs_and_images.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/text_outputs_and_images.md rename to tests/data/notebooks/outputs/ipynb_to_myst/text_outputs_and_images.md diff --git a/tests/notebooks/mirror/ipynb_to_myst/wolfram.md b/tests/data/notebooks/outputs/ipynb_to_myst/wolfram.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_myst/wolfram.md rename to tests/data/notebooks/outputs/ipynb_to_myst/wolfram.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/Notebook_with_R_magic.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/Notebook_with_R_magic.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/Notebook_with_R_magic.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/Notebook_with_R_magic.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/Notebook_with_more_R_magic_111.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/Notebook_with_more_R_magic_111.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/Notebook_with_more_R_magic_111.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/Notebook_with_more_R_magic_111.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/cat_variable.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/cat_variable.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/cat_variable.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/cat_variable.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/convert_to_py_then_test_with_update83.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/convert_to_py_then_test_with_update83.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/convert_to_py_then_test_with_update83.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/convert_to_py_then_test_with_update83.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/frozen_cell.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/frozen_cell.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/frozen_cell.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/frozen_cell.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/ir_notebook.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/ir_notebook.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/ir_notebook.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/ir_notebook.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/julia_benchmark_plotly_barchart.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/julia_benchmark_plotly_barchart.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/julia_benchmark_plotly_barchart.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/julia_benchmark_plotly_barchart.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/jupyter.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/jupyter.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/jupyter.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/jupyter.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/jupyter_again.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/jupyter_again.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/jupyter_again.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/jupyter_again.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/jupyter_with_raw_cell_in_body.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/jupyter_with_raw_cell_in_body.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/jupyter_with_raw_cell_in_body.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/jupyter_with_raw_cell_in_body.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/jupyter_with_raw_cell_on_top.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/jupyter_with_raw_cell_on_top.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/jupyter_with_raw_cell_on_top.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/jupyter_with_raw_cell_on_top.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/notebook_with_complex_metadata.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/notebook_with_complex_metadata.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/notebook_with_complex_metadata.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/notebook_with_complex_metadata.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/nteract_with_parameter.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/nteract_with_parameter.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/nteract_with_parameter.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/nteract_with_parameter.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/plotly_graphs.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/plotly_graphs.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/plotly_graphs.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/plotly_graphs.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/sample_rise_notebook_66.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/sample_rise_notebook_66.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/sample_rise_notebook_66.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/sample_rise_notebook_66.md diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/text_outputs_and_images.md b/tests/data/notebooks/outputs/ipynb_to_pandoc/text_outputs_and_images.md similarity index 100% rename from tests/notebooks/mirror/ipynb_to_pandoc/text_outputs_and_images.md rename to tests/data/notebooks/outputs/ipynb_to_pandoc/text_outputs_and_images.md diff --git a/tests/notebooks/mirror/ipynb_to_percent/Line_breaks_in_LateX_305.py b/tests/data/notebooks/outputs/ipynb_to_percent/Line_breaks_in_LateX_305.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/Line_breaks_in_LateX_305.py rename to tests/data/notebooks/outputs/ipynb_to_percent/Line_breaks_in_LateX_305.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/Notebook with function and cell metadata 164.py b/tests/data/notebooks/outputs/ipynb_to_percent/Notebook with function and cell metadata 164.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/Notebook with function and cell metadata 164.py rename to tests/data/notebooks/outputs/ipynb_to_percent/Notebook with function and cell metadata 164.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/Notebook with html and latex cells.py b/tests/data/notebooks/outputs/ipynb_to_percent/Notebook with html and latex cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/Notebook with html and latex cells.py rename to tests/data/notebooks/outputs/ipynb_to_percent/Notebook with html and latex cells.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/Notebook with many hash signs.py b/tests/data/notebooks/outputs/ipynb_to_percent/Notebook with many hash signs.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/Notebook with many hash signs.py rename to tests/data/notebooks/outputs/ipynb_to_percent/Notebook with many hash signs.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/Notebook with metadata and long cells.py b/tests/data/notebooks/outputs/ipynb_to_percent/Notebook with metadata and long cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/Notebook with metadata and long cells.py rename to tests/data/notebooks/outputs/ipynb_to_percent/Notebook with metadata and long cells.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/Notebook_with_R_magic.py b/tests/data/notebooks/outputs/ipynb_to_percent/Notebook_with_R_magic.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/Notebook_with_R_magic.py rename to tests/data/notebooks/outputs/ipynb_to_percent/Notebook_with_R_magic.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/Notebook_with_more_R_magic_111.py b/tests/data/notebooks/outputs/ipynb_to_percent/Notebook_with_more_R_magic_111.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/Notebook_with_more_R_magic_111.py rename to tests/data/notebooks/outputs/ipynb_to_percent/Notebook_with_more_R_magic_111.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/R notebook with invalid cell keys.R b/tests/data/notebooks/outputs/ipynb_to_percent/R notebook with invalid cell keys.R similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/R notebook with invalid cell keys.R rename to tests/data/notebooks/outputs/ipynb_to_percent/R notebook with invalid cell keys.R diff --git a/tests/notebooks/mirror/ipynb_to_percent/R notebook with invalid cell keys.low.r b/tests/data/notebooks/outputs/ipynb_to_percent/R notebook with invalid cell keys.low.r similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/R notebook with invalid cell keys.low.r rename to tests/data/notebooks/outputs/ipynb_to_percent/R notebook with invalid cell keys.low.r diff --git a/tests/notebooks/mirror/ipynb_to_percent/Reference Guide for Calysto Scheme.scm b/tests/data/notebooks/outputs/ipynb_to_percent/Reference Guide for Calysto Scheme.scm similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/Reference Guide for Calysto Scheme.scm rename to tests/data/notebooks/outputs/ipynb_to_percent/Reference Guide for Calysto Scheme.scm diff --git a/tests/notebooks/mirror/ipynb_to_percent/Reference Guide for Calysto Scheme.ss b/tests/data/notebooks/outputs/ipynb_to_percent/Reference Guide for Calysto Scheme.ss similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/Reference Guide for Calysto Scheme.ss rename to tests/data/notebooks/outputs/ipynb_to_percent/Reference Guide for Calysto Scheme.ss diff --git a/tests/notebooks/mirror/ipynb_to_percent/The flavors of raw cells.py b/tests/data/notebooks/outputs/ipynb_to_percent/The flavors of raw cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/The flavors of raw cells.py rename to tests/data/notebooks/outputs/ipynb_to_percent/The flavors of raw cells.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/cat_variable.py b/tests/data/notebooks/outputs/ipynb_to_percent/cat_variable.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/cat_variable.py rename to tests/data/notebooks/outputs/ipynb_to_percent/cat_variable.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/coconut_homepage_demo.coco b/tests/data/notebooks/outputs/ipynb_to_percent/coconut_homepage_demo.coco similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/coconut_homepage_demo.coco rename to tests/data/notebooks/outputs/ipynb_to_percent/coconut_homepage_demo.coco diff --git a/tests/notebooks/mirror/ipynb_to_percent/convert_to_py_then_test_with_update83.py b/tests/data/notebooks/outputs/ipynb_to_percent/convert_to_py_then_test_with_update83.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/convert_to_py_then_test_with_update83.py rename to tests/data/notebooks/outputs/ipynb_to_percent/convert_to_py_then_test_with_update83.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/csharp.cs b/tests/data/notebooks/outputs/ipynb_to_percent/csharp.cs similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/csharp.cs rename to tests/data/notebooks/outputs/ipynb_to_percent/csharp.cs diff --git a/tests/notebooks/mirror/ipynb_to_percent/demo_gdl_fbp.pro b/tests/data/notebooks/outputs/ipynb_to_percent/demo_gdl_fbp.pro similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/demo_gdl_fbp.pro rename to tests/data/notebooks/outputs/ipynb_to_percent/demo_gdl_fbp.pro diff --git a/tests/notebooks/mirror/ipynb_to_percent/evcxr_jupyter_tour.rs b/tests/data/notebooks/outputs/ipynb_to_percent/evcxr_jupyter_tour.rs similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/evcxr_jupyter_tour.rs rename to tests/data/notebooks/outputs/ipynb_to_percent/evcxr_jupyter_tour.rs diff --git a/tests/notebooks/mirror/ipynb_to_percent/frozen_cell.py b/tests/data/notebooks/outputs/ipynb_to_percent/frozen_cell.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/frozen_cell.py rename to tests/data/notebooks/outputs/ipynb_to_percent/frozen_cell.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/fsharp.fsx b/tests/data/notebooks/outputs/ipynb_to_percent/fsharp.fsx similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/fsharp.fsx rename to tests/data/notebooks/outputs/ipynb_to_percent/fsharp.fsx diff --git a/tests/notebooks/mirror/ipynb_to_percent/gnuplot_notebook.gp b/tests/data/notebooks/outputs/ipynb_to_percent/gnuplot_notebook.gp similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/gnuplot_notebook.gp rename to tests/data/notebooks/outputs/ipynb_to_percent/gnuplot_notebook.gp diff --git a/tests/notebooks/mirror/ipynb_to_percent/haskell_notebook.hs b/tests/data/notebooks/outputs/ipynb_to_percent/haskell_notebook.hs similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/haskell_notebook.hs rename to tests/data/notebooks/outputs/ipynb_to_percent/haskell_notebook.hs diff --git a/tests/notebooks/mirror/ipynb_to_percent/html-demo.clj b/tests/data/notebooks/outputs/ipynb_to_percent/html-demo.clj similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/html-demo.clj rename to tests/data/notebooks/outputs/ipynb_to_percent/html-demo.clj diff --git a/tests/notebooks/mirror/ipynb_to_percent/ijavascript.js b/tests/data/notebooks/outputs/ipynb_to_percent/ijavascript.js similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/ijavascript.js rename to tests/data/notebooks/outputs/ipynb_to_percent/ijavascript.js diff --git a/tests/notebooks/mirror/ipynb_to_percent/ir_notebook.R b/tests/data/notebooks/outputs/ipynb_to_percent/ir_notebook.R similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/ir_notebook.R rename to tests/data/notebooks/outputs/ipynb_to_percent/ir_notebook.R diff --git a/tests/notebooks/mirror/ipynb_to_percent/ir_notebook.low.r b/tests/data/notebooks/outputs/ipynb_to_percent/ir_notebook.low.r similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/ir_notebook.low.r rename to tests/data/notebooks/outputs/ipynb_to_percent/ir_notebook.low.r diff --git a/tests/notebooks/mirror/ipynb_to_percent/itypescript.ts b/tests/data/notebooks/outputs/ipynb_to_percent/itypescript.ts similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/itypescript.ts rename to tests/data/notebooks/outputs/ipynb_to_percent/itypescript.ts diff --git a/tests/notebooks/mirror/ipynb_to_percent/julia_benchmark_plotly_barchart.jl b/tests/data/notebooks/outputs/ipynb_to_percent/julia_benchmark_plotly_barchart.jl similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/julia_benchmark_plotly_barchart.jl rename to tests/data/notebooks/outputs/ipynb_to_percent/julia_benchmark_plotly_barchart.jl diff --git a/tests/notebooks/mirror/ipynb_to_percent/julia_functional_geometry.jl b/tests/data/notebooks/outputs/ipynb_to_percent/julia_functional_geometry.jl similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/julia_functional_geometry.jl rename to tests/data/notebooks/outputs/ipynb_to_percent/julia_functional_geometry.jl diff --git a/tests/notebooks/mirror/ipynb_to_percent/jupyter.py b/tests/data/notebooks/outputs/ipynb_to_percent/jupyter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/jupyter.py rename to tests/data/notebooks/outputs/ipynb_to_percent/jupyter.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/jupyter_again.py b/tests/data/notebooks/outputs/ipynb_to_percent/jupyter_again.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/jupyter_again.py rename to tests/data/notebooks/outputs/ipynb_to_percent/jupyter_again.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/jupyter_with_raw_cell_in_body.py b/tests/data/notebooks/outputs/ipynb_to_percent/jupyter_with_raw_cell_in_body.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/jupyter_with_raw_cell_in_body.py rename to tests/data/notebooks/outputs/ipynb_to_percent/jupyter_with_raw_cell_in_body.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/jupyter_with_raw_cell_on_top.py b/tests/data/notebooks/outputs/ipynb_to_percent/jupyter_with_raw_cell_on_top.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/jupyter_with_raw_cell_on_top.py rename to tests/data/notebooks/outputs/ipynb_to_percent/jupyter_with_raw_cell_on_top.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/jupytext_replication.sos b/tests/data/notebooks/outputs/ipynb_to_percent/jupytext_replication.sos similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/jupytext_replication.sos rename to tests/data/notebooks/outputs/ipynb_to_percent/jupytext_replication.sos diff --git a/tests/notebooks/mirror/ipynb_to_percent/kalman_filter_and_visualization.q b/tests/data/notebooks/outputs/ipynb_to_percent/kalman_filter_and_visualization.q similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/kalman_filter_and_visualization.q rename to tests/data/notebooks/outputs/ipynb_to_percent/kalman_filter_and_visualization.q diff --git a/tests/notebooks/mirror/ipynb_to_percent/maxima_example.mac b/tests/data/notebooks/outputs/ipynb_to_percent/maxima_example.mac similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/maxima_example.mac rename to tests/data/notebooks/outputs/ipynb_to_percent/maxima_example.mac diff --git a/tests/notebooks/mirror/ipynb_to_percent/notebook_with_complex_metadata.py b/tests/data/notebooks/outputs/ipynb_to_percent/notebook_with_complex_metadata.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/notebook_with_complex_metadata.py rename to tests/data/notebooks/outputs/ipynb_to_percent/notebook_with_complex_metadata.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/nteract_with_parameter.py b/tests/data/notebooks/outputs/ipynb_to_percent/nteract_with_parameter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/nteract_with_parameter.py rename to tests/data/notebooks/outputs/ipynb_to_percent/nteract_with_parameter.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/ocaml_notebook.ml b/tests/data/notebooks/outputs/ipynb_to_percent/ocaml_notebook.ml similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/ocaml_notebook.ml rename to tests/data/notebooks/outputs/ipynb_to_percent/ocaml_notebook.ml diff --git a/tests/notebooks/mirror/ipynb_to_percent/octave_notebook.m b/tests/data/notebooks/outputs/ipynb_to_percent/octave_notebook.m similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/octave_notebook.m rename to tests/data/notebooks/outputs/ipynb_to_percent/octave_notebook.m diff --git a/tests/notebooks/mirror/ipynb_to_percent/plotly_graphs.py b/tests/data/notebooks/outputs/ipynb_to_percent/plotly_graphs.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/plotly_graphs.py rename to tests/data/notebooks/outputs/ipynb_to_percent/plotly_graphs.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/powershell.ps1 b/tests/data/notebooks/outputs/ipynb_to_percent/powershell.ps1 similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/powershell.ps1 rename to tests/data/notebooks/outputs/ipynb_to_percent/powershell.ps1 diff --git a/tests/notebooks/mirror/ipynb_to_percent/sage_print_hello.sage b/tests/data/notebooks/outputs/ipynb_to_percent/sage_print_hello.sage similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/sage_print_hello.sage rename to tests/data/notebooks/outputs/ipynb_to_percent/sage_print_hello.sage diff --git a/tests/notebooks/mirror/ipynb_to_percent/sample_bash_notebook.sh b/tests/data/notebooks/outputs/ipynb_to_percent/sample_bash_notebook.sh similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/sample_bash_notebook.sh rename to tests/data/notebooks/outputs/ipynb_to_percent/sample_bash_notebook.sh diff --git a/tests/notebooks/mirror/ipynb_to_percent/sample_rise_notebook_66.py b/tests/data/notebooks/outputs/ipynb_to_percent/sample_rise_notebook_66.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/sample_rise_notebook_66.py rename to tests/data/notebooks/outputs/ipynb_to_percent/sample_rise_notebook_66.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/sas.sas b/tests/data/notebooks/outputs/ipynb_to_percent/sas.sas similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/sas.sas rename to tests/data/notebooks/outputs/ipynb_to_percent/sas.sas diff --git a/tests/notebooks/mirror/ipynb_to_percent/simple-helloworld.java b/tests/data/notebooks/outputs/ipynb_to_percent/simple-helloworld.java similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/simple-helloworld.java rename to tests/data/notebooks/outputs/ipynb_to_percent/simple-helloworld.java diff --git a/tests/notebooks/mirror/ipynb_to_percent/simple_robot_notebook.robot b/tests/data/notebooks/outputs/ipynb_to_percent/simple_robot_notebook.robot similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/simple_robot_notebook.robot rename to tests/data/notebooks/outputs/ipynb_to_percent/simple_robot_notebook.robot diff --git a/tests/notebooks/mirror/ipynb_to_percent/simple_scala_notebook.scala b/tests/data/notebooks/outputs/ipynb_to_percent/simple_scala_notebook.scala similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/simple_scala_notebook.scala rename to tests/data/notebooks/outputs/ipynb_to_percent/simple_scala_notebook.scala diff --git a/tests/notebooks/mirror/ipynb_to_percent/stata_notebook.do b/tests/data/notebooks/outputs/ipynb_to_percent/stata_notebook.do similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/stata_notebook.do rename to tests/data/notebooks/outputs/ipynb_to_percent/stata_notebook.do diff --git a/tests/notebooks/mirror/ipynb_to_percent/tailrecursive-factorial.groovy b/tests/data/notebooks/outputs/ipynb_to_percent/tailrecursive-factorial.groovy similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/tailrecursive-factorial.groovy rename to tests/data/notebooks/outputs/ipynb_to_percent/tailrecursive-factorial.groovy diff --git a/tests/notebooks/mirror/ipynb_to_percent/tcl_test.tcl b/tests/data/notebooks/outputs/ipynb_to_percent/tcl_test.tcl similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/tcl_test.tcl rename to tests/data/notebooks/outputs/ipynb_to_percent/tcl_test.tcl diff --git a/tests/notebooks/mirror/ipynb_to_percent/text_outputs_and_images.py b/tests/data/notebooks/outputs/ipynb_to_percent/text_outputs_and_images.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/text_outputs_and_images.py rename to tests/data/notebooks/outputs/ipynb_to_percent/text_outputs_and_images.py diff --git a/tests/notebooks/mirror/ipynb_to_percent/wolfram.wolfram b/tests/data/notebooks/outputs/ipynb_to_percent/wolfram.wolfram similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/wolfram.wolfram rename to tests/data/notebooks/outputs/ipynb_to_percent/wolfram.wolfram diff --git a/tests/notebooks/mirror/ipynb_to_percent/xcpp_by_quantstack.cpp b/tests/data/notebooks/outputs/ipynb_to_percent/xcpp_by_quantstack.cpp similarity index 100% rename from tests/notebooks/mirror/ipynb_to_percent/xcpp_by_quantstack.cpp rename to tests/data/notebooks/outputs/ipynb_to_percent/xcpp_by_quantstack.cpp diff --git a/tests/notebooks/mirror/ipynb_to_quarto/Notebook_with_more_R_magic_111.qmd b/tests/data/notebooks/outputs/ipynb_to_quarto/Notebook_with_more_R_magic_111.qmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_quarto/Notebook_with_more_R_magic_111.qmd rename to tests/data/notebooks/outputs/ipynb_to_quarto/Notebook_with_more_R_magic_111.qmd diff --git a/tests/notebooks/mirror/ipynb_to_quarto/cat_variable.qmd b/tests/data/notebooks/outputs/ipynb_to_quarto/cat_variable.qmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_quarto/cat_variable.qmd rename to tests/data/notebooks/outputs/ipynb_to_quarto/cat_variable.qmd diff --git a/tests/notebooks/mirror/ipynb_to_quarto/frozen_cell.qmd b/tests/data/notebooks/outputs/ipynb_to_quarto/frozen_cell.qmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_quarto/frozen_cell.qmd rename to tests/data/notebooks/outputs/ipynb_to_quarto/frozen_cell.qmd diff --git a/tests/notebooks/mirror/ipynb_to_quarto/julia_benchmark_plotly_barchart.qmd b/tests/data/notebooks/outputs/ipynb_to_quarto/julia_benchmark_plotly_barchart.qmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_quarto/julia_benchmark_plotly_barchart.qmd rename to tests/data/notebooks/outputs/ipynb_to_quarto/julia_benchmark_plotly_barchart.qmd diff --git a/tests/notebooks/mirror/ipynb_to_quarto/jupyter_again.qmd b/tests/data/notebooks/outputs/ipynb_to_quarto/jupyter_again.qmd similarity index 100% rename from tests/notebooks/mirror/ipynb_to_quarto/jupyter_again.qmd rename to tests/data/notebooks/outputs/ipynb_to_quarto/jupyter_again.qmd diff --git a/tests/notebooks/mirror/ipynb_to_script/Line_breaks_in_LateX_305.py b/tests/data/notebooks/outputs/ipynb_to_script/Line_breaks_in_LateX_305.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/Line_breaks_in_LateX_305.py rename to tests/data/notebooks/outputs/ipynb_to_script/Line_breaks_in_LateX_305.py diff --git a/tests/notebooks/mirror/ipynb_to_script/Notebook with function and cell metadata 164.py b/tests/data/notebooks/outputs/ipynb_to_script/Notebook with function and cell metadata 164.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/Notebook with function and cell metadata 164.py rename to tests/data/notebooks/outputs/ipynb_to_script/Notebook with function and cell metadata 164.py diff --git a/tests/notebooks/mirror/ipynb_to_script/Notebook with html and latex cells.py b/tests/data/notebooks/outputs/ipynb_to_script/Notebook with html and latex cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/Notebook with html and latex cells.py rename to tests/data/notebooks/outputs/ipynb_to_script/Notebook with html and latex cells.py diff --git a/tests/notebooks/mirror/ipynb_to_script/Notebook with metadata and long cells.py b/tests/data/notebooks/outputs/ipynb_to_script/Notebook with metadata and long cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/Notebook with metadata and long cells.py rename to tests/data/notebooks/outputs/ipynb_to_script/Notebook with metadata and long cells.py diff --git a/tests/notebooks/mirror/ipynb_to_script/Notebook_with_R_magic.py b/tests/data/notebooks/outputs/ipynb_to_script/Notebook_with_R_magic.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/Notebook_with_R_magic.py rename to tests/data/notebooks/outputs/ipynb_to_script/Notebook_with_R_magic.py diff --git a/tests/notebooks/mirror/ipynb_to_script/Notebook_with_more_R_magic_111.py b/tests/data/notebooks/outputs/ipynb_to_script/Notebook_with_more_R_magic_111.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/Notebook_with_more_R_magic_111.py rename to tests/data/notebooks/outputs/ipynb_to_script/Notebook_with_more_R_magic_111.py diff --git a/tests/notebooks/mirror/ipynb_to_script/R notebook with invalid cell keys.R b/tests/data/notebooks/outputs/ipynb_to_script/R notebook with invalid cell keys.R similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/R notebook with invalid cell keys.R rename to tests/data/notebooks/outputs/ipynb_to_script/R notebook with invalid cell keys.R diff --git a/tests/notebooks/mirror/ipynb_to_script/R notebook with invalid cell keys.low.r b/tests/data/notebooks/outputs/ipynb_to_script/R notebook with invalid cell keys.low.r similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/R notebook with invalid cell keys.low.r rename to tests/data/notebooks/outputs/ipynb_to_script/R notebook with invalid cell keys.low.r diff --git a/tests/notebooks/mirror/ipynb_to_script/Reference Guide for Calysto Scheme.scm b/tests/data/notebooks/outputs/ipynb_to_script/Reference Guide for Calysto Scheme.scm similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/Reference Guide for Calysto Scheme.scm rename to tests/data/notebooks/outputs/ipynb_to_script/Reference Guide for Calysto Scheme.scm diff --git a/tests/notebooks/mirror/ipynb_to_script/Reference Guide for Calysto Scheme.ss b/tests/data/notebooks/outputs/ipynb_to_script/Reference Guide for Calysto Scheme.ss similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/Reference Guide for Calysto Scheme.ss rename to tests/data/notebooks/outputs/ipynb_to_script/Reference Guide for Calysto Scheme.ss diff --git a/tests/notebooks/mirror/ipynb_to_script/The flavors of raw cells.py b/tests/data/notebooks/outputs/ipynb_to_script/The flavors of raw cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/The flavors of raw cells.py rename to tests/data/notebooks/outputs/ipynb_to_script/The flavors of raw cells.py diff --git a/tests/notebooks/mirror/ipynb_to_script/cat_variable.py b/tests/data/notebooks/outputs/ipynb_to_script/cat_variable.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/cat_variable.py rename to tests/data/notebooks/outputs/ipynb_to_script/cat_variable.py diff --git a/tests/notebooks/mirror/ipynb_to_script/coconut_homepage_demo.coco b/tests/data/notebooks/outputs/ipynb_to_script/coconut_homepage_demo.coco similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/coconut_homepage_demo.coco rename to tests/data/notebooks/outputs/ipynb_to_script/coconut_homepage_demo.coco diff --git a/tests/notebooks/mirror/ipynb_to_script/convert_to_py_then_test_with_update83.py b/tests/data/notebooks/outputs/ipynb_to_script/convert_to_py_then_test_with_update83.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/convert_to_py_then_test_with_update83.py rename to tests/data/notebooks/outputs/ipynb_to_script/convert_to_py_then_test_with_update83.py diff --git a/tests/notebooks/mirror/ipynb_to_script/csharp.cs b/tests/data/notebooks/outputs/ipynb_to_script/csharp.cs similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/csharp.cs rename to tests/data/notebooks/outputs/ipynb_to_script/csharp.cs diff --git a/tests/notebooks/mirror/ipynb_to_script/demo_gdl_fbp.pro b/tests/data/notebooks/outputs/ipynb_to_script/demo_gdl_fbp.pro similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/demo_gdl_fbp.pro rename to tests/data/notebooks/outputs/ipynb_to_script/demo_gdl_fbp.pro diff --git a/tests/notebooks/mirror/ipynb_to_script/evcxr_jupyter_tour.rs b/tests/data/notebooks/outputs/ipynb_to_script/evcxr_jupyter_tour.rs similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/evcxr_jupyter_tour.rs rename to tests/data/notebooks/outputs/ipynb_to_script/evcxr_jupyter_tour.rs diff --git a/tests/notebooks/mirror/ipynb_to_script/frozen_cell.py b/tests/data/notebooks/outputs/ipynb_to_script/frozen_cell.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/frozen_cell.py rename to tests/data/notebooks/outputs/ipynb_to_script/frozen_cell.py diff --git a/tests/notebooks/mirror/ipynb_to_script/fsharp.fsx b/tests/data/notebooks/outputs/ipynb_to_script/fsharp.fsx similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/fsharp.fsx rename to tests/data/notebooks/outputs/ipynb_to_script/fsharp.fsx diff --git a/tests/notebooks/mirror/ipynb_to_script/gnuplot_notebook.gp b/tests/data/notebooks/outputs/ipynb_to_script/gnuplot_notebook.gp similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/gnuplot_notebook.gp rename to tests/data/notebooks/outputs/ipynb_to_script/gnuplot_notebook.gp diff --git a/tests/notebooks/mirror/ipynb_to_script/haskell_notebook.hs b/tests/data/notebooks/outputs/ipynb_to_script/haskell_notebook.hs similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/haskell_notebook.hs rename to tests/data/notebooks/outputs/ipynb_to_script/haskell_notebook.hs diff --git a/tests/notebooks/mirror/ipynb_to_script/html-demo.clj b/tests/data/notebooks/outputs/ipynb_to_script/html-demo.clj similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/html-demo.clj rename to tests/data/notebooks/outputs/ipynb_to_script/html-demo.clj diff --git a/tests/notebooks/mirror/ipynb_to_script/ijavascript.js b/tests/data/notebooks/outputs/ipynb_to_script/ijavascript.js similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/ijavascript.js rename to tests/data/notebooks/outputs/ipynb_to_script/ijavascript.js diff --git a/tests/notebooks/mirror/ipynb_to_script/ir_notebook.R b/tests/data/notebooks/outputs/ipynb_to_script/ir_notebook.R similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/ir_notebook.R rename to tests/data/notebooks/outputs/ipynb_to_script/ir_notebook.R diff --git a/tests/notebooks/mirror/ipynb_to_script/ir_notebook.low.r b/tests/data/notebooks/outputs/ipynb_to_script/ir_notebook.low.r similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/ir_notebook.low.r rename to tests/data/notebooks/outputs/ipynb_to_script/ir_notebook.low.r diff --git a/tests/notebooks/mirror/ipynb_to_script/itypescript.ts b/tests/data/notebooks/outputs/ipynb_to_script/itypescript.ts similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/itypescript.ts rename to tests/data/notebooks/outputs/ipynb_to_script/itypescript.ts diff --git a/tests/notebooks/mirror/ipynb_to_script/julia_benchmark_plotly_barchart.jl b/tests/data/notebooks/outputs/ipynb_to_script/julia_benchmark_plotly_barchart.jl similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/julia_benchmark_plotly_barchart.jl rename to tests/data/notebooks/outputs/ipynb_to_script/julia_benchmark_plotly_barchart.jl diff --git a/tests/notebooks/mirror/ipynb_to_script/julia_functional_geometry.jl b/tests/data/notebooks/outputs/ipynb_to_script/julia_functional_geometry.jl similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/julia_functional_geometry.jl rename to tests/data/notebooks/outputs/ipynb_to_script/julia_functional_geometry.jl diff --git a/tests/notebooks/mirror/ipynb_to_script/jupyter.py b/tests/data/notebooks/outputs/ipynb_to_script/jupyter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/jupyter.py rename to tests/data/notebooks/outputs/ipynb_to_script/jupyter.py diff --git a/tests/notebooks/mirror/ipynb_to_script/jupyter_again.py b/tests/data/notebooks/outputs/ipynb_to_script/jupyter_again.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/jupyter_again.py rename to tests/data/notebooks/outputs/ipynb_to_script/jupyter_again.py diff --git a/tests/notebooks/mirror/ipynb_to_script/jupyter_with_raw_cell_in_body.py b/tests/data/notebooks/outputs/ipynb_to_script/jupyter_with_raw_cell_in_body.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/jupyter_with_raw_cell_in_body.py rename to tests/data/notebooks/outputs/ipynb_to_script/jupyter_with_raw_cell_in_body.py diff --git a/tests/notebooks/mirror/ipynb_to_script/jupyter_with_raw_cell_on_top.py b/tests/data/notebooks/outputs/ipynb_to_script/jupyter_with_raw_cell_on_top.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/jupyter_with_raw_cell_on_top.py rename to tests/data/notebooks/outputs/ipynb_to_script/jupyter_with_raw_cell_on_top.py diff --git a/tests/notebooks/mirror/ipynb_to_script/jupytext_replication.sos b/tests/data/notebooks/outputs/ipynb_to_script/jupytext_replication.sos similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/jupytext_replication.sos rename to tests/data/notebooks/outputs/ipynb_to_script/jupytext_replication.sos diff --git a/tests/notebooks/mirror/ipynb_to_script/kalman_filter_and_visualization.q b/tests/data/notebooks/outputs/ipynb_to_script/kalman_filter_and_visualization.q similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/kalman_filter_and_visualization.q rename to tests/data/notebooks/outputs/ipynb_to_script/kalman_filter_and_visualization.q diff --git a/tests/notebooks/mirror/ipynb_to_script/maxima_example.mac b/tests/data/notebooks/outputs/ipynb_to_script/maxima_example.mac similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/maxima_example.mac rename to tests/data/notebooks/outputs/ipynb_to_script/maxima_example.mac diff --git a/tests/notebooks/mirror/ipynb_to_script/notebook_with_complex_metadata.py b/tests/data/notebooks/outputs/ipynb_to_script/notebook_with_complex_metadata.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/notebook_with_complex_metadata.py rename to tests/data/notebooks/outputs/ipynb_to_script/notebook_with_complex_metadata.py diff --git a/tests/notebooks/mirror/ipynb_to_script/nteract_with_parameter.py b/tests/data/notebooks/outputs/ipynb_to_script/nteract_with_parameter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/nteract_with_parameter.py rename to tests/data/notebooks/outputs/ipynb_to_script/nteract_with_parameter.py diff --git a/tests/notebooks/mirror/ipynb_to_script/ocaml_notebook.ml b/tests/data/notebooks/outputs/ipynb_to_script/ocaml_notebook.ml similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/ocaml_notebook.ml rename to tests/data/notebooks/outputs/ipynb_to_script/ocaml_notebook.ml diff --git a/tests/notebooks/mirror/ipynb_to_script/octave_notebook.m b/tests/data/notebooks/outputs/ipynb_to_script/octave_notebook.m similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/octave_notebook.m rename to tests/data/notebooks/outputs/ipynb_to_script/octave_notebook.m diff --git a/tests/notebooks/mirror/ipynb_to_script/plotly_graphs.py b/tests/data/notebooks/outputs/ipynb_to_script/plotly_graphs.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/plotly_graphs.py rename to tests/data/notebooks/outputs/ipynb_to_script/plotly_graphs.py diff --git a/tests/notebooks/mirror/ipynb_to_script/powershell.ps1 b/tests/data/notebooks/outputs/ipynb_to_script/powershell.ps1 similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/powershell.ps1 rename to tests/data/notebooks/outputs/ipynb_to_script/powershell.ps1 diff --git a/tests/notebooks/mirror/ipynb_to_script/sage_print_hello.sage b/tests/data/notebooks/outputs/ipynb_to_script/sage_print_hello.sage similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/sage_print_hello.sage rename to tests/data/notebooks/outputs/ipynb_to_script/sage_print_hello.sage diff --git a/tests/notebooks/mirror/ipynb_to_script/sample_bash_notebook.sh b/tests/data/notebooks/outputs/ipynb_to_script/sample_bash_notebook.sh similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/sample_bash_notebook.sh rename to tests/data/notebooks/outputs/ipynb_to_script/sample_bash_notebook.sh diff --git a/tests/notebooks/mirror/ipynb_to_script/sample_rise_notebook_66.py b/tests/data/notebooks/outputs/ipynb_to_script/sample_rise_notebook_66.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/sample_rise_notebook_66.py rename to tests/data/notebooks/outputs/ipynb_to_script/sample_rise_notebook_66.py diff --git a/tests/notebooks/mirror/ipynb_to_script/sas.sas b/tests/data/notebooks/outputs/ipynb_to_script/sas.sas similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/sas.sas rename to tests/data/notebooks/outputs/ipynb_to_script/sas.sas diff --git a/tests/notebooks/mirror/ipynb_to_script/simple-helloworld.java b/tests/data/notebooks/outputs/ipynb_to_script/simple-helloworld.java similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/simple-helloworld.java rename to tests/data/notebooks/outputs/ipynb_to_script/simple-helloworld.java diff --git a/tests/notebooks/mirror/ipynb_to_script/simple_robot_notebook.robot b/tests/data/notebooks/outputs/ipynb_to_script/simple_robot_notebook.robot similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/simple_robot_notebook.robot rename to tests/data/notebooks/outputs/ipynb_to_script/simple_robot_notebook.robot diff --git a/tests/notebooks/mirror/ipynb_to_script/simple_scala_notebook.scala b/tests/data/notebooks/outputs/ipynb_to_script/simple_scala_notebook.scala similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/simple_scala_notebook.scala rename to tests/data/notebooks/outputs/ipynb_to_script/simple_scala_notebook.scala diff --git a/tests/notebooks/mirror/ipynb_to_script/stata_notebook.do b/tests/data/notebooks/outputs/ipynb_to_script/stata_notebook.do similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/stata_notebook.do rename to tests/data/notebooks/outputs/ipynb_to_script/stata_notebook.do diff --git a/tests/notebooks/mirror/ipynb_to_script/tailrecursive-factorial.groovy b/tests/data/notebooks/outputs/ipynb_to_script/tailrecursive-factorial.groovy similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/tailrecursive-factorial.groovy rename to tests/data/notebooks/outputs/ipynb_to_script/tailrecursive-factorial.groovy diff --git a/tests/notebooks/mirror/ipynb_to_script/tcl_test.tcl b/tests/data/notebooks/outputs/ipynb_to_script/tcl_test.tcl similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/tcl_test.tcl rename to tests/data/notebooks/outputs/ipynb_to_script/tcl_test.tcl diff --git a/tests/notebooks/mirror/ipynb_to_script/text_outputs_and_images.py b/tests/data/notebooks/outputs/ipynb_to_script/text_outputs_and_images.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/text_outputs_and_images.py rename to tests/data/notebooks/outputs/ipynb_to_script/text_outputs_and_images.py diff --git a/tests/notebooks/mirror/ipynb_to_script/wolfram.wolfram b/tests/data/notebooks/outputs/ipynb_to_script/wolfram.wolfram similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/wolfram.wolfram rename to tests/data/notebooks/outputs/ipynb_to_script/wolfram.wolfram diff --git a/tests/notebooks/mirror/ipynb_to_script/xcpp_by_quantstack.cpp b/tests/data/notebooks/outputs/ipynb_to_script/xcpp_by_quantstack.cpp similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script/xcpp_by_quantstack.cpp rename to tests/data/notebooks/outputs/ipynb_to_script/xcpp_by_quantstack.cpp diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Line_breaks_in_LateX_305.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Line_breaks_in_LateX_305.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Line_breaks_in_LateX_305.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Line_breaks_in_LateX_305.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook with function and cell metadata 164.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook with function and cell metadata 164.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook with function and cell metadata 164.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook with function and cell metadata 164.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook with html and latex cells.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook with html and latex cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook with html and latex cells.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook with html and latex cells.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook with many hash signs.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook with many hash signs.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook with many hash signs.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook with many hash signs.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook with metadata and long cells.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook with metadata and long cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook with metadata and long cells.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook with metadata and long cells.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook_with_R_magic.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook_with_R_magic.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook_with_R_magic.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook_with_R_magic.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook_with_more_R_magic_111.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook_with_more_R_magic_111.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/Notebook_with_more_R_magic_111.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/Notebook_with_more_R_magic_111.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/The flavors of raw cells.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/The flavors of raw cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/The flavors of raw cells.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/The flavors of raw cells.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/cat_variable.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/cat_variable.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/cat_variable.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/cat_variable.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/convert_to_py_then_test_with_update83.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/convert_to_py_then_test_with_update83.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/convert_to_py_then_test_with_update83.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/convert_to_py_then_test_with_update83.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/frozen_cell.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/frozen_cell.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/frozen_cell.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/frozen_cell.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/jupyter.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/jupyter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/jupyter.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/jupyter.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/jupyter_again.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/jupyter_again.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/jupyter_again.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/jupyter_again.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/jupyter_with_raw_cell_in_body.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/jupyter_with_raw_cell_in_body.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/jupyter_with_raw_cell_in_body.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/jupyter_with_raw_cell_in_body.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/jupyter_with_raw_cell_on_top.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/jupyter_with_raw_cell_on_top.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/jupyter_with_raw_cell_on_top.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/jupyter_with_raw_cell_on_top.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/notebook_with_complex_metadata.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/notebook_with_complex_metadata.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/notebook_with_complex_metadata.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/notebook_with_complex_metadata.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/nteract_with_parameter.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/nteract_with_parameter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/nteract_with_parameter.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/nteract_with_parameter.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/plotly_graphs.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/plotly_graphs.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/plotly_graphs.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/plotly_graphs.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/sample_rise_notebook_66.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/sample_rise_notebook_66.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/sample_rise_notebook_66.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/sample_rise_notebook_66.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/text_outputs_and_images.py b/tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/text_outputs_and_images.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/text_outputs_and_images.py rename to tests/data/notebooks/outputs/ipynb_to_script_vim_folding_markers/text_outputs_and_images.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Line_breaks_in_LateX_305.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Line_breaks_in_LateX_305.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Line_breaks_in_LateX_305.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Line_breaks_in_LateX_305.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook with function and cell metadata 164.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook with function and cell metadata 164.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook with function and cell metadata 164.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook with function and cell metadata 164.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook with html and latex cells.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook with html and latex cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook with html and latex cells.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook with html and latex cells.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook with many hash signs.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook with many hash signs.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook with many hash signs.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook with many hash signs.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook with metadata and long cells.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook with metadata and long cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook with metadata and long cells.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook with metadata and long cells.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook_with_R_magic.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook_with_R_magic.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook_with_R_magic.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook_with_R_magic.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook_with_more_R_magic_111.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook_with_more_R_magic_111.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/Notebook_with_more_R_magic_111.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/Notebook_with_more_R_magic_111.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/The flavors of raw cells.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/The flavors of raw cells.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/The flavors of raw cells.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/The flavors of raw cells.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/cat_variable.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/cat_variable.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/cat_variable.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/cat_variable.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/convert_to_py_then_test_with_update83.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/convert_to_py_then_test_with_update83.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/convert_to_py_then_test_with_update83.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/convert_to_py_then_test_with_update83.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/frozen_cell.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/frozen_cell.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/frozen_cell.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/frozen_cell.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/jupyter.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/jupyter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/jupyter.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/jupyter.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/jupyter_again.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/jupyter_again.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/jupyter_again.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/jupyter_again.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/jupyter_with_raw_cell_in_body.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/jupyter_with_raw_cell_in_body.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/jupyter_with_raw_cell_in_body.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/jupyter_with_raw_cell_in_body.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/jupyter_with_raw_cell_on_top.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/jupyter_with_raw_cell_on_top.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/jupyter_with_raw_cell_on_top.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/jupyter_with_raw_cell_on_top.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/notebook_with_complex_metadata.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/notebook_with_complex_metadata.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/notebook_with_complex_metadata.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/notebook_with_complex_metadata.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/nteract_with_parameter.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/nteract_with_parameter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/nteract_with_parameter.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/nteract_with_parameter.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/plotly_graphs.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/plotly_graphs.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/plotly_graphs.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/plotly_graphs.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/sample_rise_notebook_66.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/sample_rise_notebook_66.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/sample_rise_notebook_66.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/sample_rise_notebook_66.py diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/text_outputs_and_images.py b/tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/text_outputs_and_images.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/text_outputs_and_images.py rename to tests/data/notebooks/outputs/ipynb_to_script_vscode_folding_markers/text_outputs_and_images.py diff --git a/tests/notebooks/mirror/ipynb_to_sphinx/Line_breaks_in_LateX_305.py b/tests/data/notebooks/outputs/ipynb_to_sphinx/Line_breaks_in_LateX_305.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_sphinx/Line_breaks_in_LateX_305.py rename to tests/data/notebooks/outputs/ipynb_to_sphinx/Line_breaks_in_LateX_305.py diff --git a/tests/notebooks/mirror/ipynb_to_sphinx/cat_variable.py b/tests/data/notebooks/outputs/ipynb_to_sphinx/cat_variable.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_sphinx/cat_variable.py rename to tests/data/notebooks/outputs/ipynb_to_sphinx/cat_variable.py diff --git a/tests/notebooks/mirror/ipynb_to_sphinx/convert_to_py_then_test_with_update83.py b/tests/data/notebooks/outputs/ipynb_to_sphinx/convert_to_py_then_test_with_update83.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_sphinx/convert_to_py_then_test_with_update83.py rename to tests/data/notebooks/outputs/ipynb_to_sphinx/convert_to_py_then_test_with_update83.py diff --git a/tests/notebooks/mirror/ipynb_to_sphinx/jupyter.py b/tests/data/notebooks/outputs/ipynb_to_sphinx/jupyter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_sphinx/jupyter.py rename to tests/data/notebooks/outputs/ipynb_to_sphinx/jupyter.py diff --git a/tests/notebooks/mirror/ipynb_to_sphinx/jupyter_again.py b/tests/data/notebooks/outputs/ipynb_to_sphinx/jupyter_again.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_sphinx/jupyter_again.py rename to tests/data/notebooks/outputs/ipynb_to_sphinx/jupyter_again.py diff --git a/tests/notebooks/mirror/ipynb_to_sphinx/notebook_with_complex_metadata.py b/tests/data/notebooks/outputs/ipynb_to_sphinx/notebook_with_complex_metadata.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_sphinx/notebook_with_complex_metadata.py rename to tests/data/notebooks/outputs/ipynb_to_sphinx/notebook_with_complex_metadata.py diff --git a/tests/notebooks/mirror/ipynb_to_sphinx/nteract_with_parameter.py b/tests/data/notebooks/outputs/ipynb_to_sphinx/nteract_with_parameter.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_sphinx/nteract_with_parameter.py rename to tests/data/notebooks/outputs/ipynb_to_sphinx/nteract_with_parameter.py diff --git a/tests/notebooks/mirror/ipynb_to_sphinx/plotly_graphs.py b/tests/data/notebooks/outputs/ipynb_to_sphinx/plotly_graphs.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_sphinx/plotly_graphs.py rename to tests/data/notebooks/outputs/ipynb_to_sphinx/plotly_graphs.py diff --git a/tests/notebooks/mirror/ipynb_to_sphinx/sample_rise_notebook_66.py b/tests/data/notebooks/outputs/ipynb_to_sphinx/sample_rise_notebook_66.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_sphinx/sample_rise_notebook_66.py rename to tests/data/notebooks/outputs/ipynb_to_sphinx/sample_rise_notebook_66.py diff --git a/tests/notebooks/mirror/ipynb_to_sphinx/text_outputs_and_images.py b/tests/data/notebooks/outputs/ipynb_to_sphinx/text_outputs_and_images.py similarity index 100% rename from tests/notebooks/mirror/ipynb_to_sphinx/text_outputs_and_images.py rename to tests/data/notebooks/outputs/ipynb_to_sphinx/text_outputs_and_images.py diff --git a/tests/notebooks/mirror/ipynb_to_spin/R notebook with invalid cell keys.R b/tests/data/notebooks/outputs/ipynb_to_spin/R notebook with invalid cell keys.R similarity index 100% rename from tests/notebooks/mirror/ipynb_to_spin/R notebook with invalid cell keys.R rename to tests/data/notebooks/outputs/ipynb_to_spin/R notebook with invalid cell keys.R diff --git a/tests/notebooks/mirror/ipynb_to_spin/R notebook with invalid cell keys.low.r b/tests/data/notebooks/outputs/ipynb_to_spin/R notebook with invalid cell keys.low.r similarity index 100% rename from tests/notebooks/mirror/ipynb_to_spin/R notebook with invalid cell keys.low.r rename to tests/data/notebooks/outputs/ipynb_to_spin/R notebook with invalid cell keys.low.r diff --git a/tests/notebooks/mirror/ipynb_to_spin/ir_notebook.R b/tests/data/notebooks/outputs/ipynb_to_spin/ir_notebook.R similarity index 100% rename from tests/notebooks/mirror/ipynb_to_spin/ir_notebook.R rename to tests/data/notebooks/outputs/ipynb_to_spin/ir_notebook.R diff --git a/tests/notebooks/mirror/ipynb_to_spin/ir_notebook.low.r b/tests/data/notebooks/outputs/ipynb_to_spin/ir_notebook.low.r similarity index 100% rename from tests/notebooks/mirror/ipynb_to_spin/ir_notebook.low.r rename to tests/data/notebooks/outputs/ipynb_to_spin/ir_notebook.low.r diff --git a/tests/notebooks/mirror/md_to_ipynb/jupytext_markdown.ipynb b/tests/data/notebooks/outputs/md_to_ipynb/jupytext_markdown.ipynb similarity index 100% rename from tests/notebooks/mirror/md_to_ipynb/jupytext_markdown.ipynb rename to tests/data/notebooks/outputs/md_to_ipynb/jupytext_markdown.ipynb diff --git a/tests/notebooks/mirror/md_to_ipynb/plain_markdown.ipynb b/tests/data/notebooks/outputs/md_to_ipynb/plain_markdown.ipynb similarity index 100% rename from tests/notebooks/mirror/md_to_ipynb/plain_markdown.ipynb rename to tests/data/notebooks/outputs/md_to_ipynb/plain_markdown.ipynb diff --git a/tests/notebooks/mirror/script_to_ipynb/build.ipynb b/tests/data/notebooks/outputs/script_to_ipynb/build.ipynb similarity index 100% rename from tests/notebooks/mirror/script_to_ipynb/build.ipynb rename to tests/data/notebooks/outputs/script_to_ipynb/build.ipynb diff --git a/tests/notebooks/mirror/script_to_ipynb/hydrogen.ipynb b/tests/data/notebooks/outputs/script_to_ipynb/hydrogen.ipynb similarity index 100% rename from tests/notebooks/mirror/script_to_ipynb/hydrogen.ipynb rename to tests/data/notebooks/outputs/script_to_ipynb/hydrogen.ipynb diff --git a/tests/notebooks/mirror/script_to_ipynb/hydrogen_latex_html_R_magics.ipynb b/tests/data/notebooks/outputs/script_to_ipynb/hydrogen_latex_html_R_magics.ipynb similarity index 100% rename from tests/notebooks/mirror/script_to_ipynb/hydrogen_latex_html_R_magics.ipynb rename to tests/data/notebooks/outputs/script_to_ipynb/hydrogen_latex_html_R_magics.ipynb diff --git a/tests/notebooks/mirror/script_to_ipynb/julia_sample_script.ipynb b/tests/data/notebooks/outputs/script_to_ipynb/julia_sample_script.ipynb similarity index 100% rename from tests/notebooks/mirror/script_to_ipynb/julia_sample_script.ipynb rename to tests/data/notebooks/outputs/script_to_ipynb/julia_sample_script.ipynb diff --git a/tests/notebooks/mirror/script_to_ipynb/knitr-spin.ipynb b/tests/data/notebooks/outputs/script_to_ipynb/knitr-spin.ipynb similarity index 100% rename from tests/notebooks/mirror/script_to_ipynb/knitr-spin.ipynb rename to tests/data/notebooks/outputs/script_to_ipynb/knitr-spin.ipynb diff --git a/tests/notebooks/mirror/script_to_ipynb/light_sample.ipynb b/tests/data/notebooks/outputs/script_to_ipynb/light_sample.ipynb similarity index 100% rename from tests/notebooks/mirror/script_to_ipynb/light_sample.ipynb rename to tests/data/notebooks/outputs/script_to_ipynb/light_sample.ipynb diff --git a/tests/notebooks/mirror/script_to_ipynb/python_notebook_sample.ipynb b/tests/data/notebooks/outputs/script_to_ipynb/python_notebook_sample.ipynb similarity index 100% rename from tests/notebooks/mirror/script_to_ipynb/python_notebook_sample.ipynb rename to tests/data/notebooks/outputs/script_to_ipynb/python_notebook_sample.ipynb diff --git a/tests/notebooks/mirror/script_to_ipynb/simple_r_script.ipynb b/tests/data/notebooks/outputs/script_to_ipynb/simple_r_script.ipynb similarity index 100% rename from tests/notebooks/mirror/script_to_ipynb/simple_r_script.ipynb rename to tests/data/notebooks/outputs/script_to_ipynb/simple_r_script.ipynb diff --git a/tests/notebooks/mirror/sphinx-rst2md_to_ipynb/plot_notebook.ipynb b/tests/data/notebooks/outputs/sphinx-rst2md_to_ipynb/plot_notebook.ipynb similarity index 100% rename from tests/notebooks/mirror/sphinx-rst2md_to_ipynb/plot_notebook.ipynb rename to tests/data/notebooks/outputs/sphinx-rst2md_to_ipynb/plot_notebook.ipynb diff --git a/tests/notebooks/mirror/sphinx_to_ipynb/plot_notebook.ipynb b/tests/data/notebooks/outputs/sphinx_to_ipynb/plot_notebook.ipynb similarity index 100% rename from tests/notebooks/mirror/sphinx_to_ipynb/plot_notebook.ipynb rename to tests/data/notebooks/outputs/sphinx_to_ipynb/plot_notebook.ipynb diff --git a/tests/test_black.py b/tests/external/cli/test_black.py similarity index 91% rename from tests/test_black.py rename to tests/external/cli/test_black.py index 396e737b8..1f5f79f20 100644 --- a/tests/test_black.py +++ b/tests/external/cli/test_black.py @@ -11,19 +11,18 @@ from jupytext.compare import compare, compare_cells, compare_notebooks from jupytext.header import _DEFAULT_NOTEBOOK_METADATA -from .utils import list_notebooks, requires_autopep8, requires_black, requires_flake8 - -@requires_black -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")[:1]) -def test_apply_black_on_python_notebooks(tmpdir, cwd_tmpdir, nb_file): - copyfile(nb_file, "notebook.ipynb") +@pytest.mark.requires_black +def test_apply_black_on_python_notebooks(tmpdir, cwd_tmpdir, ipynb_py_file): + if "cell metadata" in ipynb_py_file: + pytest.skip() + copyfile(ipynb_py_file, "notebook.ipynb") jupytext(args=["notebook.ipynb", "--to", "py:percent"]) system("black", "notebook.py") jupytext(args=["notebook.py", "--to", "ipynb", "--update"]) - nb1 = read(nb_file) + nb1 = read(ipynb_py_file) nb2 = read("notebook.ipynb") nb3 = read("notebook.py") @@ -54,7 +53,7 @@ def test_black_invariant(): assert black_invariant(text_org) == black_invariant(text_black) -@requires_black +@pytest.mark.requires_black def test_pipe_into_black(): nb_org = new_notebook(cells=[new_code_cell("1 +1", id="cell-id")]) nb_dest = new_notebook(cells=[new_code_cell("1 + 1", id="cell-id")]) @@ -65,7 +64,7 @@ def test_pipe_into_black(): ) -@requires_autopep8 +@pytest.mark.requires_autopep8 def test_pipe_into_autopep8(): nb_org = new_notebook(cells=[new_code_cell("1 +1", id="cell-id")]) nb_dest = new_notebook(cells=[new_code_cell("1 + 1", id="cell-id")]) @@ -76,7 +75,7 @@ def test_pipe_into_autopep8(): ) -@requires_flake8 +@pytest.mark.requires_flake8 def test_pipe_into_flake8(): # Notebook OK nb = new_notebook(cells=[new_code_cell("# correct code\n1 + 1")]) @@ -88,12 +87,11 @@ def test_pipe_into_flake8(): pipe_notebook(nb, "flake8", update=False) -@requires_black -@requires_flake8 -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")[:1]) -def test_apply_black_through_jupytext(tmpdir, nb_file): +@pytest.mark.requires_black +@pytest.mark.requires_flake8 +def test_apply_black_through_jupytext(tmpdir, python_notebook): # Load real notebook metadata to get the 'auto' extension in --pipe-fmt to work - metadata = read(nb_file).metadata + metadata = python_notebook.metadata nb_org = new_notebook( cells=[new_code_cell("1 +1", id="cell-id")], metadata=metadata @@ -141,11 +139,10 @@ def test_apply_black_through_jupytext(tmpdir, nb_file): compare_notebooks(nb_now, nb_black) -@requires_black -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")[:1]) -def test_apply_black_and_sync_on_paired_notebook(tmpdir, cwd_tmpdir, nb_file): +@pytest.mark.requires_black +def test_apply_black_and_sync_on_paired_notebook(tmpdir, cwd_tmpdir, python_notebook): # Load real notebook metadata to get the 'auto' extension in --pipe-fmt to work - metadata = read(nb_file).metadata + metadata = python_notebook.metadata metadata["jupytext"] = {"formats": "ipynb,py"} assert "language_info" in metadata @@ -174,7 +171,7 @@ def test_apply_black_and_sync_on_paired_notebook(tmpdir, cwd_tmpdir, nb_file): compare_notebooks(nb_now, nb_black) -@requires_black +@pytest.mark.requires_black def test_apply_black_on_markdown_notebook(tmpdir): text = """--- jupyter: @@ -210,7 +207,7 @@ def test_apply_black_on_markdown_notebook(tmpdir): compare_cells(nb.cells, [new_code_cell("1 + 2 + 3 + 4")], compare_ids=False) -@requires_black +@pytest.mark.requires_black def test_black_through_tempfile( tmpdir, text="""```python @@ -231,7 +228,7 @@ def test_black_through_tempfile( compare(fp.read(), black) -@requires_black +@pytest.mark.requires_black def test_pipe_black_removes_lines_to_next_cell_metadata( tmpdir, cwd_tmpdir, @@ -255,7 +252,7 @@ def func(): assert "\n\n# %%\nfunc()" in new_text -@requires_black +@pytest.mark.requires_black @pytest.mark.parametrize( "code,black_should_fail", [("myvar = %dont_format_me", False), ("incomplete_instruction = (...", True)], @@ -288,7 +285,7 @@ def test_pipe_black_uses_warn_only_781( compare_notebooks(actual, nb) -@requires_black +@pytest.mark.requires_black def test_pipe_black_preserve_outputs(notebook_with_outputs, tmpdir, cwd_tmpdir, capsys): write(notebook_with_outputs, "test.ipynb") jupytext(["--pipe", "black", "test.ipynb"]) diff --git a/tests/test_cli_check.py b/tests/external/cli/test_cli_check.py similarity index 93% rename from tests/test_cli_check.py rename to tests/external/cli/test_cli_check.py index cbc469441..6056e3b5e 100644 --- a/tests/test_cli_check.py +++ b/tests/external/cli/test_cli_check.py @@ -4,15 +4,13 @@ from jupytext import write from jupytext.cli import jupytext -from .utils import requires_black - @pytest.fixture def non_black_notebook(python_notebook): return new_notebook(metadata=python_notebook.metadata, cells=[new_code_cell("1+1")]) -@requires_black +@pytest.mark.requires_black def test_check_notebooks_left_or_right_black(python_notebook, tmpdir, cwd_tmpdir): write(python_notebook, str(tmpdir / "nb1.ipynb")) write(python_notebook, str(tmpdir / "nb2.ipynb")) @@ -21,7 +19,7 @@ def test_check_notebooks_left_or_right_black(python_notebook, tmpdir, cwd_tmpdir jupytext(["--check", "black --check {}", "*.ipynb"]) -@requires_black +@pytest.mark.requires_black def test_check_notebooks_left_or_right_not_black( non_black_notebook, tmpdir, cwd_tmpdir ): diff --git a/tests/test_isort.py b/tests/external/cli/test_isort.py similarity index 93% rename from tests/test_isort.py rename to tests/external/cli/test_isort.py index 0c03ca17e..1df015533 100644 --- a/tests/test_isort.py +++ b/tests/external/cli/test_isort.py @@ -1,11 +1,11 @@ +import pytest + from jupytext import reads, writes from jupytext.cli import pipe_notebook from jupytext.compare import compare -from .utils import requires_isort - -@requires_isort +@pytest.mark.requires_isort def test_pipe_into_isort(): text_org = """# %% import numpy as np diff --git a/tests/external/conftest.py b/tests/external/conftest.py new file mode 100644 index 000000000..9f9cd86ba --- /dev/null +++ b/tests/external/conftest.py @@ -0,0 +1,8 @@ +import pytest +from git import Repo + + +@pytest.fixture +def tmp_repo(tmpdir): + repo = Repo.init(str(tmpdir)) + return repo diff --git a/tests/external/contents_manager/test_contentsmanager_external.py b/tests/external/contents_manager/test_contentsmanager_external.py new file mode 100644 index 000000000..06effd1e2 --- /dev/null +++ b/tests/external/contents_manager/test_contentsmanager_external.py @@ -0,0 +1,54 @@ +import re + +import pytest + +import jupytext +from jupytext.compare import compare_notebooks, notebook_model + + +@pytest.mark.requires_pandoc +def test_save_load_paired_md_pandoc_notebook(ipynb_py_R_jl_file, tmpdir): + if re.match( + r".*(functional|Notebook with|flavors|invalid|305).*", ipynb_py_R_jl_file + ): + pytest.skip() + tmp_ipynb = "notebook.ipynb" + tmp_md = "notebook.md" + + cm = jupytext.TextFileContentsManager() + cm.root_dir = str(tmpdir) + + # open ipynb, save with cm, reopen + nb = jupytext.read(ipynb_py_R_jl_file) + nb.metadata["jupytext"] = {"formats": "ipynb,md:pandoc"} + + cm.save(model=notebook_model(nb), path=tmp_ipynb) + nb_md = cm.get(tmp_md) + + compare_notebooks(nb_md["content"], nb, "md:pandoc") + assert nb_md["content"].metadata["jupytext"]["formats"] == "ipynb,md:pandoc" + + +@pytest.mark.requires_quarto +def test_save_load_paired_qmd_notebook(ipynb_py_R_jl_file, tmpdir): + if re.match( + r".*(functional|Notebook with|plotly_graphs|flavors|complex_metadata|" + "update83|raw_cell|_66|nteract|LaTeX|invalid|305|text_outputs|ir_notebook|jupyter|with_R_magic).*", + ipynb_py_R_jl_file, + ): + pytest.skip() + tmp_ipynb = "notebook.ipynb" + tmp_qmd = "notebook.qmd" + + cm = jupytext.TextFileContentsManager() + cm.root_dir = str(tmpdir) + + # open ipynb, save with cm, reopen + nb = jupytext.read(ipynb_py_R_jl_file) + nb.metadata["jupytext"] = {"formats": "ipynb,qmd"} + + cm.save(model=notebook_model(nb), path=tmp_ipynb) + nb_md = cm.get(tmp_qmd) + + compare_notebooks(nb_md["content"], nb, "qmd") + assert nb_md["content"].metadata["jupytext"]["formats"] == "ipynb,qmd" diff --git a/tests/test_using_cli.py b/tests/external/docs/test_using_cli.py similarity index 88% rename from tests/test_using_cli.py rename to tests/external/docs/test_using_cli.py index a0e54d86d..275b4313e 100644 --- a/tests/test_using_cli.py +++ b/tests/external/docs/test_using_cli.py @@ -7,14 +7,12 @@ import jupytext from jupytext.cli import jupytext as jupytext_cli -from .utils import requires_black, requires_myst, requires_user_kernel_python3 +doc_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "docs") -doc_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "docs") - -@requires_user_kernel_python3 -@requires_black -@requires_myst +@pytest.mark.requires_user_kernel_python3 +@pytest.mark.requires_black +@pytest.mark.requires_myst @pytest.mark.skipif( not os.path.isdir(doc_path), reason="Documentation folder is missing" ) diff --git a/tests/external/jupyter_fs/test_jupyter_fs.py b/tests/external/jupyter_fs/test_jupyter_fs.py new file mode 100644 index 000000000..0b703e542 --- /dev/null +++ b/tests/external/jupyter_fs/test_jupyter_fs.py @@ -0,0 +1,98 @@ +import logging + +import pytest +from nbformat.v4.nbbase import new_code_cell, new_markdown_cell, new_notebook + +import jupytext +from jupytext.compare import compare_cells, notebook_model + + +def fs_meta_manager(tmpdir): + try: + from jupyterfs.metamanager import MetaManager + except ImportError: + pytest.skip("jupyterfs is not available") + + cm_class = jupytext.build_jupytext_contents_manager_class(MetaManager) + logger = logging.getLogger("jupyter-fs") + cm = cm_class(parent=None, log=logger) + cm.initResource( + { + "url": f"osfs://{tmpdir}", + } + ) + return cm + + +def test_jupytext_jupyter_fs_metamanager(tmpdir): + """Test the basic get/save functions of Jupytext with a fs manager + https://github.com/mwouts/jupytext/issues/618""" + cm = fs_meta_manager(tmpdir) + # the hash that corresponds to the osfs + osfs = [h for h in cm._managers if h != ""][0] + + # save a few files + text = "some text\n" + cm.save(dict(type="file", content=text, format="text"), path=osfs + ":text.md") + nb = new_notebook( + cells=[new_markdown_cell("A markdown cell"), new_code_cell("1 + 1")] + ) + cm.save(notebook_model(nb), osfs + ":notebook.ipynb") + cm.save(notebook_model(nb), osfs + ":text_notebook.md") + + # list the directory + directory = cm.get(osfs + ":/") + assert {file["name"] for file in directory["content"]} == { + "text.md", + "text_notebook.md", + "notebook.ipynb", + } + + # get the files + model = cm.get(osfs + ":/text.md", type="file") + assert model["type"] == "file" + assert model["content"] == text + + model = cm.get(osfs + ":text.md", type="notebook") + assert model["type"] == "notebook" + # We only compare the cells, as kernelspecs are added to the notebook metadata + compare_cells( + model["content"].cells, [new_markdown_cell(text.strip())], compare_ids=False + ) + + for nb_file in ["notebook.ipynb", "text_notebook.md"]: + model = cm.get(osfs + ":" + nb_file) + assert model["type"] == "notebook" + actual_cells = model["content"].cells + + # saving adds 'trusted=True' to the code cell metadata + for cell in actual_cells: + cell.metadata = {} + compare_cells(actual_cells, nb.cells, compare_ids=False) + + +def test_config_jupytext_jupyter_fs_meta_manager(tmpdir): + """Test the configuration of Jupytext with a fs manager""" + tmpdir.join("jupytext.toml").write('formats = "ipynb,py"') + cm = fs_meta_manager(tmpdir) + # the hash that corresponds to the osfs + osfs = [h for h in cm._managers if h != ""][0] + + # save a few files + nb = new_notebook() + cm.save(dict(type="file", content="text", format="text"), path=osfs + ":text.md") + cm.save(notebook_model(nb), osfs + ":script.py") + cm.save(notebook_model(nb), osfs + ":text_notebook.md") + cm.save(notebook_model(nb), osfs + ":notebook.ipynb") + + # list the directory + directory = cm.get(osfs + ":/") + assert {file["name"] for file in directory["content"]} == { + "jupytext.toml", + "text.md", + "text_notebook.md", + "notebook.ipynb", + "notebook.py", + "script.py", + "script.ipynb", + } diff --git a/tests/test_pre_commit_0_ipynb_to_py.py b/tests/external/pre_commit/test_pre_commit_0_ipynb_to_py.py similarity index 91% rename from tests/test_pre_commit_0_ipynb_to_py.py rename to tests/external/pre_commit/test_pre_commit_0_ipynb_to_py.py index 87c284160..2b3af5a8f 100644 --- a/tests/test_pre_commit_0_ipynb_to_py.py +++ b/tests/external/pre_commit/test_pre_commit_0_ipynb_to_py.py @@ -7,14 +7,7 @@ from jupytext.cli import jupytext from jupytext.compare import compare_cells -from .utils import ( - skip_pre_commit_tests_on_windows, - skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo, -) - -@skip_pre_commit_tests_on_windows -@skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo def test_pre_commit_hook_ipynb_to_py( tmpdir, cwd_tmpdir, tmp_repo, jupytext_repo_root, jupytext_repo_rev ): diff --git a/tests/test_pre_commit_1_sync.py b/tests/external/pre_commit/test_pre_commit_1_sync.py similarity index 93% rename from tests/test_pre_commit_1_sync.py rename to tests/external/pre_commit/test_pre_commit_1_sync.py index 256e72fcd..6b9854040 100644 --- a/tests/test_pre_commit_1_sync.py +++ b/tests/external/pre_commit/test_pre_commit_1_sync.py @@ -8,14 +8,7 @@ from jupytext import read, write from jupytext.cli import jupytext -from .utils import ( - skip_pre_commit_tests_on_windows, - skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo, -) - -@skip_pre_commit_tests_on_windows -@skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo def test_pre_commit_hook_sync( tmpdir, cwd_tmpdir, diff --git a/tests/test_pre_commit_1_sync_with_config.py b/tests/external/pre_commit/test_pre_commit_1_sync_with_config.py similarity index 92% rename from tests/test_pre_commit_1_sync_with_config.py rename to tests/external/pre_commit/test_pre_commit_1_sync_with_config.py index 1bb3adaec..1ef521cff 100644 --- a/tests/test_pre_commit_1_sync_with_config.py +++ b/tests/external/pre_commit/test_pre_commit_1_sync_with_config.py @@ -5,14 +5,7 @@ from jupytext import TextFileContentsManager, read -from .utils import ( - skip_pre_commit_tests_on_windows, - skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo, -) - -@skip_pre_commit_tests_on_windows -@skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo def test_pre_commit_hook_sync_with_config( tmpdir, cwd_tmpdir, diff --git a/tests/test_pre_commit_1_sync_with_no_config.py b/tests/external/pre_commit/test_pre_commit_1_sync_with_no_config.py similarity index 91% rename from tests/test_pre_commit_1_sync_with_no_config.py rename to tests/external/pre_commit/test_pre_commit_1_sync_with_no_config.py index 0ba56b052..4222cc91f 100644 --- a/tests/test_pre_commit_1_sync_with_no_config.py +++ b/tests/external/pre_commit/test_pre_commit_1_sync_with_no_config.py @@ -9,14 +9,7 @@ from jupytext import TextFileContentsManager from jupytext.compare import compare_notebooks -from .utils import ( - skip_pre_commit_tests_on_windows, - skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo, -) - -@skip_pre_commit_tests_on_windows -@skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo def test_pre_commit_hook_sync_with_no_config( tmpdir, cwd_tmpdir, diff --git a/tests/test_pre_commit_2_sync_nbstripout.py b/tests/external/pre_commit/test_pre_commit_2_sync_nbstripout.py similarity index 88% rename from tests/test_pre_commit_2_sync_nbstripout.py rename to tests/external/pre_commit/test_pre_commit_2_sync_nbstripout.py index 14dc9b9e1..304a84f5b 100644 --- a/tests/test_pre_commit_2_sync_nbstripout.py +++ b/tests/external/pre_commit/test_pre_commit_2_sync_nbstripout.py @@ -5,14 +5,7 @@ from jupytext import read, write from jupytext.cli import jupytext -from .utils import ( - skip_pre_commit_tests_on_windows, - skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo, -) - -@skip_pre_commit_tests_on_windows -@skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo def test_pre_commit_hook_sync_nbstripout( tmpdir, cwd_tmpdir, diff --git a/tests/test_pre_commit_3_sync_black_nbstripout.py b/tests/external/pre_commit/test_pre_commit_3_sync_black_nbstripout.py similarity index 89% rename from tests/test_pre_commit_3_sync_black_nbstripout.py rename to tests/external/pre_commit/test_pre_commit_3_sync_black_nbstripout.py index db04796b9..01a549fec 100644 --- a/tests/test_pre_commit_3_sync_black_nbstripout.py +++ b/tests/external/pre_commit/test_pre_commit_3_sync_black_nbstripout.py @@ -4,14 +4,7 @@ from jupytext import read, write -from .utils import ( - skip_pre_commit_tests_on_windows, - skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo, -) - -@skip_pre_commit_tests_on_windows -@skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo def test_pre_commit_hook_sync_black_nbstripout( tmpdir, cwd_tmpdir, diff --git a/tests/test_pre_commit_4_sync_execute.py b/tests/external/pre_commit/test_pre_commit_4_sync_execute.py similarity index 86% rename from tests/test_pre_commit_4_sync_execute.py rename to tests/external/pre_commit/test_pre_commit_4_sync_execute.py index 0fc232b5a..92694b29b 100644 --- a/tests/test_pre_commit_4_sync_execute.py +++ b/tests/external/pre_commit/test_pre_commit_4_sync_execute.py @@ -5,16 +5,8 @@ from jupytext import read, write -from .utils import ( - requires_user_kernel_python3, - skip_pre_commit_tests_on_windows, - skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo, -) - -@requires_user_kernel_python3 -@skip_pre_commit_tests_on_windows -@skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo +@pytest.mark.requires_user_kernel_python3 def test_pre_commit_hook_sync_execute( tmpdir, cwd_tmpdir, diff --git a/tests/test_pre_commit_5_reformat_markdown.py b/tests/external/pre_commit/test_pre_commit_5_reformat_markdown.py similarity index 93% rename from tests/test_pre_commit_5_reformat_markdown.py rename to tests/external/pre_commit/test_pre_commit_5_reformat_markdown.py index 86c04e1ee..e55654dbc 100644 --- a/tests/test_pre_commit_5_reformat_markdown.py +++ b/tests/external/pre_commit/test_pre_commit_5_reformat_markdown.py @@ -5,16 +5,8 @@ from jupytext import read, write -from .utils import ( - requires_pandoc, - skip_pre_commit_tests_on_windows, - skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo, -) - -@requires_pandoc -@skip_pre_commit_tests_on_windows -@skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo +@pytest.mark.requires_pandoc def test_pre_commit_hook_sync_reformat_code_and_markdown( tmpdir, cwd_tmpdir, diff --git a/tests/test_pre_commit_mode.py b/tests/external/pre_commit/test_pre_commit_mode.py similarity index 92% rename from tests/test_pre_commit_mode.py rename to tests/external/pre_commit/test_pre_commit_mode.py index ec35f08dc..125775f9b 100644 --- a/tests/test_pre_commit_mode.py +++ b/tests/external/pre_commit/test_pre_commit_mode.py @@ -254,3 +254,23 @@ def test_sync_pre_commit_mode_respects_commit_order_780( for file in commit_order: nb = read(file) assert nb.cells[0].source == "2 + 2", file + + +@pytest.mark.requires_user_kernel_python3 +def test_skip_execution(tmpdir, cwd_tmpdir, tmp_repo, python_notebook, capsys): + write( + new_notebook(cells=[new_code_cell("1 + 1")], metadata=python_notebook.metadata), + "test.ipynb", + ) + tmp_repo.index.add("test.ipynb") + + jupytext(["--execute", "--pre-commit-mode", "test.ipynb"]) + captured = capsys.readouterr() + assert "Executing notebook" in captured.out + + nb = read("test.ipynb") + assert nb.cells[0].execution_count == 1 + + jupytext(["--execute", "--pre-commit-mode", "test.ipynb"]) + captured = capsys.readouterr() + assert "skipped" in captured.out diff --git a/tests/test_pre_commit_scripts.py b/tests/external/pre_commit/test_pre_commit_scripts.py similarity index 95% rename from tests/test_pre_commit_scripts.py rename to tests/external/pre_commit/test_pre_commit_scripts.py index ae0116355..885986db9 100644 --- a/tests/test_pre_commit_scripts.py +++ b/tests/external/pre_commit/test_pre_commit_scripts.py @@ -9,14 +9,6 @@ from jupytext.cli import jupytext, system from jupytext.compare import compare_cells, compare_notebooks -from .utils import ( - list_notebooks, - requires_black, - requires_flake8, - requires_jupytext_installed, - requires_pandoc, -) - def git_in_tmpdir(tmpdir): """Return a function that will execute git instruction in the desired directory""" @@ -43,7 +35,6 @@ def system_(*args): return system_ -@requires_jupytext_installed def test_pre_commit_hook(tmpdir): tmp_ipynb = str(tmpdir.join("nb with spaces.ipynb")) tmp_py = str(tmpdir.join("nb with spaces.py")) @@ -70,7 +61,6 @@ def test_pre_commit_hook(tmpdir): assert os.path.isfile(tmp_py) -@requires_jupytext_installed def test_sync_with_pre_commit_hook(tmpdir): # Init git and create a pre-commit hook git = git_in_tmpdir(tmpdir) @@ -149,7 +139,6 @@ def test_sync_with_pre_commit_hook(tmpdir): git("commit", "-m", "added image") -@requires_jupytext_installed def test_pre_commit_hook_in_subfolder(tmpdir): tmp_ipynb = str(tmpdir.join("nb with spaces.ipynb")) tmp_py = str(tmpdir.join("python", "nb with spaces.py")) @@ -178,7 +167,6 @@ def test_pre_commit_hook_in_subfolder(tmpdir): assert os.path.isfile(tmp_py) -@requires_jupytext_installed def test_pre_commit_hook_py_to_ipynb_and_md(tmpdir): tmp_ipynb = str(tmpdir.join("nb with spaces.ipynb")) tmp_py = str(tmpdir.join("nb with spaces.py")) @@ -214,13 +202,11 @@ def test_pre_commit_hook_py_to_ipynb_and_md(tmpdir): assert os.path.isfile(tmp_md) -@requires_black -@requires_flake8 -@requires_jupytext_installed -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")[:1]) -def test_pre_commit_hook_sync_black_flake8(tmpdir, nb_file): +@pytest.mark.requires_black +@pytest.mark.requires_flake8 +def test_pre_commit_hook_sync_black_flake8(tmpdir, python_notebook): # Load real notebook metadata to get the 'auto' extension in --pipe-fmt to work - metadata = read(nb_file).metadata + metadata = python_notebook.metadata git = git_in_tmpdir(tmpdir) hook = str(tmpdir.join(".git/hooks/pre-commit")) @@ -287,7 +273,6 @@ def hook(): assert os.path.isfile(tmp_py) -@requires_jupytext_installed def test_pre_commit_hook_with_subfolders_issue_506(tmpdir): """I have the following directory structure, where the nb/test.ipynb is paired with the py/test.py. @@ -331,8 +316,7 @@ def test_pre_commit_hook_with_subfolders_issue_506(tmpdir): assert read(str(nb_file)).cells[0].source == "A Markdown cell" -@requires_pandoc -@requires_jupytext_installed +@pytest.mark.requires_pandoc def test_wrap_markdown_cell(tmpdir): """Use a pre-commit hook to sync a notebook to a script paired in a tree, and reformat the markdown cells using pandoc""" diff --git a/tests/external/round_trip/test_mirror_external.py b/tests/external/round_trip/test_mirror_external.py new file mode 100644 index 000000000..70467a197 --- /dev/null +++ b/tests/external/round_trip/test_mirror_external.py @@ -0,0 +1,53 @@ +import pytest + +from jupytext.compare import assert_conversion_same_as_mirror + +"""--------------------------------------------------------------------------------- + +Part I: ipynb -> fmt -> ipynb + +---------------------------------------------------------------------------------""" + + +@pytest.mark.requires_pandoc +def test_ipynb_to_pandoc(ipynb_to_pandoc, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_to_pandoc, "md:pandoc", "ipynb_to_pandoc") + + +@pytest.mark.requires_quarto +def test_ipynb_to_quarto( + ipynb_to_quarto, + no_jupytext_version_number, +): + assert_conversion_same_as_mirror(ipynb_to_quarto, "qmd", "ipynb_to_quarto") + + +@pytest.mark.requires_sphinx_gallery +def test_ipynb_to_python_sphinx(ipynb_to_sphinx, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_to_sphinx, "py:sphinx", "ipynb_to_sphinx") + + +"""--------------------------------------------------------------------------------- + +Part II: text -> ipynb -> text + +---------------------------------------------------------------------------------""" + + +def test_Rmd_to_ipynb(rmd_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(rmd_file, "ipynb", "Rmd_to_ipynb") + + +@pytest.mark.requires_sphinx_gallery +def test_sphinx_to_ipynb(sphinx_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(sphinx_file, "ipynb:sphinx", "sphinx_to_ipynb") + + +@pytest.mark.requires_sphinx_gallery +def test_sphinx_md_to_ipynb(sphinx_file, no_jupytext_version_number): + assert_conversion_same_as_mirror( + sphinx_file, + {"extension": ".ipynb", "format_name": "sphinx", "rst2md": True}, + "sphinx-rst2md_to_ipynb", + compare_notebook=True, + ) diff --git a/tests/external/rst2md/test_rst2md.py b/tests/external/rst2md/test_rst2md.py new file mode 100644 index 000000000..d89c6337f --- /dev/null +++ b/tests/external/rst2md/test_rst2md.py @@ -0,0 +1,69 @@ +import os + +import pytest +from nbformat.v4.nbbase import new_markdown_cell, new_notebook + +from jupytext import TextFileContentsManager, read, write +from jupytext.cli import jupytext + + +@pytest.mark.requires_sphinx_gallery +def test_rst2md(tmpdir, cwd_tmpdir): + tmp_py = "notebook.py" + tmp_ipynb = "notebook.ipynb" + + # Write notebook in sphinx format + nb = new_notebook( + cells=[ + new_markdown_cell("A short sphinx notebook"), + new_markdown_cell(":math:`1+1`"), + ] + ) + write(nb, tmp_py, fmt="py:sphinx") + + jupytext( + [ + tmp_py, + "--from", + "py:sphinx", + "--to", + "ipynb", + "--opt", + "rst2md=True", + "--opt", + "cell_metadata_filter=-all", + ] + ) + + assert os.path.isfile(tmp_ipynb) + nb = read(tmp_ipynb) + + assert nb.metadata["jupytext"]["cell_metadata_filter"] == "-all" + assert nb.metadata["jupytext"]["rst2md"] is False + + # Was rst to md conversion effective? + assert nb.cells[2].source == "$1+1$" + + +@pytest.mark.requires_sphinx_gallery +def test_rst2md_option(tmpdir): + tmp_py = str(tmpdir.join("notebook.py")) + + # Write notebook in sphinx format + nb = new_notebook( + cells=[ + new_markdown_cell("A short sphinx notebook"), + new_markdown_cell(":math:`1+1`"), + ] + ) + write(nb, tmp_py, fmt="py:sphinx") + + cm = TextFileContentsManager() + cm.sphinx_convert_rst2md = True + cm.root_dir = str(tmpdir) + + nb2 = cm.get("notebook.py")["content"] + + # Was rst to md conversion effective? + assert nb2.cells[2].source == "$1+1$" + assert nb2.metadata["jupytext"]["rst2md"] is False diff --git a/tests/test_read_simple_pandoc.py b/tests/external/simple_external_notebooks/test_read_simple_pandoc.py similarity index 93% rename from tests/test_read_simple_pandoc.py rename to tests/external/simple_external_notebooks/test_read_simple_pandoc.py index c0c24d461..406a254b7 100644 --- a/tests/test_read_simple_pandoc.py +++ b/tests/external/simple_external_notebooks/test_read_simple_pandoc.py @@ -6,10 +6,8 @@ from jupytext.compare import compare, compare_notebooks from jupytext.pandoc import PandocError -from .utils import requires_no_pandoc, requires_pandoc - -@requires_pandoc +@pytest.mark.requires_pandoc def test_pandoc_implicit( markdown="""# Lorem ipsum @@ -31,7 +29,7 @@ def test_pandoc_implicit( compare(markdown3, markdown2) -@requires_pandoc +@pytest.mark.requires_pandoc def test_pandoc_explicit( markdown="""::: {.cell .markdown} # Lorem @@ -46,7 +44,7 @@ def test_pandoc_explicit( compare("\n".join(markdown2.splitlines()[12:]), markdown) -@requires_pandoc +@pytest.mark.requires_pandoc def test_pandoc_utf8_in_md( markdown="""::: {.cell .markdown} # Utf-8 support @@ -60,7 +58,7 @@ def test_pandoc_utf8_in_md( compare("\n".join(markdown2.splitlines()[12:]), markdown) -@requires_pandoc +@pytest.mark.requires_pandoc def test_pandoc_utf8_in_nb( nb=new_notebook( cells=[ @@ -78,7 +76,7 @@ def test_pandoc_utf8_in_nb( compare_notebooks(nb, nb2, "md:pandoc") -@requires_no_pandoc +@pytest.mark.requires_no_pandoc def test_meaningfull_error_when_pandoc_is_missing(tmpdir): nb_file = tmpdir.join("notebook.ipynb") jupytext.write(new_notebook(), str(nb_file)) diff --git a/tests/test_read_simple_quarto.py b/tests/external/simple_external_notebooks/test_read_simple_quarto.py similarity index 94% rename from tests/test_read_simple_quarto.py rename to tests/external/simple_external_notebooks/test_read_simple_quarto.py index 1e50c2394..ed9aff054 100644 --- a/tests/test_read_simple_quarto.py +++ b/tests/external/simple_external_notebooks/test_read_simple_quarto.py @@ -1,12 +1,11 @@ +import pytest from nbformat.v4.nbbase import new_code_cell, new_markdown_cell, new_notebook import jupytext from jupytext.compare import compare, compare_notebooks -from .utils import requires_quarto - -@requires_quarto +@pytest.mark.requires_quarto def test_qmd_to_ipynb( qmd="""Some text diff --git a/tests/test_cli.py b/tests/functional/cli/test_cli.py similarity index 83% rename from tests/test_cli.py rename to tests/functional/cli/test_cli.py index 1b613b175..a3d229083 100644 --- a/tests/test_cli.py +++ b/tests/functional/cli/test_cli.py @@ -17,17 +17,9 @@ from jupytext.cli import jupytext, parse_jupytext_args, str2bool, system from jupytext.compare import compare, compare_notebooks from jupytext.formats import JupytextFormatError, long_form_one_format +from jupytext.myst import is_myst_available from jupytext.paired_paths import InconsistentPath, paired_paths - -from .utils import ( - list_notebooks, - requires_jupytext_installed, - requires_myst, - requires_pandoc, - requires_sphinx_gallery, - requires_user_kernel_python3, - skip_on_windows, -) +from jupytext.pandoc import is_pandoc_available def test_str2bool(): @@ -38,20 +30,22 @@ def test_str2bool(): str2bool("UNEXPECTED") -@pytest.mark.parametrize("nb_file", list_notebooks()) -def test_cli_single_file(nb_file): - assert parse_jupytext_args([nb_file] + ["--to", "py"]).notebooks == [nb_file] +def test_cli_single_file(ipynb_py_R_jl_file): + assert parse_jupytext_args([ipynb_py_R_jl_file] + ["--to", "py"]).notebooks == [ + ipynb_py_R_jl_file + ] -@pytest.mark.parametrize("nb_files", [list_notebooks()]) -def test_cli_multiple_files(nb_files): - assert parse_jupytext_args(nb_files + ["--to", "py"]).notebooks == nb_files +def test_cli_multiple_files(ipynb_py_R_jl_files): + assert ( + parse_jupytext_args(ipynb_py_R_jl_files + ["--to", "py"]).notebooks + == ipynb_py_R_jl_files + ) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_convert_single_file_in_place(nb_file, tmpdir): - nb_org = str(tmpdir.join(os.path.basename(nb_file))) - copyfile(nb_file, nb_org) +def test_convert_single_file_in_place(ipynb_py_file, tmpdir): + nb_org = str(tmpdir.join(os.path.basename(ipynb_py_file))) + copyfile(ipynb_py_file, nb_org) base, ext = os.path.splitext(nb_org) nb_other = base + ".py" @@ -64,11 +58,9 @@ def test_convert_single_file_in_place(nb_file, tmpdir): compare_notebooks(nb2, nb1) -@requires_jupytext_installed -def test_convert_single_file_in_place_m(tmpdir): - nb_file = list_notebooks("ipynb_py")[0] - nb_org = str(tmpdir.join(os.path.basename(nb_file))) - copyfile(nb_file, nb_org) +def test_convert_single_file_in_place_m(ipynb_py_file, tmpdir): + nb_org = str(tmpdir.join(os.path.basename(ipynb_py_file))) + copyfile(ipynb_py_file, nb_org) base, ext = os.path.splitext(nb_org) @@ -83,12 +75,11 @@ def test_convert_single_file_in_place_m(tmpdir): compare_notebooks(nb2, nb1) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb") + list_notebooks("Rmd")) -def test_convert_single_file(nb_file, tmpdir, capsys): - nb_org = str(tmpdir.join(os.path.basename(nb_file))) - copyfile(nb_file, nb_org) +def test_convert_single_file(ipynb_or_rmd_file, tmpdir, capsys): + nb_org = str(tmpdir.join(os.path.basename(ipynb_or_rmd_file))) + copyfile(ipynb_or_rmd_file, nb_org) - nb1 = read(nb_file) + nb1 = read(ipynb_or_rmd_file) pynb = writes(nb1, "py") jupytext([nb_org, "--to", "py", "-o", "-"]) @@ -125,10 +116,9 @@ def test_wildcard(tmpdir): jupytext(["nb3.ipynb", "--to", "py"]) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_cpp")) -def test_to_cpluplus(nb_file, tmpdir, capsys): - nb_org = str(tmpdir.join(os.path.basename(nb_file))) - copyfile(nb_file, nb_org) +def test_to_cpluplus(ipynb_cpp_file, tmpdir, capsys): + nb_org = str(tmpdir.join(os.path.basename(ipynb_cpp_file))) + copyfile(ipynb_cpp_file, nb_org) nb1 = read(nb_org) text_cpp = writes(nb1, "cpp") @@ -139,12 +129,11 @@ def test_to_cpluplus(nb_file, tmpdir, capsys): compare(out, text_cpp) -@pytest.mark.parametrize("nb_files", [list_notebooks("ipynb_py")]) -def test_convert_multiple_file(nb_files, tmpdir): +def test_convert_multiple_file(ipynb_py_files, tmpdir): nb_orgs = [] nb_others = [] - for nb_file in nb_files: + for nb_file in ipynb_py_files: nb_org = str(tmpdir.join(os.path.basename(nb_file))) base, ext = os.path.splitext(nb_org) nb_other = base + ".py" @@ -309,13 +298,12 @@ def test_combine_lower_version_raises(tmpdir): jupytext([tmp_nbpy, "--to", "ipynb", "--update"]) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_ipynb_to_py_then_update_test(nb_file, tmpdir): +def test_ipynb_to_py_then_update_test(ipynb_py_file, tmpdir): """Reproduce https://github.com/mwouts/jupytext/issues/83""" tmp_ipynb = str(tmpdir.join("notebook.ipynb")) tmp_nbpy = str(tmpdir.join("notebook.py")) - copyfile(nb_file, tmp_ipynb) + copyfile(ipynb_py_file, tmp_ipynb) jupytext(["--to", "py", tmp_ipynb]) jupytext(["--test", "--update", "--to", "ipynb", tmp_nbpy]) @@ -350,12 +338,11 @@ def test_test_to_ipynb_ignore_version_number_414( assert jupytext(["--test", "--to", "ipynb", tmp_py]) == 0 -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_convert_to_percent_format(nb_file, tmpdir): +def test_convert_to_percent_format(ipynb_py_file, tmpdir): tmp_ipynb = str(tmpdir.join("notebook.ipynb")) tmp_nbpy = str(tmpdir.join("notebook.py")) - copyfile(nb_file, tmp_ipynb) + copyfile(ipynb_py_file, tmp_ipynb) jupytext(["--to", "py:percent", tmp_ipynb]) @@ -369,12 +356,11 @@ def test_convert_to_percent_format(nb_file, tmpdir): compare_notebooks(nb2, nb1) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_convert_to_percent_format_and_keep_magics(nb_file, tmpdir): +def test_convert_to_percent_format_and_keep_magics(ipynb_py_file, tmpdir): tmp_ipynb = str(tmpdir.join("notebook.ipynb")) tmp_nbpy = str(tmpdir.join("notebook.py")) - copyfile(nb_file, tmp_ipynb) + copyfile(ipynb_py_file, tmp_ipynb) jupytext(["--to", "py:percent", "--opt", "comment_magics=False", tmp_ipynb]) @@ -390,24 +376,22 @@ def test_convert_to_percent_format_and_keep_magics(nb_file, tmpdir): compare_notebooks(nb2, nb1) -@pytest.mark.parametrize("py_file", list_notebooks("python")) -def test_set_formats(py_file, tmpdir): +def test_set_formats(python_file, tmpdir): tmp_py = str(tmpdir.join("notebook.py")) tmp_ipynb = str(tmpdir.join("notebook.ipynb")) - copyfile(py_file, tmp_py) + copyfile(python_file, tmp_py) jupytext([tmp_py, "--set-formats", "ipynb,py:light"]) nb = read(tmp_ipynb) assert nb.metadata["jupytext"]["formats"] == "ipynb,py:light" -@pytest.mark.parametrize("py_file", list_notebooks("python")) -def test_update_metadata(py_file, tmpdir, capsys): +def test_update_metadata(python_file, tmpdir, capsys): tmp_py = str(tmpdir.join("notebook.py")) tmp_ipynb = str(tmpdir.join("notebook.ipynb")) - copyfile(py_file, tmp_py) + copyfile(python_file, tmp_py) jupytext( [ @@ -436,12 +420,11 @@ def test_update_metadata(py_file, tmpdir, capsys): assert "invalid" in err -@requires_user_kernel_python3 -@pytest.mark.parametrize("py_file", list_notebooks("python")) -def test_set_kernel_inplace(py_file, tmpdir): +@pytest.mark.requires_user_kernel_python3 +def test_set_kernel_inplace(python_file, tmpdir): tmp_py = str(tmpdir.join("notebook.py")) - copyfile(py_file, tmp_py) + copyfile(python_file, tmp_py) jupytext([tmp_py, "--set-kernel", "-"]) @@ -451,13 +434,12 @@ def test_set_kernel_inplace(py_file, tmpdir): assert cmd == "python" or os.path.samefile(cmd, sys.executable) -@requires_user_kernel_python3 -@pytest.mark.parametrize("py_file", list_notebooks("python")) -def test_set_kernel_auto(py_file, tmpdir): +@pytest.mark.requires_user_kernel_python3 +def test_set_kernel_auto(python_file, tmpdir): tmp_py = str(tmpdir.join("notebook.py")) tmp_ipynb = str(tmpdir.join("notebook.ipynb")) - copyfile(py_file, tmp_py) + copyfile(python_file, tmp_py) jupytext(["--to", "ipynb", tmp_py, "--set-kernel", "-"]) @@ -467,13 +449,12 @@ def test_set_kernel_auto(py_file, tmpdir): assert cmd == "python" or os.path.samefile(cmd, sys.executable) -@requires_user_kernel_python3 -@pytest.mark.parametrize("py_file", list_notebooks("python")) -def test_set_kernel_with_name(py_file, tmpdir): +@pytest.mark.requires_user_kernel_python3 +def test_set_kernel_with_name(python_file, tmpdir): tmp_py = str(tmpdir.join("notebook.py")) tmp_ipynb = str(tmpdir.join("notebook.ipynb")) - copyfile(py_file, tmp_py) + copyfile(python_file, tmp_py) for kernel in find_kernel_specs(): jupytext(["--to", "ipynb", tmp_py, "--set-kernel", kernel]) @@ -485,10 +466,9 @@ def test_set_kernel_with_name(py_file, tmpdir): jupytext(["--to", "ipynb", tmp_py, "--set-kernel", "non_existing_env"]) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_paired_paths(nb_file, tmpdir, capsys): +def test_paired_paths(ipynb_py_file, tmpdir, capsys): tmp_ipynb = str(tmpdir.join("notebook.ipynb")) - nb = read(nb_file) + nb = read(ipynb_py_file) nb.metadata.setdefault("jupytext", {})[ "formats" ] = "ipynb,_light.py,_percent.py:percent" @@ -505,12 +485,11 @@ def test_paired_paths(nb_file, tmpdir, capsys): } -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_sync(nb_file, tmpdir, cwd_tmpdir, capsys): +def test_sync(ipynb_py_file, tmpdir, cwd_tmpdir, capsys): tmp_ipynb = "notebook.ipynb" tmp_py = "notebook.py" tmp_rmd = "notebook.Rmd" - nb = read(nb_file) + nb = read(ipynb_py_file) write(nb, tmp_ipynb) # Test that sync issues a warning when the notebook is not paired @@ -556,14 +535,11 @@ def test_sync(nb_file, tmpdir, cwd_tmpdir, capsys): assert os.path.getmtime(tmp_ipynb) <= os.path.getmtime(tmp_py) -@requires_pandoc -@pytest.mark.parametrize( - "nb_file", list_notebooks("ipynb_py", skip="(Notebook with|flavors|305)") -) -def test_sync_pandoc(nb_file, tmpdir, cwd_tmpdir, capsys): +@pytest.mark.requires_pandoc +def test_sync_pandoc(ipynb_to_pandoc, tmpdir, cwd_tmpdir, capsys): tmp_ipynb = "notebook.ipynb" tmp_md = "notebook.md" - nb = read(nb_file) + nb = read(ipynb_to_pandoc) write(nb, tmp_ipynb) # Test that sync issues a warning when the notebook is not paired @@ -585,16 +561,12 @@ def test_sync_pandoc(nb_file, tmpdir, cwd_tmpdir, capsys): assert "pandoc" in fp.read() -@pytest.mark.parametrize( - "nb_file,ext", - [(nb_file, ".py") for nb_file in list_notebooks("ipynb_py")] - + [(nb_file, ".R") for nb_file in list_notebooks("ipynb_R")] - + [(nb_file, ".jl") for nb_file in list_notebooks("ipynb_julia")], -) -def test_cli_can_infer_jupytext_format(nb_file, ext, tmpdir, cwd_tmpdir): +def test_cli_can_infer_jupytext_format( + ipynb_py_R_jl_file, ipynb_py_R_jl_ext, tmpdir, cwd_tmpdir +): tmp_ipynb = "notebook.ipynb" - tmp_text = "notebook" + ext - nb = read(nb_file) + tmp_text = "notebook" + ipynb_py_R_jl_ext + nb = read(ipynb_py_R_jl_file) # Light format to Jupyter notebook write(nb, tmp_text) @@ -603,21 +575,16 @@ def test_cli_can_infer_jupytext_format(nb_file, ext, tmpdir, cwd_tmpdir): compare_notebooks(nb2, nb) # Percent format to Jupyter notebook - write(nb, tmp_text, fmt=ext + ":percent") + write(nb, tmp_text, fmt=ipynb_py_R_jl_ext + ":percent") jupytext(["--to", "notebook", tmp_text]) nb2 = read(tmp_ipynb) compare_notebooks(nb2, nb) -@pytest.mark.parametrize( - "nb_file,ext", - [(nb_file, ".py") for nb_file in list_notebooks("ipynb_py")] - + [(nb_file, ".R") for nb_file in list_notebooks("ipynb_R")], -) -def test_cli_to_script(nb_file, ext, tmpdir, cwd_tmpdir): +def test_cli_to_script(ipynb_py_R_jl_file, ipynb_py_R_jl_ext, tmpdir, cwd_tmpdir): tmp_ipynb = "notebook.ipynb" - tmp_text = "notebook" + ext - nb = read(nb_file) + tmp_text = "notebook" + ipynb_py_R_jl_ext + nb = read(ipynb_py_R_jl_file) write(nb, tmp_ipynb) jupytext(["--to", "script", tmp_ipynb]) @@ -625,15 +592,10 @@ def test_cli_to_script(nb_file, ext, tmpdir, cwd_tmpdir): compare_notebooks(nb2, nb) -@pytest.mark.parametrize( - "nb_file,ext", - [(nb_file, ".py") for nb_file in list_notebooks("ipynb_py")] - + [(nb_file, ".R") for nb_file in list_notebooks("ipynb_R")], -) -def test_cli_to_auto(nb_file, ext, tmpdir, cwd_tmpdir): +def test_cli_to_auto(ipynb_py_R_jl_file, ipynb_py_R_jl_ext, tmpdir, cwd_tmpdir): tmp_ipynb = "notebook.ipynb" - tmp_text = "notebook" + ext - nb = read(nb_file) + tmp_text = "notebook" + ipynb_py_R_jl_ext + nb = read(ipynb_py_R_jl_file) write(nb, tmp_ipynb) jupytext(["--to", "auto", tmp_ipynb]) @@ -641,15 +603,14 @@ def test_cli_to_auto(nb_file, ext, tmpdir, cwd_tmpdir): compare_notebooks(nb2, nb) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_cli_can_infer_jupytext_format_from_stdin(nb_file, tmpdir, cwd_tmpdir): +def test_cli_can_infer_jupytext_format_from_stdin(ipynb_py_file, tmpdir, cwd_tmpdir): tmp_ipynb = "notebook.ipynb" tmp_py = "notebook.py" tmp_rmd = "notebook.Rmd" - nb = read(nb_file) + nb = read(ipynb_py_file) # read ipynb notebook on stdin, write to python - with open(nb_file) as fp, mock.patch("sys.stdin", fp): + with open(ipynb_py_file) as fp, mock.patch("sys.stdin", fp): jupytext(["--to", "py:percent", "-o", tmp_py]) nb2 = read(tmp_py) compare_notebooks(nb2, nb) @@ -661,7 +622,7 @@ def test_cli_can_infer_jupytext_format_from_stdin(nb_file, tmpdir, cwd_tmpdir): compare_notebooks(nb2, nb) # read ipynb notebook on stdin, write to R markdown - with open(nb_file) as fp, mock.patch("sys.stdin", fp): + with open(ipynb_py_file) as fp, mock.patch("sys.stdin", fp): jupytext(["-o", tmp_rmd]) nb2 = read(tmp_rmd) compare_notebooks(nb2, nb, "Rmd") @@ -673,7 +634,7 @@ def test_cli_can_infer_jupytext_format_from_stdin(nb_file, tmpdir, cwd_tmpdir): compare_notebooks(nb2, nb, "Rmd") -@requires_user_kernel_python3 +@pytest.mark.requires_user_kernel_python3 def test_set_kernel_works_with_pipes_326(capsys): md = """```python 1 + 1 @@ -688,25 +649,6 @@ def test_set_kernel_works_with_pipes_326(capsys): assert "kernelspec" in nb.metadata -@requires_user_kernel_python3 -@skip_on_windows -@pytest.mark.filterwarnings("ignore") -def test_utf8_out_331(capsys, caplog): - py = "from IPython.core.display import HTML; HTML(u'\xd7')" - - with mock.patch("sys.stdin", StringIO(py)): - jupytext(["--to", "ipynb", "--execute", "-"]) - - out, err = capsys.readouterr() - - assert err == "" - nb = reads(out, "ipynb") - assert len(nb.cells) == 1 - print(nb.cells[0].outputs) - assert nb.cells[0].outputs[0]["data"]["text/html"] == "\xd7" - - -@requires_jupytext_installed @pytest.mark.filterwarnings("ignore:The --pre-commit argument is deprecated") def test_cli_expect_errors(tmp_ipynb): with pytest.raises(ValueError): @@ -838,44 +780,6 @@ def test_cli_sync_file_with_prefix_974(tmp_path, python_notebook): assert (tmp_path / "examples/folder1/example_paired.py").exists(), "Paired" -@requires_sphinx_gallery -def test_rst2md(tmpdir, cwd_tmpdir): - tmp_py = "notebook.py" - tmp_ipynb = "notebook.ipynb" - - # Write notebook in sphinx format - nb = new_notebook( - cells=[ - new_markdown_cell("A short sphinx notebook"), - new_markdown_cell(":math:`1+1`"), - ] - ) - write(nb, tmp_py, fmt="py:sphinx") - - jupytext( - [ - tmp_py, - "--from", - "py:sphinx", - "--to", - "ipynb", - "--opt", - "rst2md=True", - "--opt", - "cell_metadata_filter=-all", - ] - ) - - assert os.path.isfile(tmp_ipynb) - nb = read(tmp_ipynb) - - assert nb.metadata["jupytext"]["cell_metadata_filter"] == "-all" - assert nb.metadata["jupytext"]["rst2md"] is False - - # Was rst to md conversion effective? - assert nb.cells[2].source == "$1+1$" - - def test_remove_jupytext_metadata(tmpdir, cwd_tmpdir): tmp_ipynb = "notebook.ipynb" nb = new_notebook( @@ -907,26 +811,25 @@ def test_remove_jupytext_metadata(tmpdir, cwd_tmpdir): } -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) @pytest.mark.parametrize("fmt", ["py:light", "py:percent", "md"]) -def test_convert_and_update_preserves_notebook(nb_file, fmt, tmpdir, cwd_tmpdir): +def test_convert_and_update_preserves_notebook(ipynb_py_file, fmt, tmpdir, cwd_tmpdir): # cannot encode magic parameters in markdown yet - if ("magic" in nb_file or "LateX" in nb_file) and fmt == "md": + if ("magic" in ipynb_py_file or "LateX" in ipynb_py_file) and fmt == "md": return tmp_ipynb = "notebook.ipynb" - copyfile(nb_file, tmp_ipynb) + copyfile(ipynb_py_file, tmp_ipynb) ext = long_form_one_format(fmt)["extension"] tmp_text = "notebook" + ext jupytext(["--to", fmt, tmp_ipynb]) jupytext(["--to", "ipynb", "--update", tmp_text]) - nb_org = read(nb_file) + nb_org = read(ipynb_py_file) nb_now = read(tmp_ipynb) # The cell marker changes from """ to r""" on the LateX notebook #836 - if "LateX" in nb_file and fmt == "py:percent": + if "LateX" in ipynb_py_file and fmt == "py:percent": last_cell = nb_now.cells[-1] last_cell.metadata["cell_marker"] = last_cell.metadata["cell_marker"][1:] @@ -1040,15 +943,10 @@ def test_set_format_with_subfolder(tmpdir, cwd_tmpdir): def skip_if_format_missing(format_name): """Check whether MyST or Pandoc are available and skip if not""" - if format_name == "md:myst": - mark = requires_myst() - elif format_name == "md:pandoc": - mark = requires_pandoc() - else: - return - - if mark.args[0]: - pytest.skip(**mark.kwargs) + if format_name == "md:myst" and not is_myst_available(): + pytest.skip("MyST markdown is not available") + elif format_name == "md:pandoc" and not is_pandoc_available(): + pytest.skip("Pandoc is not available") @pytest.mark.parametrize("format_name", ["md", "md:myst", "md:pandoc"]) @@ -1065,7 +963,7 @@ def test_create_header_with_set_formats(format_name, cwd_tmpdir, tmpdir): assert nb["metadata"]["jupytext"]["formats"] == format_name -@requires_user_kernel_python3 +@pytest.mark.requires_user_kernel_python3 @pytest.mark.parametrize( "format_name", ["md", "md:myst", "md:pandoc", "py:light", "py:percent"] ) @@ -1136,7 +1034,7 @@ def test_pair_in_tree_and_parent(tmpdir): assert "A markdown cell" in py_file.read() -@requires_pandoc +@pytest.mark.requires_pandoc def test_sync_pipe_config(tmpdir): """Sync a notebook to a script paired in a tree, and reformat the markdown cells using pandoc""" @@ -1256,26 +1154,6 @@ def test_show_changes(tmpdir, cwd_tmpdir, capsys): assert "-2 + 2\n+1 + 1" in captured.out -@requires_user_kernel_python3 -def test_skip_execution(tmpdir, cwd_tmpdir, tmp_repo, python_notebook, capsys): - write( - new_notebook(cells=[new_code_cell("1 + 1")], metadata=python_notebook.metadata), - "test.ipynb", - ) - tmp_repo.index.add("test.ipynb") - - jupytext(["--execute", "--pre-commit-mode", "test.ipynb"]) - captured = capsys.readouterr() - assert "Executing notebook" in captured.out - - nb = read("test.ipynb") - assert nb.cells[0].execution_count == 1 - - jupytext(["--execute", "--pre-commit-mode", "test.ipynb"]) - captured = capsys.readouterr() - assert "skipped" in captured.out - - def test_glob_recursive(tmpdir, cwd_tmpdir): tmpdir.mkdir("subfolder").join("test.py").write("1 + 1\n") tmpdir.join("test.py").write("2 + 2\n") diff --git a/tests/test_cli_config.py b/tests/functional/cli/test_cli_config.py similarity index 100% rename from tests/test_cli_config.py rename to tests/functional/cli/test_cli_config.py diff --git a/tests/test_config.py b/tests/functional/config/test_config.py similarity index 100% rename from tests/test_config.py rename to tests/functional/config/test_config.py diff --git a/tests/test_changelog.py b/tests/functional/docs/test_changelog.py similarity index 92% rename from tests/test_changelog.py rename to tests/functional/docs/test_changelog.py index 787a560e9..fd6a31af7 100644 --- a/tests/test_changelog.py +++ b/tests/functional/docs/test_changelog.py @@ -34,7 +34,7 @@ def test_replace_issue_numbers_with_links(input, output): sys.version_info < (3, 5), reason="'PosixPath' object has no attribute 'read_text'" ) def test_update_changelog(): - changelog_file = Path(__file__).parent.parent / "CHANGELOG.md" + changelog_file = Path(__file__).parent.parent.parent.parent / "CHANGELOG.md" cur_text = changelog_file.read_text() new_text = replace_issue_number_with_links(cur_text) if cur_text != new_text: diff --git a/tests/test_doc_files_are_notebooks.py b/tests/functional/docs/test_doc_files_are_notebooks.py similarity index 88% rename from tests/test_doc_files_are_notebooks.py rename to tests/functional/docs/test_doc_files_are_notebooks.py index bc2ed2492..12d436554 100644 --- a/tests/test_doc_files_are_notebooks.py +++ b/tests/functional/docs/test_doc_files_are_notebooks.py @@ -6,7 +6,7 @@ def documentation_files(): - for path in (Path(__file__).parent.parent / "docs").iterdir(): + for path in (Path(__file__).parent / "../../../docs").iterdir(): if path.suffix == ".md": yield path diff --git a/tests/test_metadata_filter.py b/tests/functional/metadata/test_metadata_filter.py similarity index 99% rename from tests/test_metadata_filter.py rename to tests/functional/metadata/test_metadata_filter.py index 05004df45..3438ee061 100644 --- a/tests/test_metadata_filter.py +++ b/tests/functional/metadata/test_metadata_filter.py @@ -8,8 +8,6 @@ from jupytext.compare import compare, compare_notebooks from jupytext.metadata_filter import filter_metadata, metadata_filter_as_dict -from .utils import requires_myst - def to_dict(keys): return {key: None for key in keys} @@ -175,7 +173,7 @@ def test_default_config_has_priority_over_current_metadata( ) -@requires_myst +@pytest.mark.requires_myst def test_metadata_filter_in_notebook_757(): md = """--- jupytext: diff --git a/tests/test_metadata_filters_from_config.py b/tests/functional/metadata/test_metadata_filters_from_config.py similarity index 100% rename from tests/test_metadata_filters_from_config.py rename to tests/functional/metadata/test_metadata_filters_from_config.py diff --git a/tests/invalid_file_896.md b/tests/functional/others/invalid_file_896.md similarity index 100% rename from tests/invalid_file_896.md rename to tests/functional/others/invalid_file_896.md diff --git a/tests/test_active_cells.py b/tests/functional/others/test_active_cells.py similarity index 100% rename from tests/test_active_cells.py rename to tests/functional/others/test_active_cells.py diff --git a/tests/test_auto_ext.py b/tests/functional/others/test_auto_ext.py similarity index 78% rename from tests/test_auto_ext.py rename to tests/functional/others/test_auto_ext.py index ef0debc7f..e427ce273 100644 --- a/tests/test_auto_ext.py +++ b/tests/functional/others/test_auto_ext.py @@ -3,14 +3,9 @@ from jupytext import read, reads, writes from jupytext.formats import JupytextFormatError, auto_ext_from_metadata -from .utils import list_notebooks - -@pytest.mark.parametrize( - "nb_file", list_notebooks("ipynb_R") + list_notebooks("ipynb_py") -) -def test_auto_in_fmt(nb_file): - nb = read(nb_file) +def test_auto_in_fmt(ipynb_py_R_file): + nb = read(ipynb_py_R_file) auto_ext = auto_ext_from_metadata(nb.metadata) fmt = auto_ext[1:] + ":percent" text = writes(nb, "auto:percent") @@ -27,9 +22,8 @@ def test_auto_in_fmt(nb_file): writes(nb, "auto:percent") -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_all")) -def test_auto_from_kernelspecs_works(nb_file): - nb = read(nb_file) +def test_auto_from_kernelspecs_works(ipynb_file): + nb = read(ipynb_file) language_info = nb.metadata.pop("language_info") expected_ext = language_info.get("file_extension") if not expected_ext: @@ -46,12 +40,10 @@ def test_auto_from_kernelspecs_works(nb_file): assert auto_ext == expected_ext -@pytest.mark.parametrize( - "nb_file", - list_notebooks("ipynb_R") + list_notebooks("ipynb_py", skip="(World|plotly)"), -) -def test_auto_in_formats(nb_file): - nb = read(nb_file) +def test_auto_in_formats(ipynb_py_R_jl_file): + if any(pattern in ipynb_py_R_jl_file for pattern in ["plotly", "julia"]): + pytest.skip() + nb = read(ipynb_py_R_jl_file) nb.metadata["jupytext"] = {"formats": "ipynb,auto:percent"} fmt = auto_ext_from_metadata(nb.metadata)[1:] + ":percent" expected_formats = "ipynb," + fmt diff --git a/tests/test_cell_markers.py b/tests/functional/others/test_cell_markers.py similarity index 100% rename from tests/test_cell_markers.py rename to tests/functional/others/test_cell_markers.py diff --git a/tests/test_cell_metadata.py b/tests/functional/others/test_cell_metadata.py similarity index 100% rename from tests/test_cell_metadata.py rename to tests/functional/others/test_cell_metadata.py diff --git a/tests/test_cell_tags_are_preserved.py b/tests/functional/others/test_cell_tags_are_preserved.py similarity index 91% rename from tests/test_cell_tags_are_preserved.py rename to tests/functional/others/test_cell_tags_are_preserved.py index 1c20e5b30..2069e40cf 100644 --- a/tests/test_cell_tags_are_preserved.py +++ b/tests/functional/others/test_cell_tags_are_preserved.py @@ -2,8 +2,7 @@ from nbformat.v4.nbbase import new_code_cell, new_markdown_cell from jupytext import reads, writes - -from .utils import formats_with_support_for_cell_metadata, is_myst_available +from jupytext.formats import formats_with_support_for_cell_metadata, is_myst_available @pytest.fixture() diff --git a/tests/test_cells.py b/tests/functional/others/test_cells.py similarity index 100% rename from tests/test_cells.py rename to tests/functional/others/test_cells.py diff --git a/tests/test_combine.py b/tests/functional/others/test_combine.py similarity index 96% rename from tests/test_combine.py rename to tests/functional/others/test_combine.py index bb658ac09..c801f8fba 100644 --- a/tests/test_combine.py +++ b/tests/functional/others/test_combine.py @@ -1,14 +1,11 @@ from copy import deepcopy -import pytest from nbformat.v4.nbbase import new_code_cell, new_markdown_cell, new_notebook import jupytext from jupytext.combine import combine_inputs_with_outputs from jupytext.compare import compare, compare_notebooks -from .utils import list_notebooks - def test_combine(): nb_source = new_notebook( @@ -133,9 +130,8 @@ def test_read_text_and_combine_with_outputs(tmpdir): assert len(nb.cells) == 3 -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_all")) -def test_combine_stable(nb_file): - nb_org = jupytext.read(nb_file) +def test_combine_stable(ipynb_file): + nb_org = jupytext.read(ipynb_file) nb_source = deepcopy(nb_org) nb_outputs = deepcopy(nb_org) diff --git a/tests/test_custom_cell_magics.py b/tests/functional/others/test_custom_cell_magics.py similarity index 100% rename from tests/test_custom_cell_magics.py rename to tests/functional/others/test_custom_cell_magics.py diff --git a/tests/test_doxygen.py b/tests/functional/others/test_doxygen.py similarity index 100% rename from tests/test_doxygen.py rename to tests/functional/others/test_doxygen.py diff --git a/tests/test_hide_remove_input_outputs_rmarkdown.py b/tests/functional/others/test_hide_remove_input_outputs_rmarkdown.py similarity index 100% rename from tests/test_hide_remove_input_outputs_rmarkdown.py rename to tests/functional/others/test_hide_remove_input_outputs_rmarkdown.py diff --git a/tests/test_invalid_file.py b/tests/functional/others/test_invalid_file.py similarity index 94% rename from tests/test_invalid_file.py rename to tests/functional/others/test_invalid_file.py index ba7cf8ec1..f40767291 100644 --- a/tests/test_invalid_file.py +++ b/tests/functional/others/test_invalid_file.py @@ -8,15 +8,13 @@ from jupytext import TextFileContentsManager, read from jupytext.cli import jupytext as jupytext_cli -from .utils import skip_on_windows - @pytest.fixture def invalid_md_file(): return Path(__file__).parent / "invalid_file_896.md" -@skip_on_windows +@pytest.mark.skip_on_windows def test_read_invalid_md_file_fails(invalid_md_file): with open(invalid_md_file) as fp: with pytest.raises(UnicodeDecodeError): diff --git a/tests/test_jupytext_errors.py b/tests/functional/others/test_jupytext_errors.py similarity index 100% rename from tests/test_jupytext_errors.py rename to tests/functional/others/test_jupytext_errors.py diff --git a/tests/test_jupytext_read.py b/tests/functional/others/test_jupytext_read.py similarity index 100% rename from tests/test_jupytext_read.py rename to tests/functional/others/test_jupytext_read.py diff --git a/tests/test_nbformat_version.py b/tests/functional/others/test_nbformat_version.py similarity index 100% rename from tests/test_nbformat_version.py rename to tests/functional/others/test_nbformat_version.py diff --git a/tests/test_preserve_empty_cells.py b/tests/functional/others/test_preserve_empty_cells.py similarity index 100% rename from tests/test_preserve_empty_cells.py rename to tests/functional/others/test_preserve_empty_cells.py diff --git a/tests/test_pytest.py b/tests/functional/others/test_pytest.py similarity index 100% rename from tests/test_pytest.py rename to tests/functional/others/test_pytest.py diff --git a/tests/test_raw_strings.py b/tests/functional/others/test_raw_strings.py similarity index 100% rename from tests/test_raw_strings.py rename to tests/functional/others/test_raw_strings.py diff --git a/tests/test_read_write_functions.py b/tests/functional/others/test_read_write_functions.py similarity index 100% rename from tests/test_read_write_functions.py rename to tests/functional/others/test_read_write_functions.py diff --git a/tests/test_remove_encoding.py b/tests/functional/others/test_remove_encoding.py similarity index 100% rename from tests/test_remove_encoding.py rename to tests/functional/others/test_remove_encoding.py diff --git a/tests/test_sample_notebooks_are_normalized.py b/tests/functional/others/test_sample_notebooks_are_normalized.py similarity index 61% rename from tests/test_sample_notebooks_are_normalized.py rename to tests/functional/others/test_sample_notebooks_are_normalized.py index 54c65fc40..573fe5d68 100644 --- a/tests/test_sample_notebooks_are_normalized.py +++ b/tests/functional/others/test_sample_notebooks_are_normalized.py @@ -3,19 +3,16 @@ import jupytext -from .utils import list_notebooks - @pytest.mark.skipif(nbformat.__version__ <= "5.7", reason="normalize is not available") -@pytest.mark.parametrize("nb_file", list_notebooks("all", skip="(invalid|pyc)")) -def test_sample_notebooks_are_normalized(nb_file): - nb = jupytext.read(nb_file) +def test_sample_notebooks_are_normalized(any_nb_file): + nb = jupytext.read(any_nb_file) changes, normalized_nb = nbformat.validator.normalize(nb) nbformat.validate(normalized_nb) if changes: # pragma: no cover - with open(nb_file, "w") as fp: + with open(any_nb_file, "w") as fp: jupytext.write(normalized_nb, fp) assert not changes diff --git a/tests/test_save_multiple.py b/tests/functional/others/test_save_multiple.py similarity index 85% rename from tests/test_save_multiple.py rename to tests/functional/others/test_save_multiple.py index 0a7fb62c0..0e23c79c3 100644 --- a/tests/test_save_multiple.py +++ b/tests/functional/others/test_save_multiple.py @@ -6,15 +6,12 @@ from tornado.web import HTTPError import jupytext -from jupytext.compare import compare_notebooks +from jupytext.compare import compare_notebooks, notebook_model from jupytext.contentsmanager import TextFileContentsManager -from .utils import list_notebooks, notebook_model - -@pytest.mark.parametrize("nb_file", list_notebooks(skip="66")) -def test_rmd_is_ok(nb_file, tmpdir): - nb = jupytext.read(nb_file) +def test_rmd_is_ok(ipynb_file, tmpdir): + nb = jupytext.read(ipynb_file) tmp_ipynb = "notebook.ipynb" tmp_rmd = "notebook.Rmd" @@ -30,9 +27,8 @@ def test_rmd_is_ok(nb_file, tmpdir): compare_notebooks(nb2, nb, "Rmd") -@pytest.mark.parametrize("nb_file", list_notebooks("Rmd")) -def test_ipynb_is_ok(nb_file, tmpdir): - nb = jupytext.read(nb_file) +def test_ipynb_is_ok(rmd_file, tmpdir): + nb = jupytext.read(rmd_file) tmp_ipynb = "notebook.ipynb" tmp_rmd = "notebook.Rmd" @@ -46,9 +42,8 @@ def test_ipynb_is_ok(nb_file, tmpdir): compare_notebooks(nb2, nb) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py", skip="66")) -def test_all_files_created(nb_file, tmpdir): - nb = jupytext.read(nb_file) +def test_all_files_created(ipynb_py_file, tmpdir): + nb = jupytext.read(ipynb_py_file) tmp_ipynb = "notebook.ipynb" tmp_rmd = "notebook.Rmd" tmp_py = "notebook.py" diff --git a/tests/test_trust_notebook.py b/tests/functional/others/test_trust_notebook.py similarity index 82% rename from tests/test_trust_notebook.py rename to tests/functional/others/test_trust_notebook.py index 1a2b7c13b..05c4b24a8 100644 --- a/tests/test_trust_notebook.py +++ b/tests/functional/others/test_trust_notebook.py @@ -7,37 +7,36 @@ from jupytext.compare import compare_notebooks from jupytext.contentsmanager import TextFileContentsManager -from .utils import list_notebooks, requires_myst - -@pytest.mark.parametrize("nb_file", list_notebooks("python")) -def test_py_notebooks_are_trusted(nb_file): +def test_py_notebooks_are_trusted(python_file): cm = TextFileContentsManager() - root, file = os.path.split(nb_file) + root, file = os.path.split(python_file) cm.root_dir = root nb = cm.get(file) for cell in nb["content"].cells: assert cell.metadata.get("trusted", True) -@pytest.mark.parametrize("nb_file", list_notebooks("Rmd")) -def test_rmd_notebooks_are_trusted(nb_file): +def test_rmd_notebooks_are_trusted(rmd_file): cm = TextFileContentsManager() - root, file = os.path.split(nb_file) + root, file = os.path.split(rmd_file) cm.root_dir = root nb = cm.get(file) for cell in nb["content"].cells: assert cell.metadata.get("trusted", True) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py", skip="hash sign")) -def test_ipynb_notebooks_can_be_trusted(nb_file, tmpdir, no_jupytext_version_number): +def test_ipynb_notebooks_can_be_trusted( + ipynb_py_file, tmpdir, no_jupytext_version_number +): + if "hash sign" in ipynb_py_file: + pytest.skip() cm = TextFileContentsManager() - root, file = os.path.split(nb_file) + root, file = os.path.split(ipynb_py_file) tmp_ipynb = str(tmpdir.join(file)) py_file = file.replace(".ipynb", ".py") tmp_py = str(tmpdir.join(py_file)) - shutil.copy(nb_file, tmp_ipynb) + shutil.copy(ipynb_py_file, tmp_ipynb) cm.formats = "ipynb,py" cm.root_dir = str(tmpdir) @@ -78,16 +77,17 @@ def test_ipynb_notebooks_can_be_trusted(nb_file, tmpdir, no_jupytext_version_num cm.trust_notebook(file) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py", skip="hash sign")) def test_ipynb_notebooks_can_be_trusted_even_with_metadata_filter( - nb_file, tmpdir, no_jupytext_version_number + ipynb_py_file, tmpdir, no_jupytext_version_number ): + if "hash sign" in ipynb_py_file: + pytest.skip() cm = TextFileContentsManager() - root, file = os.path.split(nb_file) + root, file = os.path.split(ipynb_py_file) tmp_ipynb = str(tmpdir.join(file)) py_file = file.replace(".ipynb", ".py") tmp_py = str(tmpdir.join(py_file)) - shutil.copy(nb_file, tmp_ipynb) + shutil.copy(ipynb_py_file, tmp_ipynb) cm.formats = "ipynb,py" cm.notebook_metadata_filter = "all" @@ -119,12 +119,13 @@ def test_ipynb_notebooks_can_be_trusted_even_with_metadata_filter( compare_notebooks(nb2["content"], model["content"]) -@pytest.mark.parametrize("nb_file", list_notebooks("percent", skip="hash sign")) -def test_text_notebooks_can_be_trusted(nb_file, tmpdir, no_jupytext_version_number): +def test_text_notebooks_can_be_trusted( + percent_file, tmpdir, no_jupytext_version_number +): cm = TextFileContentsManager() - root, file = os.path.split(nb_file) + root, file = os.path.split(percent_file) py_file = str(tmpdir.join(file)) - shutil.copy(nb_file, py_file) + shutil.copy(percent_file, py_file) cm.root_dir = str(tmpdir) model = cm.get(file) @@ -166,7 +167,7 @@ def test_simple_notebook_is_trusted(tmpdir, python_notebook): assert cm.notary.check_signature(nb) -@requires_myst +@pytest.mark.requires_myst def test_myst_notebook_is_trusted_941( tmp_path, myst="""--- @@ -204,7 +205,7 @@ def test_myst_notebook_is_trusted_941( assert cm.notary.check_cells(nb) -@requires_myst +@pytest.mark.requires_myst def test_paired_notebook_with_outputs_is_not_trusted_941(tmp_path, python_notebook): cm = TextFileContentsManager() cm.root_dir = str(tmp_path) diff --git a/tests/test_unicode.py b/tests/functional/others/test_unicode.py similarity index 91% rename from tests/test_unicode.py rename to tests/functional/others/test_unicode.py index 81ef043e1..0dbc632a0 100644 --- a/tests/test_unicode.py +++ b/tests/functional/others/test_unicode.py @@ -1,15 +1,18 @@ -import pytest from nbformat.v4.nbbase import new_markdown_cell, new_notebook import jupytext from jupytext.compare import compare -from .utils import list_notebooks +def test_notebook_contents_is_unicode(ipynb_file): + nb = jupytext.read(ipynb_file) -@pytest.mark.parametrize("nb_file", list_notebooks() + list_notebooks("Rmd")) -def test_notebook_contents_is_unicode(nb_file): - nb = jupytext.read(nb_file) + for cell in nb.cells: + assert isinstance(cell.source, str) + + +def test_rmd_notebook_contents_is_unicode(rmd_file): + nb = jupytext.read(rmd_file) for cell in nb.cells: assert isinstance(cell.source, str) diff --git a/tests/test_write_does_not_modify_notebook.py b/tests/functional/others/test_write_does_not_modify_notebook.py similarity index 59% rename from tests/test_write_does_not_modify_notebook.py rename to tests/functional/others/test_write_does_not_modify_notebook.py index 3e1873a57..76948a81a 100644 --- a/tests/test_write_does_not_modify_notebook.py +++ b/tests/functional/others/test_write_does_not_modify_notebook.py @@ -1,5 +1,4 @@ from copy import deepcopy -from itertools import product import pytest @@ -7,18 +6,13 @@ from jupytext.compare import compare from jupytext.formats import long_form_one_format -from .utils import list_notebooks - @pytest.mark.parametrize( - "nb_file,fmt", - product( - list_notebooks("ipynb_py") + list_notebooks("ipynb_R"), - ["auto:light", "auto:percent", "md", ".Rmd", ".ipynb"], - ), + "fmt", + ["auto:light", "auto:percent", "md", ".Rmd", ".ipynb"], ) -def test_write_notebook_does_not_change_it(nb_file, fmt, tmpdir): - nb_org = read(nb_file) +def test_write_notebook_does_not_change_it(ipynb_py_R_jl_file, fmt, tmpdir): + nb_org = read(ipynb_py_R_jl_file) nb_org_copied = deepcopy(nb_org) ext = long_form_one_format(fmt, nb_org.metadata)["extension"] diff --git a/tests/test_jupytext_nbconvert_round_trip.py b/tests/functional/round_trip/test_jupytext_nbconvert_round_trip.py similarity index 89% rename from tests/test_jupytext_nbconvert_round_trip.py rename to tests/functional/round_trip/test_jupytext_nbconvert_round_trip.py index 4bd6022d6..e259df7a0 100644 --- a/tests/test_jupytext_nbconvert_round_trip.py +++ b/tests/functional/round_trip/test_jupytext_nbconvert_round_trip.py @@ -3,14 +3,13 @@ import jupytext from jupytext.header import header_to_metadata_and_cell -from .utils import list_notebooks, requires_nbconvert - -@requires_nbconvert -@pytest.mark.parametrize("md_file", list_notebooks("md", skip="jupytext")) +@pytest.mark.requires_nbconvert def test_markdown_jupytext_nbconvert_is_identity(md_file): """Test that a Markdown file, converted to a notebook, then exported back to Markdown with nbconvert, yields the original file""" + if "jupytext" in md_file: + pytest.skip() with open(md_file) as fp: md_org = fp.read() @@ -39,12 +38,13 @@ def test_markdown_jupytext_nbconvert_is_identity(md_file): jupytext.compare.compare(md_nbconvert, md_expected) -@requires_nbconvert -@pytest.mark.parametrize("nb_file", list_notebooks(skip="(html|magic)")) -def test_jupytext_markdown_similar_to_nbconvert(nb_file): +@pytest.mark.requires_nbconvert +def test_jupytext_markdown_similar_to_nbconvert(ipynb_py_R_jl_file): """Test that the nbconvert export for a notebook matches Jupytext's one""" + if "magic" in ipynb_py_R_jl_file or "html" in ipynb_py_R_jl_file: + pytest.skip() - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_R_jl_file) # Remove cell outputs and metadata for cell in nb.cells: diff --git a/tests/functional/round_trip/test_mirror.py b/tests/functional/round_trip/test_mirror.py new file mode 100644 index 000000000..8a04f75be --- /dev/null +++ b/tests/functional/round_trip/test_mirror.py @@ -0,0 +1,159 @@ +"""Here we generate mirror representation of py, Rmd and ipynb files +as py or ipynb, and make sure that these representations minimally +change on new releases. +""" + +import os +import re + +import pytest +from nbformat.v4.nbbase import new_notebook + +import jupytext +from jupytext.compare import ( + assert_conversion_same_as_mirror, + compare_notebooks, + create_mirror_file_if_missing, +) +from jupytext.formats import auto_ext_from_metadata +from jupytext.languages import _SCRIPT_EXTENSIONS + + +def test_create_mirror_file_if_missing(tmpdir, no_jupytext_version_number): + py_file = str(tmpdir.join("notebook.py")) + assert not os.path.isfile(py_file) + create_mirror_file_if_missing(py_file, new_notebook(), "py") + assert os.path.isfile(py_file) + + +"""--------------------------------------------------------------------------------- + +Part I: ipynb -> fmt -> ipynb + +---------------------------------------------------------------------------------""" + + +def test_ipynb_to_percent(ipynb_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_file, "auto:percent", "ipynb_to_percent") + + +def test_ipynb_to_hydrogen(ipynb_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_file, "auto:hydrogen", "ipynb_to_hydrogen") + + +def test_ipynb_to_light(ipynb_to_light, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_to_light, "auto", "ipynb_to_script") + + +def test_ipynb_to_md(ipynb_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_file, "md", "ipynb_to_md") + + +def test_ipynb_to_Rmd(ipynb_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_file, "Rmd", "ipynb_to_Rmd") + + +@pytest.mark.requires_myst +def test_ipynb_to_myst(ipynb_file, no_jupytext_version_number): + if re.match( + r".*(html-demo|julia_functional_geometry|xcpp_by_quantstack).*", ipynb_file + ): + pytest.skip() + assert_conversion_same_as_mirror(ipynb_file, "md:myst", "ipynb_to_myst") + + +"""--------------------------------------------------------------------------------- + +Part II: text -> ipynb -> text + +---------------------------------------------------------------------------------""" + + +def test_script_to_ipynb(script_to_ipynb, no_jupytext_version_number): + assert_conversion_same_as_mirror(script_to_ipynb, "ipynb", "script_to_ipynb") + + +def test_percent_to_ipynb(percent_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(percent_file, "ipynb:percent", "script_to_ipynb") + + +def test_hydrogen_to_ipynb(hydrogen_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(hydrogen_file, "ipynb:hydrogen", "script_to_ipynb") + + +def test_spin_to_ipynb(r_spin_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(r_spin_file, "ipynb:spin", "script_to_ipynb") + + +def test_md_to_ipynb(md_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(md_file, "ipynb", "md_to_ipynb") + + +"""--------------------------------------------------------------------------------- + +Part III: More specific round trip tests + +---------------------------------------------------------------------------------""" + + +def test_ipynb_to_percent_to_light(ipynb_file): + nb = jupytext.read(ipynb_file) + pct = jupytext.writes(nb, "auto:percent") + auto_ext = auto_ext_from_metadata(nb.metadata) + comment = _SCRIPT_EXTENSIONS[auto_ext]["comment"] + lgt = ( + pct.replace(comment + " %%\n", comment + " +\n") + .replace(comment + " %% ", comment + " + ") + .replace( + comment + " format_name: percent", + comment + " format_name: light", + ) + ) + nb2 = jupytext.reads(lgt, auto_ext) + compare_notebooks(nb2, nb) + + +def test_ipynb_to_python_vim(ipynb_py_file, no_jupytext_version_number): + assert_conversion_same_as_mirror( + ipynb_py_file, + {"extension": ".py", "cell_markers": "{{{,}}}"}, + "ipynb_to_script_vim_folding_markers", + ) + + +def test_ipynb_to_python_vscode(ipynb_py_file, no_jupytext_version_number): + assert_conversion_same_as_mirror( + ipynb_py_file, + {"extension": ".py", "cell_markers": "region,endregion"}, + "ipynb_to_script_vscode_folding_markers", + ) + + +def test_ipynb_to_r(ipynb_R_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_R_file, ".low.r", "ipynb_to_script") + + +def test_ipynb_to_r_percent(ipynb_R_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_R_file, ".low.r:percent", "ipynb_to_percent") + + +def test_ipynb_to_R_spin(ipynb_R_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_R_file, "R", "ipynb_to_spin") + + +def test_ipynb_to_r_spin(ipynb_R_file, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_R_file, ".low.r", "ipynb_to_spin") + + +@pytest.mark.parametrize("extension", ("ss", "scm")) +def test_ipynb_to_scheme(ipynb_scheme_file, extension, no_jupytext_version_number): + assert_conversion_same_as_mirror(ipynb_scheme_file, extension, "ipynb_to_script") + + +@pytest.mark.parametrize("extension", ("ss", "scm")) +def test_ipynb_to_scheme_percent( + ipynb_scheme_file, extension, no_jupytext_version_number +): + assert_conversion_same_as_mirror( + ipynb_scheme_file, f"{extension}:percent", "ipynb_to_percent" + ) diff --git a/tests/functional/round_trip/test_read_all_py.py b/tests/functional/round_trip/test_read_all_py.py new file mode 100644 index 000000000..c7e564f78 --- /dev/null +++ b/tests/functional/round_trip/test_read_all_py.py @@ -0,0 +1,12 @@ +import jupytext +from jupytext.compare import compare + + +def test_identity_source_write_read(py_file): + with open(py_file) as fp: + py = fp.read() + + nb = jupytext.reads(py, "py") + py2 = jupytext.writes(nb, "py") + + compare(py2, py) diff --git a/tests/test_rmd_to_ipynb.py b/tests/functional/round_trip/test_rmd_to_ipynb.py similarity index 75% rename from tests/test_rmd_to_ipynb.py rename to tests/functional/round_trip/test_rmd_to_ipynb.py index 8a8d237cc..2167f8e99 100644 --- a/tests/test_rmd_to_ipynb.py +++ b/tests/functional/round_trip/test_rmd_to_ipynb.py @@ -1,16 +1,11 @@ -import pytest - import jupytext from jupytext.compare import compare -from .utils import list_notebooks - -@pytest.mark.parametrize("nb_file", list_notebooks("Rmd")) -def test_identity_write_read(nb_file, no_jupytext_version_number): +def test_identity_write_read(rmd_file, no_jupytext_version_number): """Test that writing the notebook with ipynb, and read again, yields identity""" - with open(nb_file) as fp: + with open(rmd_file) as fp: rmd = fp.read() nb = jupytext.reads(rmd, "Rmd") diff --git a/tests/test_ipynb_to_R.py b/tests/functional/simple_notebooks/test_ipynb_to_R.py similarity index 59% rename from tests/test_ipynb_to_R.py rename to tests/functional/simple_notebooks/test_ipynb_to_R.py index b7be46ed6..b14451606 100644 --- a/tests/test_ipynb_to_R.py +++ b/tests/functional/simple_notebooks/test_ipynb_to_R.py @@ -1,24 +1,18 @@ -import itertools - import nbformat import pytest import jupytext from jupytext.compare import compare_notebooks -from .utils import list_notebooks - -@pytest.mark.parametrize( - "nb_file,ext", itertools.product(list_notebooks("ipynb_R"), [".r", ".R"]) -) -def test_identity_source_write_read(nb_file, ext): +@pytest.mark.parametrize("ext", [".r", ".R"]) +def test_identity_source_write_read(ipynb_R_file, ext): """ Test that writing the notebook with R, and read again, is the same as removing outputs """ - with open(nb_file) as fp: + with open(ipynb_R_file) as fp: nb1 = nbformat.read(fp, as_version=4) R = jupytext.writes(nb1, ext) diff --git a/tests/test_ipynb_to_myst.py b/tests/functional/simple_notebooks/test_ipynb_to_myst.py similarity index 96% rename from tests/test_ipynb_to_myst.py rename to tests/functional/simple_notebooks/test_ipynb_to_myst.py index 8786a1aeb..0a8551445 100644 --- a/tests/test_ipynb_to_myst.py +++ b/tests/functional/simple_notebooks/test_ipynb_to_myst.py @@ -22,10 +22,8 @@ myst_to_notebook, ) -from .utils import requires_myst, requires_no_myst - -@requires_myst +@pytest.mark.requires_myst def test_bad_notebook_metadata(): """Test exception raised if notebook metadata cannot be parsed.""" with pytest.raises(MystMetadataParsingError): @@ -40,7 +38,7 @@ def test_bad_notebook_metadata(): ) -@requires_myst +@pytest.mark.requires_myst def test_bad_code_metadata(): """Test exception raised if cell metadata cannot be parsed.""" with pytest.raises(MystMetadataParsingError): @@ -57,7 +55,7 @@ def test_bad_code_metadata(): ) -@requires_myst +@pytest.mark.requires_myst def test_bad_markdown_metadata(): """Test exception raised if markdown metadata cannot be parsed.""" with pytest.raises(MystMetadataParsingError): @@ -70,7 +68,7 @@ def test_bad_markdown_metadata(): ) -@requires_myst +@pytest.mark.requires_myst def test_bad_markdown_metadata2(): """Test exception raised if markdown metadata is not a dict.""" with pytest.raises(MystMetadataParsingError): @@ -83,7 +81,7 @@ def test_bad_markdown_metadata2(): ) -@requires_myst +@pytest.mark.requires_myst def test_matches_mystnb(): assert matches_mystnb("") is False assert matches_mystnb("```{code-cell}\n```") is False @@ -131,7 +129,7 @@ def test_not_installed(): get_format_implementation(".myst") -@requires_myst +@pytest.mark.requires_myst def test_add_source_map(): notebook = myst_to_notebook( dedent( @@ -159,7 +157,7 @@ def test_add_source_map(): PLEASE_INSTALL_MYST = "The MyST Markdown format requires .*" -@requires_no_myst +@pytest.mark.requires_no_myst def test_meaningfull_error_write_myst_missing(tmpdir): nb_file = tmpdir.join("notebook.ipynb") jupytext.write(new_notebook(), str(nb_file)) @@ -168,7 +166,7 @@ def test_meaningfull_error_write_myst_missing(tmpdir): jupytext_cli([str(nb_file), "--to", "md:myst"]) -@requires_no_myst +@pytest.mark.requires_no_myst def test_meaningfull_error_open_myst_missing(tmpdir): md_file = tmpdir.join("notebook.md") md_file.write( @@ -197,7 +195,7 @@ def test_meaningfull_error_open_myst_missing(tmpdir): cm.get("notebook.md") -@requires_myst +@pytest.mark.requires_myst @pytest.mark.parametrize("language_info", ["none", "std", "no_pygments_lexer"]) def test_myst_representation_same_cli_or_contents_manager( tmpdir, cwd_tmpdir, notebook_with_outputs, language_info diff --git a/tests/test_ipynb_to_py.py b/tests/functional/simple_notebooks/test_ipynb_to_py.py similarity index 64% rename from tests/test_ipynb_to_py.py rename to tests/functional/simple_notebooks/test_ipynb_to_py.py index 7a0915d7f..b46800189 100644 --- a/tests/test_ipynb_to_py.py +++ b/tests/functional/simple_notebooks/test_ipynb_to_py.py @@ -1,18 +1,14 @@ import nbformat -import pytest import jupytext from jupytext.compare import compare_notebooks -from .utils import list_notebooks - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_identity_source_write_read(nb_file): +def test_identity_source_write_read(ipynb_py_file): """Test that writing the notebook with jupytext, and read again, is the same as removing outputs""" - with open(nb_file) as fp: + with open(ipynb_py_file) as fp: nb1 = nbformat.read(fp, as_version=4) py = jupytext.writes(nb1, "py") diff --git a/tests/test_ipynb_to_rmd.py b/tests/functional/simple_notebooks/test_ipynb_to_rmd.py similarity index 64% rename from tests/test_ipynb_to_rmd.py rename to tests/functional/simple_notebooks/test_ipynb_to_rmd.py index 5d61a6a61..e8e7bf5bf 100644 --- a/tests/test_ipynb_to_rmd.py +++ b/tests/functional/simple_notebooks/test_ipynb_to_rmd.py @@ -1,18 +1,14 @@ import nbformat -import pytest import jupytext from jupytext.compare import compare_notebooks -from .utils import list_notebooks - -@pytest.mark.parametrize("nb_file", list_notebooks(skip="66")) -def test_identity_source_write_read(nb_file): +def test_identity_source_write_read(ipynb_py_R_jl_file): """Test that writing the notebook with rmd, and read again, is the same as removing outputs""" - with open(nb_file) as fp: + with open(ipynb_py_R_jl_file) as fp: nb1 = nbformat.read(fp, as_version=4) rmd = jupytext.writes(nb1, "Rmd") diff --git a/tests/functional/simple_notebooks/test_knitr_spin.py b/tests/functional/simple_notebooks/test_knitr_spin.py new file mode 100644 index 000000000..579a67055 --- /dev/null +++ b/tests/functional/simple_notebooks/test_knitr_spin.py @@ -0,0 +1,14 @@ +import jupytext + + +def test_jupytext_same_as_knitr_spin(r_spin_file, tmpdir): + nb = jupytext.read(r_spin_file) + rmd_jupytext = jupytext.writes(nb, "Rmd") + + # Rmd file generated with spin(hair='R/spin.R', knit=FALSE) + rmd_file = r_spin_file.replace("R_spin", "Rmd").replace(".R", ".Rmd") + + with open(rmd_file) as fp: + rmd_spin = fp.read() + + assert rmd_spin == rmd_jupytext diff --git a/tests/test_read_dotnet_try_markdown.py b/tests/functional/simple_notebooks/test_read_dotnet_try_markdown.py similarity index 100% rename from tests/test_read_dotnet_try_markdown.py rename to tests/functional/simple_notebooks/test_read_dotnet_try_markdown.py diff --git a/tests/test_read_empty_text_notebook.py b/tests/functional/simple_notebooks/test_read_empty_text_notebook.py similarity index 85% rename from tests/test_read_empty_text_notebook.py rename to tests/functional/simple_notebooks/test_read_empty_text_notebook.py index e931dfa00..e134e1c97 100644 --- a/tests/test_read_empty_text_notebook.py +++ b/tests/functional/simple_notebooks/test_read_empty_text_notebook.py @@ -3,9 +3,8 @@ import jupytext from jupytext.formats import NOTEBOOK_EXTENSIONS -from jupytext.myst import myst_extensions - -from .utils import is_myst_available, is_quarto_available +from jupytext.myst import is_myst_available, myst_extensions +from jupytext.quarto import is_quarto_available @pytest.mark.parametrize("ext", sorted(set(NOTEBOOK_EXTENSIONS) - {".ipynb"})) diff --git a/tests/test_read_folding_markers.py b/tests/functional/simple_notebooks/test_read_folding_markers.py similarity index 100% rename from tests/test_read_folding_markers.py rename to tests/functional/simple_notebooks/test_read_folding_markers.py diff --git a/tests/test_read_incomplete_rmd.py b/tests/functional/simple_notebooks/test_read_incomplete_rmd.py similarity index 100% rename from tests/test_read_incomplete_rmd.py rename to tests/functional/simple_notebooks/test_read_incomplete_rmd.py diff --git a/tests/test_read_simple_R.py b/tests/functional/simple_notebooks/test_read_simple_R.py similarity index 100% rename from tests/test_read_simple_R.py rename to tests/functional/simple_notebooks/test_read_simple_R.py diff --git a/tests/test_read_simple_clojure.py b/tests/functional/simple_notebooks/test_read_simple_clojure.py similarity index 100% rename from tests/test_read_simple_clojure.py rename to tests/functional/simple_notebooks/test_read_simple_clojure.py diff --git a/tests/test_read_simple_csharp.py b/tests/functional/simple_notebooks/test_read_simple_csharp.py similarity index 100% rename from tests/test_read_simple_csharp.py rename to tests/functional/simple_notebooks/test_read_simple_csharp.py diff --git a/tests/test_read_simple_groovy.py b/tests/functional/simple_notebooks/test_read_simple_groovy.py similarity index 100% rename from tests/test_read_simple_groovy.py rename to tests/functional/simple_notebooks/test_read_simple_groovy.py diff --git a/tests/test_read_simple_hydrogen.py b/tests/functional/simple_notebooks/test_read_simple_hydrogen.py similarity index 100% rename from tests/test_read_simple_hydrogen.py rename to tests/functional/simple_notebooks/test_read_simple_hydrogen.py diff --git a/tests/test_read_simple_ipynb.py b/tests/functional/simple_notebooks/test_read_simple_ipynb.py similarity index 100% rename from tests/test_read_simple_ipynb.py rename to tests/functional/simple_notebooks/test_read_simple_ipynb.py diff --git a/tests/test_read_simple_java.py b/tests/functional/simple_notebooks/test_read_simple_java.py similarity index 100% rename from tests/test_read_simple_java.py rename to tests/functional/simple_notebooks/test_read_simple_java.py diff --git a/tests/test_read_simple_julia.py b/tests/functional/simple_notebooks/test_read_simple_julia.py similarity index 100% rename from tests/test_read_simple_julia.py rename to tests/functional/simple_notebooks/test_read_simple_julia.py diff --git a/tests/test_read_simple_markdown.py b/tests/functional/simple_notebooks/test_read_simple_markdown.py similarity index 100% rename from tests/test_read_simple_markdown.py rename to tests/functional/simple_notebooks/test_read_simple_markdown.py diff --git a/tests/test_read_simple_matlab.py b/tests/functional/simple_notebooks/test_read_simple_matlab.py similarity index 100% rename from tests/test_read_simple_matlab.py rename to tests/functional/simple_notebooks/test_read_simple_matlab.py diff --git a/tests/test_read_simple_nomarker.py b/tests/functional/simple_notebooks/test_read_simple_nomarker.py similarity index 100% rename from tests/test_read_simple_nomarker.py rename to tests/functional/simple_notebooks/test_read_simple_nomarker.py diff --git a/tests/test_read_simple_ocaml.py b/tests/functional/simple_notebooks/test_read_simple_ocaml.py similarity index 100% rename from tests/test_read_simple_ocaml.py rename to tests/functional/simple_notebooks/test_read_simple_ocaml.py diff --git a/tests/test_read_simple_percent.py b/tests/functional/simple_notebooks/test_read_simple_percent.py similarity index 99% rename from tests/test_read_simple_percent.py rename to tests/functional/simple_notebooks/test_read_simple_percent.py index 357a443ba..eea25a5cd 100644 --- a/tests/test_read_simple_percent.py +++ b/tests/functional/simple_notebooks/test_read_simple_percent.py @@ -9,9 +9,7 @@ ) import jupytext -from jupytext.compare import compare, compare_notebooks - -from .utils import notebook_model +from jupytext.compare import compare, compare_notebooks, notebook_model def test_read_simple_file( diff --git a/tests/test_read_simple_python.py b/tests/functional/simple_notebooks/test_read_simple_python.py similarity index 100% rename from tests/test_read_simple_python.py rename to tests/functional/simple_notebooks/test_read_simple_python.py diff --git a/tests/test_read_simple_rmd.py b/tests/functional/simple_notebooks/test_read_simple_rmd.py similarity index 100% rename from tests/test_read_simple_rmd.py rename to tests/functional/simple_notebooks/test_read_simple_rmd.py diff --git a/tests/test_read_simple_rust.py b/tests/functional/simple_notebooks/test_read_simple_rust.py similarity index 100% rename from tests/test_read_simple_rust.py rename to tests/functional/simple_notebooks/test_read_simple_rust.py diff --git a/tests/test_read_simple_scheme.py b/tests/functional/simple_notebooks/test_read_simple_scheme.py similarity index 100% rename from tests/test_read_simple_scheme.py rename to tests/functional/simple_notebooks/test_read_simple_scheme.py diff --git a/tests/test_read_simple_sphinx.py b/tests/functional/simple_notebooks/test_read_simple_sphinx.py similarity index 100% rename from tests/test_read_simple_sphinx.py rename to tests/functional/simple_notebooks/test_read_simple_sphinx.py diff --git a/tests/test_execute.py b/tests/integration/cli/test_execute.py similarity index 81% rename from tests/test_execute.py rename to tests/integration/cli/test_execute.py index b1c65fb53..3b53e3185 100644 --- a/tests/test_execute.py +++ b/tests/integration/cli/test_execute.py @@ -1,23 +1,17 @@ import shutil +from io import StringIO from unittest import mock import pytest -from jupytext import read +from jupytext import read, reads from jupytext.cli import jupytext from jupytext.version import __version__ -from .utils import ( - requires_ir_kernel, - requires_nbconvert, - requires_user_kernel_python3, - skip_on_windows, -) - -@requires_user_kernel_python3 -@requires_nbconvert -@skip_on_windows +@pytest.mark.requires_user_kernel_python3 +@pytest.mark.requires_nbconvert +@pytest.mark.skip_on_windows def test_pipe_nbconvert_execute(tmpdir): tmp_ipynb = str(tmpdir.join("notebook.ipynb")) tmp_py = str(tmpdir.join("notebook.py")) @@ -45,9 +39,9 @@ def test_pipe_nbconvert_execute(tmpdir): assert nb.cells[0].outputs[0]["data"] == {"text/plain": "3"} -@requires_user_kernel_python3 -@requires_nbconvert -@skip_on_windows +@pytest.mark.requires_user_kernel_python3 +@pytest.mark.requires_nbconvert +@pytest.mark.skip_on_windows def test_pipe_nbconvert_execute_sync(tmpdir): tmp_ipynb = str(tmpdir.join("notebook.ipynb")) tmp_py = str(tmpdir.join("notebook.py")) @@ -76,9 +70,9 @@ def test_pipe_nbconvert_execute_sync(tmpdir): assert nb.cells[0].outputs[0]["data"] == {"text/plain": "3"} -@requires_user_kernel_python3 -@requires_nbconvert -@skip_on_windows +@pytest.mark.requires_user_kernel_python3 +@pytest.mark.requires_nbconvert +@pytest.mark.skip_on_windows def test_execute(tmpdir, caplog, capsys): tmp_ipynb = str(tmpdir.join("notebook.ipynb")) tmp_py = str(tmpdir.join("notebook.py")) @@ -96,8 +90,8 @@ def test_execute(tmpdir, caplog, capsys): assert nb.cells[0].outputs[0]["data"] == {"text/plain": "3"} -@requires_user_kernel_python3 -@requires_nbconvert +@pytest.mark.requires_user_kernel_python3 +@pytest.mark.requires_nbconvert def test_execute_readme_ok(tmpdir): tmp_md = str(tmpdir.join("notebook.md")) @@ -115,9 +109,9 @@ def test_execute_readme_ok(tmpdir): jupytext(args=[tmp_md, "--execute"]) -@requires_user_kernel_python3 -@requires_nbconvert -@skip_on_windows +@pytest.mark.requires_user_kernel_python3 +@pytest.mark.requires_nbconvert +@pytest.mark.skip_on_windows def test_execute_readme_not_ok(tmpdir): tmp_md = str(tmpdir.join("notebook.md")) @@ -140,9 +134,9 @@ def test_execute_readme_not_ok(tmpdir): jupytext(args=[tmp_md, "--execute"]) -@requires_user_kernel_python3 -@requires_nbconvert -@skip_on_windows +@pytest.mark.requires_user_kernel_python3 +@pytest.mark.requires_nbconvert +@pytest.mark.skip_on_windows def test_execute_sync(tmpdir, caplog, capsys): tmp_ipynb = str(tmpdir.join("notebook.ipynb")) tmp_py = str(tmpdir.join("notebook.py")) @@ -160,9 +154,9 @@ def test_execute_sync(tmpdir, caplog, capsys): assert nb.cells[0].outputs[0]["data"] == {"text/plain": "3"} -@requires_nbconvert -@requires_ir_kernel -@skip_on_windows +@pytest.mark.requires_nbconvert +@pytest.mark.requires_ir_kernel +@pytest.mark.skip_on_windows def test_execute_r(tmpdir, caplog, capsys): # pragma: no cover tmp_ipynb = str(tmpdir.join("notebook.ipynb")) tmp_md = str(tmpdir.join("notebook.md")) @@ -182,9 +176,9 @@ def test_execute_r(tmpdir, caplog, capsys): # pragma: no cover assert nb.cells[0].outputs[0]["data"]["text/markdown"] == "6" -@requires_user_kernel_python3 -@requires_nbconvert -@skip_on_windows +@pytest.mark.requires_user_kernel_python3 +@pytest.mark.requires_nbconvert +@pytest.mark.skip_on_windows def test_execute_in_subfolder(tmpdir, caplog, capsys): subfolder = tmpdir.mkdir("subfolder") @@ -263,7 +257,7 @@ def sample_md_notebook(): """ -@requires_user_kernel_python3 +@pytest.mark.requires_user_kernel_python3 def test_execute_text_file_does_update_the_metadata(sample_md_notebook, tmp_path): md_file = tmp_path / "nb.md" md_file.write_text(sample_md_notebook) @@ -275,7 +269,7 @@ def test_execute_text_file_does_update_the_metadata(sample_md_notebook, tmp_path assert "kernelspec" in new_md_text -@requires_user_kernel_python3 +@pytest.mark.requires_user_kernel_python3 def test_cat_execute_does_not_update_the_metadata(sample_md_notebook, tmp_path): md_file = tmp_path / "nb.md" md_file.write_text(sample_md_notebook) @@ -288,3 +282,21 @@ def test_cat_execute_does_not_update_the_metadata(sample_md_notebook, tmp_path): new_md_text = md_file.read_text() assert __version__ not in new_md_text assert "kernelspec" not in new_md_text + + +@pytest.mark.requires_user_kernel_python3 +@pytest.mark.skip_on_windows +@pytest.mark.filterwarnings("ignore") +def test_utf8_out_331(capsys, caplog): + py = "from IPython.core.display import HTML; HTML(u'\xd7')" + + with mock.patch("sys.stdin", StringIO(py)): + jupytext(["--to", "ipynb", "--execute", "-"]) + + out, err = capsys.readouterr() + + assert err == "" + nb = reads(out, "ipynb") + assert len(nb.cells) == 1 + print(nb.cells[0].outputs) + assert nb.cells[0].outputs[0]["data"]["text/html"] == "\xd7" diff --git a/tests/test_cm_config.py b/tests/integration/contents_manager/test_cm_config.py similarity index 99% rename from tests/test_cm_config.py rename to tests/integration/contents_manager/test_cm_config.py index 2e0d6c744..28215f247 100644 --- a/tests/test_cm_config.py +++ b/tests/integration/contents_manager/test_cm_config.py @@ -10,9 +10,7 @@ import jupytext from jupytext import TextFileContentsManager -from jupytext.compare import compare_cells - -from .utils import notebook_model +from jupytext.compare import compare_cells, notebook_model SAMPLE_NOTEBOOK = new_notebook( cells=[new_markdown_cell("A Markdown cell"), new_code_cell("# A code cell\n1 + 1")] diff --git a/tests/test_contentsmanager.py b/tests/integration/contents_manager/test_contentsmanager.py similarity index 83% rename from tests/test_contentsmanager.py rename to tests/integration/contents_manager/test_contentsmanager.py index 19556a671..731bda423 100644 --- a/tests/test_contentsmanager.py +++ b/tests/integration/contents_manager/test_contentsmanager.py @@ -1,5 +1,3 @@ -import itertools -import logging import os import re import shutil @@ -11,21 +9,12 @@ import jupytext from jupytext.cli import jupytext as jupytext_cli -from jupytext.compare import compare, compare_cells, compare_notebooks +from jupytext.compare import compare, compare_notebooks, notebook_model from jupytext.formats import auto_ext_from_metadata, read_format_from_metadata from jupytext.header import header_to_metadata_and_cell from jupytext.jupytext import read, write, writes from jupytext.kernels import kernelspec_from_language -from .utils import ( - list_notebooks, - notebook_model, - requires_pandoc, - requires_quarto, - requires_sphinx_gallery, - requires_user_kernel_python3, -) - def test_create_contentsmanager(): jupytext.TextFileContentsManager() @@ -115,8 +104,7 @@ def test_pair_unpair_notebook(tmpdir): assert len(nb2.cells[0]["outputs"]) == 0 -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb", skip="66")) -def test_load_save_rename(nb_file, tmpdir): +def test_load_save_rename(ipynb_py_R_jl_file, tmpdir): tmp_ipynb = "notebook.ipynb" tmp_rmd = "notebook.Rmd" @@ -125,7 +113,7 @@ def test_load_save_rename(nb_file, tmpdir): cm.root_dir = str(tmpdir) # open ipynb, save Rmd, reopen - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_R_jl_file) cm.save(model=notebook_model(nb), path=tmp_rmd) nb_rmd = cm.get(tmp_rmd) compare_notebooks(nb_rmd["content"], nb, "Rmd") @@ -158,8 +146,7 @@ def test_load_save_rename(nb_file, tmpdir): assert not os.path.isfile(str(tmpdir.join("new.Rmd"))) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb", skip="magic")) -def test_save_load_paired_md_notebook(nb_file, tmpdir): +def test_save_load_paired_md_notebook(ipynb_py_R_jl_file, tmpdir): tmp_ipynb = "notebook.ipynb" tmp_md = "notebook.md" @@ -167,7 +154,7 @@ def test_save_load_paired_md_notebook(nb_file, tmpdir): cm.root_dir = str(tmpdir) # open ipynb, save with cm, reopen - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_R_jl_file) nb.metadata["jupytext"] = {"formats": "ipynb,md"} cm.save(model=notebook_model(nb), path=tmp_ipynb) @@ -177,58 +164,7 @@ def test_save_load_paired_md_notebook(nb_file, tmpdir): assert nb_md["content"].metadata["jupytext"]["formats"] == "ipynb,md" -@requires_pandoc -@pytest.mark.parametrize( - "nb_file", - list_notebooks("ipynb", skip="(functional|Notebook with|flavors|invalid|305)"), -) -def test_save_load_paired_md_pandoc_notebook(nb_file, tmpdir): - tmp_ipynb = "notebook.ipynb" - tmp_md = "notebook.md" - - cm = jupytext.TextFileContentsManager() - cm.root_dir = str(tmpdir) - - # open ipynb, save with cm, reopen - nb = jupytext.read(nb_file) - nb.metadata["jupytext"] = {"formats": "ipynb,md:pandoc"} - - cm.save(model=notebook_model(nb), path=tmp_ipynb) - nb_md = cm.get(tmp_md) - - compare_notebooks(nb_md["content"], nb, "md:pandoc") - assert nb_md["content"].metadata["jupytext"]["formats"] == "ipynb,md:pandoc" - - -@requires_quarto -@pytest.mark.parametrize( - "nb_file", - list_notebooks( - "ipynb", - skip="(World|functional|Notebook with|plotly_graphs|flavors|complex_metadata|" - "update83|raw_cell|_66|nteract|LaTeX|invalid|305|text_outputs|ir_notebook|jupyter|with_R_magic)", - ), -) -def test_save_load_paired_qmd_notebook(nb_file, tmpdir): - tmp_ipynb = "notebook.ipynb" - tmp_qmd = "notebook.qmd" - - cm = jupytext.TextFileContentsManager() - cm.root_dir = str(tmpdir) - - # open ipynb, save with cm, reopen - nb = jupytext.read(nb_file) - nb.metadata["jupytext"] = {"formats": "ipynb,qmd"} - - cm.save(model=notebook_model(nb), path=tmp_ipynb) - nb_md = cm.get(tmp_qmd) - - compare_notebooks(nb_md["content"], nb, "qmd") - assert nb_md["content"].metadata["jupytext"]["formats"] == "ipynb,qmd" - - -@pytest.mark.parametrize("py_file", list_notebooks("percent")) -def test_pair_plain_script(py_file, tmpdir, caplog): +def test_pair_plain_script(percent_file, tmpdir, caplog): tmp_py = "notebook.py" tmp_ipynb = "notebook.ipynb" @@ -236,7 +172,7 @@ def test_pair_plain_script(py_file, tmpdir, caplog): cm.root_dir = str(tmpdir) # open py file, pair, save with cm - nb = jupytext.read(py_file) + nb = jupytext.read(percent_file) nb.metadata["jupytext"]["formats"] = "ipynb,py:hydrogen" cm.save(model=notebook_model(nb), path=tmp_py) @@ -246,7 +182,7 @@ def test_pair_plain_script(py_file, tmpdir, caplog): assert os.path.isfile(str(tmpdir.join(tmp_ipynb))) # Make sure we've not changed the script - with open(py_file) as fp: + with open(percent_file) as fp: script = fp.read() with open(str(tmpdir.join(tmp_py))) as fp: @@ -269,8 +205,7 @@ def test_pair_plain_script(py_file, tmpdir, caplog): assert "formats" not in nb2.metadata["jupytext"] -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_load_save_rename_nbpy(nb_file, tmpdir): +def test_load_save_rename_nbpy(ipynb_py_file, tmpdir): tmp_ipynb = "notebook.ipynb" tmp_nbpy = "notebook.nb.py" @@ -279,7 +214,7 @@ def test_load_save_rename_nbpy(nb_file, tmpdir): cm.root_dir = str(tmpdir) # open ipynb, save nb.py, reopen - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_file) cm.save(model=notebook_model(nb), path=tmp_nbpy) nbpy = cm.get(tmp_nbpy) compare_notebooks(nbpy["content"], nb) @@ -300,15 +235,17 @@ def test_load_save_rename_nbpy(nb_file, tmpdir): cm.rename_file(tmp_nbpy, "suffix_missing.py") -@pytest.mark.parametrize("script", list_notebooks("python", skip="light")) -def test_load_save_py_freeze_metadata(script, tmpdir): +def test_load_save_py_freeze_metadata(python_file, tmpdir): + if "light" in python_file: + pytest.skip() + tmp_nbpy = "notebook.py" cm = jupytext.TextFileContentsManager() cm.root_dir = str(tmpdir) # read original file - with open(script) as fp: + with open(python_file) as fp: text_py = fp.read() # write to tmp_nbpy @@ -351,8 +288,7 @@ def test_load_text_notebook(tmpdir): assert nb_model[key] == py_model[key], key -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_load_save_rename_notebook_with_dot(nb_file, tmpdir): +def test_load_save_rename_notebook_with_dot(ipynb_py_file, tmpdir): tmp_ipynb = "1.notebook.ipynb" tmp_nbpy = "1.notebook.py" @@ -361,7 +297,7 @@ def test_load_save_rename_notebook_with_dot(nb_file, tmpdir): cm.root_dir = str(tmpdir) # open ipynb, save nb.py, reopen - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_file) cm.save(model=notebook_model(nb), path=tmp_nbpy) nbpy = cm.get(tmp_nbpy) compare_notebooks(nbpy["content"], nb) @@ -378,8 +314,7 @@ def test_load_save_rename_notebook_with_dot(nb_file, tmpdir): assert os.path.isfile(str(tmpdir.join("2.new_notebook.py"))) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_load_save_rename_nbpy_default_config(nb_file, tmpdir): +def test_load_save_rename_nbpy_default_config(ipynb_py_file, tmpdir): tmp_ipynb = "notebook.ipynb" tmp_nbpy = "notebook.nb.py" @@ -388,7 +323,7 @@ def test_load_save_rename_nbpy_default_config(nb_file, tmpdir): cm.root_dir = str(tmpdir) # open ipynb, save nb.py, reopen - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_file) cm.save(model=notebook_model(nb), path=tmp_nbpy) nbpy = cm.get(tmp_nbpy) @@ -418,8 +353,7 @@ def test_load_save_rename_nbpy_default_config(nb_file, tmpdir): assert not os.path.isfile(str(tmpdir.join("new.nb.py"))) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_load_save_rename_non_ascii_path(nb_file, tmpdir): +def test_load_save_rename_non_ascii_path(ipynb_py_file, tmpdir): tmp_ipynb = "notebôk.ipynb" tmp_nbpy = "notebôk.nb.py" @@ -429,7 +363,7 @@ def test_load_save_rename_non_ascii_path(nb_file, tmpdir): cm.root_dir = tmpdir # open ipynb, save nb.py, reopen - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_file) cm.save(model=notebook_model(nb), path=tmp_nbpy) nbpy = cm.get(tmp_nbpy) @@ -459,8 +393,7 @@ def test_load_save_rename_non_ascii_path(nb_file, tmpdir): assert not os.path.isfile(os.path.join(tmpdir, "nêw.nb.py")) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")[:1]) -def test_outdated_text_notebook(nb_file, tmpdir): +def test_outdated_text_notebook(python_notebook, tmpdir): # 1. write py ipynb cm = jupytext.TextFileContentsManager() cm.formats = "py,ipynb" @@ -468,7 +401,7 @@ def test_outdated_text_notebook(nb_file, tmpdir): cm.root_dir = str(tmpdir) # open ipynb, save py, reopen - nb = jupytext.read(nb_file) + nb = python_notebook cm.save(model=notebook_model(nb), path="notebook.py") model_py = cm.get("notebook.py", load_alternative_format=False) model_ipynb = cm.get("notebook.ipynb", load_alternative_format=False) @@ -573,8 +506,7 @@ def test_outdated_text_notebook_diff_is_shown(tmpdir, python_notebook): ) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")[:1]) -def test_reload_notebook_after_jupytext_cli(nb_file, tmpdir): +def test_reload_notebook_after_jupytext_cli(python_notebook, tmpdir): tmp_ipynb = str(tmpdir.join("notebook.ipynb")) tmp_nbpy = str(tmpdir.join("notebook.py")) @@ -583,7 +515,7 @@ def test_reload_notebook_after_jupytext_cli(nb_file, tmpdir): cm.root_dir = str(tmpdir) # write the paired notebook - nb = jupytext.read(nb_file) + nb = python_notebook nb.metadata.setdefault("jupytext", {})["formats"] = "py,ipynb" cm.save(model=notebook_model(nb), path="notebook.py") @@ -601,10 +533,9 @@ def test_reload_notebook_after_jupytext_cli(nb_file, tmpdir): compare_notebooks(nb, nb2) -@pytest.mark.parametrize("nb_file", list_notebooks("percent")) -def test_load_save_percent_format(nb_file, tmpdir): +def test_load_save_percent_format(percent_file, tmpdir): tmp_py = "notebook.py" - with open(nb_file) as stream: + with open(percent_file) as stream: text_py = stream.read() with open(str(tmpdir.join(tmp_py)), "w") as stream: stream.write(text_py) @@ -631,8 +562,7 @@ def test_load_save_percent_format(nb_file, tmpdir): compare(text_py2, text_py) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_julia")) -def test_save_to_percent_format(nb_file, tmpdir): +def test_save_to_percent_format(ipynb_julia_file, tmpdir): tmp_ipynb = "notebook.ipynb" tmp_jl = "notebook.jl" @@ -640,7 +570,7 @@ def test_save_to_percent_format(nb_file, tmpdir): cm.root_dir = str(tmpdir) cm.preferred_jupytext_formats_save = "jl:percent" - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_julia_file) nb["metadata"]["jupytext"] = {"formats": "ipynb,jl"} # save to ipynb and jl @@ -655,9 +585,8 @@ def test_save_to_percent_format(nb_file, tmpdir): assert metadata["jupytext"]["formats"] == "ipynb,jl:percent" -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_save_using_preferred_and_default_format_170(nb_file, tmpdir): - nb = read(nb_file) +def test_save_using_preferred_and_default_format_170(ipynb_py_file, tmpdir): + nb = read(ipynb_py_file) # Way 0: preferred_jupytext_formats_save, no prefix + formats tmp_py = str(tmpdir.join("python/notebook.py")) @@ -704,13 +633,12 @@ def test_save_using_preferred_and_default_format_170(nb_file, tmpdir): assert nb_py.metadata["jupytext"]["text_representation"]["format_name"] == "percent" -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_open_using_preferred_and_default_format_174(nb_file, tmpdir): +def test_open_using_preferred_and_default_format_174(ipynb_py_file, tmpdir): tmp_ipynb = str(tmpdir.join("notebook.ipynb")) tmp_py = str(tmpdir.join("python/notebook.py")) tmp_py2 = str(tmpdir.join("other/notebook.py")) os.makedirs(str(tmpdir.join("other"))) - shutil.copyfile(nb_file, tmp_ipynb) + shutil.copyfile(ipynb_py_file, tmp_ipynb) cm = jupytext.TextFileContentsManager() cm.root_dir = str(tmpdir) @@ -740,11 +668,12 @@ def test_open_using_preferred_and_default_format_174(nb_file, tmpdir): assert not os.path.isfile(str(tmpdir.join("other/notebook.ipynb"))) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py", skip="many hash")) -def test_kernelspec_are_preserved(nb_file, tmpdir): +def test_kernelspec_are_preserved(ipynb_py_file, tmpdir): + if "many hash" in ipynb_py_file: + pytest.skip() tmp_ipynb = str(tmpdir.join("notebook.ipynb")) tmp_py = str(tmpdir.join("notebook.py")) - shutil.copyfile(nb_file, tmp_ipynb) + shutil.copyfile(ipynb_py_file, tmp_ipynb) cm = jupytext.TextFileContentsManager() cm.root_dir = str(tmpdir) @@ -768,8 +697,7 @@ def test_kernelspec_are_preserved(nb_file, tmpdir): compare_notebooks(model2["content"], model["content"]) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_save_to_light_percent_sphinx_format(nb_file, tmpdir): +def test_save_to_light_percent_sphinx_format(ipynb_py_file, tmpdir): tmp_ipynb = "notebook.ipynb" tmp_lgt_py = "notebook.lgt.py" tmp_pct_py = "notebook.pct.py" @@ -778,7 +706,7 @@ def test_save_to_light_percent_sphinx_format(nb_file, tmpdir): cm = jupytext.TextFileContentsManager() cm.root_dir = str(tmpdir) - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_file) nb["metadata"]["jupytext"] = { "formats": "ipynb,.pct.py:percent,.lgt.py:light,.spx.py:sphinx" } @@ -809,8 +737,7 @@ def test_save_to_light_percent_sphinx_format(nb_file, tmpdir): compare_notebooks(model["content"], nb) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_pair_notebook_with_dot(nb_file, tmpdir): +def test_pair_notebook_with_dot(ipynb_py_file, tmpdir): # Reproduce issue #138 tmp_py = "file.5.1.py" tmp_ipynb = "file.5.1.ipynb" @@ -818,7 +745,7 @@ def test_pair_notebook_with_dot(nb_file, tmpdir): cm = jupytext.TextFileContentsManager() cm.root_dir = str(tmpdir) - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_file) nb["metadata"]["jupytext"] = {"formats": "ipynb,py:percent"} # save to ipynb and three python flavors @@ -839,8 +766,7 @@ def test_pair_notebook_with_dot(nb_file, tmpdir): compare_notebooks(model["content"], nb) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")[:1]) -def test_preferred_format_allows_to_read_others_format(nb_file, tmpdir): +def test_preferred_format_allows_to_read_others_format(python_notebook, tmpdir): # 1. write py ipynb tmp_ipynb = "notebook.ipynb" tmp_nbpy = "notebook.py" @@ -850,7 +776,7 @@ def test_preferred_format_allows_to_read_others_format(nb_file, tmpdir): cm.root_dir = str(tmpdir) # load notebook and save it using the cm - nb = jupytext.read(nb_file) + nb = python_notebook nb["metadata"]["jupytext"] = {"formats": "ipynb,py"} cm.save(model=notebook_model(nb), path=tmp_ipynb) @@ -908,10 +834,9 @@ def test_preferred_formats_read_auto(tmpdir): ) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb")) -def test_save_in_auto_extension_global(nb_file, tmpdir): +def test_save_in_auto_extension_global(ipynb_py_R_jl_file, tmpdir): # load notebook - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_R_jl_file) auto_ext = auto_ext_from_metadata(nb.metadata) tmp_ipynb = "notebook.ipynb" @@ -992,10 +917,9 @@ def test_global_auto_pairing_works_with_empty_notebook(tmpdir): assert nb2.cells[0].source == "2+2" -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb")) -def test_save_in_auto_extension_global_with_format(nb_file, tmpdir): +def test_save_in_auto_extension_global_with_format(ipynb_py_R_jl_file, tmpdir): # load notebook - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_R_jl_file) auto_ext = auto_ext_from_metadata(nb.metadata) tmp_ipynb = "notebook.ipynb" @@ -1022,10 +946,9 @@ def test_save_in_auto_extension_global_with_format(nb_file, tmpdir): compare_notebooks(model["content"], nb) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb")) -def test_save_in_auto_extension_local(nb_file, tmpdir): +def test_save_in_auto_extension_local(ipynb_py_R_jl_file, tmpdir): # load notebook - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_R_jl_file) nb.metadata.setdefault("jupytext", {})["formats"] = "ipynb,auto:percent" auto_ext = auto_ext_from_metadata(nb.metadata) @@ -1049,10 +972,9 @@ def test_save_in_auto_extension_local(nb_file, tmpdir): compare_notebooks(model["content"], nb) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb")) -def test_save_in_pct_and_lgt_auto_extensions(nb_file, tmpdir): +def test_save_in_pct_and_lgt_auto_extensions(ipynb_py_R_jl_file, tmpdir): # load notebook - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_R_jl_file) auto_ext = auto_ext_from_metadata(nb.metadata) tmp_ipynb = "notebook.ipynb" @@ -1077,9 +999,10 @@ def test_save_in_pct_and_lgt_auto_extensions(nb_file, tmpdir): assert read_format_from_metadata(stream.read(), auto_ext) == "light" -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb", skip="(magic|305)")) -def test_metadata_filter_is_effective(nb_file, tmpdir): - nb = jupytext.read(nb_file) +def test_metadata_filter_is_effective(ipynb_py_R_jl_file, tmpdir): + if re.match(r".*(magic|305).*", ipynb_py_R_jl_file): + pytest.skip() + nb = jupytext.read(ipynb_py_R_jl_file) tmp_ipynb = "notebook.ipynb" tmp_script = "notebook.py" @@ -1163,12 +1086,10 @@ def test_no_metadata_added_to_scripts_139(tmpdir): compare(fp.read(), text) -@pytest.mark.parametrize( - "nb_file,ext", itertools.product(list_notebooks("ipynb_py"), [".py", ".ipynb"]) -) -def test_local_format_can_deactivate_pairing(nb_file, ext, tmpdir): +@pytest.mark.parametrize("ext", [".py", ".ipynb"]) +def test_local_format_can_deactivate_pairing(ipynb_py_file, ext, tmpdir): """This is a test for #157: local format can be used to deactivate the global pairing""" - nb = jupytext.read(nb_file) + nb = jupytext.read(ipynb_py_file) nb.metadata["jupytext_formats"] = ext[1:] # py or ipynb # create contents manager with default pairing @@ -1194,10 +1115,9 @@ def test_local_format_can_deactivate_pairing(nb_file, ext, tmpdir): compare_notebooks(nb3, nb) -@pytest.mark.parametrize("nb_file", list_notebooks("Rmd")) -def test_global_pairing_allows_to_save_other_file_types(nb_file, tmpdir): +def test_global_pairing_allows_to_save_other_file_types(rmd_file, tmpdir): """This is a another test for #157: local format can be used to deactivate the global pairing""" - nb = jupytext.read(nb_file) + nb = jupytext.read(rmd_file) # create contents manager with default pairing cm = jupytext.TextFileContentsManager() @@ -1216,12 +1136,11 @@ def test_global_pairing_allows_to_save_other_file_types(nb_file, tmpdir): compare_notebooks(nb2, nb) -@requires_user_kernel_python3 -@pytest.mark.parametrize("nb_file", list_notebooks("R")) -def test_python_kernel_preserves_R_files(nb_file, tmpdir): +@pytest.mark.requires_user_kernel_python3 +def test_python_kernel_preserves_R_files(r_file, tmpdir): """Opening a R file with a Jupyter server that has no R kernel should not modify the file""" tmp_r_file = str(tmpdir.join("script.R")) - with open(nb_file) as fp: + with open(r_file) as fp: script = fp.read() with open(tmp_r_file, "w") as fp: fp.write(script) @@ -1287,30 +1206,6 @@ def test_pair_notebook_in_dotdot_folder(tmpdir): cm.get("scripts/notebook_name.py") -@requires_sphinx_gallery -def test_rst2md_option(tmpdir): - tmp_py = str(tmpdir.join("notebook.py")) - - # Write notebook in sphinx format - nb = new_notebook( - cells=[ - new_markdown_cell("A short sphinx notebook"), - new_markdown_cell(":math:`1+1`"), - ] - ) - write(nb, tmp_py, fmt="py:sphinx") - - cm = jupytext.TextFileContentsManager() - cm.sphinx_convert_rst2md = True - cm.root_dir = str(tmpdir) - - nb2 = cm.get("notebook.py")["content"] - - # Was rst to md conversion effective? - assert nb2.cells[2].source == "$1+1$" - assert nb2.metadata["jupytext"]["rst2md"] is False - - def test_split_at_heading_option(tmpdir): text = """Markdown text @@ -1395,12 +1290,11 @@ def test_set_then_change_formats(tmpdir): assert not os.path.isfile(tmp_py) -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")[:1]) -def test_set_then_change_auto_formats(tmpdir, nb_file): +def test_set_then_change_auto_formats(tmpdir, python_notebook): tmp_ipynb = str(tmpdir.join("nb.ipynb")) tmp_py = str(tmpdir.join("nb.py")) tmp_rmd = str(tmpdir.join("nb.Rmd")) - nb = new_notebook(metadata=read(nb_file).metadata) + nb = new_notebook(metadata=python_notebook.metadata) cm = jupytext.TextFileContentsManager() cm.root_dir = str(tmpdir) @@ -1434,8 +1328,7 @@ def test_set_then_change_auto_formats(tmpdir, nb_file): cm.get("nb.ipynb") -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py")) -def test_share_py_recreate_ipynb(tmpdir, nb_file): +def test_share_py_recreate_ipynb(tmpdir, ipynb_py_R_jl_file): tmp_ipynb = str(tmpdir.join("nb.ipynb")) tmp_py = str(tmpdir.join("nb.py")) @@ -1452,7 +1345,7 @@ def test_share_py_recreate_ipynb(tmpdir, nb_file): cm.notebook_metadata_filter = "-all" cm.cell_metadata_filter = "-all" - nb = read(nb_file) + nb = read(ipynb_py_R_jl_file) model_ipynb = cm.save(model=notebook_model(nb), path="nb.ipynb") assert os.path.isfile(tmp_ipynb) @@ -1889,97 +1782,6 @@ def test_nested_prefix(tmpdir): assert tmpdir.join("nested").join("prefix").join("notebook.py").isfile() -def fs_meta_manager(tmpdir): - try: - from jupyterfs.metamanager import MetaManager - except ImportError: - pytest.skip("jupyterfs is not available") - - cm_class = jupytext.build_jupytext_contents_manager_class(MetaManager) - logger = logging.getLogger("jupyter-fs") - cm = cm_class(parent=None, log=logger) - cm.initResource( - { - "url": f"osfs://{tmpdir}", - } - ) - return cm - - -def test_jupytext_jupyter_fs_metamanager(tmpdir): - """Test the basic get/save functions of Jupytext with a fs manager - https://github.com/mwouts/jupytext/issues/618""" - cm = fs_meta_manager(tmpdir) - # the hash that corresponds to the osfs - osfs = [h for h in cm._managers if h != ""][0] - - # save a few files - text = "some text\n" - cm.save(dict(type="file", content=text, format="text"), path=osfs + ":text.md") - nb = new_notebook( - cells=[new_markdown_cell("A markdown cell"), new_code_cell("1 + 1")] - ) - cm.save(notebook_model(nb), osfs + ":notebook.ipynb") - cm.save(notebook_model(nb), osfs + ":text_notebook.md") - - # list the directory - directory = cm.get(osfs + ":/") - assert {file["name"] for file in directory["content"]} == { - "text.md", - "text_notebook.md", - "notebook.ipynb", - } - - # get the files - model = cm.get(osfs + ":/text.md", type="file") - assert model["type"] == "file" - assert model["content"] == text - - model = cm.get(osfs + ":text.md", type="notebook") - assert model["type"] == "notebook" - # We only compare the cells, as kernelspecs are added to the notebook metadata - compare_cells( - model["content"].cells, [new_markdown_cell(text.strip())], compare_ids=False - ) - - for nb_file in ["notebook.ipynb", "text_notebook.md"]: - model = cm.get(osfs + ":" + nb_file) - assert model["type"] == "notebook" - actual_cells = model["content"].cells - - # saving adds 'trusted=True' to the code cell metadata - for cell in actual_cells: - cell.metadata = {} - compare_cells(actual_cells, nb.cells, compare_ids=False) - - -def test_config_jupytext_jupyter_fs_meta_manager(tmpdir): - """Test the configuration of Jupytext with a fs manager""" - tmpdir.join("jupytext.toml").write('formats = "ipynb,py"') - cm = fs_meta_manager(tmpdir) - # the hash that corresponds to the osfs - osfs = [h for h in cm._managers if h != ""][0] - - # save a few files - nb = new_notebook() - cm.save(dict(type="file", content="text", format="text"), path=osfs + ":text.md") - cm.save(notebook_model(nb), osfs + ":script.py") - cm.save(notebook_model(nb), osfs + ":text_notebook.md") - cm.save(notebook_model(nb), osfs + ":notebook.ipynb") - - # list the directory - directory = cm.get(osfs + ":/") - assert {file["name"] for file in directory["content"]} == { - "jupytext.toml", - "text.md", - "text_notebook.md", - "notebook.ipynb", - "notebook.py", - "script.py", - "script.ipynb", - } - - def test_timestamp_is_correct_after_reload_978(tmp_path, python_notebook): """Here we reproduce the conditions in Issue #978 and make sure no warning is generated""" @@ -1989,12 +1791,12 @@ def test_timestamp_is_correct_after_reload_978(tmp_path, python_notebook): cm = jupytext.TextFileContentsManager() cm.root_dir = str(tmp_path) - ipynb_file = tmp_path / "nb.ipynb" + ipynb_py_R_jl_file = tmp_path / "nb.ipynb" py_file = tmp_path / "nb.py" # 1. Save the paired notebook cm.save(notebook_model(nb), path="nb.ipynb") - assert ipynb_file.exists() + assert ipynb_py_R_jl_file.exists() assert py_file.exists() # and reload to get the original timestamp diff --git a/tests/test_load_multiple.py b/tests/integration/contents_manager/test_load_multiple.py similarity index 100% rename from tests/test_load_multiple.py rename to tests/integration/contents_manager/test_load_multiple.py diff --git a/tests/notebooks/ipynb_py/World population.ipynb b/tests/notebooks/ipynb_py/World population.ipynb deleted file mode 100644 index a8bae7d5a..000000000 --- a/tests/notebooks/ipynb_py/World population.ipynb +++ /dev/null @@ -1,1475 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# A quick insight at world population\n", - "\n", - "## Collecting population data\n", - "\n", - "In the below we retrieve population data from the\n", - "[World Bank](http://www.worldbank.org/)\n", - "using the [wbdata](https://github.com/OliverSherouse/wbdata) python package" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import wbdata as wb\n", - "\n", - "pd.options.display.max_rows = 6\n", - "pd.options.display.max_columns = 20" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Corresponding indicator is found using search method - or, directly,\n", - "the World Bank site." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SP.POP.TOTL\tPopulation, total\n" - ] - } - ], - "source": [ - "wb.search_indicators('Population, total') # SP.POP.TOTL\n", - "# wb.search_indicators('area')\n", - "# => https://data.worldbank.org/indicator is easier to use" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we download the population data" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Population, totalSurface area (sq. km)Land area (sq. km)Arable land (% of land area)
countrydate
Afghanistan1960-01-018996351.0NaNNaNNaN
1961-01-019166764.0652860.0652860.011.717673
1962-01-019345868.0652860.0652860.011.794259
..................
Zimbabwe2015-01-0115777451.0390760.0386850.010.339925
2016-01-0116150362.0390760.0386850.0NaN
2017-01-0116529904.0390760.0386850.0NaN
\n", - "

15312 rows × 4 columns

\n", - "
" - ], - "text/plain": [ - " Population, total Surface area (sq. km) \\\n", - "country date \n", - "Afghanistan 1960-01-01 8996351.0 NaN \n", - " 1961-01-01 9166764.0 652860.0 \n", - " 1962-01-01 9345868.0 652860.0 \n", - "... ... ... \n", - "Zimbabwe 2015-01-01 15777451.0 390760.0 \n", - " 2016-01-01 16150362.0 390760.0 \n", - " 2017-01-01 16529904.0 390760.0 \n", - "\n", - " Land area (sq. km) Arable land (% of land area) \n", - "country date \n", - "Afghanistan 1960-01-01 NaN NaN \n", - " 1961-01-01 652860.0 11.717673 \n", - " 1962-01-01 652860.0 11.794259 \n", - "... ... ... \n", - "Zimbabwe 2015-01-01 386850.0 10.339925 \n", - " 2016-01-01 386850.0 NaN \n", - " 2017-01-01 386850.0 NaN \n", - "\n", - "[15312 rows x 4 columns]" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "indicators = {'SP.POP.TOTL': 'Population, total',\n", - " 'AG.SRF.TOTL.K2': 'Surface area (sq. km)',\n", - " 'AG.LND.TOTL.K2': 'Land area (sq. km)',\n", - " 'AG.LND.ARBL.ZS': 'Arable land (% of land area)'}\n", - "data = wb.get_dataframe(indicators, convert_date=True).sort_index()\n", - "data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "World is one of the countries" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Population, totalSurface area (sq. km)Land area (sq. km)Arable land (% of land area)
date
1960-01-013.032160e+09NaNNaNNaN
1961-01-013.073369e+09134043190.4129721455.49.693086
1962-01-013.126510e+09134043190.4129721435.49.726105
...............
2015-01-017.357559e+09134325130.2129732901.810.991288
2016-01-017.444157e+09134325130.2129733172.7NaN
2017-01-017.530360e+09134325130.2129733172.7NaN
\n", - "

58 rows × 4 columns

\n", - "
" - ], - "text/plain": [ - " Population, total Surface area (sq. km) Land area (sq. km) \\\n", - "date \n", - "1960-01-01 3.032160e+09 NaN NaN \n", - "1961-01-01 3.073369e+09 134043190.4 129721455.4 \n", - "1962-01-01 3.126510e+09 134043190.4 129721435.4 \n", - "... ... ... ... \n", - "2015-01-01 7.357559e+09 134325130.2 129732901.8 \n", - "2016-01-01 7.444157e+09 134325130.2 129733172.7 \n", - "2017-01-01 7.530360e+09 134325130.2 129733172.7 \n", - "\n", - " Arable land (% of land area) \n", - "date \n", - "1960-01-01 NaN \n", - "1961-01-01 9.693086 \n", - "1962-01-01 9.726105 \n", - "... ... \n", - "2015-01-01 10.991288 \n", - "2016-01-01 NaN \n", - "2017-01-01 NaN \n", - "\n", - "[58 rows x 4 columns]" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data.loc['World']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Can we classify over continents?" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Index(['Iran, Islamic Rep.', 'Congo, Dem. Rep.', 'Germany', 'Vietnam',\n", - " 'Egypt, Arab Rep.', 'Central Europe and the Baltics', 'Philippines',\n", - " 'Ethiopia', 'Japan', 'Mexico', 'Russian Federation', 'Bangladesh',\n", - " 'Nigeria', 'Pakistan', 'Brazil', 'Indonesia', 'United States',\n", - " 'Euro area', 'North America',\n", - " 'Middle East & North Africa (IDA & IBRD countries)',\n", - " 'Middle East & North Africa (excluding high income)', 'Arab World',\n", - " 'Europe & Central Asia (excluding high income)',\n", - " 'Middle East & North Africa',\n", - " 'Europe & Central Asia (IDA & IBRD countries)',\n", - " 'Fragile and conflict affected situations', 'European Union',\n", - " 'IDA blend', 'Latin America & Caribbean (excluding high income)',\n", - " 'Latin America & the Caribbean (IDA & IBRD countries)',\n", - " 'Latin America & Caribbean', 'Low income',\n", - " 'Heavily indebted poor countries (HIPC)', 'Pre-demographic dividend',\n", - " 'Europe & Central Asia', 'Least developed countries: UN classification',\n", - " 'Sub-Saharan Africa (excluding high income)',\n", - " 'Sub-Saharan Africa (IDA & IBRD countries)', 'Sub-Saharan Africa',\n", - " 'IDA only', 'Post-demographic dividend', 'High income', 'OECD members',\n", - " 'India', 'China', 'IDA total', 'South Asia (IDA & IBRD)', 'South Asia',\n", - " 'East Asia & Pacific (IDA & IBRD countries)',\n", - " 'East Asia & Pacific (excluding high income)',\n", - " 'Late-demographic dividend', 'East Asia & Pacific',\n", - " 'Upper middle income', 'Lower middle income',\n", - " 'Early-demographic dividend', 'IBRD only', 'Middle income',\n", - " 'Low & middle income', 'IDA & IBRD total', 'World'],\n", - " dtype='object', name='country')" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data.loc[(slice(None), '2017-01-01'), :]['Population, total'].dropna(\n", - ").sort_values().tail(60).index.get_level_values('country')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Extract zones manually (in order of increasing population)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "zones = ['North America', 'Middle East & North Africa',\n", - " 'Latin America & Caribbean', 'Europe & Central Asia',\n", - " 'Sub-Saharan Africa', 'South Asia',\n", - " 'East Asia & Pacific'][::-1]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And extract population information (and check total is right)" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "population = data.loc[zones]['Population, total'].swaplevel().unstack()\n", - "population = population[zones]\n", - "assert all(data.loc['World']['Population, total'] == population.sum(axis=1))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Stacked area plot with matplotlib" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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aerBUQreQY7tQfbcFJutBE9qMT4pRXlS9mvgqIiIiIj0HAUi+YFcb5hmcJiKf4CCk+bEJDlw4+y0YqzXoyxXwjwshyhnbbWiFhEREZEbR4GoT7m+KM3AIFVfmoInRdZKctRJsNuOsVkPs1KL8LgUIlPwQKHb1YqIiIiIXA0Folum40/QDAxS8aXJO2dB57ATZ7cVY7MRZrkS5lE5RD7vqBObiIiIiPS9rgYiY8zvBv434LcAI8AfdV33n3ezpl7jOn5cb5i2J0LTE6bmxCg4KU5MkkM3xk4rzno9zHIlwlI5QDHnaKioiIiIiMhz6vYKUQi4D/x94P/rci1f2kbwN9G5+2O0sehg08Y6/9yihU37mWtnHzYl10u+HeC07eek5eOk6eeo6SFT93LU8FGuqRGBiIiIiMhV6Wogcl33a8DXAIwx3SzlUvzN/O/g/10e73YZIiIiIiLynLq9QvRCjDE+wPfMpUi3ahERERERkZvvpu3H+nnOWgE8/djpbjkiIiIiInKT3bRA9AtA7JmPO90tR0REREREbrIbtWXOdd06UH/6+DacOxIRERERke65aStEIiIiIiIil6bbc4jCwOwzl6aMMT8KZF3X3epSWSIiIiIi0ie6vWXutwLffObxL57/+SvAH7/2akREREREpK90ew7RtwAdBBIRERERka7QGSIREREREelbCkQiIiIiItK3FIhERERERKRvKRCJiIiIiEjfUiASEREREZG+pUAkIiIiIiJ9S4FIRERERET6lgKRiIiIiIj0LQUiERERERHpWwpEIiIiIiLStxSIRERERESkbykQiYiIiIhI31IgEhERERGRvqVAJCIiIiIifUuBSERERERE+pYCkYiIiIiI9C0FIhERERER6VsKRCIiIiIi0rcUiEREREREpG8pEImIiIiISN9SIBIRERERkb6lQCQiIiIiIn1LgUhERERERPqWApGIiIiIiPQtBSIREREREelbCkQiIiIiItK3FIhERERERKRvKRCJiIiIiEjfUiASEREREZG+pUAkIiIiIiJ9S4FIRERERET6lgKRiIiIiIj0LQUiERERERHpWwpEIiIiIiLStxSIRERERESkbykQiYiIiIhI31IgEhERERGRvtUTgcgY8yeNMevGmJox5j1jzH/c7ZpEREREROT263ogMsb8V8BfA74K/GbgNeBrxpjx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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.clf()\n", - "plt.figure(figsize=(10, 5), dpi=100)\n", - "plt.stackplot(population.index, population.values.T / 1e9)\n", - "plt.legend(population.columns, loc='upper left')\n", - "plt.ylabel('Population count (B)')\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Stacked bar plot with plotly" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Stacked area plots (with cumulated values computed depending on\n", - "selected legends) are\n", - "[on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For\n", - "now we just do a stacked bar plot." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "" - ], - "text/vnd.plotly.v1+html": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import plotly.offline as offline\n", - "import plotly.graph_objs as go\n", - "\n", - "offline.init_notebook_mode()" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "data": [ - { - "name": "East Asia & Pacific", - "type": "bar", - "uid": "7fb5938a-ae8e-11e8-acff-2c44fd283bc0", - "x": [ - "1960-01-01", - "1961-01-01", - "1962-01-01", - "1963-01-01", - "1964-01-01", - "1965-01-01", - "1966-01-01", - "1967-01-01", - "1968-01-01", - "1969-01-01", - "1970-01-01", - "1971-01-01", - "1972-01-01", - "1973-01-01", - "1974-01-01", - "1975-01-01", - "1976-01-01", - "1977-01-01", - "1978-01-01", - "1979-01-01", - "1980-01-01", - "1981-01-01", - "1982-01-01", - "1983-01-01", - "1984-01-01", - "1985-01-01", - "1986-01-01", - "1987-01-01", - "1988-01-01", - "1989-01-01", - "1990-01-01", - "1991-01-01", - "1992-01-01", - "1993-01-01", - "1994-01-01", - "1995-01-01", - "1996-01-01", - "1997-01-01", - "1998-01-01", - "1999-01-01", - "2000-01-01", - "2001-01-01", - "2002-01-01", - "2003-01-01", - "2004-01-01", - "2005-01-01", - "2006-01-01", - "2007-01-01", - "2008-01-01", - "2009-01-01", - "2010-01-01", - "2011-01-01", - "2012-01-01", - "2013-01-01", - "2014-01-01", - "2015-01-01", - "2016-01-01", - "2017-01-01" - ], - "y": [ - 1039944591, - 1043546747, - 1058028199, - 1083802299, - 1109204182, - 1135650895, - 1165517894, - 1194424326, - 1223721283, - 1256501008, - 1289258962, - 1322942750, - 1354794332, - 1385052014, - 1415128313, - 1442241325, - 1466464716, - 1489361661, - 1512159741, - 1535391301, - 1558178982, - 1581806986, - 1607731269, - 1633628203, - 1658253758, - 1683448851, - 1710171170, - 1738276268, - 1766656203, - 1794408755, - 1821481246, - 1847530233, - 1871898268, - 1895356643, - 1918771287, - 1941918800, - 1964635058, - 1986799861, - 2008140141, - 2028095314, - 2047150745, - 2065521836, - 2082953527, - 2099545576, - 2115557365, - 2131363078, - 2147029631, - 2162104019, - 2177421759, - 2192352319, - 2207154641, - 2221934584, - 2237082761, - 2252311022, - 2267745366, - 2283108073, - 2298726779, - 2314364990 - ] - }, - { - "name": "South Asia", - "type": "bar", - "uid": "7fb5938b-ae8e-11e8-a575-2c44fd283bc0", - "x": [ - "1960-01-01", - "1961-01-01", - "1962-01-01", - "1963-01-01", - "1964-01-01", - "1965-01-01", - "1966-01-01", - "1967-01-01", - "1968-01-01", - "1969-01-01", - "1970-01-01", - "1971-01-01", - "1972-01-01", - "1973-01-01", - "1974-01-01", - "1975-01-01", - "1976-01-01", - "1977-01-01", - "1978-01-01", - "1979-01-01", - "1980-01-01", - "1981-01-01", - "1982-01-01", - "1983-01-01", - "1984-01-01", - "1985-01-01", - "1986-01-01", - "1987-01-01", - "1988-01-01", - "1989-01-01", - "1990-01-01", - "1991-01-01", - "1992-01-01", - "1993-01-01", - "1994-01-01", - "1995-01-01", - "1996-01-01", - "1997-01-01", - "1998-01-01", - "1999-01-01", - "2000-01-01", - "2001-01-01", - "2002-01-01", - "2003-01-01", - "2004-01-01", - "2005-01-01", - "2006-01-01", - "2007-01-01", - "2008-01-01", - "2009-01-01", - "2010-01-01", - "2011-01-01", - "2012-01-01", - "2013-01-01", - "2014-01-01", - "2015-01-01", - "2016-01-01", - "2017-01-01" - ], - "y": [ - 571835666, - 583894094, - 596413939, - 609391805, - 622822615, - 636701820, - 651036352, - 665826653, - 681054882, - 696697198, - 712740919, - 729173562, - 746012374, - 763310561, - 781140577, - 799553306, - 818560436, - 838142287, - 858277856, - 878933031, - 900076467, - 921696915, - 943781613, - 966293643, - 989188965, - 1012429641, - 1035982524, - 1059829211, - 1083963380, - 1108386444, - 1133089464, - 1158058109, - 1183253534, - 1208612942, - 1234059205, - 1259530819, - 1284978193, - 1310387887, - 1335777637, - 1361185289, - 1386625845, - 1412104373, - 1437568227, - 1462906674, - 1487975237, - 1512670560, - 1536943534, - 1560818860, - 1584359049, - 1607663899, - 1630806784, - 1653798614, - 1676615491, - 1699310450, - 1721847786, - 1744199944, - 1766393714, - 1788388852 - ] - }, - { - "name": "Sub-Saharan Africa", - "type": "bar", - "uid": "7fb5938c-ae8e-11e8-bc49-2c44fd283bc0", - "x": [ - "1960-01-01", - "1961-01-01", - "1962-01-01", - "1963-01-01", - "1964-01-01", - "1965-01-01", - "1966-01-01", - "1967-01-01", - "1968-01-01", - "1969-01-01", - "1970-01-01", - "1971-01-01", - "1972-01-01", - "1973-01-01", - "1974-01-01", - "1975-01-01", - "1976-01-01", - "1977-01-01", - "1978-01-01", - "1979-01-01", - "1980-01-01", - "1981-01-01", - "1982-01-01", - "1983-01-01", - "1984-01-01", - "1985-01-01", - "1986-01-01", - "1987-01-01", - "1988-01-01", - "1989-01-01", - "1990-01-01", - "1991-01-01", - "1992-01-01", - "1993-01-01", - "1994-01-01", - "1995-01-01", - "1996-01-01", - "1997-01-01", - "1998-01-01", - "1999-01-01", - "2000-01-01", - "2001-01-01", - "2002-01-01", - "2003-01-01", - "2004-01-01", - "2005-01-01", - "2006-01-01", - "2007-01-01", - "2008-01-01", - "2009-01-01", - "2010-01-01", - "2011-01-01", - "2012-01-01", - "2013-01-01", - "2014-01-01", - "2015-01-01", - "2016-01-01", - "2017-01-01" - ], - "y": [ - 228586005, - 234008580, - 239647139, - 245503266, - 251576403, - 257868609, - 264386171, - 271139271, - 278138996, - 285397999, - 292929074, - 300737023, - 308831549, - 317234127, - 325972033, - 335064879, - 344522341, - 354343159, - 364515830, - 375034981, - 385885281, - 397065556, - 408577033, - 420413518, - 432573431, - 445048059, - 457835414, - 470938971, - 484357710, - 498102752, - 512177101, - 526599014, - 541365685, - 556451898, - 571828603, - 587469624, - 603385639, - 619607956, - 636182577, - 653179863, - 670649638, - 688615995, - 707099850, - 726140617, - 745788118, - 766080507, - 787035792, - 808660166, - 830965556, - 853953657, - 877628367, - 901989926, - 927039875, - 952734072, - 979017918, - 1005850049, - 1033212743, - 1061107721 - ] - }, - { - "name": "Europe & Central Asia", - "type": "bar", - "uid": "7fb5938d-ae8e-11e8-ab88-2c44fd283bc0", - "x": [ - "1960-01-01", - "1961-01-01", - "1962-01-01", - "1963-01-01", - "1964-01-01", - "1965-01-01", - "1966-01-01", - "1967-01-01", - "1968-01-01", - "1969-01-01", - "1970-01-01", - "1971-01-01", - "1972-01-01", - "1973-01-01", - "1974-01-01", - "1975-01-01", - "1976-01-01", - "1977-01-01", - "1978-01-01", - "1979-01-01", - "1980-01-01", - "1981-01-01", - "1982-01-01", - "1983-01-01", - "1984-01-01", - "1985-01-01", - "1986-01-01", - "1987-01-01", - "1988-01-01", - "1989-01-01", - "1990-01-01", - "1991-01-01", - "1992-01-01", - "1993-01-01", - "1994-01-01", - "1995-01-01", - "1996-01-01", - "1997-01-01", - "1998-01-01", - "1999-01-01", - "2000-01-01", - "2001-01-01", - "2002-01-01", - "2003-01-01", - "2004-01-01", - "2005-01-01", - "2006-01-01", - "2007-01-01", - "2008-01-01", - "2009-01-01", - "2010-01-01", - "2011-01-01", - "2012-01-01", - "2013-01-01", - "2014-01-01", - "2015-01-01", - "2016-01-01", - "2017-01-01" - ], - "y": [ - 667246384, - 674972972, - 682938669, - 690962675, - 698905664, - 706609007, - 713341112, - 719879789, - 726161895, - 732317863, - 737948178, - 743607439, - 749815857, - 755867961, - 761701324, - 767332578, - 772838318, - 778094845, - 783298994, - 788525199, - 793937090, - 799215272, - 803972967, - 808524728, - 813281381, - 818146882, - 823155058, - 828213790, - 833315236, - 838462813, - 842848473, - 846178276, - 849656745, - 852762016, - 854723055, - 856352860, - 857652705, - 859112733, - 860236341, - 861380108, - 862304086, - 863615632, - 865196877, - 867457664, - 870030756, - 872661616, - 875343235, - 878465990, - 881965815, - 885591814, - 889016507, - 891098854, - 894679968, - 898881448, - 903123160, - 907426233, - 911686319, - 915545801 - ] - }, - { - "name": "Latin America & Caribbean", - "type": "bar", - "uid": "7fb5938e-ae8e-11e8-92ac-2c44fd283bc0", - "x": [ - "1960-01-01", - "1961-01-01", - "1962-01-01", - "1963-01-01", - "1964-01-01", - "1965-01-01", - "1966-01-01", - "1967-01-01", - "1968-01-01", - "1969-01-01", - "1970-01-01", - "1971-01-01", - "1972-01-01", - "1973-01-01", - "1974-01-01", - "1975-01-01", - "1976-01-01", - "1977-01-01", - "1978-01-01", - "1979-01-01", - "1980-01-01", - "1981-01-01", - "1982-01-01", - "1983-01-01", - "1984-01-01", - "1985-01-01", - "1986-01-01", - "1987-01-01", - "1988-01-01", - "1989-01-01", - "1990-01-01", - "1991-01-01", - "1992-01-01", - "1993-01-01", - "1994-01-01", - "1995-01-01", - "1996-01-01", - "1997-01-01", - "1998-01-01", - "1999-01-01", - "2000-01-01", - "2001-01-01", - "2002-01-01", - "2003-01-01", - "2004-01-01", - "2005-01-01", - "2006-01-01", - "2007-01-01", - "2008-01-01", - "2009-01-01", - "2010-01-01", - "2011-01-01", - "2012-01-01", - "2013-01-01", - "2014-01-01", - "2015-01-01", - "2016-01-01", - "2017-01-01" - ], - "y": [ - 220434662, - 226564469, - 232897323, - 239401268, - 246016368, - 252710310, - 259468669, - 266295880, - 273209036, - 280225795, - 287361389, - 294620137, - 301984430, - 309446959, - 316987646, - 324590837, - 332247800, - 339956419, - 347735305, - 355593111, - 363543431, - 371580994, - 379697683, - 387868173, - 396059351, - 404246768, - 412413602, - 420560827, - 428701267, - 436857131, - 445044474, - 453251622, - 461466819, - 469673465, - 477832467, - 485913138, - 493920488, - 501837820, - 509664957, - 517324344, - 524829251, - 532172709, - 539372044, - 546478662, - 553563090, - 560673962, - 567821716, - 574994397, - 582179826, - 589349327, - 596478519, - 603537118, - 610547919, - 617495658, - 624335544, - 631062657, - 637663890, - 644137666 - ] - }, - { - "name": "Middle East & North Africa", - "type": "bar", - "uid": "7fb5938f-ae8e-11e8-aa1c-2c44fd283bc0", - "x": [ - "1960-01-01", - "1961-01-01", - "1962-01-01", - "1963-01-01", - "1964-01-01", - "1965-01-01", - "1966-01-01", - "1967-01-01", - "1968-01-01", - "1969-01-01", - "1970-01-01", - "1971-01-01", - "1972-01-01", - "1973-01-01", - "1974-01-01", - "1975-01-01", - "1976-01-01", - "1977-01-01", - "1978-01-01", - "1979-01-01", - "1980-01-01", - "1981-01-01", - "1982-01-01", - "1983-01-01", - "1984-01-01", - "1985-01-01", - "1986-01-01", - "1987-01-01", - "1988-01-01", - "1989-01-01", - "1990-01-01", - "1991-01-01", - "1992-01-01", - "1993-01-01", - "1994-01-01", - "1995-01-01", - "1996-01-01", - "1997-01-01", - "1998-01-01", - "1999-01-01", - "2000-01-01", - "2001-01-01", - "2002-01-01", - "2003-01-01", - "2004-01-01", - "2005-01-01", - "2006-01-01", - "2007-01-01", - "2008-01-01", - "2009-01-01", - "2010-01-01", - "2011-01-01", - "2012-01-01", - "2013-01-01", - "2014-01-01", - "2015-01-01", - "2016-01-01", - "2017-01-01" - ], - "y": [ - 105488678, - 108374227, - 111385940, - 114471415, - 117671617, - 120973578, - 124374455, - 127947266, - 131566224, - 135279930, - 139083819, - 142950993, - 146887303, - 150999871, - 155259901, - 159722643, - 164407528, - 169318424, - 174493349, - 180000521, - 185843847, - 192015875, - 198499602, - 205229901, - 212102793, - 219095273, - 226161475, - 233285188, - 240367135, - 247283550, - 255989130, - 262659662, - 267020622, - 273204804, - 279279333, - 286917385, - 292934005, - 298982946, - 305001541, - 311053183, - 317129227, - 323196354, - 329289435, - 335522845, - 342046777, - 348956287, - 356287693, - 363996317, - 371999662, - 380192587, - 388376106, - 396573248, - 404783020, - 412953000, - 421030035, - 428974903, - 436738031, - 444322417 - ] - }, - { - "name": "North America", - "type": "bar", - "uid": "7fb59390-ae8e-11e8-95e7-2c44fd283bc0", - "x": [ - "1960-01-01", - "1961-01-01", - "1962-01-01", - "1963-01-01", - "1964-01-01", - "1965-01-01", - "1966-01-01", - "1967-01-01", - "1968-01-01", - "1969-01-01", - "1970-01-01", - "1971-01-01", - "1972-01-01", - "1973-01-01", - "1974-01-01", - "1975-01-01", - "1976-01-01", - "1977-01-01", - "1978-01-01", - "1979-01-01", - "1980-01-01", - "1981-01-01", - "1982-01-01", - "1983-01-01", - "1984-01-01", - "1985-01-01", - "1986-01-01", - "1987-01-01", - "1988-01-01", - "1989-01-01", - "1990-01-01", - "1991-01-01", - "1992-01-01", - "1993-01-01", - "1994-01-01", - "1995-01-01", - "1996-01-01", - "1997-01-01", - "1998-01-01", - "1999-01-01", - "2000-01-01", - "2001-01-01", - "2002-01-01", - "2003-01-01", - "2004-01-01", - "2005-01-01", - "2006-01-01", - "2007-01-01", - "2008-01-01", - "2009-01-01", - "2010-01-01", - "2011-01-01", - "2012-01-01", - "2013-01-01", - "2014-01-01", - "2015-01-01", - "2016-01-01", - "2017-01-01" - ], - "y": [ - 198624409, - 202007500, - 205198600, - 208253700, - 211262900, - 214031100, - 216659000, - 219176000, - 221503000, - 223759000, - 226431000, - 229361135, - 231943831, - 234332208, - 236681487, - 239235000, - 241606200, - 244088400, - 246674600, - 249385800, - 251872670, - 254421050, - 256921449, - 259303930, - 261583423, - 263922898, - 266394382, - 268896849, - 271452347, - 274256841, - 277473326, - 281211703, - 285092192, - 288811320, - 292297226, - 295691746, - 299126029, - 302704697, - 306162843, - 309600485, - 312993944, - 316113359, - 319050105, - 321847258, - 324864038, - 327892753, - 331014940, - 334184023, - 337405012, - 340465736, - 343408819, - 346051624, - 348808615, - 351451876, - 354223012, - 356937591, - 359735880, - 362492702 - ] - } - ], - "layout": { - "barmode": "stack", - "title": "World population" - } - }, - "text/html": [ - "
" - ], - "text/vnd.plotly.v1+html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "bars = [go.Bar(x=population.index, y=population[zone], name=zone)\n", - " for zone in zones]\n", - "fig = go.Figure(data=bars,\n", - " layout=go.Layout(title='World population',\n", - " barmode='stack'))\n", - "offline.iplot(fig)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tests/notebooks/mirror/ipynb_to_Rmd/World population.Rmd b/tests/notebooks/mirror/ipynb_to_Rmd/World population.Rmd deleted file mode 100644 index ec3250cc0..000000000 --- a/tests/notebooks/mirror/ipynb_to_Rmd/World population.Rmd +++ /dev/null @@ -1,112 +0,0 @@ ---- -jupyter: - kernelspec: - display_name: Python 3 - language: python - name: python3 ---- - -# A quick insight at world population - -## Collecting population data - -In the below we retrieve population data from the -[World Bank](http://www.worldbank.org/) -using the [wbdata](https://github.com/OliverSherouse/wbdata) python package - -```{python} -import pandas as pd -import wbdata as wb - -pd.options.display.max_rows = 6 -pd.options.display.max_columns = 20 -``` - -Corresponding indicator is found using search method - or, directly, -the World Bank site. - -```{python} -wb.search_indicators('Population, total') # SP.POP.TOTL -# wb.search_indicators('area') -# => https://data.worldbank.org/indicator is easier to use -``` - -Now we download the population data - -```{python} -indicators = {'SP.POP.TOTL': 'Population, total', - 'AG.SRF.TOTL.K2': 'Surface area (sq. km)', - 'AG.LND.TOTL.K2': 'Land area (sq. km)', - 'AG.LND.ARBL.ZS': 'Arable land (% of land area)'} -data = wb.get_dataframe(indicators, convert_date=True).sort_index() -data -``` - -World is one of the countries - -```{python} -data.loc['World'] -``` - -Can we classify over continents? - -```{python} -data.loc[(slice(None), '2017-01-01'), :]['Population, total'].dropna( -).sort_values().tail(60).index.get_level_values('country') -``` - -Extract zones manually (in order of increasing population) - -```{python} -zones = ['North America', 'Middle East & North Africa', - 'Latin America & Caribbean', 'Europe & Central Asia', - 'Sub-Saharan Africa', 'South Asia', - 'East Asia & Pacific'][::-1] -``` - -And extract population information (and check total is right) - -```{python} -population = data.loc[zones]['Population, total'].swaplevel().unstack() -population = population[zones] -assert all(data.loc['World']['Population, total'] == population.sum(axis=1)) -``` - -## Stacked area plot with matplotlib - -```{python} -import matplotlib.pyplot as plt -``` - -```{python} -plt.clf() -plt.figure(figsize=(10, 5), dpi=100) -plt.stackplot(population.index, population.values.T / 1e9) -plt.legend(population.columns, loc='upper left') -plt.ylabel('Population count (B)') -plt.show() -``` - -## Stacked bar plot with plotly - - -Stacked area plots (with cumulated values computed depending on -selected legends) are -[on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For -now we just do a stacked bar plot. - -```{python} -import plotly.offline as offline -import plotly.graph_objs as go - -offline.init_notebook_mode() -``` - -```{python} -bars = [go.Bar(x=population.index, y=population[zone], name=zone) - for zone in zones] -fig = go.Figure(data=bars, - layout=go.Layout(title='World population', - barmode='stack')) -offline.iplot(fig) -``` diff --git a/tests/notebooks/mirror/ipynb_to_hydrogen/World population.py b/tests/notebooks/mirror/ipynb_to_hydrogen/World population.py deleted file mode 100644 index 5fb638892..000000000 --- a/tests/notebooks/mirror/ipynb_to_hydrogen/World population.py +++ /dev/null @@ -1,110 +0,0 @@ -# --- -# jupyter: -# kernelspec: -# display_name: Python 3 -# language: python -# name: python3 -# --- - -# %% [markdown] -# # A quick insight at world population -# -# ## Collecting population data -# -# In the below we retrieve population data from the -# [World Bank](http://www.worldbank.org/) -# using the [wbdata](https://github.com/OliverSherouse/wbdata) python package - -# %% -import pandas as pd -import wbdata as wb - -pd.options.display.max_rows = 6 -pd.options.display.max_columns = 20 - -# %% [markdown] -# Corresponding indicator is found using search method - or, directly, -# the World Bank site. - -# %% -wb.search_indicators('Population, total') # SP.POP.TOTL -# wb.search_indicators('area') -# => https://data.worldbank.org/indicator is easier to use - -# %% [markdown] -# Now we download the population data - -# %% -indicators = {'SP.POP.TOTL': 'Population, total', - 'AG.SRF.TOTL.K2': 'Surface area (sq. km)', - 'AG.LND.TOTL.K2': 'Land area (sq. km)', - 'AG.LND.ARBL.ZS': 'Arable land (% of land area)'} -data = wb.get_dataframe(indicators, convert_date=True).sort_index() -data - -# %% [markdown] -# World is one of the countries - -# %% -data.loc['World'] - -# %% [markdown] -# Can we classify over continents? - -# %% -data.loc[(slice(None), '2017-01-01'), :]['Population, total'].dropna( -).sort_values().tail(60).index.get_level_values('country') - -# %% [markdown] -# Extract zones manually (in order of increasing population) - -# %% -zones = ['North America', 'Middle East & North Africa', - 'Latin America & Caribbean', 'Europe & Central Asia', - 'Sub-Saharan Africa', 'South Asia', - 'East Asia & Pacific'][::-1] - -# %% [markdown] -# And extract population information (and check total is right) - -# %% -population = data.loc[zones]['Population, total'].swaplevel().unstack() -population = population[zones] -assert all(data.loc['World']['Population, total'] == population.sum(axis=1)) - -# %% [markdown] -# ## Stacked area plot with matplotlib - -# %% -import matplotlib.pyplot as plt - -# %% -plt.clf() -plt.figure(figsize=(10, 5), dpi=100) -plt.stackplot(population.index, population.values.T / 1e9) -plt.legend(population.columns, loc='upper left') -plt.ylabel('Population count (B)') -plt.show() - -# %% [markdown] -# ## Stacked bar plot with plotly - -# %% [markdown] -# Stacked area plots (with cumulated values computed depending on -# selected legends) are -# [on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For -# now we just do a stacked bar plot. - -# %% -import plotly.offline as offline -import plotly.graph_objs as go - -offline.init_notebook_mode() - -# %% -bars = [go.Bar(x=population.index, y=population[zone], name=zone) - for zone in zones] -fig = go.Figure(data=bars, - layout=go.Layout(title='World population', - barmode='stack')) -offline.iplot(fig) diff --git a/tests/notebooks/mirror/ipynb_to_md/World population.md b/tests/notebooks/mirror/ipynb_to_md/World population.md deleted file mode 100644 index 3328426b5..000000000 --- a/tests/notebooks/mirror/ipynb_to_md/World population.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -jupyter: - kernelspec: - display_name: Python 3 - language: python - name: python3 ---- - -# A quick insight at world population - -## Collecting population data - -In the below we retrieve population data from the -[World Bank](http://www.worldbank.org/) -using the [wbdata](https://github.com/OliverSherouse/wbdata) python package - -```python -import pandas as pd -import wbdata as wb - -pd.options.display.max_rows = 6 -pd.options.display.max_columns = 20 -``` - -Corresponding indicator is found using search method - or, directly, -the World Bank site. - -```python -wb.search_indicators('Population, total') # SP.POP.TOTL -# wb.search_indicators('area') -# => https://data.worldbank.org/indicator is easier to use -``` - -Now we download the population data - -```python -indicators = {'SP.POP.TOTL': 'Population, total', - 'AG.SRF.TOTL.K2': 'Surface area (sq. km)', - 'AG.LND.TOTL.K2': 'Land area (sq. km)', - 'AG.LND.ARBL.ZS': 'Arable land (% of land area)'} -data = wb.get_dataframe(indicators, convert_date=True).sort_index() -data -``` - -World is one of the countries - -```python -data.loc['World'] -``` - -Can we classify over continents? - -```python -data.loc[(slice(None), '2017-01-01'), :]['Population, total'].dropna( -).sort_values().tail(60).index.get_level_values('country') -``` - -Extract zones manually (in order of increasing population) - -```python -zones = ['North America', 'Middle East & North Africa', - 'Latin America & Caribbean', 'Europe & Central Asia', - 'Sub-Saharan Africa', 'South Asia', - 'East Asia & Pacific'][::-1] -``` - -And extract population information (and check total is right) - -```python -population = data.loc[zones]['Population, total'].swaplevel().unstack() -population = population[zones] -assert all(data.loc['World']['Population, total'] == population.sum(axis=1)) -``` - -## Stacked area plot with matplotlib - -```python -import matplotlib.pyplot as plt -``` - -```python -plt.clf() -plt.figure(figsize=(10, 5), dpi=100) -plt.stackplot(population.index, population.values.T / 1e9) -plt.legend(population.columns, loc='upper left') -plt.ylabel('Population count (B)') -plt.show() -``` - -## Stacked bar plot with plotly - - -Stacked area plots (with cumulated values computed depending on -selected legends) are -[on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For -now we just do a stacked bar plot. - -```python -import plotly.offline as offline -import plotly.graph_objs as go - -offline.init_notebook_mode() -``` - -```python -bars = [go.Bar(x=population.index, y=population[zone], name=zone) - for zone in zones] -fig = go.Figure(data=bars, - layout=go.Layout(title='World population', - barmode='stack')) -offline.iplot(fig) -``` diff --git a/tests/notebooks/mirror/ipynb_to_myst/World population.md b/tests/notebooks/mirror/ipynb_to_myst/World population.md deleted file mode 100644 index 5e02a225d..000000000 --- a/tests/notebooks/mirror/ipynb_to_myst/World population.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -kernelspec: - display_name: Python 3 - language: python - name: python3 ---- - -# A quick insight at world population - -## Collecting population data - -In the below we retrieve population data from the -[World Bank](http://www.worldbank.org/) -using the [wbdata](https://github.com/OliverSherouse/wbdata) python package - -```{code-cell} ipython3 -import pandas as pd -import wbdata as wb - -pd.options.display.max_rows = 6 -pd.options.display.max_columns = 20 -``` - -Corresponding indicator is found using search method - or, directly, -the World Bank site. - -```{code-cell} ipython3 -wb.search_indicators('Population, total') # SP.POP.TOTL -# wb.search_indicators('area') -# => https://data.worldbank.org/indicator is easier to use -``` - -Now we download the population data - -```{code-cell} ipython3 -indicators = {'SP.POP.TOTL': 'Population, total', - 'AG.SRF.TOTL.K2': 'Surface area (sq. km)', - 'AG.LND.TOTL.K2': 'Land area (sq. km)', - 'AG.LND.ARBL.ZS': 'Arable land (% of land area)'} -data = wb.get_dataframe(indicators, convert_date=True).sort_index() -data -``` - -World is one of the countries - -```{code-cell} ipython3 -data.loc['World'] -``` - -Can we classify over continents? - -```{code-cell} ipython3 -data.loc[(slice(None), '2017-01-01'), :]['Population, total'].dropna( -).sort_values().tail(60).index.get_level_values('country') -``` - -Extract zones manually (in order of increasing population) - -```{code-cell} ipython3 -zones = ['North America', 'Middle East & North Africa', - 'Latin America & Caribbean', 'Europe & Central Asia', - 'Sub-Saharan Africa', 'South Asia', - 'East Asia & Pacific'][::-1] -``` - -And extract population information (and check total is right) - -```{code-cell} ipython3 -population = data.loc[zones]['Population, total'].swaplevel().unstack() -population = population[zones] -assert all(data.loc['World']['Population, total'] == population.sum(axis=1)) -``` - -## Stacked area plot with matplotlib - -```{code-cell} ipython3 -import matplotlib.pyplot as plt -``` - -```{code-cell} ipython3 -plt.clf() -plt.figure(figsize=(10, 5), dpi=100) -plt.stackplot(population.index, population.values.T / 1e9) -plt.legend(population.columns, loc='upper left') -plt.ylabel('Population count (B)') -plt.show() -``` - -## Stacked bar plot with plotly - -+++ - -Stacked area plots (with cumulated values computed depending on -selected legends) are -[on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For -now we just do a stacked bar plot. - -```{code-cell} ipython3 -import plotly.offline as offline -import plotly.graph_objs as go - -offline.init_notebook_mode() -``` - -```{code-cell} ipython3 -bars = [go.Bar(x=population.index, y=population[zone], name=zone) - for zone in zones] -fig = go.Figure(data=bars, - layout=go.Layout(title='World population', - barmode='stack')) -offline.iplot(fig) -``` diff --git a/tests/notebooks/mirror/ipynb_to_pandoc/World population.md b/tests/notebooks/mirror/ipynb_to_pandoc/World population.md deleted file mode 100644 index 29e7c590a..000000000 --- a/tests/notebooks/mirror/ipynb_to_pandoc/World population.md +++ /dev/null @@ -1,155 +0,0 @@ ---- -jupyter: - kernelspec: - display_name: Python 3 - language: python - name: python3 - nbformat: 4 - nbformat_minor: 2 ---- - -::: {.cell .markdown} -# A quick insight at world population - -## Collecting population data - -In the below we retrieve population data from the -[World Bank](http://www.worldbank.org/) -using the [wbdata](https://github.com/OliverSherouse/wbdata) python package -::: - -::: {.cell .code} -``` python -import pandas as pd -import wbdata as wb - -pd.options.display.max_rows = 6 -pd.options.display.max_columns = 20 -``` -::: - -::: {.cell .markdown} -Corresponding indicator is found using search method - or, directly, -the World Bank site. -::: - -::: {.cell .code} -``` python -wb.search_indicators('Population, total') # SP.POP.TOTL -# wb.search_indicators('area') -# => https://data.worldbank.org/indicator is easier to use -``` -::: - -::: {.cell .markdown} -Now we download the population data -::: - -::: {.cell .code} -``` python -indicators = {'SP.POP.TOTL': 'Population, total', - 'AG.SRF.TOTL.K2': 'Surface area (sq. km)', - 'AG.LND.TOTL.K2': 'Land area (sq. km)', - 'AG.LND.ARBL.ZS': 'Arable land (% of land area)'} -data = wb.get_dataframe(indicators, convert_date=True).sort_index() -data -``` -::: - -::: {.cell .markdown} -World is one of the countries -::: - -::: {.cell .code} -``` python -data.loc['World'] -``` -::: - -::: {.cell .markdown} -Can we classify over continents? -::: - -::: {.cell .code} -``` python -data.loc[(slice(None), '2017-01-01'), :]['Population, total'].dropna( -).sort_values().tail(60).index.get_level_values('country') -``` -::: - -::: {.cell .markdown} -Extract zones manually (in order of increasing population) -::: - -::: {.cell .code} -``` python -zones = ['North America', 'Middle East & North Africa', - 'Latin America & Caribbean', 'Europe & Central Asia', - 'Sub-Saharan Africa', 'South Asia', - 'East Asia & Pacific'][::-1] -``` -::: - -::: {.cell .markdown} -And extract population information (and check total is right) -::: - -::: {.cell .code} -``` python -population = data.loc[zones]['Population, total'].swaplevel().unstack() -population = population[zones] -assert all(data.loc['World']['Population, total'] == population.sum(axis=1)) -``` -::: - -::: {.cell .markdown} -## Stacked area plot with matplotlib -::: - -::: {.cell .code} -``` python -import matplotlib.pyplot as plt -``` -::: - -::: {.cell .code} -``` python -plt.clf() -plt.figure(figsize=(10, 5), dpi=100) -plt.stackplot(population.index, population.values.T / 1e9) -plt.legend(population.columns, loc='upper left') -plt.ylabel('Population count (B)') -plt.show() -``` -::: - -::: {.cell .markdown} -## Stacked bar plot with plotly -::: - -::: {.cell .markdown} -Stacked area plots (with cumulated values computed depending on -selected legends) are -[on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For -now we just do a stacked bar plot. -::: - -::: {.cell .code} -``` python -import plotly.offline as offline -import plotly.graph_objs as go - -offline.init_notebook_mode() -``` -::: - -::: {.cell .code} -``` python -bars = [go.Bar(x=population.index, y=population[zone], name=zone) - for zone in zones] -fig = go.Figure(data=bars, - layout=go.Layout(title='World population', - barmode='stack')) -offline.iplot(fig) -``` -::: diff --git a/tests/notebooks/mirror/ipynb_to_percent/World population.py b/tests/notebooks/mirror/ipynb_to_percent/World population.py deleted file mode 100644 index 5fb638892..000000000 --- a/tests/notebooks/mirror/ipynb_to_percent/World population.py +++ /dev/null @@ -1,110 +0,0 @@ -# --- -# jupyter: -# kernelspec: -# display_name: Python 3 -# language: python -# name: python3 -# --- - -# %% [markdown] -# # A quick insight at world population -# -# ## Collecting population data -# -# In the below we retrieve population data from the -# [World Bank](http://www.worldbank.org/) -# using the [wbdata](https://github.com/OliverSherouse/wbdata) python package - -# %% -import pandas as pd -import wbdata as wb - -pd.options.display.max_rows = 6 -pd.options.display.max_columns = 20 - -# %% [markdown] -# Corresponding indicator is found using search method - or, directly, -# the World Bank site. - -# %% -wb.search_indicators('Population, total') # SP.POP.TOTL -# wb.search_indicators('area') -# => https://data.worldbank.org/indicator is easier to use - -# %% [markdown] -# Now we download the population data - -# %% -indicators = {'SP.POP.TOTL': 'Population, total', - 'AG.SRF.TOTL.K2': 'Surface area (sq. km)', - 'AG.LND.TOTL.K2': 'Land area (sq. km)', - 'AG.LND.ARBL.ZS': 'Arable land (% of land area)'} -data = wb.get_dataframe(indicators, convert_date=True).sort_index() -data - -# %% [markdown] -# World is one of the countries - -# %% -data.loc['World'] - -# %% [markdown] -# Can we classify over continents? - -# %% -data.loc[(slice(None), '2017-01-01'), :]['Population, total'].dropna( -).sort_values().tail(60).index.get_level_values('country') - -# %% [markdown] -# Extract zones manually (in order of increasing population) - -# %% -zones = ['North America', 'Middle East & North Africa', - 'Latin America & Caribbean', 'Europe & Central Asia', - 'Sub-Saharan Africa', 'South Asia', - 'East Asia & Pacific'][::-1] - -# %% [markdown] -# And extract population information (and check total is right) - -# %% -population = data.loc[zones]['Population, total'].swaplevel().unstack() -population = population[zones] -assert all(data.loc['World']['Population, total'] == population.sum(axis=1)) - -# %% [markdown] -# ## Stacked area plot with matplotlib - -# %% -import matplotlib.pyplot as plt - -# %% -plt.clf() -plt.figure(figsize=(10, 5), dpi=100) -plt.stackplot(population.index, population.values.T / 1e9) -plt.legend(population.columns, loc='upper left') -plt.ylabel('Population count (B)') -plt.show() - -# %% [markdown] -# ## Stacked bar plot with plotly - -# %% [markdown] -# Stacked area plots (with cumulated values computed depending on -# selected legends) are -# [on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For -# now we just do a stacked bar plot. - -# %% -import plotly.offline as offline -import plotly.graph_objs as go - -offline.init_notebook_mode() - -# %% -bars = [go.Bar(x=population.index, y=population[zone], name=zone) - for zone in zones] -fig = go.Figure(data=bars, - layout=go.Layout(title='World population', - barmode='stack')) -offline.iplot(fig) diff --git a/tests/notebooks/mirror/ipynb_to_script/World population.py b/tests/notebooks/mirror/ipynb_to_script/World population.py deleted file mode 100644 index f86edd830..000000000 --- a/tests/notebooks/mirror/ipynb_to_script/World population.py +++ /dev/null @@ -1,93 +0,0 @@ -# --- -# jupyter: -# kernelspec: -# display_name: Python 3 -# language: python -# name: python3 -# --- - -# # A quick insight at world population -# -# ## Collecting population data -# -# In the below we retrieve population data from the -# [World Bank](http://www.worldbank.org/) -# using the [wbdata](https://github.com/OliverSherouse/wbdata) python package - -# + -import pandas as pd -import wbdata as wb - -pd.options.display.max_rows = 6 -pd.options.display.max_columns = 20 -# - - -# Corresponding indicator is found using search method - or, directly, -# the World Bank site. - -wb.search_indicators('Population, total') # SP.POP.TOTL -# wb.search_indicators('area') -# => https://data.worldbank.org/indicator is easier to use - -# Now we download the population data - -indicators = {'SP.POP.TOTL': 'Population, total', - 'AG.SRF.TOTL.K2': 'Surface area (sq. km)', - 'AG.LND.TOTL.K2': 'Land area (sq. km)', - 'AG.LND.ARBL.ZS': 'Arable land (% of land area)'} -data = wb.get_dataframe(indicators, convert_date=True).sort_index() -data - -# World is one of the countries - -data.loc['World'] - -# Can we classify over continents? - -data.loc[(slice(None), '2017-01-01'), :]['Population, total'].dropna( -).sort_values().tail(60).index.get_level_values('country') - -# Extract zones manually (in order of increasing population) - -zones = ['North America', 'Middle East & North Africa', - 'Latin America & Caribbean', 'Europe & Central Asia', - 'Sub-Saharan Africa', 'South Asia', - 'East Asia & Pacific'][::-1] - -# And extract population information (and check total is right) - -population = data.loc[zones]['Population, total'].swaplevel().unstack() -population = population[zones] -assert all(data.loc['World']['Population, total'] == population.sum(axis=1)) - -# ## Stacked area plot with matplotlib - -import matplotlib.pyplot as plt - -plt.clf() -plt.figure(figsize=(10, 5), dpi=100) -plt.stackplot(population.index, population.values.T / 1e9) -plt.legend(population.columns, loc='upper left') -plt.ylabel('Population count (B)') -plt.show() - -# ## Stacked bar plot with plotly - -# Stacked area plots (with cumulated values computed depending on -# selected legends) are -# [on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For -# now we just do a stacked bar plot. - -# + -import plotly.offline as offline -import plotly.graph_objs as go - -offline.init_notebook_mode() -# - - -bars = [go.Bar(x=population.index, y=population[zone], name=zone) - for zone in zones] -fig = go.Figure(data=bars, - layout=go.Layout(title='World population', - barmode='stack')) -offline.iplot(fig) diff --git a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/World population.py b/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/World population.py deleted file mode 100644 index 455a0f213..000000000 --- a/tests/notebooks/mirror/ipynb_to_script_vim_folding_markers/World population.py +++ /dev/null @@ -1,95 +0,0 @@ -# --- -# jupyter: -# jupytext: -# cell_markers: '{{{,}}}' -# kernelspec: -# display_name: Python 3 -# language: python -# name: python3 -# --- - -# # A quick insight at world population -# -# ## Collecting population data -# -# In the below we retrieve population data from the -# [World Bank](http://www.worldbank.org/) -# using the [wbdata](https://github.com/OliverSherouse/wbdata) python package - -# {{{ -import pandas as pd -import wbdata as wb - -pd.options.display.max_rows = 6 -pd.options.display.max_columns = 20 -# }}} - -# Corresponding indicator is found using search method - or, directly, -# the World Bank site. - -wb.search_indicators('Population, total') # SP.POP.TOTL -# wb.search_indicators('area') -# => https://data.worldbank.org/indicator is easier to use - -# Now we download the population data - -indicators = {'SP.POP.TOTL': 'Population, total', - 'AG.SRF.TOTL.K2': 'Surface area (sq. km)', - 'AG.LND.TOTL.K2': 'Land area (sq. km)', - 'AG.LND.ARBL.ZS': 'Arable land (% of land area)'} -data = wb.get_dataframe(indicators, convert_date=True).sort_index() -data - -# World is one of the countries - -data.loc['World'] - -# Can we classify over continents? - -data.loc[(slice(None), '2017-01-01'), :]['Population, total'].dropna( -).sort_values().tail(60).index.get_level_values('country') - -# Extract zones manually (in order of increasing population) - -zones = ['North America', 'Middle East & North Africa', - 'Latin America & Caribbean', 'Europe & Central Asia', - 'Sub-Saharan Africa', 'South Asia', - 'East Asia & Pacific'][::-1] - -# And extract population information (and check total is right) - -population = data.loc[zones]['Population, total'].swaplevel().unstack() -population = population[zones] -assert all(data.loc['World']['Population, total'] == population.sum(axis=1)) - -# ## Stacked area plot with matplotlib - -import matplotlib.pyplot as plt - -plt.clf() -plt.figure(figsize=(10, 5), dpi=100) -plt.stackplot(population.index, population.values.T / 1e9) -plt.legend(population.columns, loc='upper left') -plt.ylabel('Population count (B)') -plt.show() - -# ## Stacked bar plot with plotly - -# Stacked area plots (with cumulated values computed depending on -# selected legends) are -# [on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For -# now we just do a stacked bar plot. - -# {{{ -import plotly.offline as offline -import plotly.graph_objs as go - -offline.init_notebook_mode() -# }}} - -bars = [go.Bar(x=population.index, y=population[zone], name=zone) - for zone in zones] -fig = go.Figure(data=bars, - layout=go.Layout(title='World population', - barmode='stack')) -offline.iplot(fig) diff --git a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/World population.py b/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/World population.py deleted file mode 100644 index 5a923bbc5..000000000 --- a/tests/notebooks/mirror/ipynb_to_script_vscode_folding_markers/World population.py +++ /dev/null @@ -1,95 +0,0 @@ -# --- -# jupyter: -# jupytext: -# cell_markers: region,endregion -# kernelspec: -# display_name: Python 3 -# language: python -# name: python3 -# --- - -# # A quick insight at world population -# -# ## Collecting population data -# -# In the below we retrieve population data from the -# [World Bank](http://www.worldbank.org/) -# using the [wbdata](https://github.com/OliverSherouse/wbdata) python package - -# region -import pandas as pd -import wbdata as wb - -pd.options.display.max_rows = 6 -pd.options.display.max_columns = 20 -# endregion - -# Corresponding indicator is found using search method - or, directly, -# the World Bank site. - -wb.search_indicators('Population, total') # SP.POP.TOTL -# wb.search_indicators('area') -# => https://data.worldbank.org/indicator is easier to use - -# Now we download the population data - -indicators = {'SP.POP.TOTL': 'Population, total', - 'AG.SRF.TOTL.K2': 'Surface area (sq. km)', - 'AG.LND.TOTL.K2': 'Land area (sq. km)', - 'AG.LND.ARBL.ZS': 'Arable land (% of land area)'} -data = wb.get_dataframe(indicators, convert_date=True).sort_index() -data - -# World is one of the countries - -data.loc['World'] - -# Can we classify over continents? - -data.loc[(slice(None), '2017-01-01'), :]['Population, total'].dropna( -).sort_values().tail(60).index.get_level_values('country') - -# Extract zones manually (in order of increasing population) - -zones = ['North America', 'Middle East & North Africa', - 'Latin America & Caribbean', 'Europe & Central Asia', - 'Sub-Saharan Africa', 'South Asia', - 'East Asia & Pacific'][::-1] - -# And extract population information (and check total is right) - -population = data.loc[zones]['Population, total'].swaplevel().unstack() -population = population[zones] -assert all(data.loc['World']['Population, total'] == population.sum(axis=1)) - -# ## Stacked area plot with matplotlib - -import matplotlib.pyplot as plt - -plt.clf() -plt.figure(figsize=(10, 5), dpi=100) -plt.stackplot(population.index, population.values.T / 1e9) -plt.legend(population.columns, loc='upper left') -plt.ylabel('Population count (B)') -plt.show() - -# ## Stacked bar plot with plotly - -# Stacked area plots (with cumulated values computed depending on -# selected legends) are -# [on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For -# now we just do a stacked bar plot. - -# region -import plotly.offline as offline -import plotly.graph_objs as go - -offline.init_notebook_mode() -# endregion - -bars = [go.Bar(x=population.index, y=population[zone], name=zone) - for zone in zones] -fig = go.Figure(data=bars, - layout=go.Layout(title='World population', - barmode='stack')) -offline.iplot(fig) diff --git a/tests/notebooks/mirror/ipynb_to_sphinx/World population.py b/tests/notebooks/mirror/ipynb_to_sphinx/World population.py deleted file mode 100644 index 420b86e43..000000000 --- a/tests/notebooks/mirror/ipynb_to_sphinx/World population.py +++ /dev/null @@ -1,102 +0,0 @@ -# --- -# jupyter: -# kernelspec: -# display_name: Python 3 -# language: python -# name: python3 -# --- - -""" -# A quick insight at world population - -## Collecting population data - -In the below we retrieve population data from the -[World Bank](http://www.worldbank.org/) -using the [wbdata](https://github.com/OliverSherouse/wbdata) python package -""" - -import pandas as pd -import wbdata as wb - -pd.options.display.max_rows = 6 -pd.options.display.max_columns = 20 - -############################################################################### -# Corresponding indicator is found using search method - or, directly, -# the World Bank site. - -wb.search_indicators('Population, total') # SP.POP.TOTL -# wb.search_indicators('area') -# => https://data.worldbank.org/indicator is easier to use - -############################################################################### -# Now we download the population data - -indicators = {'SP.POP.TOTL': 'Population, total', - 'AG.SRF.TOTL.K2': 'Surface area (sq. km)', - 'AG.LND.TOTL.K2': 'Land area (sq. km)', - 'AG.LND.ARBL.ZS': 'Arable land (% of land area)'} -data = wb.get_dataframe(indicators, convert_date=True).sort_index() -data - -############################################################################### -# World is one of the countries - -data.loc['World'] - -############################################################################### -# Can we classify over continents? - -data.loc[(slice(None), '2017-01-01'), :]['Population, total'].dropna( -).sort_values().tail(60).index.get_level_values('country') - -############################################################################### -# Extract zones manually (in order of increasing population) - -zones = ['North America', 'Middle East & North Africa', - 'Latin America & Caribbean', 'Europe & Central Asia', - 'Sub-Saharan Africa', 'South Asia', - 'East Asia & Pacific'][::-1] - -############################################################################### -# And extract population information (and check total is right) - -population = data.loc[zones]['Population, total'].swaplevel().unstack() -population = population[zones] -assert all(data.loc['World']['Population, total'] == population.sum(axis=1)) - -############################################################################### -# ## Stacked area plot with matplotlib - -import matplotlib.pyplot as plt - -"" -plt.clf() -plt.figure(figsize=(10, 5), dpi=100) -plt.stackplot(population.index, population.values.T / 1e9) -plt.legend(population.columns, loc='upper left') -plt.ylabel('Population count (B)') -plt.show() - -############################################################################### -# ## Stacked bar plot with plotly - -############################################################################### -# Stacked area plots (with cumulated values computed depending on -# selected legends) are -# [on their way](https://github.com/plotly/plotly.js/pull/2960) at Plotly. For -# now we just do a stacked bar plot. - -import plotly.offline as offline -import plotly.graph_objs as go - -offline.init_notebook_mode() - -"" -bars = [go.Bar(x=population.index, y=population[zone], name=zone) - for zone in zones] -fig = go.Figure(data=bars, - layout=go.Layout(title='World population', - barmode='stack')) -offline.iplot(fig) diff --git a/tests/test_knitr_spin.py b/tests/test_knitr_spin.py deleted file mode 100644 index 7c054e801..000000000 --- a/tests/test_knitr_spin.py +++ /dev/null @@ -1,19 +0,0 @@ -import pytest - -import jupytext - -from .utils import list_notebooks - - -@pytest.mark.parametrize("r_file", list_notebooks("R_spin")) -def test_jupytext_same_as_knitr_spin(r_file, tmpdir): - nb = jupytext.read(r_file) - rmd_jupytext = jupytext.writes(nb, "Rmd") - - # Rmd file generated with spin(hair='R/spin.R', knit=FALSE) - rmd_file = r_file.replace("R_spin", "Rmd").replace(".R", ".Rmd") - - with open(rmd_file) as fp: - rmd_spin = fp.read() - - assert rmd_spin == rmd_jupytext diff --git a/tests/test_mirror.py b/tests/test_mirror.py deleted file mode 100644 index 64434be57..000000000 --- a/tests/test_mirror.py +++ /dev/null @@ -1,322 +0,0 @@ -"""Here we generate mirror representation of py, Rmd and ipynb files -as py or ipynb, and make sure that these representations minimally -change on new releases. -""" - -import os - -import pytest -from nbformat.v4.nbbase import new_notebook - -import jupytext -from jupytext.compare import combine_inputs_with_outputs, compare, compare_notebooks -from jupytext.formats import ( - auto_ext_from_metadata, - check_auto_ext, - long_form_one_format, -) -from jupytext.languages import _SCRIPT_EXTENSIONS -from jupytext.paired_paths import full_path - -from .utils import ( - list_notebooks, - requires_myst, - requires_pandoc, - requires_quarto, - requires_sphinx_gallery, -) - - -def create_mirror_file_if_missing(mirror_file, notebook, fmt): - if not os.path.isfile(mirror_file): - jupytext.write(notebook, mirror_file, fmt=fmt) - - -def test_create_mirror_file_if_missing(tmpdir, no_jupytext_version_number): - py_file = str(tmpdir.join("notebook.py")) - assert not os.path.isfile(py_file) - create_mirror_file_if_missing(py_file, new_notebook(), "py") - assert os.path.isfile(py_file) - - -def assert_conversion_same_as_mirror(nb_file, fmt, mirror_name, compare_notebook=False): - dirname, basename = os.path.split(nb_file) - file_name, org_ext = os.path.splitext(basename) - fmt = long_form_one_format(fmt) - notebook = jupytext.read(nb_file, fmt=fmt) - fmt = check_auto_ext(fmt, notebook.metadata, "") - ext = fmt["extension"] - mirror_file = os.path.join( - dirname, "..", "mirror", mirror_name, full_path(file_name, fmt) - ) - - # it's better not to have Jupytext metadata in test notebooks: - if fmt == "ipynb" and "jupytext" in notebook.metadata: # pragma: no cover - notebook.metadata.pop("jupytext") - jupytext.write(nb_file, fmt=fmt) - - create_mirror_file_if_missing(mirror_file, notebook, fmt) - - # Compare the text representation of the two notebooks - if compare_notebook: - # Read and convert the mirror file to the latest nbformat version if necessary - nb_mirror = jupytext.read(mirror_file, as_version=notebook.nbformat) - nb_mirror.nbformat_minor = notebook.nbformat_minor - compare_notebooks(nb_mirror, notebook) - return - elif ext == ".ipynb": - notebook = jupytext.read(mirror_file) - fmt.update({"extension": org_ext}) - actual = jupytext.writes(notebook, fmt) - with open(nb_file, encoding="utf-8") as fp: - expected = fp.read() - else: - actual = jupytext.writes(notebook, fmt) - with open(mirror_file, encoding="utf-8") as fp: - expected = fp.read() - - if not actual.endswith("\n"): - actual = actual + "\n" - compare(actual, expected) - - # Compare the two notebooks - if ext != ".ipynb": - notebook = jupytext.read(nb_file) - nb_mirror = jupytext.read(mirror_file, fmt=fmt) - - if fmt.get("format_name") == "sphinx": - nb_mirror.cells = nb_mirror.cells[1:] - for cell in notebook.cells: - cell.metadata = {} - for cell in nb_mirror.cells: - cell.metadata = {} - - compare_notebooks(nb_mirror, notebook, fmt) - - nb_mirror = combine_inputs_with_outputs(nb_mirror, notebook) - compare_notebooks(nb_mirror, notebook, fmt, compare_outputs=True) - - -"""--------------------------------------------------------------------------------- - -Part I: ipynb -> fmt -> ipynb - ----------------------------------------------------------------------------------""" - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_all", skip="many hash")) -def test_ipynb_to_light(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "auto", "ipynb_to_script") - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_all", skip="")) -def test_ipynb_to_percent(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "auto:percent", "ipynb_to_percent") - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_all", skip="")) -def test_ipynb_to_hydrogen(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "auto:hydrogen", "ipynb_to_hydrogen") - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_all", skip="")) -def test_ipynb_to_md(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "md", "ipynb_to_md") - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_all", skip="")) -def test_ipynb_to_Rmd(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "Rmd", "ipynb_to_Rmd") - - -@requires_pandoc -@pytest.mark.parametrize( - "nb_file", - list_notebooks("ipynb", skip="(functional|Notebook with|flavors|invalid|305)"), -) -def test_ipynb_to_pandoc(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "md:pandoc", "ipynb_to_pandoc") - - -@requires_quarto -@pytest.mark.parametrize( - "nb_file", - list_notebooks( - "ipynb", - skip="(World|functional|Notebook with|plotly_graphs|flavors|complex_metadata|" - "update83|raw_cell|_66|nteract|LaTeX|invalid|305|text_outputs|ir_notebook|jupyter|with_R_magic)", - ), -) -def test_ipynb_to_quarto( - nb_file, - no_jupytext_version_number, -): - assert_conversion_same_as_mirror(nb_file, "qmd", "ipynb_to_quarto") - - -@requires_myst -@pytest.mark.parametrize( - "nb_file", - list_notebooks( - "ipynb_all", skip="html-demo|julia_functional_geometry|xcpp_by_quantstack" - ), -) -def test_ipynb_to_myst(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "md:myst", "ipynb_to_myst") - - -@requires_sphinx_gallery -@pytest.mark.parametrize( - "nb_file", list_notebooks("ipynb_py", skip="(raw|hash|frozen|magic|html|164|long)") -) -def test_ipynb_to_python_sphinx(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "py:sphinx", "ipynb_to_sphinx") - - -"""--------------------------------------------------------------------------------- - -Part II: text -> ipynb -> text - ----------------------------------------------------------------------------------""" - - -@pytest.mark.parametrize( - "nb_file", - list_notebooks("julia") - + list_notebooks("python") - + list_notebooks("R") - + list_notebooks("ps1"), -) -def test_script_to_ipynb(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "ipynb", "script_to_ipynb") - - -@pytest.mark.parametrize("nb_file", list_notebooks("percent")) -def test_percent_to_ipynb(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "ipynb:percent", "script_to_ipynb") - - -@pytest.mark.parametrize("nb_file", list_notebooks("hydrogen")) -def test_hydrogen_to_ipynb(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "ipynb:hydrogen", "script_to_ipynb") - - -@pytest.mark.parametrize("nb_file", list_notebooks("R_spin")) -def test_spin_to_ipynb(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "ipynb:spin", "script_to_ipynb") - - -@pytest.mark.parametrize("nb_file", list_notebooks("md")) -def test_md_to_ipynb(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "ipynb", "md_to_ipynb") - - -@pytest.mark.parametrize("nb_file", list_notebooks("Rmd")) -def test_Rmd_to_ipynb(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "ipynb", "Rmd_to_ipynb") - - -@requires_sphinx_gallery -@pytest.mark.parametrize("nb_file", list_notebooks("sphinx")) -def test_sphinx_to_ipynb(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "ipynb:sphinx", "sphinx_to_ipynb") - - -@requires_sphinx_gallery -@pytest.mark.parametrize("nb_file", list_notebooks("sphinx")) -def test_sphinx_md_to_ipynb(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror( - nb_file, - {"extension": ".ipynb", "format_name": "sphinx", "rst2md": True}, - "sphinx-rst2md_to_ipynb", - compare_notebook=True, - ) - - -"""--------------------------------------------------------------------------------- - -Part III: More specific round trip tests - ----------------------------------------------------------------------------------""" - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_all", skip="")) -def test_ipynb_to_percent_to_light(nb_file): - nb = jupytext.read(nb_file) - pct = jupytext.writes(nb, "auto:percent") - auto_ext = auto_ext_from_metadata(nb.metadata) - comment = _SCRIPT_EXTENSIONS[auto_ext]["comment"] - lgt = ( - pct.replace(comment + " %%\n", comment + " +\n") - .replace(comment + " %% ", comment + " + ") - .replace( - comment + " format_name: percent", - comment + " format_name: light", - ) - ) - nb2 = jupytext.reads(lgt, auto_ext) - compare_notebooks(nb2, nb) - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py", skip="")) -def test_ipynb_to_python_vim(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror( - nb_file, - {"extension": ".py", "cell_markers": "{{{,}}}"}, - "ipynb_to_script_vim_folding_markers", - ) - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_py", skip="")) -def test_ipynb_to_python_vscode(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror( - nb_file, - {"extension": ".py", "cell_markers": "region,endregion"}, - "ipynb_to_script_vscode_folding_markers", - ) - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_R")) -def test_ipynb_to_r(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, ".low.r", "ipynb_to_script") - - -@pytest.mark.parametrize( - "nb_file,extension", - [ - (nb_file, extension) - for nb_file in list_notebooks("ipynb_scheme") - for extension in ("ss", "scm") - ], -) -def test_ipynb_to_scheme(nb_file, extension, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, extension, "ipynb_to_script") - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_R")) -def test_ipynb_to_r_percent(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, ".low.r:percent", "ipynb_to_percent") - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_R")) -def test_ipynb_to_R_spin(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, "R", "ipynb_to_spin") - - -@pytest.mark.parametrize("nb_file", list_notebooks("ipynb_R")) -def test_ipynb_to_r_spin(nb_file, no_jupytext_version_number): - assert_conversion_same_as_mirror(nb_file, ".low.r", "ipynb_to_spin") - - -@pytest.mark.parametrize( - "nb_file,extension", - [ - (nb_file, extension) - for nb_file in list_notebooks("ipynb_scheme") - for extension in ("ss", "scm") - ], -) -def test_ipynb_to_scheme_percent(nb_file, extension, no_jupytext_version_number): - assert_conversion_same_as_mirror( - nb_file, f"{extension}:percent", "ipynb_to_percent" - ) diff --git a/tests/test_read_all_py.py b/tests/test_read_all_py.py deleted file mode 100644 index 01209e064..000000000 --- a/tests/test_read_all_py.py +++ /dev/null @@ -1,25 +0,0 @@ -import pytest - -import jupytext -from jupytext.compare import compare - -from .utils import list_notebooks - - -@pytest.mark.parametrize( - "py_file", - [ - py_file - for py_file in list_notebooks("../src/jupytext") + list_notebooks(".") - if py_file.endswith(".py") - if "folding_markers" not in py_file - ], -) -def test_identity_source_write_read(py_file): - with open(py_file) as fp: - py = fp.read() - - nb = jupytext.reads(py, "py") - py2 = jupytext.writes(nb, "py") - - compare(py2, py) diff --git a/tests/test_cell_id.py b/tests/unit/test_cell_id.py similarity index 100% rename from tests/test_cell_id.py rename to tests/unit/test_cell_id.py diff --git a/tests/test_compare.py b/tests/unit/test_compare.py similarity index 100% rename from tests/test_compare.py rename to tests/unit/test_compare.py diff --git a/tests/test_escape_magics.py b/tests/unit/test_escape_magics.py similarity index 98% rename from tests/test_escape_magics.py rename to tests/unit/test_escape_magics.py index 36c5bb05c..e4c6585d3 100644 --- a/tests/test_escape_magics.py +++ b/tests/unit/test_escape_magics.py @@ -2,7 +2,7 @@ from nbformat.v4.nbbase import new_code_cell, new_notebook import jupytext -from jupytext.compare import compare, compare_notebooks +from jupytext.compare import compare, compare_notebooks, notebook_model from jupytext.magics import ( _PYTHON_MAGIC_ASSIGN, comment_magic, @@ -11,8 +11,6 @@ unesc, ) -from .utils import notebook_model - def test_unesc(): assert unesc("# comment", "python") == "comment" diff --git a/tests/test_formats.py b/tests/unit/test_formats.py similarity index 95% rename from tests/test_formats.py rename to tests/unit/test_formats.py index bdae30112..796b25ef7 100644 --- a/tests/test_formats.py +++ b/tests/unit/test_formats.py @@ -16,18 +16,14 @@ validate_one_format, ) -from .utils import list_notebooks, requires_myst, requires_pandoc - -@pytest.mark.parametrize("nb_file", list_notebooks("python")) -def test_guess_format_light(nb_file): - with open(nb_file) as stream: +def test_guess_format_light(python_file): + with open(python_file) as stream: assert guess_format(stream.read(), ext=".py")[0] == "light" -@pytest.mark.parametrize("nb_file", list_notebooks("percent")) -def test_guess_format_percent(nb_file): - with open(nb_file) as stream: +def test_guess_format_percent(percent_file): + with open(percent_file) as stream: assert guess_format(stream.read(), ext=".py")[0] == "percent" @@ -57,9 +53,8 @@ def test_guess_format_simple_hydrogen_with_magic( assert guess_format(nb, ext=".py")[0] == "hydrogen" -@pytest.mark.parametrize("nb_file", list_notebooks("sphinx")) -def test_guess_format_sphinx(nb_file): - with open(nb_file) as stream: +def test_guess_format_sphinx(sphinx_file): + with open(sphinx_file) as stream: assert guess_format(stream.read(), ext=".py")[0] == "sphinx" @@ -347,7 +342,7 @@ def test_set_auto_ext(): long_form_multiple_formats("ipynb,auto:percent", {}) -@requires_pandoc +@pytest.mark.requires_pandoc def test_pandoc_format_is_preserved(): formats_org = "ipynb,md,.pandoc.md:pandoc,py:light" long = long_form_multiple_formats(formats_org) @@ -356,7 +351,7 @@ def test_pandoc_format_is_preserved(): compare(formats_new, formats_org) -@requires_myst +@pytest.mark.requires_myst def test_write_as_myst(tmpdir): """Inspired by https://github.com/mwouts/jupytext/issues/462""" nb = new_notebook() diff --git a/tests/test_header.py b/tests/unit/test_header.py similarity index 100% rename from tests/test_header.py rename to tests/unit/test_header.py diff --git a/tests/test_markdown_in_code_cells.py b/tests/unit/test_markdown_in_code_cells.py similarity index 97% rename from tests/test_markdown_in_code_cells.py rename to tests/unit/test_markdown_in_code_cells.py index 73cb0638b..42df77f6b 100644 --- a/tests/test_markdown_in_code_cells.py +++ b/tests/unit/test_markdown_in_code_cells.py @@ -1,12 +1,11 @@ """Issue #712""" +import pytest from nbformat.v4.nbbase import new_code_cell, new_notebook from jupytext import reads, writes from jupytext.cell_to_text import three_backticks_or_more from jupytext.compare import compare, compare_notebooks -from .utils import requires_myst - def test_three_backticks_or_more(): assert three_backticks_or_more([""]) == "```" @@ -53,7 +52,7 @@ def test_triple_backticks_in_code_cell( compare_notebooks(actual_nb, nb) -@requires_myst +@pytest.mark.requires_myst def test_triple_backticks_in_code_cell_myst( no_jupytext_version_number, nb=new_notebook( diff --git a/tests/test_paired_paths.py b/tests/unit/test_paired_paths.py similarity index 100% rename from tests/test_paired_paths.py rename to tests/unit/test_paired_paths.py diff --git a/tests/test_pep8.py b/tests/unit/test_pep8.py similarity index 95% rename from tests/test_pep8.py rename to tests/unit/test_pep8.py index 2728d314b..4150e883d 100644 --- a/tests/test_pep8.py +++ b/tests/unit/test_pep8.py @@ -1,4 +1,3 @@ -import pytest from nbformat.v4.nbbase import new_code_cell, new_notebook from jupytext import read, reads, writes @@ -11,8 +10,6 @@ pep8_lines_between_cells, ) -from .utils import list_notebooks - def test_next_instruction_is_function_or_class(): text = """@pytest.mark.parametrize('py_file', @@ -185,14 +182,6 @@ def f(x): compare(text2, text) -@pytest.mark.parametrize( - "py_file", - [ - py_file - for py_file in list_notebooks("../src/jupytext") + list_notebooks(".") - if py_file.endswith(".py") and "folding_markers" not in py_file - ], -) def test_no_metadata_when_py_is_pep8(py_file): """This test assumes that all Python files in the jupytext folder follow PEP8 rules""" nb = read(py_file) diff --git a/tests/test_stringparser.py b/tests/unit/test_stringparser.py similarity index 100% rename from tests/test_stringparser.py rename to tests/unit/test_stringparser.py diff --git a/tests/utils.py b/tests/utils.py deleted file mode 100644 index 54f67d43e..000000000 --- a/tests/utils.py +++ /dev/null @@ -1,147 +0,0 @@ -import itertools -import json -import re -import sys -from pathlib import Path - -import pytest -from jupyter_client.kernelspec import find_kernel_specs, get_kernel_spec - -from jupytext.cell_reader import rst2md -from jupytext.cli import system -from jupytext.formats import JUPYTEXT_FORMATS -from jupytext.myst import is_myst_available -from jupytext.pandoc import is_pandoc_available -from jupytext.quarto import is_quarto_available - - -def tool_version(tool): - try: - args = tool.split(" ") - args.append("--version") - return system(*args) - except (OSError, SystemExit): # pragma: no cover - return None - - -def isort_version(): - try: - import isort - - return isort.__version__ - except ImportError: - return None - - -requires_jupytext_installed = pytest.mark.skipif( - not tool_version("jupytext"), reason="jupytext is not installed" -) -requires_black = pytest.mark.skipif(not tool_version("black"), reason="black not found") -requires_isort = pytest.mark.skipif( - not isort_version() or isort_version() <= "5.3.0", - reason="isort not found", -) -requires_flake8 = pytest.mark.skipif( - not tool_version("flake8"), reason="flake8 not found" -) -requires_autopep8 = pytest.mark.skipif( - not tool_version("autopep8"), reason="autopep8 not found" -) -requires_nbconvert = pytest.mark.skipif( - not tool_version("jupyter nbconvert"), reason="nbconvert not found" -) -requires_sphinx_gallery = pytest.mark.skipif( - not rst2md, reason="sphinx_gallery is not available" -) -requires_pandoc = pytest.mark.skipif( - # The mirror files changed slightly when Pandoc 2.11 was introduced - # https://github.com/mwouts/jupytext/commit/c07d919702999056ce47f92b74f63a15c8361c5d - # The mirror files changed again when Pandoc 2.16 was introduced - # https://github.com/mwouts/jupytext/pull/919/commits/1fa1451ecdaa6ad8d803bcb6fb0c0cf09e5371bf - not is_pandoc_available(min_version="2.16.2", max_version="2.16.2"), - reason="pandoc>=2.11 is not available", -) -requires_quarto = pytest.mark.skipif( - not is_quarto_available(min_version="0.2.0"), reason="quarto>=0.2 is not available" -) -requires_no_pandoc = pytest.mark.skipif( - is_pandoc_available(), reason="Pandoc is installed" -) -requires_ir_kernel = pytest.mark.skipif( - not any(get_kernel_spec(name).language == "R" for name in find_kernel_specs()), - reason="irkernel is not installed", -) -requires_user_kernel_python3 = pytest.mark.skipif( - "python_kernel" not in find_kernel_specs(), - reason="Please run 'python -m ipykernel install --name python_kernel --user'", -) -requires_myst = pytest.mark.skipif( - not is_myst_available(), reason="myst_parser not found" -) -requires_no_myst = pytest.mark.skipif(is_myst_available(), reason="myst is available") -skip_on_windows = pytest.mark.skipif(sys.platform.startswith("win"), reason="Issue 489") -skip_pre_commit_tests_on_windows = pytest.mark.skipif( - sys.platform.startswith("win"), - reason="OSError: [WinError 193] %1 is not a valid Win32 application", -) -skip_pre_commit_tests_when_jupytext_folder_is_not_a_git_repo = pytest.mark.skipif( - not (Path(__file__).parent.parent / ".git").is_dir(), - reason="Jupytext folder is not a git repository #814", -) - - -def list_notebooks(path="ipynb", skip="World"): - """All notebooks in the directory notebooks/path, - or in the package itself""" - if path == "ipynb": - return ( - list_notebooks("ipynb_julia", skip=skip) - + list_notebooks("ipynb_py", skip=skip) - + list_notebooks("ipynb_R", skip=skip) - ) - - nb_path = Path(__file__).parent / "notebooks" - - if path == "ipynb_all": - return itertools.chain( - *( - list_notebooks(folder.name, skip=skip) - for folder in nb_path.iterdir() - if folder.name.startswith("ipynb_") - ) - ) - - if path == "all": - return itertools.chain( - *(list_notebooks(folder.name, skip=skip) for folder in nb_path.iterdir()) - ) - - if path.startswith("."): - nb_path = nb_path / ".." / path - else: - nb_path = nb_path / path - - if skip: - skip_re = re.compile(".*" + skip + ".*") - return [ - str(nb_file) - for nb_file in nb_path.iterdir() - if nb_file.is_file() and not skip_re.match(nb_file.name) - ] - - return [str(nb_file) for nb_file in nb_path.iterdir() if nb_file.is_file()] - - -def notebook_model(nb): - """Return a notebook model, with content a dictionary rather than a notebook object""" - return dict(type="notebook", content=json.loads(json.dumps(nb))) - - -def formats_with_support_for_cell_metadata(): - for fmt in JUPYTEXT_FORMATS: - if fmt.format_name == "myst" and not is_myst_available(): - continue - if fmt.format_name == "pandoc" and not is_pandoc_available(): - continue - if fmt.format_name not in ["sphinx", "nomarker", "spin", "quarto"]: - yield f"{fmt.extension[1:]}:{fmt.format_name}"