fastcore 2.1.3__tar.gz → 2.1.4__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.
- {fastcore-2.1.3/fastcore.egg-info → fastcore-2.1.4}/PKG-INFO +1 -1
- fastcore-2.1.4/fastcore/__init__.py +1 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/_modidx.py +16 -4
- fastcore-2.1.4/fastcore/editskill.py +63 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/nbio.py +96 -16
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/tools.py +4 -3
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/xml.py +8 -20
- {fastcore-2.1.3 → fastcore-2.1.4/fastcore.egg-info}/PKG-INFO +1 -1
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore.egg-info/SOURCES.txt +1 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore.egg-info/entry_points.txt +1 -1
- {fastcore-2.1.3 → fastcore-2.1.4}/pyproject.toml +1 -1
- fastcore-2.1.3/fastcore/__init__.py +0 -1
- {fastcore-2.1.3 → fastcore-2.1.4}/CONTRIBUTING.md +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/LICENSE +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/MANIFEST.in +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/README.md +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/aio.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/all.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/ansi.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/basics.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/dispatch.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/docments.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/docscrape.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/foundation.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/imghdr.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/imports.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/meta.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/nb_imports.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/net.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/parallel.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/py2pyi.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/script.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/shutil.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/style.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/test.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/transform.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/utils.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/xdg.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/xtras.py +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore.egg-info/dependency_links.txt +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore.egg-info/requires.txt +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/fastcore.egg-info/top_level.txt +0 -0
- {fastcore-2.1.3 → fastcore-2.1.4}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.4"
|
|
@@ -354,6 +354,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
354
354
|
'fastcore.docments.sig2str': ('docments.html#sig2str', 'fastcore/docments.py'),
|
|
355
355
|
'fastcore.docments.sig_source': ('docments.html#sig_source', 'fastcore/docments.py')},
|
|
356
356
|
'fastcore.docscrape': {},
|
|
357
|
+
'fastcore.editskill': {},
|
|
357
358
|
'fastcore.foundation': { 'fastcore.foundation.CollBase': ('foundation.html#collbase', 'fastcore/foundation.py'),
|
|
358
359
|
'fastcore.foundation.CollBase.__delitem__': ( 'foundation.html#collbase.__delitem__',
|
|
359
360
|
'fastcore/foundation.py'),
|
|
@@ -479,7 +480,12 @@ d = { 'settings': { 'branch': 'main',
|
|
|
479
480
|
'fastcore.meta.use_kwargs': ('meta.html#use_kwargs', 'fastcore/meta.py'),
|
|
480
481
|
'fastcore.meta.use_kwargs_dict': ('meta.html#use_kwargs_dict', 'fastcore/meta.py')},
|
|
481
482
|
'fastcore.nb_imports': {},
|
|
482
|
-
'fastcore.nbio': { 'fastcore.nbio.
|
|
483
|
+
'fastcore.nbio': { 'fastcore.nbio.CellRow': ('nbio.html#cellrow', 'fastcore/nbio.py'),
|
|
484
|
+
'fastcore.nbio.CellRow.__init__': ('nbio.html#cellrow.__init__', 'fastcore/nbio.py'),
|
|
485
|
+
'fastcore.nbio.CellRow.__repr__': ('nbio.html#cellrow.__repr__', 'fastcore/nbio.py'),
|
|
486
|
+
'fastcore.nbio.CellRows': ('nbio.html#cellrows', 'fastcore/nbio.py'),
|
|
487
|
+
'fastcore.nbio.CellRows.__repr__': ('nbio.html#cellrows.__repr__', 'fastcore/nbio.py'),
|
|
488
|
+
'fastcore.nbio.NbCell': ('nbio.html#nbcell', 'fastcore/nbio.py'),
|
|
483
489
|
'fastcore.nbio.NbCell.__eq__': ('nbio.html#nbcell.__eq__', 'fastcore/nbio.py'),
|
|
484
490
|
'fastcore.nbio.NbCell.__hash__': ('nbio.html#nbcell.__hash__', 'fastcore/nbio.py'),
|
|
485
491
|
'fastcore.nbio.NbCell.__init__': ('nbio.html#nbcell.__init__', 'fastcore/nbio.py'),
|
|
@@ -504,14 +510,18 @@ d = { 'settings': { 'branch': 'main',
|
|
|
504
510
|
'fastcore.nbio.Notebook.add': ('nbio.html#notebook.add', 'fastcore/nbio.py'),
|
|
505
511
|
'fastcore.nbio.Notebook.cells': ('nbio.html#notebook.cells', 'fastcore/nbio.py'),
|
|
506
512
|
'fastcore.nbio.Notebook.concise': ('nbio.html#notebook.concise', 'fastcore/nbio.py'),
|
|
507
|
-
'fastcore.nbio.Notebook.
|
|
513
|
+
'fastcore.nbio.Notebook.find_cells': ('nbio.html#notebook.find_cells', 'fastcore/nbio.py'),
|
|
508
514
|
'fastcore.nbio.Notebook.md': ('nbio.html#notebook.md', 'fastcore/nbio.py'),
|
|
509
515
|
'fastcore.nbio.Notebook.meta': ('nbio.html#notebook.meta', 'fastcore/nbio.py'),
|
|
510
516
|
'fastcore.nbio.Notebook.move': ('nbio.html#notebook.move', 'fastcore/nbio.py'),
|
|
511
517
|
'fastcore.nbio.Notebook.open': ('nbio.html#notebook.open', 'fastcore/nbio.py'),
|
|
512
518
|
'fastcore.nbio.Notebook.save': ('nbio.html#notebook.save', 'fastcore/nbio.py'),
|
|
513
|
-
'fastcore.nbio.Notebook.
|
|
519
|
+
'fastcore.nbio.Notebook.summary': ('nbio.html#notebook.summary', 'fastcore/nbio.py'),
|
|
520
|
+
'fastcore.nbio.Notebook.to_dict': ('nbio.html#notebook.to_dict', 'fastcore/nbio.py'),
|
|
521
|
+
'fastcore.nbio.Notebook.view_cell': ('nbio.html#notebook.view_cell', 'fastcore/nbio.py'),
|
|
522
|
+
'fastcore.nbio._cell_method': ('nbio.html#_cell_method', 'fastcore/nbio.py'),
|
|
514
523
|
'fastcore.nbio._dict2obj': ('nbio.html#_dict2obj', 'fastcore/nbio.py'),
|
|
524
|
+
'fastcore.nbio._dir_attrs': ('nbio.html#_dir_attrs', 'fastcore/nbio.py'),
|
|
515
525
|
'fastcore.nbio._dir_line': ('nbio.html#_dir_line', 'fastcore/nbio.py'),
|
|
516
526
|
'fastcore.nbio._dir_pre': ('nbio.html#_dir_pre', 'fastcore/nbio.py'),
|
|
517
527
|
'fastcore.nbio._directive': ('nbio.html#_directive', 'fastcore/nbio.py'),
|
|
@@ -534,7 +544,9 @@ d = { 'settings': { 'branch': 'main',
|
|
|
534
544
|
'fastcore.nbio.cells2xml': ('nbio.html#cells2xml', 'fastcore/nbio.py'),
|
|
535
545
|
'fastcore.nbio.concat_streams': ('nbio.html#concat_streams', 'fastcore/nbio.py'),
|
|
536
546
|
'fastcore.nbio.dict2nb': ('nbio.html#dict2nb', 'fastcore/nbio.py'),
|
|
547
|
+
'fastcore.nbio.find_cells': ('nbio.html#find_cells', 'fastcore/nbio.py'),
|
|
537
548
|
'fastcore.nbio.first_code_ln': ('nbio.html#first_code_ln', 'fastcore/nbio.py'),
|
|
549
|
+
'fastcore.nbio.item2xml': ('nbio.html#item2xml', 'fastcore/nbio.py'),
|
|
538
550
|
'fastcore.nbio.mk_cell': ('nbio.html#mk_cell', 'fastcore/nbio.py'),
|
|
539
551
|
'fastcore.nbio.mk_display': ('nbio.html#mk_display', 'fastcore/nbio.py'),
|
|
540
552
|
'fastcore.nbio.mk_error': ('nbio.html#mk_error', 'fastcore/nbio.py'),
|
|
@@ -552,6 +564,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
552
564
|
'fastcore.nbio.render_text': ('nbio.html#render_text', 'fastcore/nbio.py'),
|
|
553
565
|
'fastcore.nbio.repair_cell': ('nbio.html#repair_cell', 'fastcore/nbio.py'),
|
|
554
566
|
'fastcore.nbio.repair_nb': ('nbio.html#repair_nb', 'fastcore/nbio.py'),
|
|
567
|
+
'fastcore.nbio.summary_nb': ('nbio.html#summary_nb', 'fastcore/nbio.py'),
|
|
555
568
|
'fastcore.nbio.validate_cell': ('nbio.html#validate_cell', 'fastcore/nbio.py'),
|
|
556
569
|
'fastcore.nbio.validate_nb': ('nbio.html#validate_nb', 'fastcore/nbio.py'),
|
|
557
570
|
'fastcore.nbio.view_cell': ('nbio.html#view_cell', 'fastcore/nbio.py'),
|
|
@@ -735,7 +748,6 @@ d = { 'settings': { 'branch': 'main',
|
|
|
735
748
|
'fastcore.xml.dict2xml': ('xml.html#dict2xml', 'fastcore/xml.py'),
|
|
736
749
|
'fastcore.xml.ft': ('xml.html#ft', 'fastcore/xml.py'),
|
|
737
750
|
'fastcore.xml.highlight': ('xml.html#highlight', 'fastcore/xml.py'),
|
|
738
|
-
'fastcore.xml.item2xml': ('xml.html#item2xml', 'fastcore/xml.py'),
|
|
739
751
|
'fastcore.xml.mk_getattr': ('xml.html#mk_getattr', 'fastcore/xml.py'),
|
|
740
752
|
'fastcore.xml.showtags': ('xml.html#showtags', 'fastcore/xml.py'),
|
|
741
753
|
'fastcore.xml.to_xml': ('xml.html#to_xml', 'fastcore/xml.py'),
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Text, file, cell, and notebook editing from `fastcore.tools` and `fastcore.nbio`, plus the conventions the whole fastai editing toolkit follows. Read this before working with the editing tools in any package that shares them.
|
|
2
|
+
|
|
3
|
+
`from fastcore.editskill import *` loads the fastcore editing layer: the text primitives and file tools of `fastcore.tools`, and the notebook I/O and cell editors of `fastcore.nbio`. Sibling packages extend the same toolkit, and where they are installed prefer them as noted: `exhash` for hash-verified editing, `rgapi` for search, `llmsurgery`/`dialoghelper` for dialogs.
|
|
4
|
+
|
|
5
|
+
## Carriers
|
|
6
|
+
|
|
7
|
+
Each editing function works on one carrier: text (a str in memory), a file (a path on disk), a cell (one cell's source in an .ipynb file, addressed by `path, cell_id`), or a notebook (the parsed .ipynb; `Notebook` and `NbCell` are its held-object forms). These are the representation layer: an .ipynb is a file of cells, whatever produced it. The dialog layer above adds the msg and dlg carriers: a Solveit dialog is an .ipynb whose cells are messages (notes, runnable code, prompt/reply pairs), and `llmsurgery.dlgskill` and `dialoghelper` provide the message tools, following the conventions here with their own nouns. The word picks the layer: cell tools answer representation questions ("why does Jupyter reject this file?"), msg tools answer content questions ("what does this message say?").
|
|
8
|
+
|
|
9
|
+
## Naming
|
|
10
|
+
|
|
11
|
+
Two name shapes cover the toolkit, and the pivot is the verb's direct object:
|
|
12
|
+
|
|
13
|
+
- An operation on a whole carrier takes the carrier as its noun: verb_carrier. `view_file`, `create_file`, `read_nb`, `write_nb`, `view_cell`, `validate_nb`; in the dialog layer `view_msg` and `view_dlg`. Coined verbs follow the same shape: `lnhashview_cell` is "lnhashview this cell". When the verb's object is instead the medium's unit, and that unit names its carrier uniquely, no prefix is needed - the unit noun is the carrier signal: `find_msgs`, `add_msg`, `del_msgs` (msgs live only in dialogs), `find_cells`, `summary_nb`'s rows (cells live only in notebooks).
|
|
14
|
+
- An operation within a carrier already owns its noun (`insert_line`, `del_lines`, `replace_lines`, `str_replace`), so the carrier prefixes as a namespace and the op name survives intact: carrier_op, as in `file_del_lines`, `cell_del_lines`, `msg_del_lines`. The bare op names are the text-level primitives, and every carrier version keeps the identical signature after its address arguments, so each family is learned once and recognized everywhere.
|
|
15
|
+
|
|
16
|
+
The exceptions are deliberate and closed. `str_replace` keeps the name and argument order established by Anthropic's text editor tool. Instrument-named ops put the instrument first and elide their unit: `ast_replace` (the AST pattern is how the edit finds its target) and `exhash` (hash-verified line addresses travel inside its commands), carrier-prefixed like any other line-level op: `file_ast_replace`, `msg_ast_replace`, `file_exhash`, `cell_exhash`. Converters are named x2y (`nb2dict`, `cell2xml`; in llmsurgery, `dlg` on exactly one side of every converter), and on a held object the converter is a `to_y` method (`nb.to_dict()`). Plural marks arity: `view_cell` takes one cell, `lnhashview_cells` several, `del_msgs` many.
|
|
17
|
+
|
|
18
|
+
## Parameters
|
|
19
|
+
|
|
20
|
+
One vocabulary, identical wherever it appears:
|
|
21
|
+
|
|
22
|
+
- The carrier's address comes first (`text`; `path`; `path, cell_id`; a message `id`), the payload next, and ambient context last as keyword-only (message tools name their dialog that way).
|
|
23
|
+
- `start_line`/`end_line`: 1-based, inclusive, `None` for first/last, negative counting from the end. Destructive ops (`del_lines`) accept no defaults: state the range.
|
|
24
|
+
- `re_filter`/`invert_filter`: restrict an edit to lines matching (or not matching) a regex, like ex's `g//` and `g!//`; combines with the range.
|
|
25
|
+
- Searches read patterns as regex by default; editors read them as literal text until `use_regex=True`. Searching is read-only, so its default favors power; editing favors safety.
|
|
26
|
+
- `nums` and `lnhashs` on any view: line numbers, or `lineno|hash|` addresses. `maxlen` caps characters per summary line; `trunc_out`/`trunc_in` truncate outputs and sources in dialog views.
|
|
27
|
+
- Search tools share one filter vocabulary: `pattern` first, `root='.'`, and the same include/exclude/ext/hidden/ignore block across `fd`, `ls`, `rg`, and `nbrg`. Variants differ by defaults, not API: `ls` is `fd` with listing defaults. Boolean filters narrow as `only_*` and widen as `include_*`.
|
|
28
|
+
- `context=` counts the medium's own units: lines (or blocks in summary mode) for files, cells for notebooks, messages for dialogs. Dialog search defaults to context 1 because the neighbouring note usually explains the match: the why lives next to the what.
|
|
29
|
+
- Every editor returns a diff ("none: No changes." when nothing changed). The diff is the verification: read it instead of re-viewing the target.
|
|
30
|
+
|
|
31
|
+
## Functions and methods
|
|
32
|
+
|
|
33
|
+
Every operation has two shapes with one contract each. The function is a transaction: it addresses a file by path, applies the edit, writes, and returns a diff. The method is a session: it mutates the object in hand, and nothing reaches disk until an explicit save. The correspondence is mechanical - the method is the function minus its address arguments, keeping its name except that a carrier token which became `self` drops: `cell_str_replace(path, cell_id, ...)` is `c.str_replace(...)` on a held `NbCell`, `find_cells(path, pat)` is `nb.find_cells(pat)` on a held `Notebook`. Function-side reads return dead snapshot rows carrying addresses, source, and meta; session-side reads return the live objects. Use one shape at a time per file: save before switching to functions, reopen after.
|
|
34
|
+
|
|
35
|
+
## Addresses
|
|
36
|
+
|
|
37
|
+
Edits say where with line numbers or lnhash addresses. Take addresses from the read you were already doing instead of with a second look: views accept `nums=True` or `lnhashs=True`, and searches return addresses directly (`rg(lnhashs=True)`). Prefer lnhash addresses whenever `exhash` is installed: they are verified against current content at edit time, so a stale address fails loudly instead of editing nearby text, which is exactly what makes taking addresses early safe. Plain line numbers are unverified and shift as edits apply, so re-view after each edit and apply multi-edits bottom-to-top. `exhash.skill` owns the address format and the verified editor.
|
|
38
|
+
|
|
39
|
+
When you don't yet know where to edit, locate with a summary first: `rgapi`'s `rg(summary=True)` and `nbrg`, and llmsurgery's `summary_dlg`, each show one line per natural unit of their medium (block, cell, message), carrying the unit's address. Summaries locate, views read, addresses edit, diffs confirm.
|
|
40
|
+
|
|
41
|
+
## What's where
|
|
42
|
+
|
|
43
|
+
- `fastcore.tools`: text primitives, file tools, and `line_hash`/`lnhash`/`lnhash_at` for creating addresses without exhash installed.
|
|
44
|
+
- `fastcore.nbio`: notebook read/write/validate/repair, cell construction, cell editors, and the `Notebook`/`NbCell` session objects with their snapshot queries (`find_cells`, `summary_nb`).
|
|
45
|
+
- `exhash.skill`: hash-verified editing for files and cells; prefer it for edits where installed.
|
|
46
|
+
- `rgapi.skill`: `rg`/`fd`/`ls`/`nbrg` search with lnhash output.
|
|
47
|
+
- `remold`: structural search and rewrite for Python source (declarative ast-grep rules, LibCST matcher transforms, symbol queries); the engine behind `ast_replace`.
|
|
48
|
+
- `llmsurgery.dlgskill`, `dialoghelper`: the dialog layer, including its own theory of dialogs and projections.
|
|
49
|
+
|
|
50
|
+
Docs: https://fastcore.fast.ai/tools.html.md and https://fastcore.fast.ai/nbio.html.md
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
from fastcore.tools import (insert_line, str_replace, strs_replace, replace_lines, del_lines, ast_replace,
|
|
54
|
+
file_insert_line, file_str_replace, file_strs_replace, file_replace_lines, file_del_lines, file_ast_replace,
|
|
55
|
+
view_file, create_file, line_hash, lnhash, lnhash_at)
|
|
56
|
+
from fastcore.nbio import (read_nb, write_nb, new_nb, mk_cell, validate_nb, validate_cell, repair_nb, repair_cell,
|
|
57
|
+
view_cell, cell_insert_line, cell_str_replace, cell_strs_replace, cell_replace_lines, cell_del_lines, cell_ast_replace, Notebook, NbCell, find_cells, summary_nb)
|
|
58
|
+
|
|
59
|
+
__all__ = ['insert_line', 'str_replace', 'strs_replace', 'replace_lines', 'del_lines', 'ast_replace',
|
|
60
|
+
'file_insert_line', 'file_str_replace', 'file_strs_replace', 'file_replace_lines', 'file_del_lines', 'file_ast_replace',
|
|
61
|
+
'view_file', 'create_file', 'line_hash', 'lnhash', 'lnhash_at',
|
|
62
|
+
'read_nb', 'write_nb', 'new_nb', 'mk_cell', 'validate_nb', 'validate_cell', 'repair_nb', 'repair_cell',
|
|
63
|
+
'view_cell', 'cell_insert_line', 'cell_str_replace', 'cell_strs_replace', 'cell_replace_lines', 'cell_del_lines', 'cell_ast_replace', 'Notebook', 'NbCell', 'find_cells', 'summary_nb']
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Reading and writing Jupyter notebooks
|
|
2
2
|
|
|
3
|
-
Cell tools apply `fastcore.tools`' string editing primitives to one notebook cell's source, addressed by path and cell id, mirroring that module's file tools: the same operations and parameters, with `path, cell_id` in place of `path`. Each editor returns a diff of the change, and `view_cell` shows a cell's source with optional line numbers or exhash addresses.
|
|
3
|
+
Cell tools apply `fastcore.tools`' string editing primitives to one notebook cell's source, addressed by path and cell id, mirroring that module's file tools: the same operations and parameters, with `path, cell_id` in place of `path`. Each editor (including the structural `cell_ast_replace`) returns a diff of the change, and `view_cell` shows a cell's source with optional line numbers or exhash addresses.
|
|
4
|
+
|
|
5
|
+
Naming and parameter conventions shared across the editing toolkit are documented in `fastcore.editskill`, which also re-exports this module's editing tools.
|
|
4
6
|
|
|
5
7
|
Docs: https://fastcore.fast.ai/nbio.html.md"""
|
|
6
8
|
|
|
@@ -8,10 +10,11 @@ Docs: https://fastcore.fast.ai/nbio.html.md"""
|
|
|
8
10
|
|
|
9
11
|
# %% auto #0
|
|
10
12
|
__all__ = ['langs', 'cell_insert_line', 'cell_str_replace', 'cell_strs_replace', 'cell_replace_lines', 'cell_del_lines',
|
|
11
|
-
'nb_lang', 'NbCell', 'dict2nb', 'read_nb', 'mk_cell', 'new_nb', 'first_code_ln',
|
|
12
|
-
'write_nb', 'cell_edit', 'view_cell', 'validate_cell', 'validate_nb', 'repair_cell',
|
|
13
|
-
'preferred_out', 'mk_stream', 'mk_result', 'mk_display', 'mk_error', 'concat_streams',
|
|
14
|
-
'render_output', 'render_outputs', 'render_text', '
|
|
13
|
+
'cell_ast_replace', 'nb_lang', 'NbCell', 'dict2nb', 'read_nb', 'mk_cell', 'new_nb', 'first_code_ln',
|
|
14
|
+
'nb2dict', 'nb2str', 'write_nb', 'cell_edit', 'view_cell', 'validate_cell', 'validate_nb', 'repair_cell',
|
|
15
|
+
'repair_nb', 'preferred_out', 'mk_stream', 'mk_result', 'mk_display', 'mk_error', 'concat_streams',
|
|
16
|
+
'preferred_msg_out', 'render_output', 'render_outputs', 'render_text', 'item2xml', 'cell2xml', 'cells2xml',
|
|
17
|
+
'Notebook', 'CellRow', 'CellRows', 'summary_nb', 'find_cells']
|
|
15
18
|
|
|
16
19
|
# %% ../nbs/13_nbio.ipynb #954ca1aa
|
|
17
20
|
from .basics import *
|
|
@@ -19,9 +22,9 @@ from .xtras import rtoken_hex,clean_cli_output,take_lines,str_diff
|
|
|
19
22
|
from .imports import *
|
|
20
23
|
from .ansi import ansi2html
|
|
21
24
|
from .meta import delegates,splice_sig
|
|
22
|
-
from .tools import insert_line,str_replace,strs_replace,replace_lines,del_lines,lnhash
|
|
25
|
+
from .tools import insert_line,str_replace,strs_replace,replace_lines,del_lines,ast_replace,lnhash
|
|
23
26
|
|
|
24
|
-
import ast,functools
|
|
27
|
+
import ast,copy,functools
|
|
25
28
|
from collections import defaultdict
|
|
26
29
|
from pprint import pformat,pprint
|
|
27
30
|
from json import loads,dumps
|
|
@@ -154,13 +157,17 @@ def _directive(s, lang='python'):
|
|
|
154
157
|
return name, ('' if v=='true' else v)
|
|
155
158
|
|
|
156
159
|
# %% ../nbs/13_nbio.ipynb #16c08410
|
|
157
|
-
def _meta_directives(
|
|
158
|
-
"Directives from the `nbdev` dict in
|
|
159
|
-
d =
|
|
160
|
+
def _meta_directives(meta):
|
|
161
|
+
"Directives from the `nbdev` dict in metadata dict `meta`, as `{name: value}`; values must be str (`'true'` for a bare directive)"
|
|
162
|
+
d = (meta or {}).get('nbdev',{})
|
|
160
163
|
if bad := [k for k,v in d.items() if not isinstance(v,str)]:
|
|
161
164
|
raise TypeError(f"`nbdev` metadata directive values must be str (e.g 'true'/'false'), got non-str for: {bad}")
|
|
162
165
|
return {k:'' if v=='true' else v for k,v in d.items()}
|
|
163
166
|
|
|
167
|
+
def _dir_attrs(meta):
|
|
168
|
+
"Metadata directives as XML attrs: bare directives become True, so they render as bare attributes"
|
|
169
|
+
return {k:v or True for k,v in _meta_directives(meta).items()}
|
|
170
|
+
|
|
164
171
|
def _unparse_dir(v):
|
|
165
172
|
"Inverse of `_meta_directives` value parsing: value string back to a metadata value"
|
|
166
173
|
return 'true' if v=='' else v
|
|
@@ -182,7 +189,7 @@ def _directives_get(self):
|
|
|
182
189
|
if '_directives_' not in self:
|
|
183
190
|
dirs,_ = self._partition()
|
|
184
191
|
cmts = dict(t for t in (_directive(s, self.lang_) for s in dirs) if t)
|
|
185
|
-
meta = _meta_directives(self)
|
|
192
|
+
meta = _meta_directives(self.get('metadata'))
|
|
186
193
|
self['_meta_names_'] = set(meta) - set(cmts)
|
|
187
194
|
self['_directives_'] = cmts | {k:v for k,v in meta.items() if k not in cmts}
|
|
188
195
|
return dict(self['_directives_'])
|
|
@@ -244,7 +251,9 @@ def nb2dict(d, k=None):
|
|
|
244
251
|
# %% ../nbs/13_nbio.ipynb #21ffe533
|
|
245
252
|
def nb2str(nb):
|
|
246
253
|
"Convert `nb` to a `str`"
|
|
247
|
-
|
|
254
|
+
# Unconditional: a plain dict can hold live NbCells (e.g. spliced in by a processor), whose working
|
|
255
|
+
# attrs (`idx_` etc) and str `source` must not serialize. Idempotent on already-canonical dicts.
|
|
256
|
+
nb = nb2dict(nb)
|
|
248
257
|
return dumps(nb, sort_keys=True, indent=1, ensure_ascii=False) + "\n"
|
|
249
258
|
|
|
250
259
|
# %% ../nbs/13_nbio.ipynb #d979e25a
|
|
@@ -272,6 +281,7 @@ def _nb_cell(nb, cell_id):
|
|
|
272
281
|
return res[0]
|
|
273
282
|
|
|
274
283
|
def cell_edit(f, name=None):
|
|
284
|
+
"Wrap text editor `f` as a cell editing function: `path, cell_id` addressing, diff-or-error return"
|
|
275
285
|
def wrapper(path:str, cell_id:str, *args, **kw):
|
|
276
286
|
nb = read_nb(path)
|
|
277
287
|
cell = _nb_cell(nb, cell_id)
|
|
@@ -292,6 +302,7 @@ cell_str_replace = cell_edit(str_replace, 'cell_str_replace')
|
|
|
292
302
|
cell_strs_replace = cell_edit(strs_replace, 'cell_strs_replace')
|
|
293
303
|
cell_replace_lines = cell_edit(replace_lines, 'cell_replace_lines')
|
|
294
304
|
cell_del_lines = cell_edit(del_lines, 'cell_del_lines')
|
|
305
|
+
cell_ast_replace = cell_edit(ast_replace, 'cell_ast_replace')
|
|
295
306
|
|
|
296
307
|
# %% ../nbs/13_nbio.ipynb #421b2b9c
|
|
297
308
|
def view_cell(
|
|
@@ -377,7 +388,7 @@ def repair_nb(nb):
|
|
|
377
388
|
return res
|
|
378
389
|
|
|
379
390
|
# %% ../nbs/13_nbio.ipynb #530b9cd1
|
|
380
|
-
from .xml import NB,to_xml,ft
|
|
391
|
+
from .xml import NB,to_xml,ft
|
|
381
392
|
|
|
382
393
|
# %% ../nbs/13_nbio.ipynb #9f22b923
|
|
383
394
|
def preferred_out(data, html1st=True, include_imgs=False):
|
|
@@ -480,11 +491,25 @@ def render_text(outputs, html1st=False):
|
|
|
480
491
|
if not items: return ''
|
|
481
492
|
return items[0][0] if len(items)==1 else '\n'.join(o[1] for o in items)
|
|
482
493
|
|
|
494
|
+
# %% ../nbs/13_nbio.ipynb #d32fc4bc
|
|
495
|
+
def item2xml(
|
|
496
|
+
typ, # Tag name: the cell or message type, e.g. 'code', 'markdown', 'raw', 'prompt'
|
|
497
|
+
content='', # The item's source text
|
|
498
|
+
out='', # Rendered output text
|
|
499
|
+
id=None, # Optional id attribute
|
|
500
|
+
meta=None, # Cell/message metadata: directives in its `nbdev` dict render as attrs, bare ones as bare attrs
|
|
501
|
+
**attrs, # Extra attributes; falsy values are dropped, names stay verbatim (no hyphenation)
|
|
502
|
+
):
|
|
503
|
+
"A notebook cell or dialog message as concise XML: content, then an `<out>` section when `out` is non-empty"
|
|
504
|
+
kw = {k:v for k,v in {'id':id, **_dir_attrs(meta), **attrs}.items() if v}
|
|
505
|
+
return ft(typ, content, ft('out', out), attrmap=str, **kw) if out else ft(typ, content, attrmap=str, **kw)
|
|
506
|
+
|
|
483
507
|
# %% ../nbs/13_nbio.ipynb #ca73be1c
|
|
484
508
|
def cell2xml(cell, ids=True, incl_out=True):
|
|
485
509
|
"Convert NbCell to concise XML format"
|
|
486
510
|
outputs = getattr(cell, 'outputs', None) if incl_out else None
|
|
487
|
-
return item2xml(cell.cell_type, cell.source, render_text(outputs) if outputs else '', id=cell.id if ids else None
|
|
511
|
+
return item2xml(cell.cell_type, cell.source, render_text(outputs) if outputs else '', id=cell.id if ids else None,
|
|
512
|
+
meta=cell.get('metadata'))
|
|
488
513
|
|
|
489
514
|
def cells2xml(cells, wrap=NB, ids=True, incl_out=True, **kw):
|
|
490
515
|
"Convert notebook cells to XML format"
|
|
@@ -523,6 +548,19 @@ class Notebook:
|
|
|
523
548
|
@property
|
|
524
549
|
def concise (self): return cells2xml(self.cells, path=self.path.name, incl_out=False)
|
|
525
550
|
|
|
551
|
+
# %% ../nbs/13_nbio.ipynb #af50ea0b
|
|
552
|
+
def _cell_method(f):
|
|
553
|
+
def method(self, *args, **kw):
|
|
554
|
+
old = self.source
|
|
555
|
+
self.set_source(f(old, *args, **kw))
|
|
556
|
+
return PrettyString(str_diff(old, self.source) or 'none: No changes.')
|
|
557
|
+
res = splice_sig(method, f, 'text')
|
|
558
|
+
res.__name__ = res.__qualname__ = f.__name__
|
|
559
|
+
res.__doc__ = (f.__doc__ or '') + "\nIn-memory edit of this cell's `source`, returning a diff; nothing is written to disk."
|
|
560
|
+
return res
|
|
561
|
+
|
|
562
|
+
for _f in (insert_line, str_replace, strs_replace, replace_lines, del_lines, ast_replace): setattr(NbCell, _f.__name__, _cell_method(_f))
|
|
563
|
+
|
|
526
564
|
# %% ../nbs/13_nbio.ipynb #161b6662
|
|
527
565
|
@patch
|
|
528
566
|
def add(self:Notebook, source, cell_type='code', idx=None, after=None, before=None, **kwargs):
|
|
@@ -545,7 +583,7 @@ def md(self:Notebook, source, idx=None, after=None, before=None, **kwargs):
|
|
|
545
583
|
|
|
546
584
|
# %% ../nbs/13_nbio.ipynb #5a09a2fa
|
|
547
585
|
@patch
|
|
548
|
-
def
|
|
586
|
+
def find_cells(self:Notebook, pat, cell_type=None):
|
|
549
587
|
"Find cells with source matching regex `pat`"
|
|
550
588
|
return [c for c in self.cells if re.search(pat, c.source) and (not cell_type or c.cell_type==cell_type)]
|
|
551
589
|
|
|
@@ -563,8 +601,50 @@ def move(self:Notebook, src_ids, after=None, before=None):
|
|
|
563
601
|
|
|
564
602
|
# %% ../nbs/13_nbio.ipynb #e064c39e
|
|
565
603
|
@patch
|
|
566
|
-
def
|
|
604
|
+
def view_cell(self:Notebook, id, nums=True):
|
|
567
605
|
"Show cell source with optional line numbers"
|
|
568
606
|
lines = self[id].source.splitlines()
|
|
569
607
|
if nums: lines = [f'{i+1:6d} │ {l}' for i,l in enumerate(lines)]
|
|
570
608
|
return '\n'.join(lines)
|
|
609
|
+
|
|
610
|
+
# %% ../nbs/13_nbio.ipynb #804670bc
|
|
611
|
+
class CellRow:
|
|
612
|
+
"Snapshot of one cell, shown as `id:t[directives]:source` (t: c=code m=markdown r=raw)"
|
|
613
|
+
def __init__(self, c, maxlen=120):
|
|
614
|
+
self.id,self.cell_type,self.source,self.maxlen = c.id,c.cell_type,c.source,maxlen
|
|
615
|
+
self.meta = copy.deepcopy(dict(c.get('metadata',{})))
|
|
616
|
+
def __repr__(self):
|
|
617
|
+
d = self.meta.get('nbdev',{})
|
|
618
|
+
tag = ' '.join(k if v in ('','true') else f'{k}={v}' for k,v in d.items())
|
|
619
|
+
src = self.source.replace('\n', '\\n')
|
|
620
|
+
if len(src)>self.maxlen: src = src[:self.maxlen]+'…'
|
|
621
|
+
return f"{self.id}:{self.cell_type[0]}{'['+tag+']' if tag else ''}:{src}"
|
|
622
|
+
|
|
623
|
+
class CellRows(list):
|
|
624
|
+
def __repr__(self): return '\n'.join(repr(o) for o in self)
|
|
625
|
+
|
|
626
|
+
# %% ../nbs/13_nbio.ipynb #62d7f8d1
|
|
627
|
+
@patch
|
|
628
|
+
def summary(self:Notebook, maxlen=120):
|
|
629
|
+
"One `CellRow` line per cell"
|
|
630
|
+
return CellRows(CellRow(c, maxlen) for c in self.cells)
|
|
631
|
+
|
|
632
|
+
def summary_nb(
|
|
633
|
+
path, # Notebook file to read
|
|
634
|
+
maxlen:int=120, # Maximum source characters per line
|
|
635
|
+
):
|
|
636
|
+
"One snapshot line per cell of the notebook at `path`"
|
|
637
|
+
return Notebook.open(path).summary(maxlen)
|
|
638
|
+
|
|
639
|
+
def find_cells(
|
|
640
|
+
path, # Notebook file to search
|
|
641
|
+
pat:str='', # Regex over cell source
|
|
642
|
+
cell_type:str=None, # Optional limit by type ('code', 'markdown', or 'raw')
|
|
643
|
+
):
|
|
644
|
+
"Snapshot `CellRows` for matching cells in the notebook at `path`"
|
|
645
|
+
return CellRows(CellRow(c) for c in Notebook.open(path).find_cells(pat, cell_type))
|
|
646
|
+
|
|
647
|
+
@patch
|
|
648
|
+
def to_dict(self:Notebook):
|
|
649
|
+
"The plain dict form of the held notebook (`nb2dict`): the representation layer"
|
|
650
|
+
return nb2dict(self.nb)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""Text and file editing primitives shared by the fastai editing tools
|
|
2
2
|
|
|
3
|
-
The editors here are string-level: each takes `text` plus edit parameters and returns the new text, raising `ValueError` when an edit can't apply. The file tools below wrap them with path I/O and diff reporting; message-level wrappers live in llmsurgery. (This module previously held experimental LLM path-editing and command tools, superseded by safecmd, rgapi, and the tools here.)
|
|
3
|
+
The editors here are string-level: each takes `text` plus edit parameters and returns the new text, raising `ValueError` when an edit can't apply. The file tools below wrap them with path I/O and diff reporting; message-level wrappers live in llmsurgery. (This module previously held experimental LLM path-editing and command tools, superseded by safecmd, rgapi, and the tools here.) Naming, parameter, and workflow conventions for the whole editing toolkit, this module included, are documented in `fastcore.editskill`, which also re-exports these tools alongside `fastcore.nbio`'s.
|
|
4
4
|
|
|
5
5
|
`line_hash`, `lnhash`, and `lnhash_at` implement the [exhash](https://answerdotai.github.io/exhash) line-address format in pure Python: `lineno|hash|`, where the hash is 4 hex chars of crc32. They let any tool create lnhash-addressed views of text it holds, without depending on the exhash package.
|
|
6
6
|
|
|
7
|
-
File tools wrap the primitives with path I/O, returning unified diffs of what changed ("none: No changes." / "error: ..." otherwise). The path is the first argument
|
|
7
|
+
File tools wrap the primitives with path I/O, returning unified diffs of what changed ("none: No changes." / "error: ..." otherwise). The path is the first argument, e.g:
|
|
8
8
|
|
|
9
9
|
view_file('~/a/b.py', 3)
|
|
10
10
|
create_file('~/a/b/c.py', 'content here')
|
|
@@ -12,7 +12,7 @@ File tools wrap the primitives with path I/O, returning unified diffs of what ch
|
|
|
12
12
|
file_del_lines('myfile.py', 2, 4)
|
|
13
13
|
file_replace_lines('myfile.py', new_content=src) # no line numbers: replace the entire contents
|
|
14
14
|
|
|
15
|
-
`file_str_replace`, `file_strs_replace`, and `file_del_lines` support `re_filter` and `invert_filter` for targeting only lines matching (or not matching) a regex, like ex's `g//` and `g!//`, combined with `start_line`/`end_line` to restrict to a region. `ast_replace(text, repls)` and `file_ast_replace(path, repls)` apply ast-grep `(pattern, replacement)` rules with `$VAR` metavariables (requires the optional `remold` package).
|
|
15
|
+
`file_str_replace`, `file_strs_replace`, and `file_del_lines` support `re_filter` and `invert_filter` for targeting only lines matching (or not matching) a regex, like ex's `g//` and `g!//`, combined with `start_line`/`end_line` to restrict to a region. `ast_replace(text, repls)` and `file_ast_replace(path, repls)` apply ast-grep `(pattern, replacement)` rules with `$VAR` metavariables (requires the optional `remold` package).
|
|
16
16
|
|
|
17
17
|
Docs: https://fastcore.fast.ai/tools.html.md"""
|
|
18
18
|
|
|
@@ -204,6 +204,7 @@ returns: diff of changes, or "none: No changes.", or "error: ..."
|
|
|
204
204
|
|
|
205
205
|
# %% ../nbs/12_tools.ipynb #a6f7361d
|
|
206
206
|
def file_edit(f, name=None):
|
|
207
|
+
"Wrap text editor `f` as a file editing function: `path` addressing, diff-or-error return"
|
|
207
208
|
def wrapper(path:str, *args, **kw):
|
|
208
209
|
path = Path(path).expanduser()
|
|
209
210
|
text = path.read_text()
|
|
@@ -5,14 +5,14 @@ Docs: https://fastcore.fast.ai/xml.html.md"""
|
|
|
5
5
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/09_xml.ipynb.
|
|
6
6
|
|
|
7
7
|
# %% auto #0
|
|
8
|
-
__all__ = ['voids', 'attrmap', 'valmap', 'FT', 'ft', 'Html', 'Safe', 'to_xml', 'dict2xml', '
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
8
|
+
__all__ = ['voids', 'attrmap', 'valmap', 'FT', 'ft', 'Html', 'Safe', 'to_xml', 'dict2xml', 'highlight', 'showtags', 'mk_getattr',
|
|
9
|
+
'Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code', 'Div', 'Span', 'P', 'H1', 'H2', 'H3', 'H4',
|
|
10
|
+
'H5', 'H6', 'Strong', 'Em', 'B', 'I', 'U', 'S', 'Strike', 'Sub', 'Sup', 'Hr', 'Br', 'Img', 'A', 'Nav', 'Ul',
|
|
11
|
+
'Ol', 'Li', 'Dl', 'Dt', 'Dd', 'Table', 'Thead', 'Tbody', 'Tfoot', 'Tr', 'Th', 'Td', 'Caption', 'Col',
|
|
12
|
+
'Colgroup', 'Form', 'Input', 'Textarea', 'Button', 'Select', 'Option', 'Label', 'Fieldset', 'Legend',
|
|
13
|
+
'Details', 'Summary', 'Main', 'Header', 'Footer', 'Section', 'Article', 'Aside', 'Figure', 'Figcaption',
|
|
14
|
+
'Mark', 'Small', 'Iframe', 'Object', 'Embed', 'Param', 'Video', 'Audio', 'Source', 'Canvas', 'Svg', 'Math',
|
|
15
|
+
'Script', 'Noscript', 'Template', 'Slot']
|
|
16
16
|
|
|
17
17
|
# %% ../nbs/09_xml.ipynb #f6c9a7f5
|
|
18
18
|
from .utils import *
|
|
@@ -241,18 +241,6 @@ def dict2xml(d, do_escape=False, unwrap=None):
|
|
|
241
241
|
if first(d) in listify(unwrap): return first(d.values())
|
|
242
242
|
return to_xml(*[ft(k, str(v)) for k,v in d.items()], do_escape=do_escape)
|
|
243
243
|
|
|
244
|
-
# %% ../nbs/09_xml.ipynb #7b26e0f9
|
|
245
|
-
def item2xml(
|
|
246
|
-
typ, # Tag name: the cell or message type, e.g. 'code', 'markdown', 'raw', 'prompt'
|
|
247
|
-
content='', # The item's source text
|
|
248
|
-
out='', # Rendered output text
|
|
249
|
-
id=None, # Optional id attribute
|
|
250
|
-
**attrs, # Extra attributes; falsy values are dropped
|
|
251
|
-
):
|
|
252
|
-
"A notebook cell or dialog message as concise XML: content, then an `<out>` section when `out` is non-empty"
|
|
253
|
-
kw = {k:v for k,v in dict(id=id, **attrs).items() if v}
|
|
254
|
-
return ft(typ, content, ft('out', out), **kw) if out else ft(typ, content, **kw)
|
|
255
|
-
|
|
256
244
|
# %% ../nbs/09_xml.ipynb #5f0e91e0
|
|
257
245
|
def highlight(s, lang='html'):
|
|
258
246
|
"Markdown to syntax-highlight `s` in language `lang`"
|
|
@@ -22,7 +22,7 @@ Documentation = "https://fastcore.fast.ai/"
|
|
|
22
22
|
fastcore = "fastcore._modidx:d"
|
|
23
23
|
|
|
24
24
|
[project.entry-points.pyskills]
|
|
25
|
-
"fastcore.
|
|
25
|
+
"fastcore.editskill" = "fastcore.editskill"
|
|
26
26
|
|
|
27
27
|
[project.optional-dependencies]
|
|
28
28
|
dev = ['numpy', 'nbdev>=0.2.39', 'matplotlib', 'pillow', 'torch', 'pandas', 'nbclassic', 'pysym2md>=0.0.6', 'llms-txt', 'plum-dispatch', 'toolslm']
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.1.3"
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|