class ScriptMagics(Magics, Configurable): """Magics for talking to scripts This defines a base `%%script` cell magic for running a cell with a program in a subprocess, and registers a few top-level magics that call %%script with common interpreters. """ script_magics = List( config=True, help="""Extra script cell magics to define This generates simple wrappers of `%%script foo` as `%%foo`. If you want to add script magics that aren't on your path, specify them in script_paths """, ) def _script_magics_default(self): """default to a common list of programs""" defaults = [ 'sh', 'bash', 'perl', 'ruby', 'python', 'python3', 'pypy', ] if os.name == 'nt': defaults.extend([ 'cmd', 'powershell', ]) return defaults script_paths = Dict( config=True, help= """Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby' Only necessary for items in script_magics where the default path will not find the right interpreter. """) def __init__(self, shell=None): Configurable.__init__(self, config=shell.config) self._generate_script_magics() Magics.__init__(self, shell=shell) self.job_manager = BackgroundJobManager() self.bg_processes = [] atexit.register(self.kill_bg_processes) def __del__(self): self.kill_bg_processes() def _generate_script_magics(self): cell_magics = self.magics['cell'] for name in self.script_magics: cell_magics[name] = self._make_script_magic(name) def _make_script_magic(self, name): """make a named magic, that calls %%script with a particular program""" # expand to explicit path if necessary: script = self.script_paths.get(name, name) @magic_arguments.magic_arguments() @script_args def named_script_magic(line, cell): # if line, add it as cl-flags if line: line = "%s %s" % (script, line) else: line = script return self.shebang(line, cell) # write a basic docstring: named_script_magic.__doc__ = \ """%%{name} script magic Run cells with {script} in a subprocess. This is a shortcut for `%%script {script}` """.format(**locals()) return named_script_magic @magic_arguments.magic_arguments() @script_args @cell_magic("script") def shebang(self, line, cell): """Run a cell via a shell command The `%%script` line is like the #! line of script, specifying a program (bash, perl, ruby, etc.) with which to run. The rest of the cell is run by that program. Examples -------- :: In [1]: %%script bash ...: for i in 1 2 3; do ...: echo $i ...: done 1 2 3 """ argv = arg_split(line, posix=not sys.platform.startswith('win')) args, cmd = self.shebang.parser.parse_known_args(argv) try: p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE) except OSError as e: if e.errno == errno.ENOENT: print("Couldn't find program: %r" % cmd[0]) return else: raise cell = cell.encode('utf8', 'replace') if args.bg: self.bg_processes.append(p) self._gc_bg_processes() if args.out: self.shell.user_ns[args.out] = p.stdout if args.err: self.shell.user_ns[args.err] = p.stderr self.job_manager.new(self._run_script, p, cell, daemon=True) if args.proc: self.shell.user_ns[args.proc] = p return try: out, err = p.communicate(cell) except KeyboardInterrupt: try: p.send_signal(signal.SIGINT) time.sleep(0.1) if p.poll() is not None: print("Process is interrupted.") return p.terminate() time.sleep(0.1) if p.poll() is not None: print("Process is terminated.") return p.kill() print("Process is killed.") except OSError: pass except Exception as e: print("Error while terminating subprocess (pid=%i): %s" \ % (p.pid, e)) return out = py3compat.bytes_to_str(out) err = py3compat.bytes_to_str(err) if args.out: self.shell.user_ns[args.out] = out else: sys.stdout.write(out) sys.stdout.flush() if args.err: self.shell.user_ns[args.err] = err else: sys.stderr.write(err) sys.stderr.flush() def _run_script(self, p, cell): """callback for running the script in the background""" p.stdin.write(cell) p.stdin.close() p.wait() @line_magic("killbgscripts") def killbgscripts(self, _nouse_=''): """Kill all BG processes started by %%script and its family.""" self.kill_bg_processes() print("All background processes were killed.") def kill_bg_processes(self): """Kill all BG processes which are still running.""" for p in self.bg_processes: if p.poll() is None: try: p.send_signal(signal.SIGINT) except: pass time.sleep(0.1) for p in self.bg_processes: if p.poll() is None: try: p.terminate() except: pass time.sleep(0.1) for p in self.bg_processes: if p.poll() is None: try: p.kill() except: pass self._gc_bg_processes() def _gc_bg_processes(self): self.bg_processes = [p for p in self.bg_processes if p.poll() is None]
class ScriptMagics(Magics, Configurable): """Magics for talking to scripts This defines a base `%%script` cell magic for running a cell with a program in a subprocess, and registers a few top-level magics that call %%script with common interpreters. """ script_magics = List(config=True, help="""Extra script cell magics to define This generates simple wrappers of `%%script foo` as `%%foo`. If you want to add script magics that aren't on your path, specify them in script_paths """, ) def _script_magics_default(self): """default to a common list of programs""" defaults = [ 'sh', 'bash', 'perl', 'ruby', 'python', 'python3', 'pypy', ] if os.name == 'nt': defaults.extend([ 'cmd', 'powershell', ]) return defaults script_paths = Dict(config=True, help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby' Only necessary for items in script_magics where the default path will not find the right interpreter. """ ) def __init__(self, shell=None): Configurable.__init__(self, config=shell.config) self._generate_script_magics() Magics.__init__(self, shell=shell) self.job_manager = BackgroundJobManager() self.bg_processes = [] atexit.register(self.kill_bg_processes) def __del__(self): self.kill_bg_processes() def _generate_script_magics(self): cell_magics = self.magics['cell'] for name in self.script_magics: cell_magics[name] = self._make_script_magic(name) def _make_script_magic(self, name): """make a named magic, that calls %%script with a particular program""" # expand to explicit path if necessary: script = self.script_paths.get(name, name) @magic_arguments.magic_arguments() @script_args def named_script_magic(line, cell): # if line, add it as cl-flags if line: line = "%s %s" % (script, line) else: line = script return self.shebang(line, cell) # write a basic docstring: named_script_magic.__doc__ = \ """%%{name} script magic Run cells with {script} in a subprocess. This is a shortcut for `%%script {script}` """.format(**locals()) return named_script_magic @magic_arguments.magic_arguments() @script_args @cell_magic("script") def shebang(self, line, cell): """Run a cell via a shell command The `%%script` line is like the #! line of script, specifying a program (bash, perl, ruby, etc.) with which to run. The rest of the cell is run by that program. Examples -------- :: In [1]: %%script bash ...: for i in 1 2 3; do ...: echo $i ...: done 1 2 3 """ argv = arg_split(line, posix = not sys.platform.startswith('win')) args, cmd = self.shebang.parser.parse_known_args(argv) try: p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE) except OSError as e: if e.errno == errno.ENOENT: print("Couldn't find program: %r" % cmd[0]) return else: raise cell = cell.encode('utf8', 'replace') if args.bg: self.bg_processes.append(p) self._gc_bg_processes() if args.out: self.shell.user_ns[args.out] = p.stdout if args.err: self.shell.user_ns[args.err] = p.stderr self.job_manager.new(self._run_script, p, cell, daemon=True) if args.proc: self.shell.user_ns[args.proc] = p return try: out, err = p.communicate(cell) except KeyboardInterrupt: try: p.send_signal(signal.SIGINT) time.sleep(0.1) if p.poll() is not None: print("Process is interrupted.") return p.terminate() time.sleep(0.1) if p.poll() is not None: print("Process is terminated.") return p.kill() print("Process is killed.") except OSError: pass except Exception as e: print("Error while terminating subprocess (pid=%i): %s" \ % (p.pid, e)) return out = py3compat.bytes_to_str(out) err = py3compat.bytes_to_str(err) if args.out: self.shell.user_ns[args.out] = out else: sys.stdout.write(out) sys.stdout.flush() if args.err: self.shell.user_ns[args.err] = err else: sys.stderr.write(err) sys.stderr.flush() def _run_script(self, p, cell): """callback for running the script in the background""" p.stdin.write(cell) p.stdin.close() p.wait() @line_magic("killbgscripts") def killbgscripts(self, _nouse_=''): """Kill all BG processes started by %%script and its family.""" self.kill_bg_processes() print("All background processes were killed.") def kill_bg_processes(self): """Kill all BG processes which are still running.""" for p in self.bg_processes: if p.poll() is None: try: p.send_signal(signal.SIGINT) except: pass time.sleep(0.1) for p in self.bg_processes: if p.poll() is None: try: p.terminate() except: pass time.sleep(0.1) for p in self.bg_processes: if p.poll() is None: try: p.kill() except: pass self._gc_bg_processes() def _gc_bg_processes(self): self.bg_processes = [p for p in self.bg_processes if p.poll() is None]
class AnyBodyMagics(Magics): """Magics for talking to scripts This defines a base `%%script` cell magic for running a cell with a program in a subprocess, and registers a few top-level magics that call %%script with common interpreters. """ def __init__(self, shell): super(AnyBodyMagics, self).__init__(shell) self.job_manager = BackgroundJobManager() self.bg_processes = [] atexit.register(self.kill_bg_processes) def __del__(self): self.kill_bg_processes() @magic_arguments.magic_arguments() @script_args @cell_magic("anybody") def run_cell(self, line, cell): """Run a cell via a shell command The `%%anybody` invokes the anybody console application on the rest of the cell. Parameters ---------- --dir <Path> --out <output var> --bg <> --proc <baground process variable > --anybodycon <path to anybodycon> Examples -------- :: In [1]: %%anybody ...: load "mymodel.any" ...: operation Main.MyStudy.Kinematics ...: run """ argv = arg_split(line, posix=not sys.platform.startswith('win')) args, dummy = self.run_cell.parser.parse_known_args(argv) if args.anybodycon: if os.path.exists(args.anybodycon): abcpath = args.anybodycon elif self.shell.user_ns.has_key(args.anybodycon): abcpath = self.shell.user_ns[args.anybodycon] elif sys.platform == 'win32': import _winreg try: abpath = _winreg.QueryValue( _winreg.HKEY_CLASSES_ROOT, 'AnyBody.AnyScript\shell\open\command') abpath = abpath.rsplit(' ', 1)[0].strip('"') abcpath = os.path.join(os.path.dirname(abpath), 'AnyBodyCon.exe') except: raise Exception( 'Could not find AnyBody Modeling System in windows registry' ) else: raise Exception('Cannot find the specified anybodycon') if not os.path.exists(abcpath): raise Exception('Cannot find the specified anybodycon: %s' % abcpath) if args.dir and os.path.isdir(args.dir): folder = args.dir elif self.shell.user_ns.has_key(args.dir): folder = self.shell.user_ns[args.dir] else: folder = os.getcwd() cell = cell.encode('utf8', 'replace') macro = cell if cell.endswith('\n') else cell + '\n' macrofile = NamedTemporaryFile(mode='w+b', prefix='macro_', suffix='.anymcr', dir=folder, delete=False) macrofile.write(macro) macrofile.flush() cmd = [ abcpath, '/d', folder, '--macro=', macrofile.name, '/ni', "1>&2" ] try: p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, shell=True) except OSError as e: if e.errno == errno.ENOENT: print "Couldn't find program: %r" % cmd[0] return else: raise if args.bg: self.bg_processes.append(p) self._gc_bg_processes() if args.out: self.shell.user_ns[args.out] = p.stderr self.job_manager.new(self._run_script, p, macrofile, daemon=True) if args.proc: self.shell.user_ns[args.proc] = p return random_tag = ''.join(random.sample(string.ascii_uppercase, 6)) def htmlbox(text): raw_html = """<div id="anyscriptbox_{0}" style="height: 120px ; width : auto; border:1px dotted black;padding:0.5em;overflow:auto;background-color:#E0E0E0 ; font:3px Geogia"><font size="2px" face="courier"> {1} </font></div> <script> var myDiv = document.getElementById("anyscriptbox_{0}"); myDiv.scrollTop = myDiv.scrollHeight;</script> """.format( random_tag, text) return HTML(raw_html) try: raw_out = [] for line in iter(p.stderr.readline, b''): line = py3compat.bytes_to_str(line) raw_out.append(line) if not args.pager: clear_output() display(htmlbox("<br/>".join(raw_out))) #sys.stdout.flush() if args.pager: page("".join(raw_out)) p.communicate() except KeyboardInterrupt: try: p.send_signal(signal.SIGINT) time.sleep(0.1) if p.poll() is not None: print "Process is interrupted." return p.terminate() time.sleep(0.1) if p.poll() is not None: print "Process is terminated." return p.kill() print "Process is killed." except OSError: pass except Exception as e: print "Error while terminating subprocess (pid=%i): %s" \ % (p.pid, e) return if args.out: self.shell.user_ns[args.out] = "\n".join(raw_out) if args.dump: output = _parse_anybodycon_output("\n".join(raw_out)) if len(output.keys()): print 'Dumped variables:' for k, v in output.iteritems(): varname = k.replace('.', '_') self.shell.user_ns[varname] = v print '- ' + varname try: macrofile.close() os.remove(macrofile.name) except: print 'Error removing macro file' def _run_script(self, p, macrofile): """callback for running the script in the background""" p.communicate() try: macrofile.close() os.remove(macrofile.name) except: print 'Error removing macro file' return @line_magic("killbganybodycon") def killbgscripts(self, _nouse_=''): """Kill all BG processes started by %%anybody and its family.""" self.kill_bg_processes() print "All background processes were killed." def kill_bg_processes(self): """Kill all BG processes which are still running.""" for p in self.bg_processes: if p.poll() is None: try: p.send_signal(signal.SIGINT) except: pass time.sleep(0.1) for p in self.bg_processes: if p.poll() is None: try: p.terminate() except: pass time.sleep(0.1) for p in self.bg_processes: if p.poll() is None: try: p.kill() except: pass self._gc_bg_processes() def _gc_bg_processes(self): self.bg_processes = [p for p in self.bg_processes if p.poll() is None]
class ScriptMagics(Magics): """Magics for talking to scripts This defines a base `%%script` cell magic for running a cell with a program in a subprocess, and registers a few top-level magics that call %%script with common interpreters. """ script_magics = List(help="""Extra script cell magics to define This generates simple wrappers of `%%script foo` as `%%foo`. If you want to add script magics that aren't on your path, specify them in script_paths """, ).tag(config=True) @default('script_magics') def _script_magics_default(self): """default to a common list of programs""" defaults = [ 'sh', 'bash', 'perl', 'ruby', 'python', 'python2', 'python3', 'pypy', ] if os.name == 'nt': defaults.extend([ 'cmd', ]) return defaults script_paths = Dict( help= """Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby' Only necessary for items in script_magics where the default path will not find the right interpreter. """).tag(config=True) def __init__(self, shell=None): super(ScriptMagics, self).__init__(shell=shell) self._generate_script_magics() self.job_manager = BackgroundJobManager() self.bg_processes = [] atexit.register(self.kill_bg_processes) def __del__(self): self.kill_bg_processes() def _generate_script_magics(self): cell_magics = self.magics['cell'] for name in self.script_magics: cell_magics[name] = self._make_script_magic(name) def _make_script_magic(self, name): """make a named magic, that calls %%script with a particular program""" # expand to explicit path if necessary: script = self.script_paths.get(name, name) @magic_arguments.magic_arguments() @script_args def named_script_magic(line, cell): # if line, add it as cl-flags if line: line = "%s %s" % (script, line) else: line = script return self.shebang(line, cell) # write a basic docstring: named_script_magic.__doc__ = \ """%%{name} script magic Run cells with {script} in a subprocess. This is a shortcut for `%%script {script}` """.format(**locals()) return named_script_magic @magic_arguments.magic_arguments() @script_args @cell_magic("script") def shebang(self, line, cell): """Run a cell via a shell command The `%%script` line is like the #! line of script, specifying a program (bash, perl, ruby, etc.) with which to run. The rest of the cell is run by that program. Examples -------- :: In [1]: %%script bash ...: for i in 1 2 3; do ...: echo $i ...: done 1 2 3 """ async def _handle_stream(stream, stream_arg, file_object): while True: line = (await stream.readline()).decode("utf8") if not line: break if stream_arg: self.shell.user_ns[stream_arg] = line else: file_object.write(line) file_object.flush() async def _stream_communicate(process, cell): process.stdin.write(cell) process.stdin.close() stdout_task = asyncio.create_task( _handle_stream(process.stdout, args.out, sys.stdout)) stderr_task = asyncio.create_task( _handle_stream(process.stderr, args.err, sys.stderr)) await asyncio.wait([stdout_task, stderr_task]) await process.wait() if sys.platform.startswith("win"): asyncio.set_event_loop_policy( asyncio.WindowsProactorEventLoopPolicy()) loop = asyncio.get_event_loop() argv = arg_split(line, posix=not sys.platform.startswith("win")) args, cmd = self.shebang.parser.parse_known_args(argv) try: p = loop.run_until_complete( asyncio.create_subprocess_exec( *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE, )) except OSError as e: if e.errno == errno.ENOENT: print("Couldn't find program: %r" % cmd[0]) return else: raise if not cell.endswith('\n'): cell += '\n' cell = cell.encode('utf8', 'replace') if args.bg: self.bg_processes.append(p) self._gc_bg_processes() to_close = [] if args.out: self.shell.user_ns[args.out] = p.stdout else: to_close.append(p.stdout) if args.err: self.shell.user_ns[args.err] = p.stderr else: to_close.append(p.stderr) self.job_manager.new(self._run_script, p, cell, to_close, daemon=True) if args.proc: self.shell.user_ns[args.proc] = p return try: loop.run_until_complete(_stream_communicate(p, cell)) except KeyboardInterrupt: try: p.send_signal(signal.SIGINT) time.sleep(0.1) if p.returncode is not None: print("Process is interrupted.") return p.terminate() time.sleep(0.1) if p.returncode is not None: print("Process is terminated.") return p.kill() print("Process is killed.") except OSError: pass except Exception as e: print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e)) return if args.raise_error and p.returncode != 0: # If we get here and p.returncode is still None, we must have # killed it but not yet seen its return code. We don't wait for it, # in case it's stuck in uninterruptible sleep. -9 = SIGKILL rc = p.returncode or -9 raise CalledProcessError(rc, cell) def _run_script(self, p, cell, to_close): """callback for running the script in the background""" p.stdin.write(cell) p.stdin.close() for s in to_close: s.close() p.wait() @line_magic("killbgscripts") def killbgscripts(self, _nouse_=''): """Kill all BG processes started by %%script and its family.""" self.kill_bg_processes() print("All background processes were killed.") def kill_bg_processes(self): """Kill all BG processes which are still running.""" if not self.bg_processes: return for p in self.bg_processes: if p.returncode is None: try: p.send_signal(signal.SIGINT) except: pass time.sleep(0.1) self._gc_bg_processes() if not self.bg_processes: return for p in self.bg_processes: if p.returncode is None: try: p.terminate() except: pass time.sleep(0.1) self._gc_bg_processes() if not self.bg_processes: return for p in self.bg_processes: if p.returncode is None: try: p.kill() except: pass self._gc_bg_processes() def _gc_bg_processes(self): self.bg_processes = [ p for p in self.bg_processes if p.returncode is None ]
class AnyBodyMagics(Magics): """Magics for talking to scripts This defines a base `%%script` cell magic for running a cell with a program in a subprocess, and registers a few top-level magics that call %%script with common interpreters. """ def __init__(self, shell): super(AnyBodyMagics,self).__init__(shell) self.job_manager = BackgroundJobManager() self.bg_processes = [] atexit.register(self.kill_bg_processes) def __del__(self): self.kill_bg_processes() @magic_arguments.magic_arguments() @script_args @cell_magic("anybody") def run_cell(self, line, cell): """Run a cell via a shell command The `%%anybody` invokes the anybody console application on the rest of the cell. Parameters ---------- --dir <Path> --out <output var> --bg <> --proc <baground process variable > --anybodycon <path to anybodycon> Examples -------- :: In [1]: %%anybody ...: load "mymodel.any" ...: operation Main.MyStudy.Kinematics ...: run """ argv = arg_split(line, posix = not sys.platform.startswith('win')) args, dummy = self.run_cell.parser.parse_known_args(argv) if args.anybodycon: if os.path.exists(args.anybodycon): abcpath = args.anybodycon elif self.shell.user_ns.has_key(args.anybodycon): abcpath = self.shell.user_ns[args.anybodycon] elif sys.platform == 'win32': import _winreg try: abpath = _winreg.QueryValue(_winreg.HKEY_CLASSES_ROOT, 'AnyBody.AnyScript\shell\open\command') abpath = abpath.rsplit(' ',1)[0].strip('"') abcpath = os.path.join(os.path.dirname(abpath),'AnyBodyCon.exe') except: raise Exception('Could not find AnyBody Modeling System in windows registry') else: raise Exception('Cannot find the specified anybodycon') if not os.path.exists(abcpath): raise Exception('Cannot find the specified anybodycon: %s'%abcpath) if args.dir and os.path.isdir(args.dir): folder = args.dir elif self.shell.user_ns.has_key(args.dir): folder = self.shell.user_ns[args.dir] else: folder = os.getcwd() cell = cell.encode('utf8', 'replace') macro = cell if cell.endswith('\n') else cell+'\n' macrofile = NamedTemporaryFile(mode='w+b', prefix ='macro_', suffix='.anymcr', dir= folder, delete = False) macrofile.write(macro) macrofile.flush() cmd = [abcpath ,'/d',folder, '--macro=', macrofile.name, '/ni', "1>&2"] try: p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE, shell= True) except OSError as e: if e.errno == errno.ENOENT: print "Couldn't find program: %r" % cmd[0] return else: raise if args.bg: self.bg_processes.append(p) self._gc_bg_processes() if args.out: self.shell.user_ns[args.out] = p.stderr self.job_manager.new(self._run_script, p, macrofile, daemon=True) if args.proc: self.shell.user_ns[args.proc] = p return random_tag = ''.join( random.sample(string.ascii_uppercase,6) ) def htmlbox(text): raw_html = """<div id="anyscriptbox_{0}" style="height: 120px ; width : auto; border:1px dotted black;padding:0.5em;overflow:auto;background-color:#E0E0E0 ; font:3px Geogia"><font size="2px" face="courier"> {1} </font></div> <script> var myDiv = document.getElementById("anyscriptbox_{0}"); myDiv.scrollTop = myDiv.scrollHeight;</script> """.format(random_tag, text ) return HTML(raw_html) try: raw_out = [] for line in iter(p.stderr.readline,b''): line = py3compat.bytes_to_str( line ) raw_out.append(line) if not args.pager: clear_output() display( htmlbox("<br/>".join( raw_out ) ) ) #sys.stdout.flush() if args.pager: page("".join(raw_out)) p.communicate(); except KeyboardInterrupt: try: p.send_signal(signal.SIGINT) time.sleep(0.1) if p.poll() is not None: print "Process is interrupted." return p.terminate() time.sleep(0.1) if p.poll() is not None: print "Process is terminated." return p.kill() print "Process is killed." except OSError: pass except Exception as e: print "Error while terminating subprocess (pid=%i): %s" \ % (p.pid, e) return if args.out: self.shell.user_ns[args.out] = "\n".join(raw_out) if args.dump: output = _parse_anybodycon_output( "\n".join(raw_out) ) if len(output.keys()): print 'Dumped variables:' for k,v in output.iteritems(): varname = k.replace('.','_') self.shell.user_ns[varname] = v print '- ' + varname try: macrofile.close() os.remove(macrofile.name) except: print 'Error removing macro file' def _run_script(self, p, macrofile): """callback for running the script in the background""" p.communicate(); try: macrofile.close() os.remove(macrofile.name) except: print 'Error removing macro file' return @line_magic("killbganybodycon") def killbgscripts(self, _nouse_=''): """Kill all BG processes started by %%anybody and its family.""" self.kill_bg_processes() print "All background processes were killed." def kill_bg_processes(self): """Kill all BG processes which are still running.""" for p in self.bg_processes: if p.poll() is None: try: p.send_signal(signal.SIGINT) except: pass time.sleep(0.1) for p in self.bg_processes: if p.poll() is None: try: p.terminate() except: pass time.sleep(0.1) for p in self.bg_processes: if p.poll() is None: try: p.kill() except: pass self._gc_bg_processes() def _gc_bg_processes(self): self.bg_processes = [p for p in self.bg_processes if p.poll() is None]
class ScriptMagics(Magics, Configurable): """Magics for talking to scripts This defines a base `%%script` cell magic for running a cell with a program in a subprocess, and registers a few top-level magics that call %%script with common interpreters. """ script_magics = List(config=True, help="""Extra script cell magics to define This generates simple wrappers of `%%script foo` as `%%foo`. If you want to add script magics that aren't on your path, specify them in script_paths """, ) def _script_magics_default(self): """default to a common list of programs if we find them""" defaults = [] to_try = [] if os.name == 'nt': defaults.append('cmd') to_try.append('powershell') to_try.extend([ 'sh', 'bash', 'perl', 'ruby', 'python3', 'pypy', ]) for cmd in to_try: if cmd in self.script_paths: defaults.append(cmd) else: try: find_cmd(cmd) except FindCmdError: # command not found, ignore it pass except ImportError: # Windows without pywin32, find_cmd doesn't work pass else: defaults.append(cmd) return defaults script_paths = Dict(config=True, help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby' Only necessary for items in script_magics where the default path will not find the right interpreter. """ ) def __init__(self, shell=None): Configurable.__init__(self, config=shell.config) self._generate_script_magics() Magics.__init__(self, shell=shell) self.job_manager = BackgroundJobManager() def _generate_script_magics(self): cell_magics = self.magics['cell'] for name in self.script_magics: cell_magics[name] = self._make_script_magic(name) def _make_script_magic(self, name): """make a named magic, that calls %%script with a particular program""" # expand to explicit path if necessary: script = self.script_paths.get(name, name) @magic_arguments.magic_arguments() @script_args def named_script_magic(line, cell): # if line, add it as cl-flags if line: line = "%s %s" % (script, line) else: line = script return self.shebang(line, cell) # write a basic docstring: named_script_magic.__doc__ = \ """%%{name} script magic Run cells with {script} in a subprocess. This is a shortcut for `%%script {script}` """.format(**locals()) return named_script_magic @magic_arguments.magic_arguments() @script_args @cell_magic("script") def shebang(self, line, cell): """Run a cell via a shell command The `%%script` line is like the #! line of script, specifying a program (bash, perl, ruby, etc.) with which to run. The rest of the cell is run by that program. Examples -------- :: In [1]: %%script bash ...: for i in 1 2 3; do ...: echo $i ...: done 1 2 3 """ argv = arg_split(line, posix = not sys.platform.startswith('win')) args, cmd = self.shebang.parser.parse_known_args(argv) p = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE) cell = cell.encode('utf8', 'replace') if args.bg: if args.out: self.shell.user_ns[args.out] = p.stdout if args.err: self.shell.user_ns[args.err] = p.stderr self.job_manager.new(self._run_script, p, cell) return out, err = p.communicate(cell) out = py3compat.bytes_to_str(out) err = py3compat.bytes_to_str(err) if args.out: self.shell.user_ns[args.out] = out else: sys.stdout.write(out) sys.stdout.flush() if args.err: self.shell.user_ns[args.err] = err else: sys.stderr.write(err) sys.stderr.flush() def _run_script(self, p, cell): """callback for running the script in the background""" p.stdin.write(cell) p.stdin.close() p.wait()