def AppBundleMain(argv): b = os.path.basename(argv[0]) main_name, ext = os.path.splitext(b) if main_name in ('opy_', 'opy') and ext: # opy_.py or opy.ovm try: first_arg = argv[1] except IndexError: raise args.UsageError('Missing required applet name.') # TODO: We don't have this if first_arg in ('-h', '--help'): #builtin.Help(['bundle-usage'], util.GetResourceLoader()) raise NotImplementedError('OPy help not implemented') sys.exit(0) if first_arg in ('-V', '--version'): _ShowVersion() sys.exit(0) main_name = first_arg argv0 = argv[1] main_argv = argv[2:] else: argv0 = argv[0] main_argv = argv[1:] if main_name == 'opy': status = OpyMain(argv0, main_argv) return status elif main_name == 'opyc': return opy_main.OpyCommandMain(main_argv) else: raise args.UsageError('Invalid applet name %r.' % main_name)
def Export(argv, mem): arg, i = EXPORT_SPEC.Parse(argv) if arg.n: for name in argv[i:]: m = match.IsValidVarName(name) if not m: raise args.UsageError('export: Invalid variable name %r' % name) # NOTE: bash doesn't care if it wasn't found. mem.ClearFlag(name, var_flags_e.Exported, scope_e.Dynamic) else: for arg in argv[i:]: parts = arg.split('=', 1) if len(parts) == 1: name = parts[0] val = None # Creates an empty variable else: name, s = parts val = runtime.Str(s) m = match.IsValidVarName(name) if not m: raise args.UsageError('export: Invalid variable name %r' % name) #log('%s %s', name, val) mem.SetVar(runtime.LhsName(name), val, (var_flags_e.Exported, ), scope_e.Dynamic) return 0
def AppBundleMain(argv): login_shell = False b = os.path.basename(argv[0]) main_name, ext = os.path.splitext(b) if main_name.startswith('-'): login_shell = True main_name = main_name[1:] if main_name == 'oil' and ext: # oil.py or oil.ovm try: first_arg = argv[1] except IndexError: raise args.UsageError('Missing required applet name.') if first_arg in ('-h', '--help'): builtin.Help(['bundle-usage'], util.GetResourceLoader()) sys.exit(0) if first_arg in ('-V', '--version'): _ShowVersion() sys.exit(0) main_name = first_arg if main_name.startswith('-'): # TODO: Remove duplication above login_shell = True main_name = main_name[1:] argv0 = argv[1] main_argv = argv[2:] else: argv0 = argv[0] main_argv = argv[1:] if main_name in ('osh', 'sh'): status = OshMain(argv0, main_argv, login_shell) _tlog('done osh main') return status elif main_name == 'oshc': return OshCommandMain(main_argv) elif main_name == 'oil': return OilMain(main_argv) elif main_name == 'wok': return WokMain(main_argv) elif main_name == 'boil': return BoilMain(main_argv) # For testing latency elif main_name == 'true': return 0 elif main_name == 'false': return 1 elif main_name == 'readlink': return readlink.main(main_argv) else: raise args.UsageError('Invalid applet name %r.' % main_name)
def _SetOption(self, opt_name, b): """Private version for synchronizing from SHELLOPTS.""" assert '_' not in opt_name if opt_name not in _SET_OPTION_NAMES: raise args.UsageError('Invalid option %r' % opt_name) if opt_name == 'errexit': self.errexit.Set(b) else: # strict-control-flow -> strict_control_flow opt_name = opt_name.replace('-', '_') setattr(self, opt_name, b)
def UnAlias(argv, aliases): if not argv: raise args.UsageError('unalias NAME...') status = 0 for name in argv: try: del aliases[name] except KeyError: util.error('alias %r is not defined', name) status = 1 return status
def _SetOption(self, opt_name, b): """Private version for synchronizing from SHELLOPTS.""" assert '_' not in opt_name if opt_name not in _SET_OPTION_NAMES: raise args.UsageError('Invalid option %r' % opt_name) if opt_name == 'errexit': self.errexit.Set(b) elif opt_name in ('vi', 'emacs'): if self.readline: self.readline.parse_and_bind("set editing-mode " + opt_name) else: # TODO error message copied from 'cmd_exec.py'; refactor? util.error('Oil was not built with readline/completion.') else: # strict-control-flow -> strict_control_flow opt_name = opt_name.replace('-', '_') setattr(self, opt_name, b)
def Shopt(argv, exec_opts): arg, i = SHOPT_SPEC.Parse(argv) #log('%s', arg) b = None if arg.s: b = True elif arg.u: b = False if b is None: raise NotImplementedError # Display options for opt_name in argv[i:]: if opt_name not in ('nullglob', 'failglob'): raise args.UsageError('shopt: Invalid option %r' % opt_name) setattr(exec_opts, opt_name, b) return 0
def GetOpts(argv, mem): """ Vars to set: OPTIND - initialized to 1 at startup OPTARG - argument Vars used: OPTERR: disable printing of error messages """ # TODO: need to handle explicit args. try: # NOTE: If first char is a colon, error reporting is different. Alpine # might not use that? spec_str = argv[0] var_name = argv[1] except IndexError: raise args.UsageError('getopts optstring name [arg]') try: spec = _GETOPTS_CACHE[spec_str] except KeyError: spec = _ParseOptSpec(spec_str) _GETOPTS_CACHE[spec_str] = spec # These errors are fatal errors, not like the builtin exiting with code 1. # Because the invariants of the shell have been violated! v = mem.GetVar('OPTIND') if v.tag != value_e.Str: e_die('OPTIND should be a string, got %r', v) try: optind = int(v.s) except ValueError: e_die("OPTIND doesn't look like an integer, got %r", v.s) user_argv = argv[2:] or mem.GetArgv() status, opt_char, optarg, optind = _GetOpts(spec, user_argv, optind) # Bug fix: bash-completion uses a *local* OPTIND ! Not global. state.SetStringDynamic(mem, var_name, opt_char) state.SetStringDynamic(mem, 'OPTARG', optarg) state.SetStringDynamic(mem, 'OPTIND', str(optind)) return status
def Umask(argv): if len(argv) == 0: # umask() has a dumb API: you can't get it without modifying it first! # NOTE: dash disables interrupts around the two umask() calls, but that # shouldn't be a concern for us. Signal handlers won't call umask(). mask = posix.umask(0) posix.umask(mask) # print('0%03o' % mask) # octal format return 0 if len(argv) == 1: a = argv[0] try: new_mask = int(a, 8) except ValueError: # NOTE: This happens if we have '8' or '9' in the input too. util.warn('*** umask with symbolic input not implemented ***') return 1 else: posix.umask(new_mask) return 0 raise args.UsageError('umask: unexpected arguments')
def OshCommandMain(argv): """Run an 'oshc' tool. 'osh' is short for "osh compiler" or "osh command". TODO: - oshc --help oshc deps --path: the $PATH to use to find executables. What about libraries? NOTE: we're leaving out su -c, find, xargs, etc.? Those should generally run functions using the $0 pattern. --chained-command sudo """ try: action = argv[0] except IndexError: raise args.UsageError('oshc: Missing required subcommand.') if action not in SUBCOMMANDS: raise args.UsageError('oshc: Invalid subcommand %r.' % action) try: script_name = argv[1] except IndexError: script_name = '<stdin>' f = sys.stdin else: try: f = open(script_name) except IOError as e: util.error("Couldn't open %r: %s", script_name, os.strerror(e.errno)) return 2 pool = alloc.Pool() arena = pool.NewArena() arena.PushSource(script_name) line_reader = reader.FileLineReader(f, arena) _, c_parser = parse_lib.MakeParser(line_reader, arena) try: node = c_parser.ParseWholeFile() except util.ParseError as e: ui.PrettyPrintError(e, arena, sys.stderr) print('parse error: %s' % e.UserErrorString(), file=sys.stderr) return 2 else: # TODO: Remove this older form of error handling. if not node: err = c_parser.Error() assert err, err # can't be empty ui.PrintErrorStack(err, arena, sys.stderr) return 2 # parse error is code 2 f.close() # Columns for list-* # path line name # where name is the binary path, variable name, or library path. # bin-deps and lib-deps can be used to make an app bundle. # Maybe I should list them together? 'deps' can show 4 columns? # # path, line, type, name # # --pretty can show the LST location. # stderr: show how we're following imports? if action == 'translate': # TODO: FIx this invocation up. #debug_spans = opt.debug_spans debug_spans = False osh2oil.PrintAsOil(arena, node, debug_spans) elif action == 'format': # TODO: autoformat code raise NotImplementedError(action) elif action == 'deps': deps.Deps(node) elif action == 'undefined-vars': # could be environment variables pass else: raise AssertionError # Checked above return 0
def OshCommandMain(argv): """Run an 'oshc' tool. 'osh' is short for "osh compiler" or "osh command". TODO: - oshc --help oshc deps --path: the $PATH to use to find executables. What about libraries? NOTE: we're leaving out su -c, find, xargs, etc.? Those should generally run functions using the $0 pattern. --chained-command sudo """ try: action = argv[0] except IndexError: raise args.UsageError('oshc: Missing required subcommand.') if action not in SUBCOMMANDS: raise args.UsageError('oshc: Invalid subcommand %r.' % action) try: script_name = argv[1] except IndexError: script_name = '<stdin>' f = sys.stdin else: try: f = open(script_name) except IOError as e: util.error("Couldn't open %r: %s", script_name, posix.strerror(e.errno)) return 2 pool = alloc.Pool() arena = pool.NewArena() arena.PushSource(script_name) line_reader = reader.FileLineReader(f, arena) aliases = {} # Dummy value; not respecting aliases! parse_ctx = parse_lib.ParseContext(arena, aliases) _, c_parser = parse_ctx.MakeParser(line_reader) try: node = main_loop.ParseWholeFile(c_parser) except util.ParseError as e: ui.PrettyPrintError(e, arena, sys.stderr) return 2 assert node is not None f.close() # Columns for list-* # path line name # where name is the binary path, variable name, or library path. # bin-deps and lib-deps can be used to make an app bundle. # Maybe I should list them together? 'deps' can show 4 columns? # # path, line, type, name # # --pretty can show the LST location. # stderr: show how we're following imports? if action == 'translate': osh2oil.PrintAsOil(arena, node) elif action == 'arena': # for debugging osh2oil.PrintArena(arena) elif action == 'spans': # for debugging osh2oil.PrintSpans(arena) elif action == 'format': # TODO: autoformat code raise NotImplementedError(action) elif action == 'deps': deps.Deps(node) elif action == 'undefined-vars': # could be environment variables raise NotImplementedError else: raise AssertionError # Checked above return 0
def Trap(argv, traps, nodes_to_run, ex): arg, i = TRAP_SPEC.Parse(argv) if arg.p: # Print registered handlers for name, value in traps.iteritems(): print(name) print(value) print() sys.stdout.flush() return 0 if arg.l: # List valid signals and hooks ordered = _SIGNAL_NAMES.items() ordered.sort(key=lambda x: x[1]) for name in _HOOK_NAMES: print(' %s' % name) for name, int_val in ordered: print('%2d %s' % (int_val, name)) sys.stdout.flush() return 0 try: code_str = argv[0] sig_spec = argv[1] except IndexError: raise args.UsageError('trap CODE SIGNAL_SPEC') # NOTE: sig_spec isn't validated when removing handlers. if code_str == '-': if sig_spec in _HOOK_NAMES: try: del traps[sig_spec] except KeyError: pass return 0 sig_val = _GetSignalValue(sig_spec) if sig_val is not None: try: del traps[sig_spec] except KeyError: pass # Restore default if sig_val == signal.SIGINT: RegisterSigIntHandler() else: signal.signal(sig_val, signal.SIG_DFL) return 0 util.error("Can't remove invalid trap %r" % sig_spec) return 1 # Try parsing the code first. node = ex.ParseTrapCode(code_str) if node is None: return 1 # ParseTrapCode() prints an error for us. # Register a hook. if sig_spec in _HOOK_NAMES: if sig_spec in ('ERR', 'RETURN', 'DEBUG'): util.warn("*** The %r isn't yet implemented in OSH ***", sig_spec) traps[sig_spec] = _TrapHandler(node, nodes_to_run) return 0 # Register a signal. sig_val = _GetSignalValue(sig_spec) if sig_val is not None: handler = _TrapHandler(node, nodes_to_run) # For signal handlers, the traps dictionary is used only for debugging. traps[sig_spec] = handler signal.signal(sig_val, handler) return 0 util.error('Invalid trap %r' % sig_spec) return 1
def OpyCommandMain(argv): """Dispatch to the right action.""" # TODO: Use core/args. #opts, argv = Options().parse_args(argv) try: action = argv[0] except IndexError: raise args.UsageError('opy: Missing required subcommand.') if action in ('parse', 'compile', 'eval', 'repl', 'run'): loader = util.GetResourceLoader() f = loader.open(PICKLE_REL_PATH) gr = grammar.Grammar() gr.load(f) f.close() # In Python 2 code, always use from __future__ import print_function. try: del gr.keywords["print"] except KeyError: pass symbols = Symbols(gr) pytree.Init(symbols) # for type_repr() pretty printing transformer.Init(symbols) # for _names and other dicts tr = transformer.Transformer() else: # e.g. pgen2 doesn't use any of these. Maybe we should make a different # tool. gr = None symbols = None tr = None # # Actions # if action == 'pgen2': grammar_path = argv[1] pickle_path = argv[2] WriteGrammar(grammar_path, pickle_path) elif action == 'stdlib-parse': # This is what the compiler/ package was written against. import parser py_path = argv[1] with open(py_path) as f: st = parser.suite(f.read()) tree = st.totuple() printer = TupleTreePrinter(HostStdlibNames()) printer.Print(tree) n = CountTupleTree(tree) log('COUNT %d', n) elif action == 'lex': py_path = argv[1] with open(py_path) as f: tokens = tokenize.generate_tokens(f.readline) for typ, val, start, end, unused_line in tokens: print('%10s %10s %-10s %r' % (start, end, token.tok_name[typ], val)) elif action == 'parse': py_path = argv[1] with open(py_path) as f: tokens = tokenize.generate_tokens(f.readline) p = parse.Parser(gr, convert=skeleton.py2st) parse_tree = driver.PushTokens(p, tokens, gr.symbol2number['file_input']) if isinstance(parse_tree, tuple): n = CountTupleTree(parse_tree) log('COUNT %d', n) printer = TupleTreePrinter(transformer._names) printer.Print(parse_tree) else: tree.PrettyPrint(sys.stdout) log('\tChildren: %d' % len(tree.children), file=sys.stderr) elif action == 'compile': # 'opyc compile' is pgen2 + compiler2 py_path = argv[1] out_path = argv[2] with open(py_path) as f: co = skeleton.Compile(f, py_path, gr, 'file_input', 'exec') log("Compiled to %d bytes of bytecode", len(co.co_code)) # Write the .pyc file with open(out_path, 'wb') as out_f: h = misc.getPycHeader(py_path) out_f.write(h) marshal.dump(co, out_f) elif action == 'eval': # Like compile, but parses to a code object and prints it py_expr = argv[1] f = cStringIO.StringIO(py_expr) co = skeleton.Compile(f, '<eval input>', gr, 'eval_input', 'eval') v = dis_tool.Visitor() v.show_code(co) print() print('RESULT:') print(eval(co)) elif action == 'repl': # Like eval in a loop while True: py_expr = raw_input('opy> ') f = cStringIO.StringIO(py_expr) # TODO: change this to 'single input'? Why doesn't this work? co = skeleton.Compile(f, '<REPL input>', gr, 'eval_input', 'eval') v = dis_tool.Visitor() v.show_code(co) print(eval(co)) elif action == 'dis': pyc_path = argv[1] try: report_path = argv[2] report_f = open(report_path, 'w') except IndexError: report_f = sys.stdout with open(pyc_path, 'rb') as f: # TODO: Make this a flag. #v = dis_tool.Visitor(dis_bytecode=False) v = dis_tool.Visitor() #v = dis_tool.Visitor(co_name='_parse') v.Visit(f) v.Report(report_f) elif action == 'dis-md5': pyc_paths = argv[1:] if not pyc_paths: raise args.UsageError( 'dis-md5: At least one .pyc path is required.') for path in pyc_paths: h = hashlib.md5() with open(path) as f: magic = f.read(4) h.update(magic) ignored_timestamp = f.read(4) while True: b = f.read(64 * 1024) if not b: break h.update(b) print('%6d %s %s' % (os.path.getsize(path), h.hexdigest(), path)) elif action == 'run': # TODO: Add an option like -v in __main__ #level = logging.DEBUG if args.verbose else logging.WARNING #logging.basicConfig(level=level) #logging.basicConfig(level=logging.DEBUG) # Compile and run, without writing pyc file py_path = argv[1] opy_argv = argv[1:] if py_path.endswith('.py'): with open(py_path) as f: co = skeleton.Compile(f, py_path, gr, 'file_input', 'exec') execfile.run_code_object(co, opy_argv) elif py_path.endswith('.pyc') or py_path.endswith('.opyc'): with open(py_path) as f: f.seek(8) # past header. TODO: validate it! co = marshal.load(f) execfile.run_code_object(co, opy_argv) else: raise args.UsageError('Invalid path %r' % py_path) else: raise args.UsageError('Invalid action %r' % action)
def OpyCommandMain(argv): """Dispatch to the right action.""" # TODO: Use core/args. #opts, argv = Options().parse_args(argv) try: action = argv[0] except IndexError: raise args.UsageError('opy: Missing required subcommand.') argv = argv[1:] # TODO: Should I do input.ReadRequiredArg()? # That will shift the input. if action in ('parse', 'compile', 'dis', 'cfg', 'compile-ovm', 'eval', 'repl', 'run', 'run-ovm'): loader = util.GetResourceLoader() f = loader.open(PICKLE_REL_PATH) gr = grammar.Grammar() gr.load(f) f.close() # In Python 2 code, always use from __future__ import print_function. try: del gr.keywords["print"] except KeyError: pass symbols = Symbols(gr) pytree.Init(symbols) # for type_repr() pretty printing transformer.Init(symbols) # for _names and other dicts compiler = skeleton.Compiler(gr) else: # e.g. pgen2 doesn't use any of these. Maybe we should make a different # tool. compiler = None # TODO: Also have a run_spec for 'opyc run'. compile_spec = args.OilFlags() compile_spec.Flag('-emit-docstring', args.Bool, default=True, help='Whether to emit docstrings') compile_spec.Flag('-fast-ops', args.Bool, default=True, help='Whether to emit LOAD_FAST, STORE_FAST, etc.') compile_spec.Flag('-oil-subset', args.Bool, default=False, help='Only allow the constructs necessary to implement' 'Oil. Example: using multiple inheritance will abort ' 'compilation.') # # Actions # if action == 'pgen2': grammar_path = argv[0] pickle_path = argv[1] WriteGrammar(grammar_path, pickle_path) elif action == 'stdlib-parse': # This is what the compiler/ package was written against. import parser py_path = argv[1] with open(py_path) as f: st = parser.suite(f.read()) tree = st.totuple() printer = TupleTreePrinter(HostStdlibNames()) printer.Print(tree) n = CountTupleTree(tree) log('COUNT %d', n) elif action == 'lex': py_path = argv[0] with open(py_path) as f: tokens = tokenize.generate_tokens(f.readline) for typ, val, start, end, unused_line in tokens: print('%10s %10s %-10s %r' % (start, end, token.tok_name[typ], val)) elif action == 'parse': py_path = argv[0] with open(py_path) as f: tokens = tokenize.generate_tokens(f.readline) p = parse.Parser(gr, convert=skeleton.py2st) parse_tree = driver.PushTokens(p, tokens, gr.symbol2number['file_input']) if isinstance(parse_tree, tuple): n = CountTupleTree(parse_tree) log('COUNT %d', n) printer = TupleTreePrinter(transformer._names) printer.Print(parse_tree) else: tree.PrettyPrint(sys.stdout) log('\tChildren: %d' % len(tree.children), file=sys.stderr) elif action == 'cfg': # output Control Flow Graph opt, i = compile_spec.Parse(argv) py_path = argv[i] with open(py_path) as f: graph = compiler.Compile(f, opt, 'exec', return_cfg=True) print(graph) elif action == 'compile': # 'opyc compile' is pgen2 + compiler2 # spec.Arg('action', ['foo', 'bar']) # But that leads to some duplication. opt, i = compile_spec.Parse(argv) py_path = argv[i] out_path = argv[i + 1] with open(py_path) as f: co = compiler.Compile(f, opt, 'exec') log("Compiled to %d bytes of top-level bytecode", len(co.co_code)) # Write the .pyc file with open(out_path, 'wb') as out_f: h = misc.getPycHeader(py_path) out_f.write(h) marshal.dump(co, out_f) elif action == 'compile-ovm': opt, i = compile_spec.Parse(argv) py_path = argv[i] out_path = argv[i + 1] # Compile to OVM bytecode with open(py_path) as f: co = compiler.Compile(f, opt, 'ovm') log("Compiled to %d bytes of top-level bytecode", len(co.co_code)) # Write the .pyc file with open(out_path, 'wb') as out_f: if 1: out_f.write(co.co_code) else: h = misc.getPycHeader(py_path) out_f.write(h) marshal.dump(co, out_f) log('Wrote only the bytecode to %r', out_path) elif action == 'eval': # Like compile, but parses to a code object and prints it opt, i = compile_spec.Parse(argv) py_expr = argv[i] f = skeleton.StringInput(py_expr, '<eval input>') co = compiler.Compile(f, opt, 'eval') v = dis_tool.Visitor() v.show_code(co) print() print('RESULT:') print(eval(co)) elif action == 'repl': # Like eval in a loop while True: py_expr = raw_input('opy> ') f = skeleton.StringInput(py_expr, '<REPL input>') # TODO: change this to 'single input'? Why doesn't this work? co = compiler.Compile(f, opt, 'eval') v = dis_tool.Visitor() v.show_code(co) print(eval(co)) elif action == 'dis-tables': out_dir = argv[0] pyc_paths = argv[1:] out = TableOutput(out_dir) for pyc_path in pyc_paths: with open(pyc_path) as f: magic, unixtime, timestamp, code = dis_tool.unpack_pyc(f) WriteDisTables(pyc_path, code, out) out.Close() elif action == 'dis': opt, i = compile_spec.Parse(argv) path = argv[i] v = dis_tool.Visitor() if path.endswith('.py'): with open(path) as f: co = compiler.Compile(f, opt, 'exec') log("Compiled to %d bytes of top-level bytecode", len(co.co_code)) v.show_code(co) else: # assume pyc_path with open(path, 'rb') as f: v.Visit(f) elif action == 'dis-md5': pyc_paths = argv if not pyc_paths: raise args.UsageError( 'dis-md5: At least one .pyc path is required.') for path in pyc_paths: h = hashlib.md5() with open(path) as f: magic = f.read(4) h.update(magic) ignored_timestamp = f.read(4) while True: b = f.read(64 * 1024) if not b: break h.update(b) print('%6d %s %s' % (os.path.getsize(path), h.hexdigest(), path)) elif action == 'run': # Compile and run, without writing pyc file # TODO: Add an option like -v in __main__ #level = logging.DEBUG if args.verbose else logging.WARNING #logging.basicConfig(level=level) #logging.basicConfig(level=logging.DEBUG) opt, i = compile_spec.Parse(argv) py_path = argv[i] opy_argv = argv[i:] if py_path.endswith('.py'): with open(py_path) as f: co = compiler.Compile(f, opt, 'exec') num_ticks = execfile.run_code_object(co, opy_argv) elif py_path.endswith('.pyc') or py_path.endswith('.opyc'): with open(py_path) as f: f.seek(8) # past header. TODO: validate it! co = marshal.load(f) num_ticks = execfile.run_code_object(co, opy_argv) else: raise args.UsageError('Invalid path %r' % py_path) elif action == 'run-ovm': # Compile and run, without writing pyc file opt, i = compile_spec.Parse(argv) py_path = argv[1] opy_argv = argv[1:] if py_path.endswith('.py'): #mode = 'exec' mode = 'ovm' # OVM bytecode is different! with open(py_path) as f: co = compiler.Compile(f, opt, mode) log('Compiled to %d bytes of OVM code', len(co.co_code)) num_ticks = ovm.run_code_object(co, opy_argv) elif py_path.endswith('.pyc') or py_path.endswith('.opyc'): with open(py_path) as f: f.seek(8) # past header. TODO: validate it! co = marshal.load(f) num_ticks = ovm.run_code_object(co, opy_argv) else: raise args.UsageError('Invalid path %r' % py_path) else: raise args.UsageError('Invalid action %r' % action)
def SetShoptOption(self, opt_name, b): """ For shopt -s/-u. """ if opt_name not in self.SHOPT_OPTIONS: raise args.UsageError('Invalid option %r' % opt_name) setattr(self, opt_name, b)
def _BuildCompletionChain(argv, arg, ex): """Given flags to complete/compgen, built a ChainedCompleter.""" actions = [] # NOTE: bash doesn't actually check the name until completion time, but # obviously it's better to check here. if arg.F: func_name = arg.F func = ex.funcs.get(func_name) if func is None: raise args.UsageError('Function %r not found' % func_name) actions.append(completion.ShellFuncAction(ex, func)) # NOTE: We need completion for -A action itself!!! bash seems to have it. for name in arg.actions: if name == 'alias': a = _SortedWordsAction(ex.aliases) elif name == 'binding': # TODO: Where do we get this from? a = _SortedWordsAction(['vi-delete']) elif name == 'command': # TODO: This needs keywords too actions.append(_SortedWordsAction(builtin.BUILTIN_NAMES)) a = completion.ExternalCommandAction(ex.mem) elif name == 'directory': a = completion.FileSystemAction(dirs_only=True) elif name == 'file': a = completion.FileSystemAction() elif name == 'function': a = _SortedWordsAction(ex.funcs) elif name == 'job': a = _SortedWordsAction(['jobs-not-implemented']) elif name == 'user': a = _UsersAction() elif name == 'variable': a = completion.VariablesAction(ex.mem) elif name == 'helptopic': a = _SortedWordsAction(osh_help.TOPIC_LOOKUP) elif name == 'setopt': a = _SortedWordsAction(state.SET_OPTION_NAMES) elif name == 'shopt': a = _SortedWordsAction(state.SHOPT_OPTION_NAMES) elif name == 'signal': a = _SortedWordsAction(['TODO:signals']) elif name == 'stopped': a = _SortedWordsAction(['jobs-not-implemented']) else: raise NotImplementedError(name) actions.append(a) # e.g. -W comes after -A directory if arg.W: # TODO: Split with IFS. Is that done at registration time or completion # time? actions.append(completion.WordsAction(arg.W.split())) if not actions: raise args.UsageError('No actions defined in completion: %s' % argv) chain = completion.ChainedCompleter( actions, prefix=arg.P or '', suffix=arg.S or '') return chain
def OpyCommandMain(argv): """Dispatch to the right action.""" # TODO: Use core/args. opts, argv = Options().parse_args(argv) try: action = argv[0] except IndexError: raise args.UsageError('opy: Missing required subcommand.') if action in ('parse', 'compile'): loader = util.GetResourceLoader() f = loader.open(PICKLE_REL_PATH) gr = grammar.Grammar() gr.load(f) f.close() # In Python 2 code, always use from __future__ import print_function. try: del gr.keywords["print"] except KeyError: pass FILE_INPUT = gr.symbol2number['file_input'] symbols = Symbols(gr) pytree.Init(symbols) # for type_repr() pretty printing transformer.Init(symbols) # for _names and other dicts else: # e.g. pgen2 doesn't use any of these. Maybe we should make a different # tool. gr = None FILE_INPUT = None symbols = None #do_glue = False do_glue = True if do_glue: # Make it a flag # Emulating parser.st structures from parsermodule.c. # They have a totuple() method, which outputs tuples like this. def py2st(gr, raw_node): type, value, context, children = raw_node # See pytree.Leaf if context: _, (lineno, column) = context else: lineno = 0 # default in Leaf column = 0 if children: return (type, ) + tuple(children) else: return (type, value, lineno, column) convert = py2st else: convert = pytree.convert dr = driver.Driver(gr, convert=convert) if action == 'pgen2': grammar_path = argv[1] pickle_path = argv[2] WriteGrammar(grammar_path, pickle_path) elif action == 'stdlib-parse': # This is what the compiler/ package was written against. import parser py_path = argv[1] with open(py_path) as f: st = parser.suite(f.read()) tree = st.totuple() n = CountTupleTree(tree) log('COUNT %d', n) printer = TupleTreePrinter(HostStdlibNames()) printer.Print(tree) elif action == 'parse': py_path = argv[1] with open(py_path) as f: tokens = tokenize.generate_tokens(f.readline) tree = dr.parse_tokens(tokens, start_symbol=FILE_INPUT) if isinstance(tree, tuple): n = CountTupleTree(tree) log('COUNT %d', n) printer = TupleTreePrinter(transformer._names) printer.Print(tree) else: tree.PrettyPrint(sys.stdout) log('\tChildren: %d' % len(tree.children), file=sys.stderr) elif action == 'compile': # 'opy compile' is pgen2 + compiler2 py_path = argv[1] out_path = argv[2] if do_glue: py_parser = Pgen2PythonParser(dr, FILE_INPUT) printer = TupleTreePrinter(transformer._names) tr = transformer.Pgen2Transformer(py_parser, printer) else: tr = transformer.Transformer() with open(py_path) as f: contents = f.read() co = pycodegen.compile(contents, py_path, 'exec', transformer=tr) log("Code length: %d", len(co.co_code)) # Write the .pyc file with open(out_path, 'wb') as out_f: h = pycodegen.getPycHeader(py_path) out_f.write(h) marshal.dump(co, out_f) elif action == 'dis': pyc_path = argv[1] try: report_path = argv[2] report_f = open(report_path, 'w') except IndexError: report_f = sys.stdout with open(pyc_path, 'rb') as f: # TODO: Make this a flag. #v = inspect_pyc.Visitor(dis_bytecode=False) v = inspect_pyc.Visitor() v.Visit(f) v.Report(report_f) elif action == 'dis-md5': pyc_paths = argv[1:] if not pyc_paths: raise args.UsageError( 'dis-md5: At least one .pyc path is required.') for path in pyc_paths: h = hashlib.md5() with open(path) as f: magic = f.read(4) h.update(magic) ignored_timestamp = f.read(4) while True: b = f.read(64 * 1024) if not b: break h.update(b) print('%6d %s %s' % (os.path.getsize(path), h.hexdigest(), path)) # NOTE: Unused elif action == 'old-compile': py_path = argv[1] out_path = argv[2] if do_glue: py_parser = Pgen2PythonParser(dr, FILE_INPUT) printer = TupleTreePrinter(transformer._names) tr = transformer.Pgen2Transformer(py_parser, printer) else: tr = transformer.Transformer() f = open(py_path) contents = f.read() co = pycodegen.compile(contents, py_path, 'exec', transformer=tr) log("Code length: %d", len(co.co_code)) # Write the .pyc file with open(out_path, 'wb') as out_f: h = pycodegen.getPycHeader(py_path) out_f.write(h) marshal.dump(co, out_f) # TODO: Not used elif action == 'compile2': in_path = argv[1] out_path = argv[2] from compiler2 import pycodegen as pycodegen2 from misc import stdlib_compile stdlib_compile.compileAndWrite(in_path, out_path, pycodegen2.compile) elif action == 'run': # TODO: Add an option like -v in __main__ #level = logging.DEBUG if args.verbose else logging.WARNING #logging.basicConfig(level=level) #logging.basicConfig(level=logging.DEBUG) # Compile and run, without writing pyc file py_path = argv[1] opy_argv = argv[1:] if py_path.endswith('.py'): py_parser = Pgen2PythonParser(dr, FILE_INPUT) printer = TupleTreePrinter(transformer._names) tr = transformer.Pgen2Transformer(py_parser, printer) with open(py_path) as f: contents = f.read() co = pycodegen.compile(contents, py_path, 'exec', transformer=tr) #execfile.run_code_object(co, opy_argv) elif py_path.endswith('.pyc') or py_path.endswith('.opyc'): with open(py_path) as f: f.seek(8) # past header. TODO: validate it! co = marshal.load(f) #execfile.run_code_object(co, opy_argv) else: raise args.UsageError('Invalid path %r' % py_path) else: raise args.UsageError('Invalid action %r' % action)