Skip to content

Commit e444129

Browse files
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent adae269 commit e444129

7 files changed

Lines changed: 98 additions & 65 deletions

File tree

β€Žpapermill/cli.pyβ€Ž

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,9 @@ def papermill(
271271
@click.command('profile', context_settings=dict(help_option_names=['-h', '--help']))
272272
@click.argument('notebook_path')
273273
@click.option(
274-
'--output', '-o', default=None,
274+
'--output',
275+
'-o',
276+
default=None,
275277
help='Path to write profile JSON (default: <notebook>.profile.json).',
276278
)
277279
def papermill_profile(notebook_path, output):
@@ -296,10 +298,7 @@ def papermill_profile(notebook_path, output):
296298

297299
if profile.get('bottleneck'):
298300
b = profile['bottleneck']
299-
click.echo(
300-
f"Bottleneck: [{b['cell_index']}] in Β«{b['section']}Β» "
301-
f"β€” {b['duration_s']}s ({b['pct_of_total']}%)"
302-
)
301+
click.echo(f"Bottleneck: [{b['cell_index']}] in Β«{b['section']}Β» β€” {b['duration_s']}s ({b['pct_of_total']}%)")
303302

304303
click.echo("\nSections:")
305304
for s in profile['sections']:

β€Žpapermill/engines.pyβ€Ž

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,9 @@ class NotebookExecutionManager:
9696
COMPLETED = "completed"
9797
FAILED = "failed"
9898

99-
def __init__(self, nb, output_path=None, log_output=False, progress_bar=True, autosave_cell_every=30,
100-
live_display=None):
99+
def __init__(
100+
self, nb, output_path=None, log_output=False, progress_bar=True, autosave_cell_every=30, live_display=None
101+
):
101102
self.nb = nb
102103
self.output_path = output_path
103104
self.log_output = log_output

β€Žpapermill/execute.pyβ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,10 @@ def execute_notebook(
120120
_live_display = None
121121
if live_tree:
122122
from .live_tree import LiveTreeDisplay, is_available as _rich_ok
123+
123124
if _rich_ok():
124125
import os
126+
125127
nb_name = os.path.basename(input_path) if isinstance(input_path, str) else "notebook.ipynb"
126128
_live_display = LiveTreeDisplay(nb, nb_name)
127129
progress_bar = False # Rich tree replaces tqdm

β€Žpapermill/live_tree.pyβ€Ž

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def is_available() -> bool:
4545
"""Return True if the ``rich`` package is installed."""
4646
try:
4747
import rich # noqa: F401
48+
4849
return True
4950
except ImportError:
5051
return False
@@ -53,11 +54,11 @@ def is_available() -> bool:
5354
# ── Glyph / colour helpers ────────────────────────────────────────────────────
5455

5556
_GLYPH = {
56-
"pending": ("Β·", "dim"),
57-
"running": ("⟳", "yellow bold"),
57+
"pending": ("Β·", "dim"),
58+
"running": ("⟳", "yellow bold"),
5859
"completed": ("βœ“", "green"),
59-
"failed": ("βœ—", "red bold"),
60-
"skipped": ("β€”", "dim"),
60+
"failed": ("βœ—", "red bold"),
61+
"skipped": ("β€”", "dim"),
6162
}
6263

6364

@@ -86,16 +87,13 @@ class LiveTreeDisplay:
8687
def __init__(self, nb: "nbformat.NotebookNode", nb_name: str, refresh_per_second: int = 4):
8788
if not is_available():
8889
raise ImportError(
89-
"The 'rich' package is required for live tree display. "
90-
"Install it with: pip install 'papermill[rich]'"
90+
"The 'rich' package is required for live tree display. Install it with: pip install 'papermill[rich]'"
9191
)
9292
from rich.console import Console
9393
from rich.live import Live
9494

9595
self._sections: list[SectionProfile] = build_sections(nb)
96-
self._cell_map: dict[int, CellProfile] = {
97-
cp.index: cp for sec in self._sections for cp in sec.cells
98-
}
96+
self._cell_map: dict[int, CellProfile] = {cp.index: cp for sec in self._sections for cp in sec.cells}
9997
self._nb_name = nb_name
10098
self._n_total = sum(1 for c in nb.cells if c.cell_type == "code")
10199
self._n_done = 0
@@ -111,10 +109,7 @@ def _build_tree(self):
111109
from rich.tree import Tree
112110

113111
pct = 100 * self._n_done / self._n_total if self._n_total else 0
114-
header = (
115-
f"[bold]{self._nb_name}[/] "
116-
f"[dim]{self._n_done}/{self._n_total} cells {pct:.0f}%[/]"
117-
)
112+
header = f"[bold]{self._nb_name}[/] [dim]{self._n_done}/{self._n_total} cells {pct:.0f}%[/]"
118113
root = Tree(header, guide_style="dim")
119114
level_nodes: dict = {0: root}
120115

@@ -125,10 +120,14 @@ def _build_tree(self):
125120
code_cells = [c for c in sec.cells if c.cell_type == "code"]
126121
sec_status = sec.status
127122
is_running = sec_status == "running"
128-
live_start = next(
129-
(c._live_start for c in code_cells if getattr(c, "_live_start", None) and c.status == "running"),
130-
None,
131-
) if is_running else None
123+
live_start = (
124+
next(
125+
(c._live_start for c in code_cells if getattr(c, "_live_start", None) and c.status == "running"),
126+
None,
127+
)
128+
if is_running
129+
else None
130+
)
132131

133132
glyph_ch, glyph_style = _GLYPH.get(sec_status, ("?", "dim"))
134133
glyph = Text(glyph_ch, style=glyph_style)
@@ -163,6 +162,7 @@ def _build_tree(self):
163162
@staticmethod
164163
def _dur_text(dur, running: bool = False, live_start=None):
165164
from rich.text import Text
165+
166166
if running and live_start is not None:
167167
elapsed = time.monotonic() - live_start
168168
return Text(f"{elapsed:.1f}s…", style="yellow")

β€Žpapermill/profile.pyβ€Ž

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def to_dict(self) -> dict:
132132
class SectionProfile:
133133
"""A section of cells bounded by a markdown heading."""
134134

135-
title: str # original markdown heading text
135+
title: str # original markdown heading text
136136
level: int
137137
number: str = "" # e.g. "1", "1.2", "1.2.3"
138138
cells: list = field(default_factory=list)
@@ -331,9 +331,7 @@ def build_profile(notebook_path: str, nb: nbformat.NotebookNode) -> dict:
331331
n_errors = sum(1 for c in all_code if c.exception)
332332

333333
def _label(idx: int) -> str:
334-
return next(
335-
(s.display_label for s in sections if any(x.index == idx for x in s.cells)), "?"
336-
)
334+
return next((s.display_label for s in sections if any(x.index == idx for x in s.cells)), "?")
337335

338336
slowest = [{**c.to_dict(), "section": _label(c.index)} for c in sorted_dur[:5]]
339337

β€Žpapermill/tests/test_profile.pyβ€Ž

Lines changed: 69 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
# ── Fixtures ──────────────────────────────────────────────────────────────────
1717

18+
1819
def _make_nb(cells):
1920
"""Build a minimal NotebookNode from a list of (type, source, pm_meta) tuples."""
2021
nb = nbformat.v4.new_notebook()
@@ -25,8 +26,11 @@ def _make_nb(cells):
2526
else:
2627
cell = nbformat.v4.new_code_cell(source)
2728
cell.metadata["papermill"] = pm_meta or {
28-
"start_time": None, "end_time": None,
29-
"duration": None, "status": "pending", "exception": False,
29+
"start_time": None,
30+
"end_time": None,
31+
"duration": None,
32+
"status": "pending",
33+
"exception": False,
3034
}
3135
nb.cells.append(cell)
3236
return nb
@@ -44,6 +48,7 @@ def _executed_cell_meta(duration, status="completed", exception=False):
4448

4549
# ── build_sections ────────────────────────────────────────────────────────────
4650

51+
4752
class TestBuildSections:
4853
def test_no_headings_creates_preamble(self):
4954
nb = _make_nb([("code", "x = 1", None), ("code", "y = 2", None)])
@@ -54,54 +59,75 @@ def test_no_headings_creates_preamble(self):
5459
assert len(sections[0].cells) == 2
5560

5661
def test_single_heading(self):
57-
nb = _make_nb([
58-
("markdown", "# Data Loading", None),
59-
("code", "import pandas", None),
60-
])
62+
nb = _make_nb(
63+
[
64+
("markdown", "# Data Loading", None),
65+
("code", "import pandas", None),
66+
]
67+
)
6168
sections = build_sections(nb)
6269
assert len(sections) == 1
6370
assert sections[0].number == "1"
6471
assert sections[0].display_label == "Section 1"
6572

6673
def test_sequential_numbering(self):
67-
nb = _make_nb([
68-
("markdown", "# First", None), ("code", "a=1", None),
69-
("markdown", "# Second", None), ("code", "b=2", None),
70-
("markdown", "# Third", None), ("code", "c=3", None),
71-
])
74+
nb = _make_nb(
75+
[
76+
("markdown", "# First", None),
77+
("code", "a=1", None),
78+
("markdown", "# Second", None),
79+
("code", "b=2", None),
80+
("markdown", "# Third", None),
81+
("code", "c=3", None),
82+
]
83+
)
7284
sections = build_sections(nb)
7385
assert [s.number for s in sections] == ["1", "2", "3"]
7486
assert [s.display_label for s in sections] == ["Section 1", "Section 2", "Section 3"]
7587

7688
def test_nested_sub_sections(self):
77-
nb = _make_nb([
78-
("markdown", "# Analysis", None), ("code", "x=1", None),
79-
("markdown", "## Cleaning", None), ("code", "y=2", None),
80-
("markdown", "## Feature Engineering", None), ("code", "z=3", None),
81-
("markdown", "# Results", None), ("code", "w=4", None),
82-
])
89+
nb = _make_nb(
90+
[
91+
("markdown", "# Analysis", None),
92+
("code", "x=1", None),
93+
("markdown", "## Cleaning", None),
94+
("code", "y=2", None),
95+
("markdown", "## Feature Engineering", None),
96+
("code", "z=3", None),
97+
("markdown", "# Results", None),
98+
("code", "w=4", None),
99+
]
100+
)
83101
sections = build_sections(nb)
84102
labels = [s.display_label for s in sections]
85103
assert labels == ["Section 1", "Sub-section 1.1", "Sub-section 1.2", "Section 2"]
86104

87105
def test_sub_section_counter_resets_across_top_sections(self):
88-
nb = _make_nb([
89-
("markdown", "# A", None), ("code", "a=1", None),
90-
("markdown", "## A1", None), ("code", "b=2", None),
91-
("markdown", "# B", None), ("code", "c=3", None),
92-
("markdown", "## B1", None), ("code", "d=4", None),
93-
])
106+
nb = _make_nb(
107+
[
108+
("markdown", "# A", None),
109+
("code", "a=1", None),
110+
("markdown", "## A1", None),
111+
("code", "b=2", None),
112+
("markdown", "# B", None),
113+
("code", "c=3", None),
114+
("markdown", "## B1", None),
115+
("code", "d=4", None),
116+
]
117+
)
94118
sections = build_sections(nb)
95119
numbers = [s.number for s in sections]
96120
# After # B the sub-counter resets, so ## B1 becomes 2.1 not 1.2
97121
assert numbers == ["1", "1.1", "2", "2.1"]
98122

99123
def test_heading_cells_not_added_to_cell_list(self):
100-
nb = _make_nb([
101-
("markdown", "# Title", None),
102-
("markdown", "Some prose (no heading)", None),
103-
("code", "x=1", None),
104-
])
124+
nb = _make_nb(
125+
[
126+
("markdown", "# Title", None),
127+
("markdown", "Some prose (no heading)", None),
128+
("code", "x=1", None),
129+
]
130+
)
105131
sections = build_sections(nb)
106132
assert len(sections) == 1
107133
# Only the prose markdown + code cell should be in cells
@@ -110,6 +136,7 @@ def test_heading_cells_not_added_to_cell_list(self):
110136

111137
# ── SectionProfile ────────────────────────────────────────────────────────────
112138

139+
113140
class TestSectionProfile:
114141
def test_display_label_preamble(self):
115142
s = SectionProfile(title="[preamble]", level=0, number="")
@@ -149,16 +176,19 @@ def test_to_dict_contains_label_and_title(self):
149176

150177
# ── build_profile ─────────────────────────────────────────────────────────────
151178

179+
152180
class TestBuildProfile:
153181
def _make_executed_nb(self):
154-
nb = _make_nb([
155-
("markdown", "# Imports", None),
156-
("code", "import numpy as np", _executed_cell_meta(0.1)),
157-
("markdown", "## Heavy computation", None),
158-
("code", "result = np.sum(range(1000))", _executed_cell_meta(5.0)),
159-
("markdown", "# Results", None),
160-
("code", "print(result)", _executed_cell_meta(0.05)),
161-
])
182+
nb = _make_nb(
183+
[
184+
("markdown", "# Imports", None),
185+
("code", "import numpy as np", _executed_cell_meta(0.1)),
186+
("markdown", "## Heavy computation", None),
187+
("code", "result = np.sum(range(1000))", _executed_cell_meta(5.0)),
188+
("markdown", "# Results", None),
189+
("code", "print(result)", _executed_cell_meta(0.05)),
190+
]
191+
)
162192
nb.metadata["papermill"] = {
163193
"start_time": "2026-01-01T00:00:00+00:00",
164194
"end_time": "2026-01-01T00:00:06+00:00",
@@ -203,6 +233,7 @@ def test_n_errors_counts_exceptions(self):
203233

204234
# ── profile_notebook ──────────────────────────────────────────────────────────
205235

236+
206237
class TestProfileNotebook:
207238
def test_returns_dict(self, tmp_path):
208239
nb = nbformat.v4.new_notebook()
@@ -236,7 +267,9 @@ def test_writes_json_file(self, tmp_path):
236267

237268
# ── live_tree availability guard ──────────────────────────────────────────────
238269

270+
239271
class TestLiveTreeAvailability:
240272
def test_is_available_returns_bool(self):
241273
from papermill.live_tree import is_available
274+
242275
assert isinstance(is_available(), bool)

β€Žpyproject.tomlβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ optional-dependencies.docs = [
108108
optional-dependencies.gcs = [ "gcsfs>=0.2" ]
109109
optional-dependencies.github = [ "pygithub>=1.55" ]
110110
optional-dependencies.hdfs = [ "pyarrow>=2" ]
111-
optional-dependencies.rich = [ "rich>=13.0" ]
111+
optional-dependencies.rich = [ "rich>=13" ]
112112
optional-dependencies.s3 = [ "boto3" ]
113113
optional-dependencies.test = [
114114
"attrs>=17.4",

0 commit comments

Comments
Β (0)