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.
Files changed (43) hide show
  1. {fastcore-2.1.3/fastcore.egg-info → fastcore-2.1.4}/PKG-INFO +1 -1
  2. fastcore-2.1.4/fastcore/__init__.py +1 -0
  3. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/_modidx.py +16 -4
  4. fastcore-2.1.4/fastcore/editskill.py +63 -0
  5. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/nbio.py +96 -16
  6. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/tools.py +4 -3
  7. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/xml.py +8 -20
  8. {fastcore-2.1.3 → fastcore-2.1.4/fastcore.egg-info}/PKG-INFO +1 -1
  9. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore.egg-info/SOURCES.txt +1 -0
  10. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore.egg-info/entry_points.txt +1 -1
  11. {fastcore-2.1.3 → fastcore-2.1.4}/pyproject.toml +1 -1
  12. fastcore-2.1.3/fastcore/__init__.py +0 -1
  13. {fastcore-2.1.3 → fastcore-2.1.4}/CONTRIBUTING.md +0 -0
  14. {fastcore-2.1.3 → fastcore-2.1.4}/LICENSE +0 -0
  15. {fastcore-2.1.3 → fastcore-2.1.4}/MANIFEST.in +0 -0
  16. {fastcore-2.1.3 → fastcore-2.1.4}/README.md +0 -0
  17. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/aio.py +0 -0
  18. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/all.py +0 -0
  19. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/ansi.py +0 -0
  20. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/basics.py +0 -0
  21. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/dispatch.py +0 -0
  22. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/docments.py +0 -0
  23. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/docscrape.py +0 -0
  24. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/foundation.py +0 -0
  25. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/imghdr.py +0 -0
  26. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/imports.py +0 -0
  27. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/meta.py +0 -0
  28. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/nb_imports.py +0 -0
  29. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/net.py +0 -0
  30. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/parallel.py +0 -0
  31. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/py2pyi.py +0 -0
  32. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/script.py +0 -0
  33. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/shutil.py +0 -0
  34. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/style.py +0 -0
  35. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/test.py +0 -0
  36. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/transform.py +0 -0
  37. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/utils.py +0 -0
  38. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/xdg.py +0 -0
  39. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore/xtras.py +0 -0
  40. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore.egg-info/dependency_links.txt +0 -0
  41. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore.egg-info/requires.txt +0 -0
  42. {fastcore-2.1.3 → fastcore-2.1.4}/fastcore.egg-info/top_level.txt +0 -0
  43. {fastcore-2.1.3 → fastcore-2.1.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastcore
3
- Version: 2.1.3
3
+ Version: 2.1.4
4
4
  Summary: Python supercharged for fastai development
5
5
  Author-email: Jeremy Howard and Sylvain Gugger <infos@fast.ai>
6
6
  License: Apache-2.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.NbCell': ('nbio.html#nbcell', 'fastcore/nbio.py'),
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.find': ('nbio.html#notebook.find', 'fastcore/nbio.py'),
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.view': ('nbio.html#notebook.view', 'fastcore/nbio.py'),
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', 'nb2dict', 'nb2str',
12
- 'write_nb', 'cell_edit', 'view_cell', 'validate_cell', 'validate_nb', 'repair_cell', 'repair_nb',
13
- 'preferred_out', 'mk_stream', 'mk_result', 'mk_display', 'mk_error', 'concat_streams', 'preferred_msg_out',
14
- 'render_output', 'render_outputs', 'render_text', 'cell2xml', 'cells2xml', 'Notebook']
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(cell):
158
- "Directives from the `nbdev` dict in cell metadata, as `{name: value}`; values must be str (`'true'` for a bare directive)"
159
- d = cell.get('metadata',{}).get('nbdev',{})
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
- if isinstance(nb, (AttrDict,list)): nb = nb2dict(nb)
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,item2xml
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 find(self:Notebook, pat, cell_type=None):
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 view(self:Notebook, id, nums=True):
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 (with `view_file`/`create_file` named verb-first, since the file is the verb's object rather than the location of an edit), e.g:
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). Where the `exhash` package is available, prefer it for editing: its hash-verified addressing fails loudly on stale context instead of editing nearby text.
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', 'item2xml', 'highlight', 'showtags',
9
- 'mk_getattr', 'Head', 'Title', 'Meta', 'Link', 'Style', 'Body', 'Pre', 'Code', 'Div', 'Span', 'P', 'H1',
10
- 'H2', 'H3', 'H4', 'H5', 'H6', 'Strong', 'Em', 'B', 'I', 'U', 'S', 'Strike', 'Sub', 'Sup', 'Hr', 'Br', 'Img',
11
- 'A', 'Nav', 'Ul', 'Ol', 'Li', 'Dl', 'Dt', 'Dd', 'Table', 'Thead', 'Tbody', 'Tfoot', 'Tr', 'Th', 'Td',
12
- 'Caption', 'Col', 'Colgroup', 'Form', 'Input', 'Textarea', 'Button', 'Select', 'Option', 'Label', 'Fieldset',
13
- 'Legend', 'Details', 'Summary', 'Main', 'Header', 'Footer', 'Section', 'Article', 'Aside', 'Figure',
14
- 'Figcaption', 'Mark', 'Small', 'Iframe', 'Object', 'Embed', 'Param', 'Video', 'Audio', 'Source', 'Canvas',
15
- 'Svg', 'Math', 'Script', 'Noscript', 'Template', 'Slot']
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`"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastcore
3
- Version: 2.1.3
3
+ Version: 2.1.4
4
4
  Summary: Python supercharged for fastai development
5
5
  Author-email: Jeremy Howard and Sylvain Gugger <infos@fast.ai>
6
6
  License: Apache-2.0
@@ -12,6 +12,7 @@ fastcore/basics.py
12
12
  fastcore/dispatch.py
13
13
  fastcore/docments.py
14
14
  fastcore/docscrape.py
15
+ fastcore/editskill.py
15
16
  fastcore/foundation.py
16
17
  fastcore/imghdr.py
17
18
  fastcore/imports.py
@@ -6,4 +6,4 @@ replace_wildcards = fastcore.py2pyi:replace_wildcards
6
6
  fastcore = fastcore._modidx:d
7
7
 
8
8
  [pyskills]
9
- fastcore.tools = fastcore.tools
9
+ fastcore.editskill = fastcore.editskill
@@ -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.tools" = "fastcore.tools"
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