diff --git a/nbqa/__main__.py b/nbqa/__main__.py index b1fecd1c..fdcddd66 100644 --- a/nbqa/__main__.py +++ b/nbqa/__main__.py @@ -618,10 +618,7 @@ def _main(cli_args: CLIArgs, configs: Configs) -> int: try: # pylint disable=R0912 if not nb_to_tmp_mapping: - sys.stderr.write( - "No notebooks found in given directories: " - f"{' '.join(i for i in cli_args.root_dirs if os.path.isdir(i))}\n" - ) + sys.stderr.write("No notebooks found in given path(s)\n") return 0 saved_sources = SAVE_SOURCES[configs["md"]]( nb_to_tmp_mapping, diff --git a/nbqa/path_utils.py b/nbqa/path_utils.py index d44ee4fa..7349cc66 100644 --- a/nbqa/path_utils.py +++ b/nbqa/path_utils.py @@ -2,7 +2,7 @@ import json import os from pathlib import Path -from typing import Tuple +from typing import Any, Dict, Optional, Tuple def remove_prefix(string: str, prefix: str) -> str: @@ -61,7 +61,24 @@ def get_relative_and_absolute_paths(path: str) -> Tuple[str, str]: return str(relative_path), str(absolute_path) -def read_notebook(notebook): +def read_notebook(notebook: str) -> Tuple[Optional[Dict[str, Any]], Optional[bool]]: + """ + Read notebook. + + If it's .md, try reading it with jupytext. If can't, ignore it. + + Parameters + ---------- + notebook + Path of notebook. + + Returns + ------- + notebook_json + Parsed notebook + trailing_newline + Whether the notebook originally had a trailing newline + """ trailing_newline = True _, ext = os.path.splitext(notebook) with open(notebook, encoding="utf-8") as handle: @@ -69,34 +86,28 @@ def read_notebook(notebook): if ext == ".ipynb": trailing_newline = content.endswith("\n") return json.loads(content), trailing_newline - elif ext == ".md": - try: - import jupytext - except ImportError: - return None, None - md_content = jupytext.jupytext.read(notebook) - - # get lexer: see https://github.com/mwouts/jupytext/issues/993 - from markdown_it import MarkdownIt # must be installed if you have jupytext - - parser = ( - MarkdownIt("commonmark") - # we only need to parse block level components (for efficiency) - .disable("inline", True) - ) - parsed = parser.parse(content) - lexer = None - for token in parsed: - if token.type == "fence" and token.info.startswith("{code-cell}"): - lexer = remove_prefix( - parser.parse(content)[4].info, "{code-cell}" - ).strip() - md_content["metadata"]["language_info"] = {"pygments_lexer": lexer} - break - - for cell in md_content["cells"]: - cell["source"] = cell["source"].splitlines(keepends=True) - if "format_name" in md_content.get("metadata", {}).get("jupytext", {}).get( - "text_representation", {} - ): - return md_content, True + assert ext == ".md" + try: + import jupytext # pylint: disable=import-outside-toplevel + from markdown_it import MarkdownIt # pylint: disable=import-outside-toplevel + except ImportError: + return None, None + md_content = jupytext.jupytext.read(notebook) + + # get lexer: see https://github.com/mwouts/jupytext/issues/993 + parser = MarkdownIt("commonmark").disable("inline", True) + parsed = parser.parse(content) + lexer = None + for token in parsed: + if token.type == "fence" and token.info.startswith("{code-cell}"): + lexer = remove_prefix(parser.parse(content)[4].info, "{code-cell}").strip() + md_content["metadata"]["language_info"] = {"pygments_lexer": lexer} + break + + for cell in md_content["cells"]: + cell["source"] = cell["source"].splitlines(keepends=True) + if "format_name" in md_content.get("metadata", {}).get("jupytext", {}).get( + "text_representation", {} + ): + return md_content, True + return None, None diff --git a/nbqa/replace_source.py b/nbqa/replace_source.py index f4d54828..3b9d23e9 100644 --- a/nbqa/replace_source.py +++ b/nbqa/replace_source.py @@ -10,7 +10,7 @@ import sys from difflib import unified_diff from shutil import move -from typing import Any, Iterator, List, Mapping, MutableMapping, Sequence, Set +from typing import Any, Dict, Iterator, List, Mapping, MutableMapping, Sequence, Set import tokenize_rt @@ -172,7 +172,21 @@ def _notebook_cells( yield cell -def _write_notebook(temp_notebook, trailing_newline, notebook_json): +def _write_notebook( + temp_notebook: str, trailing_newline: bool, notebook_json: Dict[str, Any] +) -> None: + """ + Write notebook to disc. + + Parameters + ---------- + temp_notebook + Location of temporary notebook + trailing_newline + Whether notebook originally had trailing newline + notebook_json + New source for notebook. + """ _, ext = os.path.splitext(temp_notebook) if ext == ".ipynb": with open(temp_notebook, "w", encoding="utf-8") as handle: @@ -185,7 +199,7 @@ def _write_notebook(temp_notebook, trailing_newline, notebook_json): f"{json.dumps(notebook_json, indent=1, ensure_ascii=False)}" ) elif ext == ".md": - import jupytext + import jupytext # pylint: disable=import-outside-toplevel for cell in notebook_json["cells"]: cell["source"] = "".join(cell["source"]) @@ -213,6 +227,9 @@ def mutate( Whether mutation actually happened. """ notebook_json, trailing_newline = read_notebook(notebook) + assert notebook_json is not None # if we got here, it was a valid notebook + assert trailing_newline is not None + original_notebook_json = copy.deepcopy(notebook_json) cells_to_remove = [] @@ -302,6 +319,7 @@ def diff( Whether non-null diff was produced. """ notebook_json, _ = read_notebook(notebook) + assert notebook_json is not None # if we got here, it was a valid notebook cells = _get_cells(python_file, len(notebook_info.temporary_lines), md=md) diff --git a/requirements-dev.txt b/requirements-dev.txt index 3c17da7c..10aee292 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,12 +5,14 @@ blacken-docs coverage[toml] flake8 isort>=5.4.2 +jupytext mdformat mypy pre-commit-hooks pydocstyle pylint pytest +pytest-cov pytest-randomly pyupgrade yapf diff --git a/tests/data/notebook_for_testing.md b/tests/data/notebook_for_testing.md deleted file mode 100644 index d65c2b1b..00000000 --- a/tests/data/notebook_for_testing.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -jupytext: - text_representation: - extension: .md - format_name: myst - format_version: 0.13 - jupytext_version: 1.14.1 -kernelspec: - display_name: Python 3 - language: python - name: python3 ---- - -```{code-cell} ipython3 -:tags: [skip-flake8] - -import os - -import glob - -import nbqa -``` - -# Some markdown cell containing \\n - -+++ {"tags": ["skip-mdformat"]} - -# First level heading - -```{code-cell} ipython3 -:tags: [flake8-skip] - -%%time foo -def hello(name: str = "world\n"): - """ - Greet user. - - Examples - -------- - >>> hello() - 'hello world\\n' - - >>> hello("goodbye") - 'hello goodbye' - """ - - return "hello {}".format(name) - - -!ls -hello(3) -``` - -```python -2 +2 -``` - -```{code-cell} ipython3 - %%bash - - pwd -``` - -```{code-cell} ipython3 -from random import randint - -if __debug__: - %time randint(5,10) -``` - -```{code-cell} ipython3 -import pprint -import sys - -if __debug__: - pretty_print_object = pprint.PrettyPrinter( - indent=4, width=80, stream=sys.stdout, compact=True, depth=5 - ) - -pretty_print_object.isreadable(["Hello", "World"]) -``` diff --git a/tests/test_non_python_notebook.py b/tests/test_non_python_notebook.py index 15c0508d..3cdfca4d 100644 --- a/tests/test_non_python_notebook.py +++ b/tests/test_non_python_notebook.py @@ -24,7 +24,8 @@ def test_non_python_notebook(capsys: "CaptureFixture") -> None: "No valid notebooks found in given path(s)\n" "\n" "\x1b[1m\n" - "If you believe the notebook(s) to be valid, please report a bug at https://github.com/nbQA-dev/nbQA/issues \x1b[0m\n" + "If you believe the notebook(s) to be valid, please report a bug " + "at https://github.com/nbQA-dev/nbQA/issues \x1b[0m\n" "\n" ) assert err == expected_err diff --git a/tests/test_runtime_errors.py b/tests/test_runtime_errors.py index 96e82a76..f7621b32 100644 --- a/tests/test_runtime_errors.py +++ b/tests/test_runtime_errors.py @@ -235,14 +235,7 @@ def test_directory_without_notebooks(capsys: "CaptureFixture") -> None: capsys Pytest fixture to capture stdout and stderr. """ - main(["black", "docs"]) + main(["black", "LICENSES"]) _, err = capsys.readouterr() - expected_err = ( - 'No valid notebooks found in given path(s)\n' - '\n' - '\x1b[1m\n' - 'If you believe the notebook(s) to be valid, please report a bug at ' - 'https://github.com/nbQA-dev/nbQA/issues \x1b[0m\n' - '\n' - ) + expected_err = "No notebooks found in given path(s)\n" assert err == expected_err