def workflow_from_notebook_cell(self, cell, fh, idx=0): # in non-all mode, markdown cells are ignored because they can be mistakenly # treated as markdown content of an action or script #806 if cell.cell_type != "code": return # # Non-sos code cells are also ignored if 'kernel' in cell.metadata and cell.metadata['kernel'] not in ('sos', 'SoS', None): return lines = cell.source.split('\n') valid_cell = False for idx, line in enumerate(lines): if valid_cell or (line.startswith('%include') or line.startswith('%from')): fh.write(line + '\n') elif SOS_SECTION_HEADER.match(line): valid_cell = True # look retrospectively for comments c = idx - 1 comment = '' while c >= 0 and lines[c].startswith('#'): comment = lines[c] + '\n' + comment c -= 1 fh.write(comment + line + '\n') # other content, namely non-%include lines before section header is ignored if valid_cell: fh.write('\n') return idx
def from_notebook_cell(self, cell, fh, idx=0): if self.export_all: meta = ' '.join(f'{x}={y}' for x, y in cell.metadata.items()) if not hasattr(cell, 'execution_count') or cell.execution_count is None: fh.write(f'%cell {cell.cell_type} {meta}\n') else: idx += 1 fh.write( f'%cell {cell.cell_type} {cell.execution_count} {meta}\n') if cell.cell_type == 'code': fh.write(cell.source.strip() + '\n') elif cell.cell_type == "markdown": fh.write('\n'.join('#! ' + x for x in cell.source.split('\n')) + '\n') fh.write('\n') else: # in non-all mode, markdown cells are ignored because they can be mistakenly # treated as markdown content of an action or script #806 if cell.cell_type != "code": return # # Non-sos code cells are also ignored if 'kernel' in cell.metadata and cell.metadata['kernel'] not in ( 'sos', 'SoS', None): return lines = cell.source.split('\n') valid_cell = False for line in lines: if valid_cell or (line.startswith('%include') or line.startswith('%from')): fh.write(line + '\n') elif SOS_SECTION_HEADER.match(line): valid_cell = True fh.write(line + '\n') # other content, namely non-%include lines before section header is ignored if valid_cell: fh.write('\n') return idx
def runfile(script=None, raw_args='', wdir='.', code=None, kernel=None, **kwargs): # this has something to do with Prefix matching rule of parse_known_args # # That is to say # # --rep 3 # # would be parsed as # # args.workflow=3, unknown --rep # # instead of # # args.workflow=None, unknown --rep 3 # # we then have to change the parse to disable args.workflow when # there is no workflow option. raw_args = shlex.split(raw_args) if isinstance(raw_args, str) else raw_args if (script is None and code is None) or '-h' in raw_args: parser = get_run_parser(interactive=True, with_workflow=True) parser.print_help() return if raw_args and raw_args[0].lstrip().startswith('-'): parser = get_run_parser(interactive=True, with_workflow=False) parser.error = _parse_error args, workflow_args = parser.parse_known_args(raw_args) args.workflow = None else: parser = get_run_parser(interactive=True, with_workflow=True) parser.error = _parse_error args, workflow_args = parser.parse_known_args(raw_args) # for reporting purpose sys.argv = ['%run'] + raw_args env.verbosity = args.verbosity if kernel and not isinstance(env.logger.handlers[0], NotebookLoggingHandler): env.logger.handlers = [] levels = { 0: logging.ERROR, 1: logging.WARNING, 2: logging.INFO, 3: logging.DEBUG, 4: logging.TRACE, None: logging.INFO } env.logger.addHandler( NotebookLoggingHandler(levels[env.verbosity], kernel, title=' '.join(sys.argv))) else: env.logger.handers[0].setTitle(' '.join(sys.argv)) dt = datetime.datetime.now().strftime('%m%d%y_%H%M') if args.__dag__ is None: args.__dag__ = f'workflow_{dt}.dot' elif args.__dag__ == '': args.__dag__ = None if args.__report__ is None: args.__report__ = f'workflow_{dt}.html' elif args.__report__ == '': args.__report__ = None if args.__remote__: from sos.utils import load_config_files cfg = load_config_files(args.__config__) env.sos_dict.set('CONFIG', cfg) # if executing on a remote host... from sos.hosts import Host host = Host(args.__remote__) # if script is None: if not code.strip(): return script = os.path.join('.sos', '__interactive__.sos') with open(script, 'w') as s: s.write(code) # copy script to remote host... host.send_to_host(script) from sos.utils import remove_arg argv = shlex.split(raw_args) if isinstance(raw_args, str) else raw_args argv = remove_arg(argv, '-r') argv = remove_arg(argv, '-c') # execute the command on remote host try: with kernel.redirect_sos_io(): ret = host._host_agent.run_command(['sos', 'run', script] + argv, wait_for_task=True, realtime=True) if ret: kernel.send_response( kernel.iopub_socket, 'stream', dict(name='stderr', text= f'remote execution of workflow exited with code {ret}' )) except Exception as e: if kernel: kernel.send_response(kernel.iopub_socket, 'stream', { 'name': 'stdout', 'text': str(e) }) return if args.__bin_dirs__: for d in args.__bin_dirs__: if d == '~/.sos/bin' and not os.path.isdir(os.path.expanduser(d)): os.makedirs(os.path.expanduser(d), exist_ok=True) os.environ['PATH'] = os.pathsep.join( [os.path.expanduser(x) for x in args.__bin_dirs__]) + os.pathsep + os.environ['PATH'] # clear __step_input__, __step_output__ etc because there is # no concept of passing input/outputs across cells. env.sos_dict.set('__step_output__', sos_targets([])) for k in [ '__step_input__', '__default_output__', 'step_input', 'step_output', 'step_depends', '_input', '_output', '_depends' ]: env.sos_dict.pop(k, None) try: if script is None: if not code.strip(): return if kernel is None: script = SoS_Script(content=code) else: if kernel._workflow_mode: # in workflow mode, the content is sent by magics %run and %sosrun script = SoS_Script(content=code) else: # this is a scratch step... # if there is no section header, add a header so that the block # appears to be a SoS script with one section if not any([ SOS_SECTION_HEADER.match(line) or line.startswith('%from') or line.startswith('%include') for line in code.splitlines() ]): code = '[scratch_0]\n' + code script = SoS_Script(content=code) else: #kernel.send_frontend_msg('stream', # {'name': 'stdout', 'text': 'Workflow cell can only be executed with magic %run or %sosrun.'}, # title='# SoS warning') return else: script = SoS_Script(filename=script) workflow = script.workflow(args.workflow, use_default=not args.__targets__) env.config: DefaultDict[str, Union[None, bool, str]] = defaultdict(str) executor = Interactive_Executor( workflow, args=workflow_args, config={ 'config_file': args.__config__, 'output_dag': args.__dag__, 'output_report': args.__report__, 'sig_mode': 'ignore' if args.dryrun else args.__sig_mode__, 'default_queue': '' if args.__queue__ is None else args.__queue__, 'wait_for_task': True if args.__wait__ is True or args.dryrun else (False if args.__no_wait__ else None), 'resume_mode': kernel is not None and kernel._resume_execution, 'run_mode': 'dryrun' if args.dryrun else 'interactive', 'verbosity': args.verbosity, # wait if -w or in dryrun mode, not wait if -W, otherwise use queue default 'max_procs': args.__max_procs__, 'max_running_jobs': args.__max_running_jobs__, # for infomration and resume only 'workdir': os.getcwd(), 'script': "interactive", 'workflow': args.workflow, 'targets': args.__targets__, 'bin_dirs': args.__bin_dirs__, 'workflow_args': workflow_args }) return executor.run(args.__targets__)['__last_res__'] except PendingTasks: raise except SystemExit: # this happens because the executor is in resume mode but nothing # needs to be resumed, we simply pass return except Exception: if args.verbosity and args.verbosity > 2: sys.stderr.write(get_traceback()) raise finally: env.config['sig_mode'] = 'ignore' env.verbosity = 2
def execute_scratch_cell(code, raw_args, kernel): # we then have to change the parse to disable args.workflow when # there is no workflow option. raw_args = shlex.split(raw_args) if isinstance(raw_args, str) else raw_args if code is None or '-h' in raw_args: parser = get_run_parser(interactive=True, with_workflow=True) parser.print_help() return if raw_args and raw_args[0].lstrip().startswith('-'): parser = get_run_parser(interactive=True, with_workflow=False) parser.error = _parse_error args, workflow_args = parser.parse_known_args(raw_args) args.workflow = None else: parser = get_run_parser(interactive=True, with_workflow=True) parser.error = _parse_error args, workflow_args = parser.parse_known_args(raw_args) if not code.strip(): return # for reporting purpose sys.argv = ['%run'] + raw_args env.verbosity = args.verbosity if not any( isinstance(x, NotebookLoggingHandler) for x in env.logger.handlers): env.logger.handlers = [ x for x in env.logger.handlers if type(x) is not logging.StreamHandler ] levels = { 0: logging.ERROR, 1: logging.WARNING, 2: logging.INFO, 3: logging.DEBUG, 4: logging.DEBUG, None: logging.INFO } env.logger.addHandler( NotebookLoggingHandler(levels[env.verbosity], kernel, title=' '.join(sys.argv))) else: env.logger.handers[0].setTitle(' '.join(sys.argv)) global last_cell_id # we retain step_input etc only when we step through a cell #256 if kernel and kernel.cell_id != last_cell_id: # clear __step_input__, __step_output__ etc because there is # no concept of passing input/outputs across cells. env.sos_dict.set('__step_output__', sos_targets([])) for k in [ '__step_input__', '__default_output__', 'step_input', 'step_output', 'step_depends', '_input', '_output', '_depends' ]: env.sos_dict.pop(k, None) last_cell_id = kernel.cell_id config = { 'config_file': args.__config__, 'default_queue': args.__queue__, 'run_mode': 'dryrun' if args.dryrun else 'interactive', # issue 230, ignore sig mode in interactive mode 'sig_mode': 'ignore', 'verbosity': args.verbosity, # for backward compatibility, we try both args.__worker_procs__ and args.__max_procs__ 'worker_procs': args.__worker_procs__ if hasattr(args, '__worker_procs__') else args.__max_procs__, 'max_running_jobs': args.__max_running_jobs__, # for infomration and resume only 'workdir': os.getcwd(), 'workflow': args.workflow, 'targets': args.__targets__, 'workflow_args': workflow_args, 'workflow_id': textMD5(code), # interactive work is also a slave of the controller 'slave_id': kernel.cell_id, } env.sos_dict.set('workflow_id', config['workflow_id']) env.config.update(config) try: if not any([ SOS_SECTION_HEADER.match(line) or line.startswith('%from') or line.startswith('%include') for line in code.splitlines() ]): code = f'[cell_{str(kernel.cell_id)[:8] if kernel and kernel.cell_id else "0"}]\n' + code script = SoS_Script(content=code) else: return workflow = script.workflow(args.workflow) section = workflow.sections[0] res = analyze_section(section) env.sos_dict.quick_update({ '__signature_vars__': res['signature_vars'], '__environ_vars__': res['environ_vars'], '__changed_vars__': res['changed_vars'] }) executor = Interactive_Step_Executor(section, mode='interactive') ret = executor.run() try: return ret['__last_res__'] except Exception as e: raise RuntimeError( f'Unknown result returned from executor {ret}: {e}') except (UnknownTarget, RemovedTarget) as e: raise RuntimeError(f'Unavailable target {e.target}') except TerminateExecution as e: return except SystemExit: # this happens because the executor is in resume mode but nothing # needs to be resumed, we simply pass return except Exception: env.log_to_file('PROCESS', get_traceback()) raise
def convert(self, script_file, notebook_file, args=None, unknown_args=None): ''' Convert a sos script to iPython notebook (.ipynb) so that it can be opened by Jupyter notebook. ''' if unknown_args: raise ValueError(f'Unrecognized parameter {unknown_args}') cells = [] cell_count = 1 cell_type = 'code' metainfo = {} content = [] def add_cell(cells, content, cell_type, cell_count, metainfo): # if a section consist of all report, report it as a markdown cell if not content: return if cell_type not in ('code', 'markdown'): env.logger.warning( f'Unrecognized cell type {cell_type}, code assumed.') if cell_type == 'markdown' and any( x.strip() and not x.startswith('#! ') for x in content): env.logger.warning( 'Markdown lines not starting with #!, code cell assumed.') cell_type = 'code' # if cell_type == 'markdown': cells.append( new_markdown_cell(source=''.join([x[3:] for x in content ]).strip(), metadata=metainfo)) else: cells.append( new_code_cell( # remove any trailing blank lines... source=''.join(content).strip(), execution_count=cell_count, metadata=metainfo)) with open(script_file) as script: first_block = True for line in script: if line.startswith('#') and first_block: if line.startswith('#!'): continue if line.startswith('#fileformat='): if not line[12:].startswith('SOS'): raise RuntimeError( f'{script_file} is not a SoS script according to #fileformat line.' ) continue first_block = False mo = SOS_SECTION_HEADER.match(line) if mo: # get rid of empty content if not any(x.strip() for x in content): content = [] if content: # the comment should be absorbed into the next section i = len(content) - 1 while i >= 0 and content[i].startswith('#'): i -= 1 # i point to the last non comment line if i >= 0: add_cell(cells, content[:i + 1], cell_type, cell_count, metainfo) content = content[i + 1:] cell_type = 'code' cell_count += 1 metainfo = {'kernel': 'SoS'} content += [line] continue if line.startswith('#!'): if cell_type == 'markdown': content.append(line) continue else: # get ride of empty content if not any(x.strip() for x in content): content = [] if content: add_cell(cells, content, cell_type, cell_count, metainfo) cell_type = 'markdown' cell_count += 1 content = [line] continue # other cases content.append(line) # if content and any(x.strip() for x in content): add_cell(cells, content, cell_type, cell_count, metainfo) # nb = new_notebook(cells=cells, metadata={ 'kernelspec': { "display_name": "SoS", "language": "sos", "name": "sos" }, "language_info": { 'codemirror_mode': 'sos', "file_extension": ".sos", "mimetype": "text/x-sos", "name": "sos", "pygments_lexer": "python", 'nbconvert_exporter': 'sos_notebook.converter.SoS_Exporter', }, 'sos': { 'kernels': [['SoS', 'sos', '', '']] } }) if not notebook_file: nbformat.write(nb, sys.stdout, 4) else: with open(notebook_file, 'w') as notebook: nbformat.write(nb, notebook, 4) env.logger.info(f'Jupyter notebook saved to {notebook_file}')
def script_to_notebook(script_file, notebook_file, args=None, unknown_args=None): ''' Convert a sos script to iPython notebook (.ipynb) so that it can be opened by Jupyter notebook. ''' if unknown_args: raise ValueError(f'Unrecognized parameter {unknown_args}') cells = [] cell_count = 1 cell_type = 'code' metainfo = {} content = [] with open(script_file) as script: split_step = '%cell ' not in script.read() with open(script_file) as script: first_block = True for line in script: if line.startswith('#') and first_block: if line.startswith('#!'): continue if line.startswith('#fileformat='): if not line[12:].startswith('SOS'): raise RuntimeError( f'{script_file} is not a SoS script according to #fileformat line.' ) continue first_block = False mo = SOS_CELL_LINE.match(line) if mo: # get ride of empty content if not any(x.strip() for x in content): content = [] if content: add_cell(cells, content, cell_type, cell_count, metainfo) cell_type = mo.group('cell_type') if not cell_type: cell_type = 'code' cc = mo.group('cell_count') if cc: cell_count = int(cc) else: cell_count += 1 metainfo = mo.group('metainfo') if metainfo: pieces = [ piece.split('=', 1) for piece in metainfo.split() ] for idx, piece in enumerate(pieces): if len(piece) == 1: env.logger.warning(f'Incorrect metadata {piece}') pieces[idx].append('') if piece[1] == 'True': pieces[idx][1] = True elif piece[1] == 'False': pieces[idx][1] = False metainfo = {x: y for x, y in pieces} else: metainfo = {} content = [] continue if split_step: mo = SOS_SECTION_HEADER.match(line) if mo: # get ride of empty content if not any(x.strip() for x in content): content = [] if content: add_cell(cells, content, cell_type, cell_count, metainfo) cell_type = 'code' cell_count += 1 metainfo = {'kernel': 'SoS'} content = [line] continue if line.startswith('#!'): if cell_type == 'markdown': content.append(line) continue else: # get ride of empty content if not any(x.strip() for x in content): content = [] if content: add_cell(cells, content, cell_type, cell_count, metainfo) cell_type = 'markdown' cell_count += 1 content = [line] continue # other cases content.append(line) # if content and any(x.strip() for x in content): add_cell(cells, content, cell_type, cell_count, metainfo) # nb = new_notebook(cells=cells, metadata={ 'kernelspec': { "display_name": "SoS", "language": "sos", "name": "sos" }, "language_info": { 'codemirror_mode': 'sos', "file_extension": ".sos", "mimetype": "text/x-sos", "name": "sos", "pygments_lexer": "python", 'nbconvert_exporter': 'sos_notebook.converter.SoS_Exporter', }, 'sos': { 'kernels': [['SoS', 'sos', '', '']] } }) #err = None # if args and args.execute: # ep = SoS_ExecutePreprocessor(timeout=600, kernel_name='sos') # try: # ep.preprocess(nb, {'metadata': {'path': '.'}}) # except CellExecutionError as e: # err = e # if not notebook_file: nbformat.write(nb, sys.stdout, 4) else: with open(notebook_file, 'w') as notebook: nbformat.write(nb, notebook, 4) env.logger.info(f'Jupyter notebook saved to {notebook_file}')
def script_to_notebook(script_file, notebook_file, args=None, unknown_args=None): ''' Convert a sos script to iPython notebook (.ipynb) so that it can be opened by Jupyter notebook. ''' if unknown_args: raise ValueError(f'Unrecognized parameter {unknown_args}') cells = [] cell_count = 1 cell_type = 'code' metainfo = {} content = [] with open(script_file) as script: first_block = True for line in script: if line.startswith('#') and first_block: if line.startswith('#!'): continue if line.startswith('#fileformat='): if not line[12:].startswith('SOS'): raise RuntimeError( f'{script_file} is not a SoS script according to #fileformat line.' ) continue first_block = False mo = SOS_SECTION_HEADER.match(line) if mo: # get ride of empty content if not any(x.strip() for x in content): content = [] if content: add_cell(cells, content, cell_type, cell_count, metainfo) cell_type = 'code' cell_count += 1 metainfo = {'kernel': 'SoS'} content = [line] continue if line.startswith('#!'): if cell_type == 'markdown': content.append(line) continue else: # get ride of empty content if not any(x.strip() for x in content): content = [] if content: add_cell(cells, content, cell_type, cell_count, metainfo) cell_type = 'markdown' cell_count += 1 content = [line] continue # other cases content.append(line) # if content and any(x.strip() for x in content): add_cell(cells, content, cell_type, cell_count, metainfo) # nb = new_notebook(cells=cells, metadata={ 'kernelspec': { "display_name": "SoS", "language": "sos", "name": "sos" }, "language_info": { 'codemirror_mode': 'sos', "file_extension": ".sos", "mimetype": "text/x-sos", "name": "sos", "pygments_lexer": "python", 'nbconvert_exporter': 'sos_notebook.converter.SoS_Exporter', }, 'sos': { 'kernels': [['SoS', 'sos', '', '']] } }) if not notebook_file: nbformat.write(nb, sys.stdout, 4) else: with open(notebook_file, 'w') as notebook: nbformat.write(nb, notebook, 4) env.logger.info(f'Jupyter notebook saved to {notebook_file}')
def runfile(script=None, raw_args='', wdir='.', code=None, kernel=None, **kwargs): # this has something to do with Prefix matching rule of parse_known_args # # That is to say # # --rep 3 # # would be parsed as # # args.workflow=3, unknown --rep # # instead of # # args.workflow=None, unknown --rep 3 # # we then have to change the parse to disable args.workflow when # there is no workflow option. args = shlex.split(raw_args) if isinstance(raw_args, str) else raw_args if (script is None and code is None) or '-h' in args: parser = get_run_parser(interactive=True, with_workflow=True) parser.print_help() return if args and args[0].lstrip().startswith('-'): parser = get_run_parser(interactive=True, with_workflow=False) parser.error = _parse_error args, workflow_args = parser.parse_known_args(args) args.workflow = None else: parser = get_run_parser(interactive=True, with_workflow=True) parser.error = _parse_error args, workflow_args = parser.parse_known_args(args) # no multi-processing in interactive mode env.max_jobs = 1 env.verbosity = args.verbosity if args.__queue__ == '': from sos.hosts import list_queues list_queues(args.__config__, args.verbosity) return if args.__remote__: from sos.utils import load_config_files cfg = load_config_files(args.__config__) env.sos_dict.set('CONFIG', cfg) if args.__remote__ == '': from .hosts import list_queues list_queues(cfg, args.verbosity) return # if executing on a remote host... from sos.hosts import Host host = Host(args.__remote__) # if script is None: if not code.strip(): return script = os.path.join('.sos', '__interactive__.sos') with open(script, 'w') as s: s.write(code) # copy script to remote host... host.send_to_host(script) from sos.utils import remove_arg argv = shlex.split(raw_args) if isinstance(raw_args, str) else raw_args argv = remove_arg(argv, '-r') argv = remove_arg(argv, '-c') # execute the command on remote host try: with kernel.redirect_sos_io(): ret = host._host_agent.run_command(['sos', 'run', script] + argv, wait_for_task=True, realtime=True) if ret: kernel.send_response( kernel.iopub_socket, 'stream', dict(name='stderr', text= f'remote execution of workflow exited with code {ret}' )) except Exception as e: if kernel: kernel.send_response(kernel.iopub_socket, 'stream', { 'name': 'stdout', 'text': str(e) }) return if args.__bin_dirs__: import fasteners for d in args.__bin_dirs__: if d == '~/.sos/bin' and not os.path.isdir(os.path.expanduser(d)): with fasteners.InterProcessLock( os.path.join(tempfile.gettempdir(), 'sos_lock_bin')): os.makedirs(os.path.expanduser(d)) elif not os.path.isdir(os.path.expanduser(d)): raise ValueError(f'directory does not exist: {d}') os.environ['PATH'] = os.pathsep.join( [os.path.expanduser(x) for x in args.__bin_dirs__]) + os.pathsep + os.environ['PATH'] # clear __step_input__, __step_output__ etc because there is # no concept of passing input/outputs across cells. env.sos_dict.set('__step_output__', []) for k in ['__step_input__', '__default_output__', 'input', 'output', \ 'depends', '_input', '_output', '_depends']: env.sos_dict.pop(k, None) try: if script is None: if not code.strip(): return if kernel is None: script = SoS_Script(content=code) else: if kernel._workflow_mode: # in workflow mode, the content is sent by magics %run and %sosrun script = SoS_Script(content=code) else: # this is a scratch step... # if there is no section header, add a header so that the block # appears to be a SoS script with one section if not any([ SOS_SECTION_HEADER.match(line) or line.startswith('%from') or line.startswith('%include') for line in code.splitlines() ]): code = '[scratch_0]\n' + code script = SoS_Script(content=code) else: if kernel.cell_idx == -1: kernel.send_frontend_msg( 'stream', { 'name': 'stdout', 'text': 'Workflow can only be executed with magic %run or %sosrun.' }) return else: script = SoS_Script(filename=script) workflow = script.workflow(args.workflow) executor = Interactive_Executor( workflow, args=workflow_args, config={ 'config_file': args.__config__, 'output_dag': args.__dag__, 'sig_mode': args.__sig_mode__, 'default_queue': '' if args.__queue__ is None else args.__queue__, 'wait_for_task': True if args.__wait__ is True or args.dryrun else (False if args.__no_wait__ else None), 'resume_mode': kernel is not None and kernel._resume_execution, 'run_mode': 'dryrun' if args.dryrun else 'interactive', 'verbosity': args.verbosity, # wait if -w or in dryrun mode, not wait if -W, otherwise use queue default 'max_procs': 1, 'max_running_jobs': args.__max_running_jobs__, # for infomration and resume only 'workdir': os.getcwd(), 'script': "interactive", 'workflow': args.workflow, 'targets': args.__targets__, 'bin_dirs': args.__bin_dirs__, 'workflow_args': workflow_args }) return executor.run(args.__targets__) except PendingTasks: raise except SystemExit: # this happens because the executor is in resume mode but nothing # needs to be resumed, we simply pass return except Exception: if args.verbosity and args.verbosity > 2: sys.stderr.write(get_traceback()) raise finally: env.config['sig_mode'] = 'ignore' env.verbosity = 2