nbdevAuto 0.3.63__tar.gz → 0.3.65__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nbdevauto-0.3.63/nbdevAuto.egg-info → nbdevauto-0.3.65}/PKG-INFO +2 -1
- nbdevauto-0.3.65/nbdevAuto/__init__.py +1 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/nbdevAuto/_modidx.py +18 -1
- nbdevauto-0.3.65/nbdevAuto/automate.py +341 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65/nbdevAuto.egg-info}/PKG-INFO +2 -1
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/nbdevAuto.egg-info/requires.txt +1 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/pyproject.toml +1 -1
- nbdevauto-0.3.63/nbdevAuto/__init__.py +0 -1
- nbdevauto-0.3.63/nbdevAuto/automate.py +0 -97
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/LICENSE +0 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/MANIFEST.in +0 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/README.md +0 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/nbdevAuto/core.py +0 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/nbdevAuto/functions.py +0 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/nbdevAuto/github.py +0 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/nbdevAuto/pdf.py +0 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/nbdevAuto.egg-info/SOURCES.txt +0 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/nbdevAuto.egg-info/dependency_links.txt +0 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/nbdevAuto.egg-info/entry_points.txt +0 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/nbdevAuto.egg-info/top_level.txt +0 -0
- {nbdevauto-0.3.63 → nbdevauto-0.3.65}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nbdevAuto
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.65
|
|
4
4
|
Summary: automating nbdev
|
|
5
5
|
Author-email: Benedict Thekkel <bthekkel1@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -23,6 +23,7 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
Requires-Dist: graphviz
|
|
25
25
|
Requires-Dist: fastcore
|
|
26
|
+
Requires-Dist: rich
|
|
26
27
|
Provides-Extra: gh
|
|
27
28
|
Requires-Dist: githubkit>=0.12; extra == "gh"
|
|
28
29
|
Dynamic: license-file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.65"
|
|
@@ -5,7 +5,24 @@ d = { 'settings': { 'branch': 'main',
|
|
|
5
5
|
'doc_host': 'https://bthek1.github.io',
|
|
6
6
|
'git_url': 'https://github.com/bthek1/nbdevAuto',
|
|
7
7
|
'lib_path': 'nbdevAuto'},
|
|
8
|
-
'syms': { 'nbdevAuto.automate': { 'nbdevAuto.automate.
|
|
8
|
+
'syms': { 'nbdevAuto.automate': { 'nbdevAuto.automate.StepFailed': ('automate.html#stepfailed', 'nbdevAuto/automate.py'),
|
|
9
|
+
'nbdevAuto.automate.StepFailed.__init__': ( 'automate.html#stepfailed.__init__',
|
|
10
|
+
'nbdevAuto/automate.py'),
|
|
11
|
+
'nbdevAuto.automate._auto_msg': ('automate.html#_auto_msg', 'nbdevAuto/automate.py'),
|
|
12
|
+
'nbdevAuto.automate._banner': ('automate.html#_banner', 'nbdevAuto/automate.py'),
|
|
13
|
+
'nbdevAuto.automate._commit_summary': ('automate.html#_commit_summary', 'nbdevAuto/automate.py'),
|
|
14
|
+
'nbdevAuto.automate._done': ('automate.html#_done', 'nbdevAuto/automate.py'),
|
|
15
|
+
'nbdevAuto.automate._dur': ('automate.html#_dur', 'nbdevAuto/automate.py'),
|
|
16
|
+
'nbdevAuto.automate._push_summary': ('automate.html#_push_summary', 'nbdevAuto/automate.py'),
|
|
17
|
+
'nbdevAuto.automate._quiet': ('automate.html#_quiet', 'nbdevAuto/automate.py'),
|
|
18
|
+
'nbdevAuto.automate._rule': ('automate.html#_rule', 'nbdevAuto/automate.py'),
|
|
19
|
+
'nbdevAuto.automate._run': ('automate.html#_run', 'nbdevAuto/automate.py'),
|
|
20
|
+
'nbdevAuto.automate._skip': ('automate.html#_skip', 'nbdevAuto/automate.py'),
|
|
21
|
+
'nbdevAuto.automate._status_table': ('automate.html#_status_table', 'nbdevAuto/automate.py'),
|
|
22
|
+
'nbdevAuto.automate._step': ('automate.html#_step', 'nbdevAuto/automate.py'),
|
|
23
|
+
'nbdevAuto.automate._test_summary': ('automate.html#_test_summary', 'nbdevAuto/automate.py'),
|
|
24
|
+
'nbdevAuto.automate._version_summary': ('automate.html#_version_summary', 'nbdevAuto/automate.py'),
|
|
25
|
+
'nbdevAuto.automate.gacp': ('automate.html#gacp', 'nbdevAuto/automate.py'),
|
|
9
26
|
'nbdevAuto.automate.help_output': ('automate.html#help_output', 'nbdevAuto/automate.py'),
|
|
10
27
|
'nbdevAuto.automate.prep': ('automate.html#prep', 'nbdevAuto/automate.py'),
|
|
11
28
|
'nbdevAuto.automate.release': ('automate.html#release', 'nbdevAuto/automate.py'),
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"""Automate
|
|
2
|
+
|
|
3
|
+
Docs: https://bthek1.github.io/nbdevAuto/automate.html.md"""
|
|
4
|
+
|
|
5
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_Automate.ipynb.
|
|
6
|
+
|
|
7
|
+
# %% auto #0
|
|
8
|
+
__all__ = ['prep', 'gacp', 'status', 'upload', 'release_git', 'release_pypi', 'release', 'help_output']
|
|
9
|
+
|
|
10
|
+
# %% ../nbs/01_Automate.ipynb #6d1499d1-aa55-47d8-92fd-fa056b1fb135
|
|
11
|
+
from fastcore.script import *
|
|
12
|
+
from rich import box
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.markup import escape
|
|
15
|
+
from rich.rule import Rule
|
|
16
|
+
from rich.table import Table
|
|
17
|
+
|
|
18
|
+
# %% ../nbs/01_Automate.ipynb #7c1e5a90-3f52-4d21-9b6a-2c0f4e8d1a37
|
|
19
|
+
import re
|
|
20
|
+
import subprocess
|
|
21
|
+
from contextlib import contextmanager, redirect_stderr, redirect_stdout
|
|
22
|
+
from io import StringIO
|
|
23
|
+
from time import perf_counter
|
|
24
|
+
|
|
25
|
+
from nbdevAuto import __version__
|
|
26
|
+
|
|
27
|
+
console = Console()
|
|
28
|
+
|
|
29
|
+
# A rule drawn across a 200-column terminal is a wall of dashes. 88 reads as a header.
|
|
30
|
+
_RULE_W = 88
|
|
31
|
+
|
|
32
|
+
# `git status -s` codes: colour, the word for the third column, and the verb a commit
|
|
33
|
+
# subject gets when every staged path carries that same code.
|
|
34
|
+
_CODE_STYLE = {"M":"yellow", "A":"green", "D":"red", "R":"cyan", "C":"cyan", "U":"magenta", "?":"dim"}
|
|
35
|
+
_CODE_LABEL = {"M":"modified", "A":"added", "D":"deleted", "R":"renamed", "C":"copied",
|
|
36
|
+
"U":"conflict", "?":"untracked"}
|
|
37
|
+
_CODE_VERB = {"M":"update", "A":"add", "D":"remove", "R":"rename", "C":"copy",
|
|
38
|
+
"U":"merge", "?":"add"}
|
|
39
|
+
|
|
40
|
+
_RE_VERSION = re.compile(r"^(Old|New) version:\s*(\S+)", re.M)
|
|
41
|
+
_RE_TIMING = re.compile(r"^(?P<nb>.+?):\s+(?P<secs>[\d.]+)\s*secs?$")
|
|
42
|
+
_RE_COMMIT = re.compile(r"^\[(?P<branch>\S+)\s+(?P<sha>[0-9a-f]+)\]")
|
|
43
|
+
_RE_COUNTS = re.compile(r"(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?")
|
|
44
|
+
_RE_WROTE = re.compile(r"Writing objects:.*?,\s*(?P<size>[\d.]+\s*[KMG]?i?B)")
|
|
45
|
+
|
|
46
|
+
class StepFailed(Exception):
|
|
47
|
+
"A stage exited non-zero. Carries the output so `_step` can show what went wrong"
|
|
48
|
+
def __init__(self, cmd, code, output=""):
|
|
49
|
+
self.cmd, self.code, self.output = cmd, code, output
|
|
50
|
+
super().__init__(f"{cmd} exited {code}")
|
|
51
|
+
|
|
52
|
+
def _dur(t0):
|
|
53
|
+
"Elapsed time since `t0`, at a readable scale"
|
|
54
|
+
s = perf_counter() - t0
|
|
55
|
+
if s < 1: return f"{s*1000:.0f}ms"
|
|
56
|
+
if s < 60: return f"{s:.1f}s"
|
|
57
|
+
return f"{int(s)//60}m{int(s)%60:02d}s"
|
|
58
|
+
|
|
59
|
+
def _rule(text, style):
|
|
60
|
+
"A rule capped at `_RULE_W`; `console.rule` otherwise spans the whole terminal"
|
|
61
|
+
console.print(Rule(text, style=style), width=min(console.width, _RULE_W))
|
|
62
|
+
|
|
63
|
+
def _banner(cmd):
|
|
64
|
+
"Command header, with the package version alongside"
|
|
65
|
+
_rule(f"[bold cyan]{cmd}[/bold cyan] [dim]nbdevAuto {__version__}[/dim]", "cyan")
|
|
66
|
+
|
|
67
|
+
def _done(cmd, t0):
|
|
68
|
+
"Closing summary for a multi-stage command"
|
|
69
|
+
_rule(f"[green]{cmd} finished[/green] [dim]in {_dur(t0)}[/dim]", "green")
|
|
70
|
+
|
|
71
|
+
@contextmanager
|
|
72
|
+
def _step(n, total, label):
|
|
73
|
+
"""Run one stage, reporting its outcome and duration.
|
|
74
|
+
|
|
75
|
+
Yields a list: append short strings and they print under the step, which is how a
|
|
76
|
+
stage says what it did without letting the underlying tool print it all. Re-raises
|
|
77
|
+
so nothing is swallowed, and a `StepFailed` shows the output it carries."""
|
|
78
|
+
console.print(f"[dim]{n}/{total}[/dim] [bold]{label}[/bold]")
|
|
79
|
+
t0, detail = perf_counter(), []
|
|
80
|
+
try:
|
|
81
|
+
yield detail
|
|
82
|
+
except StepFailed as e:
|
|
83
|
+
console.print(f" [bold red]FAIL[/bold red] [dim]{_dur(t0)}, exit {e.code}[/dim]")
|
|
84
|
+
for line in e.output.splitlines():
|
|
85
|
+
if line.strip(): console.print(f" [red]{escape(line.rstrip())}[/red]")
|
|
86
|
+
console.print()
|
|
87
|
+
raise
|
|
88
|
+
except BaseException:
|
|
89
|
+
console.print(f" [bold red]FAIL[/bold red] [dim]{_dur(t0)}[/dim]\n")
|
|
90
|
+
raise
|
|
91
|
+
for line in detail: console.print(f" [dim]{escape(line)}[/dim]")
|
|
92
|
+
console.print(f" [green]OK[/green] [dim]{_dur(t0)}[/dim]\n")
|
|
93
|
+
|
|
94
|
+
def _skip(n, total, label, why):
|
|
95
|
+
"Report a stage that was deliberately not run"
|
|
96
|
+
console.print(f"[dim]{n}/{total}[/dim] [bold]{label}[/bold]")
|
|
97
|
+
console.print(f" [yellow]SKIP[/yellow] [dim]{why}[/dim]\n")
|
|
98
|
+
|
|
99
|
+
def _run(*cmd):
|
|
100
|
+
"""Run `cmd` with output captured, returning stdout+stderr.
|
|
101
|
+
|
|
102
|
+
A bare `subprocess.run` returns an exit code nobody reads, so a failed `git push`
|
|
103
|
+
still printed a green OK. Raising is what makes that OK mean something."""
|
|
104
|
+
r = subprocess.run(cmd, capture_output=True, text=True)
|
|
105
|
+
out = f"{r.stdout or ''}{r.stderr or ''}"
|
|
106
|
+
if r.returncode: raise StepFailed(" ".join(cmd), r.returncode, out)
|
|
107
|
+
return out
|
|
108
|
+
|
|
109
|
+
@contextmanager
|
|
110
|
+
def _quiet():
|
|
111
|
+
"Collect what a wrapped nbdev call prints, so the step can summarise it instead"
|
|
112
|
+
buf = StringIO()
|
|
113
|
+
try:
|
|
114
|
+
with redirect_stdout(buf), redirect_stderr(buf): yield buf
|
|
115
|
+
except BaseException:
|
|
116
|
+
# A failing stage is the one time every raw line is worth having.
|
|
117
|
+
text = buf.getvalue().strip()
|
|
118
|
+
if text: console.print(f"[dim]{escape(text)}[/dim]")
|
|
119
|
+
raise
|
|
120
|
+
|
|
121
|
+
def _version_summary(out):
|
|
122
|
+
"The two lines `nbdev_bump_version` prints, as one arrow"
|
|
123
|
+
v = dict(_RE_VERSION.findall(out))
|
|
124
|
+
if "Old" in v and "New" in v: return [f"{v['Old']} -> {v['New']}"]
|
|
125
|
+
return [l.strip() for l in out.splitlines() if l.strip()]
|
|
126
|
+
|
|
127
|
+
def _test_summary(out, slow_secs=1.0, keep=3):
|
|
128
|
+
"`nbdev_test` prints a line per notebook; keep the count and only the genuinely slow ones"
|
|
129
|
+
times = []
|
|
130
|
+
for line in out.splitlines():
|
|
131
|
+
m = _RE_TIMING.match(line.strip())
|
|
132
|
+
if m:
|
|
133
|
+
try: times.append((m["nb"], float(m["secs"])))
|
|
134
|
+
except ValueError: pass
|
|
135
|
+
if not times: return [l.strip() for l in out.splitlines() if l.strip()][-3:]
|
|
136
|
+
head = f"{len(times)} notebook{'' if len(times) == 1 else 's'}"
|
|
137
|
+
slow = [t for t in sorted(times, key=lambda t: -t[1]) if t[1] >= slow_secs][:keep]
|
|
138
|
+
if not slow: return [f"{head}, none slower than {slow_secs:g}s"]
|
|
139
|
+
return [f"{head}, slowest:"] + [f" {nb} {s:.1f}s" for nb, s in slow]
|
|
140
|
+
|
|
141
|
+
def _commit_summary(out):
|
|
142
|
+
"Where the commit landed and how big it was, without echoing the subject back"
|
|
143
|
+
lines = [l.strip() for l in out.splitlines() if l.strip()]
|
|
144
|
+
if not lines: return []
|
|
145
|
+
m, c = _RE_COMMIT.match(lines[0]), _RE_COUNTS.search(out)
|
|
146
|
+
head = f"{m['branch']} {m['sha']}" if m else lines[0]
|
|
147
|
+
if not c: return [head]
|
|
148
|
+
files, ins, dels = c.group(1), c.group(2) or "0", c.group(3) or "0"
|
|
149
|
+
return [f"{head} {files} file{'' if files == '1' else 's'}, +{ins} -{dels}"]
|
|
150
|
+
|
|
151
|
+
def _push_summary(out):
|
|
152
|
+
"What moved and how much went over the wire; the progress meters are noise"
|
|
153
|
+
lines = [l.rstrip() for l in out.splitlines() if l.strip()]
|
|
154
|
+
if not lines: return ["nothing to push"]
|
|
155
|
+
if any("Everything up-to-date" in l for l in lines): return ["everything up-to-date"]
|
|
156
|
+
detail = []
|
|
157
|
+
for i, l in enumerate(lines):
|
|
158
|
+
if l.startswith("To ") and i + 1 < len(lines):
|
|
159
|
+
detail += [lines[i + 1].strip(), f"to {l[3:].strip()}"]
|
|
160
|
+
break
|
|
161
|
+
m = _RE_WROTE.search(out)
|
|
162
|
+
if m: detail.append(f"{m['size'].strip()} written")
|
|
163
|
+
return detail or lines[-1:]
|
|
164
|
+
|
|
165
|
+
def _auto_msg(porcelain, limit=3):
|
|
166
|
+
"A commit subject built from the staged paths. The raw status output made a poor one"
|
|
167
|
+
rows = [(l[:2].strip()[:1], l[3:].strip()) for l in porcelain.splitlines() if l.strip()]
|
|
168
|
+
if not rows: return "update"
|
|
169
|
+
verb = _CODE_VERB.get(rows[0][0], "update") if len({c for c, _ in rows}) == 1 else "update"
|
|
170
|
+
names = [p.split(" -> ")[-1] for _, p in rows]
|
|
171
|
+
head = ", ".join(names[:limit])
|
|
172
|
+
if len(names) > limit: head += f" and {len(names) - limit} more"
|
|
173
|
+
return f"{verb} {head}"
|
|
174
|
+
|
|
175
|
+
def _status_table(porcelain):
|
|
176
|
+
"Render `git status -s` output as a table, one row per path"
|
|
177
|
+
t = Table(box=box.SIMPLE, show_header=False, pad_edge=False, expand=False)
|
|
178
|
+
t.add_column("code", no_wrap=True, justify="right")
|
|
179
|
+
t.add_column("path", overflow="fold")
|
|
180
|
+
t.add_column("what", style="dim", no_wrap=True)
|
|
181
|
+
n = 0
|
|
182
|
+
for line in porcelain.splitlines():
|
|
183
|
+
if not line.strip(): continue
|
|
184
|
+
code, path = line[:2].strip(), line[3:]
|
|
185
|
+
style = _CODE_STYLE.get(code[:1], "white")
|
|
186
|
+
t.add_row(f"[{style}]{code}[/{style}]", escape(path), _CODE_LABEL.get(code[:1], ""))
|
|
187
|
+
n += 1
|
|
188
|
+
return t, n
|
|
189
|
+
|
|
190
|
+
# %% ../nbs/01_Automate.ipynb #3a6f81d2-5c47-4e19-8b03-9d2a7f6c0e15
|
|
191
|
+
@call_parse
|
|
192
|
+
def prep(
|
|
193
|
+
p:int = 2, # Increment Part
|
|
194
|
+
):
|
|
195
|
+
"Bump version part `p`, then export, test and clean the notebooks, refreshing _quarto.yml and README"
|
|
196
|
+
|
|
197
|
+
import nbdev.test, nbdev.clean, nbdev.quarto, nbdev.release
|
|
198
|
+
_banner("prep")
|
|
199
|
+
t0 = perf_counter()
|
|
200
|
+
with _step(1, 6, f"bump version (part {p})") as d, _quiet() as out:
|
|
201
|
+
nbdev.release.nbdev_bump_version(p)
|
|
202
|
+
d += _version_summary(out.getvalue())
|
|
203
|
+
with _step(2, 6, "nbdev_export"), _quiet():
|
|
204
|
+
nbdev.quarto.nbdev_export.__wrapped__()
|
|
205
|
+
with _step(3, 6, "nbdev_test") as d, _quiet() as out:
|
|
206
|
+
nbdev.test.nbdev_test.__wrapped__(
|
|
207
|
+
n_workers = 8, # Number of workers
|
|
208
|
+
timing = True, # Time each notebook to see which are slow
|
|
209
|
+
)
|
|
210
|
+
d += _test_summary(out.getvalue())
|
|
211
|
+
with _step(4, 6, "nbdev_clean"), _quiet():
|
|
212
|
+
nbdev.clean.nbdev_clean.__wrapped__()
|
|
213
|
+
with _step(5, 6, "refresh_quarto_yml"), _quiet():
|
|
214
|
+
nbdev.quarto.refresh_quarto_yml()
|
|
215
|
+
with _step(6, 6, "nbdev_readme"), _quiet():
|
|
216
|
+
nbdev.quarto.nbdev_readme.__wrapped__(chk_time=True)
|
|
217
|
+
_done("prep", t0)
|
|
218
|
+
|
|
219
|
+
# %% ../nbs/01_Automate.ipynb #f66ef35f-b7fa-4039-aa07-e9b782d204bb
|
|
220
|
+
@call_parse
|
|
221
|
+
def gacp(
|
|
222
|
+
m:str = '', # Commit message
|
|
223
|
+
):
|
|
224
|
+
"git add, commit and push. Without `-m` the message is built from the staged paths"
|
|
225
|
+
|
|
226
|
+
_banner("gacp")
|
|
227
|
+
t0 = perf_counter()
|
|
228
|
+
with _step(1, 3, "git add"):
|
|
229
|
+
_run("git", "add", ".")
|
|
230
|
+
# NOT via `_run`: `git status -s` pads the code to two columns, and the capture has
|
|
231
|
+
# to keep that leading space or the first path shifts by one.
|
|
232
|
+
status = subprocess.check_output(["git", "status", "-s"]).decode('utf-8')
|
|
233
|
+
staged = bool(status.strip())
|
|
234
|
+
if staged:
|
|
235
|
+
table, n = _status_table(status)
|
|
236
|
+
console.print(table)
|
|
237
|
+
console.print(f"[dim]{n} path{'' if n == 1 else 's'} staged[/dim]\n")
|
|
238
|
+
else:
|
|
239
|
+
console.print("[dim]nothing staged, the tree is clean[/dim]\n")
|
|
240
|
+
|
|
241
|
+
if staged:
|
|
242
|
+
with _step(2, 3, "git commit") as d:
|
|
243
|
+
d += _commit_summary(_run("git", "commit", "-m", m if m != '' else _auto_msg(status)))
|
|
244
|
+
else:
|
|
245
|
+
# `git commit` exits 1 with nothing staged, which is now a hard failure rather
|
|
246
|
+
# than a silent one. There may still be unpushed commits, so go on to the push.
|
|
247
|
+
_skip(2, 3, "git commit", "nothing staged")
|
|
248
|
+
with _step(3, 3, "git push") as d:
|
|
249
|
+
d += _push_summary(_run("git", "push"))
|
|
250
|
+
_done("gacp", t0)
|
|
251
|
+
|
|
252
|
+
# %% ../nbs/01_Automate.ipynb #88017a1b-55c1-4833-aeb6-277a7e5b6314
|
|
253
|
+
def status():
|
|
254
|
+
"Show the working tree state"
|
|
255
|
+
import subprocess
|
|
256
|
+
|
|
257
|
+
def _git(*a, **kw): return subprocess.check_output(["git", *a], **kw).decode('utf-8').strip()
|
|
258
|
+
|
|
259
|
+
_banner("status")
|
|
260
|
+
branch = _git("branch", "--show-current") or "DETACHED"
|
|
261
|
+
line = f"on [bold]{branch}[/bold]"
|
|
262
|
+
try:
|
|
263
|
+
behind, ahead = _git("rev-list", "--left-right", "--count", "@{upstream}...HEAD",
|
|
264
|
+
stderr=subprocess.DEVNULL).split()
|
|
265
|
+
if behind != "0": line += f" [yellow]behind {behind}[/yellow]"
|
|
266
|
+
if ahead != "0": line += f" [cyan]ahead {ahead}[/cyan]"
|
|
267
|
+
if behind == ahead == "0": line += " [dim]in step with upstream[/dim]"
|
|
268
|
+
except subprocess.CalledProcessError:
|
|
269
|
+
line += " [dim]no upstream[/dim]"
|
|
270
|
+
console.print(line)
|
|
271
|
+
|
|
272
|
+
# NOT via _git: `git status -s` pads the code to two columns, so stripping the
|
|
273
|
+
# output would eat the leading space of the first line and shift its path by one.
|
|
274
|
+
porcelain = subprocess.check_output(["git", "status", "-s"]).decode('utf-8')
|
|
275
|
+
if not porcelain:
|
|
276
|
+
console.print("[green]clean[/green]")
|
|
277
|
+
return
|
|
278
|
+
table, n = _status_table(porcelain)
|
|
279
|
+
console.print(table)
|
|
280
|
+
console.print(f"[dim]{n} path{'' if n == 1 else 's'} changed[/dim]")
|
|
281
|
+
|
|
282
|
+
# %% ../nbs/01_Automate.ipynb #8d777a41-0160-47f8-b51c-b14360b62da4
|
|
283
|
+
@call_parse
|
|
284
|
+
def upload(
|
|
285
|
+
m:str = '', # Commit message
|
|
286
|
+
p:int = 2, # Increment part
|
|
287
|
+
):
|
|
288
|
+
"prep then gacp: the everyday command for shipping a notebook change"
|
|
289
|
+
prep(p)
|
|
290
|
+
gacp(m)
|
|
291
|
+
|
|
292
|
+
# %% ../nbs/01_Automate.ipynb #dee37b06-c946-4b40-be0f-056e133751ea
|
|
293
|
+
def release_git():
|
|
294
|
+
"Bump the MINOR version, then tag and create a GitHub release"
|
|
295
|
+
import nbdev.release
|
|
296
|
+
_banner("gitrelease")
|
|
297
|
+
t0 = perf_counter()
|
|
298
|
+
with _step(1, 2, "bump minor version") as d, _quiet() as out:
|
|
299
|
+
nbdev.release.nbdev_bump_version(1)
|
|
300
|
+
d += _version_summary(out.getvalue())
|
|
301
|
+
with _step(2, 2, "tag and create the GitHub release"):
|
|
302
|
+
nbdev.release.release_git()
|
|
303
|
+
_done("gitrelease", t0)
|
|
304
|
+
|
|
305
|
+
# %% ../nbs/01_Automate.ipynb #288708c6-194a-4059-9ff0-ae330079c199
|
|
306
|
+
def release_pypi():
|
|
307
|
+
"Build the sdist and wheel, then upload to PyPI with twine. CI publishes on push, so this is the manual path"
|
|
308
|
+
import nbdev.release
|
|
309
|
+
_banner("piprelease")
|
|
310
|
+
t0 = perf_counter()
|
|
311
|
+
# Deliberately not quieted: a twine upload is worth watching line by line.
|
|
312
|
+
with _step(1, 1, "build and upload to PyPI"):
|
|
313
|
+
nbdev.release.release_pypi()
|
|
314
|
+
_done("piprelease", t0)
|
|
315
|
+
|
|
316
|
+
# %% ../nbs/01_Automate.ipynb #dd798178-9d58-457e-969a-d86a5e8bf875
|
|
317
|
+
def release():
|
|
318
|
+
"release_git then release_pypi"
|
|
319
|
+
release_git()
|
|
320
|
+
release_pypi()
|
|
321
|
+
|
|
322
|
+
# %% ../nbs/01_Automate.ipynb #c4d2bd52-04c4-43fe-890b-b4cb5da8d206
|
|
323
|
+
def help_output():
|
|
324
|
+
"Print every console script this package installs, with its help"
|
|
325
|
+
from importlib.metadata import distribution
|
|
326
|
+
try:
|
|
327
|
+
eps = [e for e in distribution("nbdevAuto").entry_points if e.group == "console_scripts"]
|
|
328
|
+
except Exception:
|
|
329
|
+
# Running from a source tree with nothing installed: fall back to fastcore.
|
|
330
|
+
from fastcore.xtras import console_help
|
|
331
|
+
return console_help('nbdevAuto')
|
|
332
|
+
_banner("commands")
|
|
333
|
+
t = Table(box=box.SIMPLE, show_header=False, pad_edge=False, expand=False)
|
|
334
|
+
t.add_column("command", style="bold cyan", no_wrap=True)
|
|
335
|
+
t.add_column("does", overflow="fold")
|
|
336
|
+
for e in sorted(eps, key=lambda e: e.name):
|
|
337
|
+
try: doc = escape((e.load().__doc__ or "").strip().splitlines()[0])
|
|
338
|
+
except Exception: doc = "[red]could not load[/red]"
|
|
339
|
+
t.add_row(e.name, doc)
|
|
340
|
+
console.print(t)
|
|
341
|
+
console.print(escape("ghstatus needs the gh extra: pip install 'nbdevAuto[gh]'"), style="dim")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nbdevAuto
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.65
|
|
4
4
|
Summary: automating nbdev
|
|
5
5
|
Author-email: Benedict Thekkel <bthekkel1@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -23,6 +23,7 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
Requires-Dist: graphviz
|
|
25
25
|
Requires-Dist: fastcore
|
|
26
|
+
Requires-Dist: rich
|
|
26
27
|
Provides-Extra: gh
|
|
27
28
|
Requires-Dist: githubkit>=0.12; extra == "gh"
|
|
28
29
|
Dynamic: license-file
|
|
@@ -24,7 +24,7 @@ classifiers = [
|
|
|
24
24
|
"Programming Language :: Python :: 3.13",
|
|
25
25
|
"Programming Language :: Python :: 3.14",
|
|
26
26
|
]
|
|
27
|
-
dependencies = ['graphviz', 'fastcore']
|
|
27
|
+
dependencies = ['graphviz', 'fastcore', 'rich']
|
|
28
28
|
|
|
29
29
|
[project.optional-dependencies]
|
|
30
30
|
# An extra, not a dependency: githubkit pulls pydantic + httpx + hishel, which is
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.63"
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
"""Automate
|
|
2
|
-
|
|
3
|
-
Docs: https://bthek1.github.io/nbdevAuto/automate.html.md"""
|
|
4
|
-
|
|
5
|
-
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_Automate.ipynb.
|
|
6
|
-
|
|
7
|
-
# %% auto #0
|
|
8
|
-
__all__ = ['prep', 'gacp', 'status', 'upload', 'release_git', 'release_pypi', 'release', 'help_output']
|
|
9
|
-
|
|
10
|
-
# %% ../nbs/01_Automate.ipynb #6d1499d1-aa55-47d8-92fd-fa056b1fb135
|
|
11
|
-
from fastcore.script import *
|
|
12
|
-
|
|
13
|
-
@call_parse
|
|
14
|
-
def prep(
|
|
15
|
-
p:int = 2, # Increment Part
|
|
16
|
-
):
|
|
17
|
-
"Bump version part `p`, then export, test and clean the notebooks, refreshing _quarto.yml and README"
|
|
18
|
-
|
|
19
|
-
import nbdev.test, nbdev.clean, nbdev.quarto, nbdev.release
|
|
20
|
-
nbdev.release.nbdev_bump_version(p)
|
|
21
|
-
|
|
22
|
-
nbdev.quarto.nbdev_export.__wrapped__()
|
|
23
|
-
print(f'### nbdev_export finished ###')
|
|
24
|
-
nbdev.test.nbdev_test.__wrapped__(
|
|
25
|
-
n_workers = 8, # Number of workers
|
|
26
|
-
timing = True, # Time each notebook to see which are slow
|
|
27
|
-
)
|
|
28
|
-
print(f'### nbdev_test finished ###')
|
|
29
|
-
nbdev.clean.nbdev_clean.__wrapped__()
|
|
30
|
-
print(f'### nbdev_clean finished ###')
|
|
31
|
-
nbdev.quarto.refresh_quarto_yml()
|
|
32
|
-
print(f'### refresh_quarto_yml finished ###')
|
|
33
|
-
nbdev.quarto.nbdev_readme.__wrapped__(chk_time=True)
|
|
34
|
-
print(f'### nbdev_readme finished ###')
|
|
35
|
-
|
|
36
|
-
# %% ../nbs/01_Automate.ipynb #f66ef35f-b7fa-4039-aa07-e9b782d204bb
|
|
37
|
-
@call_parse
|
|
38
|
-
def gacp(
|
|
39
|
-
m:str = '', # Commit message
|
|
40
|
-
):
|
|
41
|
-
"git add, commit and push. Without `-m` the commit message is the status output"
|
|
42
|
-
|
|
43
|
-
import subprocess
|
|
44
|
-
subprocess.run(["git", "add", "."])
|
|
45
|
-
print(f'### git added ###')
|
|
46
|
-
status = subprocess.check_output(["git", "status", "-s"]).decode('utf-8')
|
|
47
|
-
print(f'### git status: "{status}" ###')
|
|
48
|
-
|
|
49
|
-
if m != '':
|
|
50
|
-
subprocess.run(["git", "commit", "-m", f'{m}'])
|
|
51
|
-
print(f'### git commited "{m}"###')
|
|
52
|
-
else:
|
|
53
|
-
subprocess.run(["git", "commit", "-m", f'{status}'])
|
|
54
|
-
print(f'### git commited status ###')
|
|
55
|
-
subprocess.run(["git", "push"])
|
|
56
|
-
print(f'### git pushed ###')
|
|
57
|
-
|
|
58
|
-
# %% ../nbs/01_Automate.ipynb #88017a1b-55c1-4833-aeb6-277a7e5b6314
|
|
59
|
-
def status():
|
|
60
|
-
"Show the working tree state"
|
|
61
|
-
import subprocess
|
|
62
|
-
subprocess.run(["git", "status"])
|
|
63
|
-
|
|
64
|
-
# %% ../nbs/01_Automate.ipynb #8d777a41-0160-47f8-b51c-b14360b62da4
|
|
65
|
-
@call_parse
|
|
66
|
-
def upload(
|
|
67
|
-
m:str = '', # Commit message
|
|
68
|
-
p:int = 2, #Increment part
|
|
69
|
-
):
|
|
70
|
-
"prep then gacp: the everyday command for shipping a notebook change"
|
|
71
|
-
prep(p)
|
|
72
|
-
gacp(m)
|
|
73
|
-
|
|
74
|
-
# %% ../nbs/01_Automate.ipynb #dee37b06-c946-4b40-be0f-056e133751ea
|
|
75
|
-
def release_git():
|
|
76
|
-
"Bump the MINOR version, then tag and create a GitHub release"
|
|
77
|
-
import nbdev.release
|
|
78
|
-
nbdev.release.nbdev_bump_version(1)
|
|
79
|
-
nbdev.release.release_git()
|
|
80
|
-
|
|
81
|
-
# %% ../nbs/01_Automate.ipynb #288708c6-194a-4059-9ff0-ae330079c199
|
|
82
|
-
def release_pypi():
|
|
83
|
-
"Build the sdist and wheel, then upload to PyPI with twine. CI publishes on push, so this is the manual path"
|
|
84
|
-
import nbdev.release
|
|
85
|
-
nbdev.release.release_pypi()
|
|
86
|
-
|
|
87
|
-
# %% ../nbs/01_Automate.ipynb #dd798178-9d58-457e-969a-d86a5e8bf875
|
|
88
|
-
def release():
|
|
89
|
-
"release_git then release_pypi"
|
|
90
|
-
release_git()
|
|
91
|
-
release_pypi()
|
|
92
|
-
|
|
93
|
-
# %% ../nbs/01_Automate.ipynb #c4d2bd52-04c4-43fe-890b-b4cb5da8d206
|
|
94
|
-
def help_output():
|
|
95
|
-
"Print every console script this package installs, with its help"
|
|
96
|
-
from fastcore.xtras import console_help
|
|
97
|
-
console_help('nbdevAuto')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|