diff --git a/nbqa/output_parser.py b/nbqa/output_parser.py index c9d5406f..79a91c49 100644 --- a/nbqa/output_parser.py +++ b/nbqa/output_parser.py @@ -38,7 +38,9 @@ def _get_pattern( ( rf"(?<=^error: cannot format {re.escape(str(notebook))}: Cannot parse: )\d+", standard_substitution, - ) + ), + (r"(?<=line )\d+(?=\)\nOh no! )", standard_substitution), + (r"line cell_(?=\d+:\d+\)\nOh no! )", "cell_"), ] if command == "doctest": diff --git a/tests/invalid_data/assignment_to_literal.ipynb b/tests/invalid_data/assignment_to_literal.ipynb new file mode 100644 index 00000000..d207c831 --- /dev/null +++ b/tests/invalid_data/assignment_to_literal.ipynb @@ -0,0 +1,35 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "1 = [1,2,3]" + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "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.8.0-final" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/tests/tools/test_black.py b/tests/tools/test_black.py index 91362188..27cc2887 100644 --- a/tests/tools/test_black.py +++ b/tests/tools/test_black.py @@ -19,6 +19,8 @@ SPARKLES = "\N{sparkles}" SHORTCAKE = "\N{shortcake}" +COLLISION = "\N{collision symbol}" +BROKEN_HEART = "\N{broken heart}" def test_black_works(tmp_notebook_for_testing: Path, capsys: "CaptureFixture") -> None: @@ -343,3 +345,39 @@ def test_black_works_with_leading_comment(capsys: "CaptureFixture") -> None: expected_err = "" assert expected_out == out assert expected_err == err + + +def test_black_works_with_literal_assignment(capsys: "CaptureFixture") -> None: + """ + Check black works with notebooks with invalid syntax (e.g. assignment to literal). + + Parameters + ---------- + capsys + Pytest fixture to capture stdout and stderr. + """ + path = os.path.abspath( + os.path.join("tests", "invalid_data", "assignment_to_literal.ipynb") + ) + + with pytest.raises(SystemExit): + main(["black", path]) + + out, err = capsys.readouterr() + expected_out = "" + expected_err = ( + ( + f"error: cannot format {path}: " + "cannot use --safe with this file; failed to parse source file. AST error message: " + "can't assign to literal (, cell_1:1)\nOh no! " + f"{COLLISION} {BROKEN_HEART} {COLLISION}\n1 file failed to reformat.\n" + ) + .encode("ascii", "backslashreplace") + .decode() + ) + # This is required because linux supports emojis + # so both should have \\ for comparison + err = err.encode("ascii", "backslashreplace").decode() + + assert expected_out == out + assert expected_err == err