22
33# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/12_tools.ipynb.
44
5- # %% auto # 0
6- __all__ = ['run_cmd ' , 'rg ' , 'sed ' , 'valid_path ' , 'view ' , 'create ' , 'insert ' , 'str_replace ' , 'strs_replace ' , 'replace_lines ' ,
7- 'move_lines' , 'get_callable' ]
5+ # %% auto 0
6+ __all__ = ['explain_exc ' , 'ensure ' , 'valid_path ' , 'run_cmd ' , 'rg ' , 'sed ' , 'view ' , 'create ' , 'insert ' , 'str_replace ' ,
7+ 'strs_replace' , 'replace_lines' , ' move_lines' , 'get_callable' ]
88
99# %% ../nbs/12_tools.ipynb #578246d2
1010from .imports import *
11+ from .xtras import truncstr
1112from shlex import split
1213from subprocess import run , DEVNULL
1314
14- # %% ../nbs/12_tools.ipynb #1182336d
15+ # %% ../nbs/12_tools.ipynb
16+ def explain_exc (task = '' ):
17+ """Convert an current exception to an LLM friendly error message."""
18+ try : raise sys .exc_info ()[1 ]
19+ except (AssertionError , ZeroDivisionError , ValueError , FileNotFoundError ) as e :
20+ return f"Error: { e } "
21+ except Exception as e : return f"Error { task } : { repr (e )} "
22+
23+
24+ # %% ../nbs/12_tools.ipynb
25+ def ensure (b : bool , msg :str = "" ):
26+ "Works like assert b, msg but raise ValueError and is not disabled when run with python -O"
27+ if not b : raise ValueError (msg )
28+
29+
30+ # %% ../nbs/12_tools.ipynb
31+ def valid_path (path :str , must_exist :bool = True ) -> Path :
32+ 'Return expanded/resolved Path, raising FileNotFoundError if must_exist and missing'
33+ p = Path (path ).expanduser ().resolve ()
34+ if must_exist and not p .exists (): raise FileNotFoundError (f'File not found: { p } ' )
35+ return p
36+
37+ # %% ../nbs/12_tools.ipynb
1538def run_cmd (
1639 cmd :str , # The command name to run
1740 argstr :str = '' , # All args to the command, will be split with shlex
1841 disallow_re :str = None , # optional regex which, if matched on argstr, will disallow the command
1942 allow_re :str = None # optional regex which, if not matched on argstr, will disallow the command
2043):
2144 "Run `cmd` passing split `argstr`, optionally checking for allowed argstr"
22- if disallow_re and re .search (disallow_re , argstr ): return 'Error: args disallowed'
23- if allow_re and re .search ( allow_re , argstr ): return 'Error: args not allowed'
24- try : outp = run ([cmd ] + split (argstr ), text = True , stdin = DEVNULL , capture_output = True )
25- except Exception as e : return f'Error running cmd: { str (e )} '
45+ try :
46+ ensure (not (disallow_re and re .search (disallow_re , argstr )), 'args disallowed' )
47+ ensure (not (allow_re and re .search ( allow_re , argstr )), 'args not allowed' )
48+ outp = run ([cmd ] + split (argstr ), text = True , stdin = DEVNULL , capture_output = True )
49+ except : return explain_exc (f'running cmd' )
2650 res = outp .stdout
2751 if res and outp .stderr : res += '\n '
2852 return res + outp .stderr
@@ -45,15 +69,8 @@ def sed(
4569 "Run the `sed` command with the args in `argstr` (e.g for reading a section of a file)"
4670 return run_cmd ('sed' , argstr , allow_re = allow_re , disallow_re = disallow_re )
4771
48- # %% ../nbs/12_tools.ipynb #5dd1aaf3
49- def valid_path (path :str , must_exist :bool = True ) -> Path :
50- 'Return expanded/resolved Path, raising FileNotFoundError if must_exist and missing'
51- p = Path (path ).expanduser ().resolve ()
52- if must_exist and not p .exists (): raise FileNotFoundError (f'File not found: { p } ' )
53- return p
54-
55- # %% ../nbs/12_tools.ipynb #b718d65b
56- def _fmt_path (f , p , skip_folders = ()):
72+ # %% ../nbs/12_tools.ipynb
73+ def _fmt_path (f , p ):
5774 'Format path with emoji for dirs/symlinks or size for files'
5875 parts = f .relative_to (p ).parts
5976 if any (part .startswith ('.' ) for part in parts ): return None
@@ -80,13 +97,13 @@ def view(
8097 s , e = 1 , len (lines )
8198 if view_range :
8299 s ,e = view_range
83- if not (1 <= s <= len (lines )): return f'Error: Invalid start line { s } '
84- if e != - 1 and not ( s <= e <= len (lines )): return f'Error: Invalid end line { e } '
100+ ensure (1 <= s <= len (lines ), f' Invalid start line { s } ')
101+ ensure ( e == - 1 or s <= e <= len (lines ), f' Invalid end line { e } ')
85102 lines = lines [s - 1 :None if e == - 1 else e ]
86103 if nums : lines = [f'{ i + s :6d} │ { l } ' for i , l in enumerate (lines )]
87104 content = '\n ' .join (lines )
88105 return f'{ header } \n { content } ' if header else content
89- except Exception as e : return f'Error viewing: { str ( e ) } '
106+ except : return explain_exc ( ' viewing' )
90107
91108# %% ../nbs/12_tools.ipynb #36f58e38
92109def create (
@@ -102,7 +119,7 @@ def create(
102119 p .parent .mkdir (parents = True , exist_ok = True )
103120 p .write_text (file_text )
104121 return f'Created file { p } .'
105- except Exception as e : return f'Error creating file: { str ( e ) } '
122+ except : return explain_exc ( ' creating file' )
106123
107124# %% ../nbs/12_tools.ipynb #434147ef
108125def insert (
@@ -114,12 +131,12 @@ def insert(
114131 try :
115132 p = valid_path (path )
116133 content = p .read_text ().splitlines ()
117- if not ( 0 <= insert_line <= len (content )): return f'Error: Invalid line number { insert_line } '
134+ ensure ( 0 <= insert_line <= len (content ), f' Invalid line number { insert_line } ')
118135 content .insert (insert_line , new_str )
119136 new_content = '\n ' .join (content )
120137 p .write_text (new_content )
121138 return f'Inserted text at line { insert_line } in { p } '
122- except Exception as e : return f'Error inserting text: { str ( e ) } '
139+ except : return explain_exc ( ' inserting text' )
123140
124141# %% ../nbs/12_tools.ipynb #9272ff7d
125142def str_replace (
@@ -132,12 +149,12 @@ def str_replace(
132149 p = valid_path (path )
133150 content = p .read_text ()
134151 count = content .count (old_str )
135- if count == 0 : return 'Error: Text not found in file'
136- if count > 1 : return f'Error: Multiple matches found ({ count } )'
152+ if count == 0 : return f 'Error: Text " { truncstr ( old_str , 10 ) } " not found in file'
153+ if count > 1 : return f'Error: Multiple matches found ({ count } ) of " { truncstr ( old_str , 10 ) } " '
137154 new_content = content .replace (old_str , new_str , 1 )
138155 p .write_text (new_content )
139156 return f'Replaced text in { p } '
140- except Exception as e : return f'Error replacing text: { str ( e ) } '
157+ except : return explain_exc ( ' replacing text' )
141158
142159# %% ../nbs/12_tools.ipynb #eb907119
143160def strs_replace (
@@ -164,7 +181,7 @@ def replace_lines(
164181 content [start_line - 1 :end_line ] = [new_content ]
165182 p .write_text ('' .join (content ))
166183 return f"Replaced lines { start_line } to { end_line } ."
167- except Exception as e : return f'Error replacing lines: { str ( e ) } '
184+ except : return explain_exc ( ' replacing lines' )
168185
169186# %% ../nbs/12_tools.ipynb #b136abf8
170187def move_lines (
@@ -177,9 +194,9 @@ def move_lines(
177194 try :
178195 p = valid_path (path )
179196 lines = p .read_text ().splitlines ()
180- assert 1 <= start_line <= end_line <= len (lines ), f"Invalid range { start_line } -{ end_line } "
181- assert 1 <= dest_line <= len (lines ) + 1 , f"Invalid destination { dest_line } "
182- assert not (start_line <= dest_line <= end_line + 1 ), "Destination within source range"
197+ ensure ( 1 <= start_line <= end_line <= len (lines ), f"Invalid range { start_line } -{ end_line } " )
198+ ensure ( 1 <= dest_line <= len (lines ) + 1 , f"Invalid destination { dest_line } " )
199+ ensure ( not (start_line <= dest_line <= end_line + 1 ), "Destination within source range" )
183200
184201 chunk = lines [start_line - 1 :end_line ]
185202 del lines [start_line - 1 :end_line ]
@@ -188,7 +205,7 @@ def move_lines(
188205 lines [dest_line - 1 :dest_line - 1 ] = chunk
189206 p .write_text ('\n ' .join (lines ) + '\n ' )
190207 return f"Moved lines { start_line } -{ end_line } to line { dest_line } "
191- except Exception as e : return f'Error: { str ( e ) } '
208+ except : return explain_exc ()
192209
193210# %% ../nbs/12_tools.ipynb #fc53b116
194211def get_callable ():
0 commit comments