Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid importing IPython if notebook cells do not contain magics #3782

Merged
merged 9 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

<!-- Changes that improve Black's performance. -->

- Avoid importing `IPython` if notebook cells do not contain magics (#3782)

### Output

<!-- Changes to Black's terminal output and error messages -->
Expand Down
2 changes: 1 addition & 1 deletion src/black/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ def get_sources(
p = Path(f"{STDIN_PLACEHOLDER}{str(p)}")

if p.suffix == ".ipynb" and not jupyter_dependencies_are_installed(
verbose=verbose, quiet=quiet
warn=verbose or not quiet
):
continue

Expand Down
2 changes: 1 addition & 1 deletion src/black/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def gen_python_files(

elif child.is_file():
if child.suffix == ".ipynb" and not jupyter_dependencies_are_installed(
verbose=verbose, quiet=quiet
warn=verbose or not quiet
):
continue
include_match = include.search(normalized_path) if include else True
Expand Down
31 changes: 12 additions & 19 deletions src/black/handle_ipynb_magics.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import secrets
import sys
from functools import lru_cache
from importlib.util import find_spec
from typing import Dict, List, Optional, Tuple

if sys.version_info >= (3, 10):
Expand Down Expand Up @@ -56,25 +57,17 @@ class Replacement:


@lru_cache
def jupyter_dependencies_are_installed(*, verbose: bool, quiet: bool) -> bool:
try:
# isort: off
# tokenize_rt is less commonly installed than IPython
# and IPython is expensive to import
import tokenize_rt # noqa:F401
import IPython # noqa:F401

# isort: on
except ModuleNotFoundError:
if verbose or not quiet:
msg = (
"Skipping .ipynb files as Jupyter dependencies are not installed.\n"
'You can fix this by running ``pip install "black[jupyter]"``'
)
out(msg)
return False
else:
return True
def jupyter_dependencies_are_installed(*, warn: bool) -> bool:
installed = (
find_spec("tokenize_rt") is not None and find_spec("IPython") is not None
)
if not installed and warn:
msg = (
"Skipping .ipynb files as Jupyter dependencies are not installed.\n"
'You can fix this by running ``pip install "black[jupyter]"``'
)
out(msg)
return installed


def remove_trailing_semicolon(src: str) -> Tuple[str, bool]:
Expand Down
12 changes: 4 additions & 8 deletions tests/test_ipynb.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,17 +440,13 @@ def test_cache_isnt_written_if_no_jupyter_deps_single(
nb = get_case_path("jupyter", "notebook_trailing_newline.ipynb")
tmp_nb = tmp_path / "notebook.ipynb"
tmp_nb.write_bytes(nb.read_bytes())
monkeypatch.setattr(
"black.jupyter_dependencies_are_installed", lambda verbose, quiet: False
)
monkeypatch.setattr("black.jupyter_dependencies_are_installed", lambda warn: False)
result = runner.invoke(
main, [str(tmp_path / "notebook.ipynb"), f"--config={EMPTY_CONFIG}"]
)
assert "No Python files are present to be formatted. Nothing to do" in result.output
jupyter_dependencies_are_installed.cache_clear()
monkeypatch.setattr(
"black.jupyter_dependencies_are_installed", lambda verbose, quiet: True
)
monkeypatch.setattr("black.jupyter_dependencies_are_installed", lambda warn: True)
result = runner.invoke(
main, [str(tmp_path / "notebook.ipynb"), f"--config={EMPTY_CONFIG}"]
)
Expand All @@ -466,13 +462,13 @@ def test_cache_isnt_written_if_no_jupyter_deps_dir(
tmp_nb = tmp_path / "notebook.ipynb"
tmp_nb.write_bytes(nb.read_bytes())
monkeypatch.setattr(
"black.files.jupyter_dependencies_are_installed", lambda verbose, quiet: False
"black.files.jupyter_dependencies_are_installed", lambda warn: False
)
result = runner.invoke(main, [str(tmp_path), f"--config={EMPTY_CONFIG}"])
assert "No Python files are present to be formatted. Nothing to do" in result.output
jupyter_dependencies_are_installed.cache_clear()
monkeypatch.setattr(
"black.files.jupyter_dependencies_are_installed", lambda verbose, quiet: True
"black.files.jupyter_dependencies_are_installed", lambda warn: True
)
result = runner.invoke(main, [str(tmp_path), f"--config={EMPTY_CONFIG}"])
assert "reformatted" in result.output
Expand Down