99from fastcore .script import call_parse
1010from fastcore .ansi import ansi2html
1111
12- import mistletoe ,multiprocessing ,types ,traceback
12+ import mistletoe ,multiprocessing ,types ,traceback , signal
1313try :
1414 if sys .platform == 'darwin' : multiprocessing .set_start_method ("fork" )
1515except RuntimeError : pass # if re-running cell
3737__all__ = ['CaptureShell' , 'format_exc' , 'render_outputs' , 'find_output' , 'out_exec' , 'out_stream' , 'out_error' , 'exec_nb' ,
3838 'SmartCompleter' ]
3939
40- # %% ../nbs/02_shell.ipynb 5
40+ # %% ../nbs/02_shell.ipynb
4141class _CustDisplayHook (DisplayHook ):
4242 def write_output_prompt (self ): pass
4343 def write_format_data (self , data , md_dict ): pass
@@ -49,12 +49,14 @@ def __repr__(self: ExecutionInfo): return f'cell: {self.raw_cell}; id: {self.cel
4949@patch
5050def __repr__ (self : ExecutionResult ): return f'result: { self .result } ; err: { self .error_in_exec } ; info: <{ self .info } >'
5151
52- # %% ../nbs/02_shell.ipynb 6
52+ # %% ../nbs/02_shell.ipynb
5353class CaptureShell (InteractiveShell ):
5454 displayhook_class = _CustDisplayHook
5555
56- def __init__ (self , path :str | Path = None , mpl_format = 'retina' ):
56+ def __init__ (self , path :str | Path = None , mpl_format = 'retina' , history = False , timeout = None ):
5757 super ().__init__ ()
58+ self .history_manager .enabled = history
59+ self .timeout = timeout
5860 self .result ,self .exc = None ,None
5961 if path : self .set_path (path )
6062 self .display_formatter .active = True
@@ -65,11 +67,17 @@ def __init__(self, path:str|Path=None, mpl_format='retina'):
6567 self .run_cell (f"set_matplotlib_formats('{ mpl_format } ')" )
6668
6769 def run_cell (self , raw_cell , store_history = False , silent = False , shell_futures = True , cell_id = None ,
68- stdout = True , stderr = True , display = True ):
70+ stdout = True , stderr = True , display = True , timeout = None ):
71+ if not timeout : timeout = self .timeout
6972 # TODO what if there's a comment?
7073 semic = raw_cell .rstrip ().endswith (';' )
74+ if timeout :
75+ def handler (* args ): raise TimeoutError ()
76+ signal .signal (signal .SIGALRM , handler )
77+ signal .alarm (timeout )
7178 with capture_output (display = display , stdout = stdout , stderr = stderr ) as c :
7279 result = super ().run_cell (raw_cell , store_history , silent , shell_futures = shell_futures , cell_id = cell_id )
80+ if timeout : signal .alarm (0 )
7381 return AttrDict (result = result , stdout = '' if semic else c .stdout , stderr = c .stderr ,
7482 display_objects = c .outputs , exception = result .error_in_exec , quiet = semic )
7583
@@ -81,12 +89,12 @@ def set_path(self, path):
8189
8290 def enable_gui (self , gui = None ): pass
8391
84- # %% ../nbs/02_shell.ipynb 23
92+ # %% ../nbs/02_shell.ipynb
8593def format_exc (e ):
8694 "Format exception `e` as a string"
8795 return '' .join (traceback .format_exception (type (e ), e , e .__traceback__ ))
8896
89- # %% ../nbs/02_shell.ipynb 24
97+ # %% ../nbs/02_shell.ipynb
9098def _out_stream (text , name ): return dict (name = name , output_type = 'stream' , text = text .splitlines (True ))
9199def _out_exc (e ):
92100 ename = type (e ).__name__
@@ -116,7 +124,7 @@ def _out_nb(o, fmt):
116124 res .append (_mk_out (* fmt .format (r ), 'execute_result' ))
117125 return res
118126
119- # %% ../nbs/02_shell.ipynb 25
127+ # %% ../nbs/02_shell.ipynb
120128@patch
121129def run (self :CaptureShell ,
122130 code :str , # Python/IPython code to run
@@ -128,7 +136,7 @@ def run(self:CaptureShell,
128136 self .exc = res .exception
129137 return _out_nb (res , self .display_formatter )
130138
131- # %% ../nbs/02_shell.ipynb 31
139+ # %% ../nbs/02_shell.ipynb
132140def render_outputs (outputs , ansi_renderer = strip_ansi ):
133141 def render_output (out ):
134142 otype = out ['output_type' ]
@@ -150,7 +158,7 @@ def render_output(out):
150158
151159 return '\n ' .join (map (render_output , outputs ))
152160
153- # %% ../nbs/02_shell.ipynb 44
161+ # %% ../nbs/02_shell.ipynb
154162@patch
155163def cell (self :CaptureShell , cell , stdout = True , stderr = True ):
156164 "Run `cell`, skipping if not code, and store outputs back in cell"
@@ -162,32 +170,32 @@ def cell(self:CaptureShell, cell, stdout=True, stderr=True):
162170 for o in outs :
163171 if 'execution_count' in o : cell ['execution_count' ] = o ['execution_count' ]
164172
165- # %% ../nbs/02_shell.ipynb 47
173+ # %% ../nbs/02_shell.ipynb
166174def find_output (outp , # Output from `run`
167175 ot = 'execute_result' # Output_type to find
168176 ):
169177 "Find first output of type `ot` in `CaptureShell.run` output"
170178 return first (o for o in outp if o ['output_type' ]== ot )
171179
172- # %% ../nbs/02_shell.ipynb 50
180+ # %% ../nbs/02_shell.ipynb
173181def out_exec (outp ):
174182 "Get data from execution result in `outp`."
175183 out = find_output (outp )
176184 if out : return '\n ' .join (first (out ['data' ].values ()))
177185
178- # %% ../nbs/02_shell.ipynb 52
186+ # %% ../nbs/02_shell.ipynb
179187def out_stream (outp ):
180188 "Get text from stream in `outp`."
181189 out = find_output (outp , 'stream' )
182190 if out : return ('\n ' .join (out ['text' ])).strip ()
183191
184- # %% ../nbs/02_shell.ipynb 54
192+ # %% ../nbs/02_shell.ipynb
185193def out_error (outp ):
186194 "Get traceback from error in `outp`."
187195 out = find_output (outp , 'error' )
188196 if out : return '\n ' .join (out ['traceback' ])
189197
190- # %% ../nbs/02_shell.ipynb 56
198+ # %% ../nbs/02_shell.ipynb
191199def _false (o ): return False
192200
193201@patch
@@ -207,7 +215,7 @@ def run_all(self:CaptureShell,
207215 postproc (cell )
208216 if self .exc and exc_stop : raise self .exc from None
209217
210- # %% ../nbs/02_shell.ipynb 70
218+ # %% ../nbs/02_shell.ipynb
211219@patch
212220def execute (self :CaptureShell ,
213221 src :str | Path , # Notebook path to read from
@@ -228,7 +236,7 @@ def execute(self:CaptureShell,
228236 inject_code = inject_code , inject_idx = inject_idx )
229237 if dest : write_nb (nb , dest )
230238
231- # %% ../nbs/02_shell.ipynb 74
239+ # %% ../nbs/02_shell.ipynb
232240@patch
233241def prettytb (self :CaptureShell ,
234242 fname :str | Path = None ): # filename to print alongside the traceback
@@ -240,7 +248,7 @@ def prettytb(self:CaptureShell,
240248 fname_str = f' in { fname } ' if fname else ''
241249 return f"{ type (self .exc ).__name__ } { fname_str } :\n { _fence } \n { cell_str } \n "
242250
243- # %% ../nbs/02_shell.ipynb 93
251+ # %% ../nbs/02_shell.ipynb
244252@call_parse
245253def exec_nb (
246254 src :str , # Notebook path to read from
@@ -254,7 +262,7 @@ def exec_nb(
254262 CaptureShell ().execute (src , dest , exc_stop = exc_stop , inject_code = inject_code ,
255263 inject_path = inject_path , inject_idx = inject_idx )
256264
257- # %% ../nbs/02_shell.ipynb 96
265+ # %% ../nbs/02_shell.ipynb
258266class SmartCompleter (IPCompleter ):
259267 def __init__ (self , shell , namespace = None , jedi = False ):
260268 if namespace is None : namespace = shell .user_ns
@@ -274,7 +282,7 @@ def __call__(self, c):
274282 for o in self .completions (c , len (c ))
275283 if o .type not in ('magic' , 'path' )]
276284
277- # %% ../nbs/02_shell.ipynb 98
285+ # %% ../nbs/02_shell.ipynb
278286@patch
279287def complete (self :CaptureShell , c ):
280288 if not hasattr (self , '_completer' ): self ._completer = SmartCompleter (self )
0 commit comments