def main(): port_var = 'MYSTIKOS_PDB_PORT' port = os.environ.get(port_var) if not port: print( 'MYSTIKOS_PDB_PORT environment variable not set. Defaulting to port 5678' ) port = 5678 else: port = int(port) print('Mystikos pdb waiting for connections at port %d' % port) sys.stdout.flush() host = '127.0.0.1' client_socket = socket.create_server((host, port), family=socket.AF_INET, reuse_port=False) client_socket.listen(1) connection, address = client_socket.accept() fd = connection.makefile('rw') args = sys.argv[1:] mainpyfile = args[0] run_as_module = False if args[0] == '-m': run_as_module = True mainpyfile = args[1] args = args[1:] # Update args sys.argv[:] = args if not run_as_module: mainpyfile = os.path.realpath(mainpyfile) # Replace pdb's dir with script's dir in front of module search path. sys.path[0] = os.path.dirname(mainpyfile) else: runpy._get_module_details(mainpyfile) pdb = Pdb(completekey='tab', stdin=fd, stdout=fd) # Alias n, c, s commands to show source listing. pdb.rcLines.extend( ['alias n n;; l .', 'alias c c;; l .', 'alias s s;; l .']) try: if run_as_module: pdb._runmodule(mainpyfile) else: pdb._runscript(mainpyfile) except: pass connection.close() client_socket.close()
def setup_connection(): opts = ptvsd.options pydevd.apply_debugger_options({ 'server': not opts.client, 'client': opts.host, 'port': opts.port, 'multiprocess': opts.multiprocess, }) if opts.multiprocess: listen_for_subprocesses() # We need to set up sys.argv[0] before invoking attach() or enable_attach(), # because they use it to report the 'process' event. Thus, we can't rely on # run_path() and run_module() doing that, even though they will eventually. if opts.target_kind == 'code': sys.argv[0] = '-c' elif opts.target_kind == 'file': sys.argv[0] = opts.target elif opts.target_kind == 'module': # Add current directory to path, like Python itself does for -m. This must # be in place before trying to use find_spec below to resolve submodules. sys.path.insert(0, '') # We want to do the same thing that run_module() would do here, without # actually invoking it. On Python 3, it's exposed as a public API, but # on Python 2, we have to invoke a private function in runpy for this. # Either way, if it fails to resolve for any reason, just leave argv as is. try: if sys.version_info >= (3,): from importlib.util import find_spec spec = find_spec(opts.target) if spec is not None: sys.argv[0] = spec.origin else: _, _, _, sys.argv[0] = runpy._get_module_details(opts.target) except Exception: ptvsd.log.exception('Error determining module path for sys.argv') else: assert False ptvsd.log.debug('sys.argv after patching: {0!r}', sys.argv) addr = (opts.host, opts.port) global daemon if opts.no_debug: daemon = ptvsd.runner.Daemon() if not daemon.wait_for_launch(addr): return elif opts.client: daemon = ptvsd._remote.attach(addr) else: daemon = ptvsd._remote.enable_attach(addr) if opts.wait: ptvsd.wait_for_attach()
def run_module(mod_name, init_globals=None, run_name=None, alter_sys=False): """Execute a module's code without importing it Returns the resulting top level namespace dictionary """ mod_name, mod_spec, code = _get_module_details(mod_name) if run_name is None: run_name = mod_name if alter_sys: return _run_module_code(code, init_globals, run_name, mod_spec) raise Exception("alter_sys must be true")
def main(): parser = get_arg_parser() args, rest = parser.parse_known_args() if args.run or args.run_module: if args.run: code = compile(open(args.stats, mode='rb').read(), '__main__', 'exec', dont_inherit=True) fname = args.stats elif args.run_module: if PY2: mod_name, loader, code, fname = runpy._get_module_details(args.stats) else: mod_name, mod_spec, code = runpy._get_module_details(args.stats) fname = mod_spec.origin s = get_profiler(args.cpu) globs = { '__file__': fname, '__name__': '__main__', '__package__': None, } sys.argv[:] = [fname] + rest sys.path.insert(0, os.path.dirname(args.stats)) try: s.runctx(code, globs, None) except SystemExit: pass s.create_stats() else: s = pstats.Stats(args.stats) if args.out and args.pstat: filename = os.path.splitext(args.out)[0] + '.pstat' s.dump_stats(filename) render(s.stats, get_out(args.out), args.format, args.threshold / 100, args.width, args.row_height, args.font_size, args.log_mult)
def convert(self, value, param, ctx): # inspired by @htch's fork. # https://github.com/htch/profiling/commit/4a4eb6e try: mod_name, loader, code, filename = runpy._get_module_details(value) except ImportError as exc: ctx.fail(str(exc)) # follow runpy's behavior. pkg_name = mod_name.rpartition('.')[0] globals_ = sys.modules['__main__'].__dict__.copy() globals_.update(__name__='__main__', __file__=filename, __loader__=loader, __package__=pkg_name) return (filename, code, globals_)
def run_module(mod_name, init_globals=None, run_name=None, alter_sys=False): """Execute a module's code without importing it Returns the resulting top level namespace dictionary """ mod_name, mod_spec, code = _get_module_details(mod_name) if run_name is None: run_name = mod_name if alter_sys: return _run_module_code(code, init_globals, run_name, mod_spec) else: # Leave the sys module alone return _run_code(code, {}, init_globals, run_name, mod_spec)
def main_type(arg): """ Lookup a Python callable, either a script, module/package, or entry-point. """ module_spec = None # If the argument is a readable file-system path, assume it's a script. path = pathlib.Path(arg) try: source = path.read_text() except (OSError, IOError): logging.debug( "Could not open %r as a file path, " "assuming the argument is a module/package or an entry-point.", arg, exc_info=True, ) source = None code = None if source is not None: code = compile(source, str(path), "exec") return module_spec, code entry_point = None if ":" in arg: entry_point_src = arg if "=" not in entry_point_src: entry_point_src = "_={}".format(entry_point_src) entry_point = pkg_resources.EntryPoint.parse(entry_point_src) return None, entry_point.resolve() try: _, module_spec, code = runpy._get_module_details(arg) except ImportError: logging.debug( "Could not import %r as module/package, " "assuming the argument is a script or an entry-point.", arg, exc_info=True, ) code = None if code is not None: return module_spec, code raise argparse.ArgumentTypeError( ( "Could not resolve {!r} " "as either a script, module/package or entry point." ).format(arg) )
def run_module(): # Add current directory to path, like Python itself does for -m. This must # be in place before trying to use find_spec below to resolve submodules. sys.path.insert(0, "") # We want to do the same thing that run_module() would do here, without # actually invoking it. On Python 3, it's exposed as a public API, but # on Python 2, we have to invoke a private function in runpy for this. # Either way, if it fails to resolve for any reason, just leave argv as is. argv_0 = sys.argv[0] try: if sys.version_info >= (3,): from importlib.util import find_spec spec = find_spec(options.target) if spec is not None: argv_0 = spec.origin else: _, _, _, argv_0 = runpy._get_module_details(options.target) except Exception: log.swallow_exception("Error determining module path for sys.argv") start_debugging(argv_0) # On Python 2, module name must be a non-Unicode string, because it ends up # a part of module's __package__, and Python will refuse to run the module # if __package__ is Unicode. target = ( compat.filename_bytes(options.target) if sys.version_info < (3,) else options.target ) log.describe_environment("Pre-launch environment:") log.info("Running module {0!r}", target) # Docs say that runpy.run_module is equivalent to -m, but it's not actually # the case for packages - -m sets __name__ to "__main__", but run_module sets # it to "pkg.__main__". This breaks everything that uses the standard pattern # __name__ == "__main__" to detect being run as a CLI app. On the other hand, # runpy._run_module_as_main is a private function that actually implements -m. try: run_module_as_main = runpy._run_module_as_main except AttributeError: log.warning("runpy._run_module_as_main is missing, falling back to run_module.") runpy.run_module(target, alter_sys=True) else: run_module_as_main(target, alter_argv=True)
def _runmodule(self, module_name): self._wait_for_mainpyfile = True self._user_requested_quit = False import runpy mod_name, mod_spec, code = runpy._get_module_details(module_name) self.mainpyfile = self.canonic(code.co_filename) import __main__ __main__.__dict__.update({ "__name__": "__main__", "__file__": self.mainpyfile, "__package__": mod_spec.parent, "__loader__": mod_spec.loader, "__spec__": mod_spec, "__builtins__": __builtins__, }) self.run(code)
def import_module(name, cache=modules): #{ """Execute a module's code without importing it Returns the resulting top level namespace dictionary """ if name in cache: return cache[name] mod_name, loader, code, fname = _get_module_details(name) pkg_name = mod_name.rpartition('.')[0] module = new_module(mod_name) module.__dict__.update( __name__ = mod_name, __file__ = fname, __loader__ = loader, __package__ = pkg_name ) exec code in module.__dict__ return module
def update_event(self, inp=-1): self.set_output_val(0, runpy._get_module_details(self.input(0), self.input(1)))
if __name__ == '__main__': parser = get_arg_parser() args, rest = parser.parse_known_args() if args.run or args.run_module: if args.run: code = compile(open(args.stats, mode='rb').read(), '__main__', 'exec', dont_inherit=True) fname = args.stats elif args.run_module: if PY2: mod_name, loader, code, fname = runpy._get_module_details( args.stats) else: mod_name, mod_spec, code = runpy._get_module_details( args.stats) fname = mod_spec.origin s = get_profiler(args.cpu) globs = { '__file__': fname, '__name__': '__main__', '__package__': None, } sys.argv[:] = [fname] + rest sys.path.insert(0, os.path.dirname(args.stats)) try:
def load(self, clear=False): """ Loads all the config plugin modules to build a working configuration. If there is a ``localconfig`` module on the python path, it will be loaded last, overriding other settings. :param bool clear: Clear out the previous settings before loading """ if clear: self.settings = {} defer = [] # Load all config plugins for conf in pkg_resources.iter_entry_points('pyconfig'): if conf.attrs: raise RuntimeError("config must be a module") mod_name = conf.module_name base_name = conf.name if conf.name != 'any' else None log.info("Loading module '%s'", mod_name) mod_dict = runpy.run_module(mod_name) # If this module wants to be deferred, save it for later if mod_dict.get('deferred', None) is deferred: log.info("Deferring module '%s'", mod_name) mod_dict.pop('deferred') defer.append((mod_name, base_name, mod_dict)) continue self._update(mod_dict, base_name) # Load deferred modules for mod_name, base_name, mod_dict in defer: log.info("Loading deferred module '%s'", mod_name) self._update(mod_dict, base_name) if etcd().configured: # Load etcd stuff mod_dict = etcd().load() if mod_dict: self._update(mod_dict) # Allow localconfig overrides mod_dict = None try: mod_dict = runpy.run_module('localconfig') except ImportError: pass except ValueError as err: if getattr(err, 'message') != '__package__ set to non-string': raise # This is a bad work-around to make this work transparently... # shouldn't really access core stuff like this, but F**k It[tm] mod_name = 'localconfig' if sys.version_info < (2, 7): loader, code, fname = runpy._get_module_details(mod_name) else: _, loader, code, fname = runpy._get_module_details(mod_name) mod_dict = runpy._run_code(code, {}, {}, mod_name, fname, loader, pkg_name=None) if mod_dict: log.info("Loading module 'localconfig'") self._update(mod_dict) self.call_reload_hooks()
def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument('--version', action='version', version='trace 2.0') grp = parser.add_argument_group('Main options', 'One of these (or --report) must be given') grp.add_argument('-c', '--count', action='store_true', help='Count the number of times each line is executed and write ' 'the counts to <module>.cover for each module executed, in ' 'the module\'s directory. See also --coverdir, --file, ' '--no-report below.') grp.add_argument('-t', '--trace', action='store_true', help='Print each line to sys.stdout before it is executed') grp.add_argument('-l', '--listfuncs', action='store_true', help='Keep track of which functions are executed at least once ' 'and write the results to sys.stdout after the program exits. ' 'Cannot be specified alongside --trace or --count.') grp.add_argument('-T', '--trackcalls', action='store_true', help='Keep track of caller/called pairs and write the results to ' 'sys.stdout after the program exits.') grp = parser.add_argument_group('Modifiers') _grp = grp.add_mutually_exclusive_group() _grp.add_argument('-r', '--report', action='store_true', help='Generate a report from a counts file; does not execute any ' 'code. --file must specify the results file to read, which ' 'must have been created in a previous run with --count ' '--file=FILE') _grp.add_argument('-R', '--no-report', action='store_true', help='Do not generate the coverage report files. ' 'Useful if you want to accumulate over several runs.') grp.add_argument('-f', '--file', help='File to accumulate counts over several runs') grp.add_argument('-C', '--coverdir', help='Directory where the report files go. The coverage report ' 'for <package>.<module> will be written to file ' '<dir>/<package>/<module>.cover') grp.add_argument('-m', '--missing', action='store_true', help='Annotate executable lines that were not executed with ' '">>>>>> "') grp.add_argument('-s', '--summary', action='store_true', help='Write a brief summary for each file to sys.stdout. ' 'Can only be used with --count or --report') grp.add_argument('-g', '--timing', action='store_true', help='Prefix each line with the time since the program started. ' 'Only used while tracing') grp = parser.add_argument_group('Filters', 'Can be specified multiple times') grp.add_argument('--ignore-module', action='append', default=[], help='Ignore the given module(s) and its submodules ' '(if it is a package). Accepts comma separated list of ' 'module names.') grp.add_argument('--ignore-dir', action='append', default=[], help='Ignore files in the given directory ' '(multiple directories can be joined by os.pathsep).') parser.add_argument('--module', action='store_true', default=False, help='Trace a module. ') parser.add_argument('progname', nargs='?', help='file to run as main program') parser.add_argument('arguments', nargs=argparse.REMAINDER, help='arguments to the program') opts = parser.parse_args() if opts.ignore_dir: rel_path = 'lib', 'python{0.major}.{0.minor}'.format(sys.version_info) _prefix = os.path.join(sys.base_prefix, *rel_path) _exec_prefix = os.path.join(sys.base_exec_prefix, *rel_path) def parse_ignore_dir(s): s = os.path.expanduser(os.path.expandvars(s)) s = s.replace('$prefix', _prefix).replace('$exec_prefix', _exec_prefix) return os.path.normpath(s) opts.ignore_module = [mod.strip() for i in opts.ignore_module for mod in i.split(',')] opts.ignore_dir = [parse_ignore_dir(s) for i in opts.ignore_dir for s in i.split(os.pathsep)] if opts.report: if not opts.file: parser.error('-r/--report requires -f/--file') results = CoverageResults(infile=opts.file, outfile=opts.file) return results.write_results(opts.missing, opts.summary, opts.coverdir) if not any([opts.trace, opts.count, opts.listfuncs, opts.trackcalls]): parser.error('must specify one of --trace, --count, --report, ' '--listfuncs, or --trackcalls') if opts.listfuncs and (opts.count or opts.trace): parser.error('cannot specify both --listfuncs and (--trace or --count)') if opts.summary and not opts.count: parser.error('--summary can only be used with --count or --report') if opts.progname is None: parser.error('progname is missing: required with the main options') t = Trace(opts.count, opts.trace, countfuncs=opts.listfuncs, countcallers=opts.trackcalls, ignoremods=opts.ignore_module, ignoredirs=opts.ignore_dir, infile=opts.file, outfile=opts.file, timing=opts.timing) try: if opts.module: import runpy module_name = opts.progname mod_name, mod_spec, code = runpy._get_module_details(module_name) sys.argv = [code.co_filename, *opts.arguments] globs = { '__name__': '__main__', '__file__': code.co_filename, '__package__': mod_spec.parent, '__loader__': mod_spec.loader, '__spec__': mod_spec, '__cached__': None, } else: sys.argv = [opts.progname, *opts.arguments] sys.path[0] = os.path.dirname(opts.progname) with open(opts.progname) as fp: code = compile(fp.read(), opts.progname, 'exec') # try to emulate __main__ namespace as much as possible globs = { '__file__': opts.progname, '__name__': '__main__', '__package__': None, '__cached__': None, } t.runctx(code, globs, globs) except OSError as err: sys.exit("Cannot run file %r because: %s" % (sys.argv[0], err)) except SystemExit: pass results = t.results() if not opts.no_report: results.write_results(opts.missing, opts.summary, opts.coverdir)
def main() -> None: """ Main entry point for the CLI. Handles all the argument parsing and runs the subcommands. All arguments are read from sys.args """ _logger = logging.getLogger() parser = argparse.ArgumentParser( prog="pytb", description="Python Toolkit CLI Interface") subcommands = parser.add_subparsers(help="sub-command", dest="command") notify_parser = subcommands.add_parser( "notify", help="Statusnotification about long-running tasks.") notify_parser.add_argument("--every", help="Send a notification every X seconds", metavar="X", type=int) notify_parser.add_argument( "--when-stalled", help= "Send a notification if the script seems to be stalled for more than X seconds", metavar="X", type=int, ) notify_parser.add_argument( "--when-done", action="store_true", default=False, help="Send a notification whenever the script finishes", ) notify_subcommands = notify_parser.add_subparsers(help="notifier", dest="notifier") notify_config = current_config["notify"] notify_via_email = notify_subcommands.add_parser("via-email") notify_via_email.add_argument( "--recipients", nargs="+", help="Recipient addresses for the notifications", default=notify_config.getlist("email_addresses"), ) notify_via_email.add_argument( "--smtp-host", help= "Address of the external SMTP Server used to send notifications via E-Mail", default=notify_config["smtp_host"], ) notify_via_email.add_argument( "--smtp-port", type=int, help="Port the external SMTP Server listens for incoming connections", default=notify_config["smtp_port"], ) notify_via_email.add_argument( "--sender", help="Sender Address for notifications", default=notify_config["sender"], ) notify_via_email.add_argument( "--use-ssl", action="store_true", help="Use a SSL connection to communicate with the SMTP server", default=notify_config.getboolean("smtp_ssl"), ) notify_via_email.add_argument( "-m", action="store_true", help="Load an executable module or package instead of a file", default=False, dest="run_as_module", ) notify_via_email.add_argument("script", help="script path or module name to run") notify_via_email.add_argument( "args", help="additional parameter passed to the script", nargs=argparse.REMAINDER, metavar="args", ) notify_via_stream = notify_subcommands.add_parser("via-stream") notify_via_stream.add_argument( "--stream", help="The writable stream. This can be a filepath or the special \ values `<stdout>` or `<stderr>`", type=to_stream, required=True, ) notify_via_stream.add_argument( "-m", action="store_true", help="Load an executable module or package instead of a file", default=False, dest="run_as_module", ) notify_via_stream.add_argument("script", help="script path or module name to run") notify_via_stream.add_argument( "args", help="additional parameter passed to the script", nargs=argparse.REMAINDER, metavar="args", ) rdb_parser = subcommands.add_parser("rdb", help="Remote debugging over TCP") rdb_subcommands = rdb_parser.add_subparsers(help="function", dest="function") rdb_config = current_config["rdb"] rdb_server = rdb_subcommands.add_parser("server") rdb_server.add_argument( "--host", help="The interface to bind the socket to", default=rdb_config["bind_to"], ) rdb_server.add_argument( "--port", type=int, help="The port to listen for incoming connections", default=rdb_config["port"], ) rdb_server.add_argument( "--patch-stdio", action="store_true", help="Redirect stdio streams to the remote client during debugging", default=rdb_config["patch_stdio"], ) rdb_server.add_argument( "-c", help="commands executed before the script is run", metavar="commands", dest="commands", ) rdb_server.add_argument( "-m", action="store_true", help="Load an executable module or package instead of a file", default=False, dest="run_as_module", ) rdb_server.add_argument("script", help="script path or module name to run") rdb_server.add_argument( "args", help="additional parameter passed to the script", nargs=argparse.REMAINDER, metavar="args", ) rdb_client = rdb_subcommands.add_parser("client") rdb_client.add_argument( "--host", help="Remote host where the debug sessino is running", default=rdb_config["host"], ) rdb_client.add_argument("--port", type=int, help="Remote port to connect to", default=rdb_config["port"]) args = parser.parse_args() if not args.command: parser.error("You need to specify a subcommand") elif args.command == "rdb": if not args.function: rdb_parser.error("You need to specify the function") elif args.function == "client": # create the client instance _logger.info( f"trying to connect to remote debugger at {args.host}:{args.port}..." ) try: RdbClient(args.host, args.port) _logger.warning( f"connection to {args.host}:{args.port} closed.") except ConnectionRefusedError: _logger.error( f"connection to {args.host}:{args.port} refused.") sys.exit(2) elif args.function == "server": mainpyfile = Path(args.script) if not args.run_as_module and not mainpyfile.exists(): _logger.error(f"{args.script} does not exist") sys.exit(1) # overwrite the arguments with the user provided args sys.argv = [str(mainpyfile)] + args.args # Replace this modules dir with script's dir in front of module search path. if not args.run_as_module: sys.path[0] = str(mainpyfile.parent) rdb = Rdb(args.host, args.port, args.patch_stdio) if args.commands: rdb.rcLines.extend(args.commands) # type: ignore while True: # pylint: disable=broad-except,protected-access try: if args.run_as_module: rdb._runmodule(str(mainpyfile)) else: rdb._runscript(str(mainpyfile)) if rdb._user_requested_quit: # type: ignore break _logger.info("The program finished and will be restarted") except PdbRestart: _logger.info( f"Restarting {mainpyfile} with arguments:\n\t {' '.join(args.args)}" ) except SystemExit: # In most cases SystemExit does not warrant a post-mortem session. _logger.info( "The program exited via sys.exit(). Exit status:", end=" ") _logger.info(sys.exc_info()[1]) except SyntaxError: traceback.print_exc() rdb.do_quit(None) sys.exit(1) except Exception: traceback.print_exc() _logger.error( "Uncaught exception. Entering post mortem debugging") _logger.error( "Running 'cont' or 'step' will restart the program") current_tb = sys.exc_info()[2] rdb.interaction(None, current_tb) # type: ignore _logger.info( f"Post mortem debugger finished. The {mainpyfile} will be restarted" ) rdb.do_quit(None) elif args.command == "notify": if not args.when_done and args.every is None and args.when_stalled is None: notify_parser.error( "You need to specify at least one of the notification options \ --when-done, --every or --when-stalled\n") if not args.notifier: notify_parser.error( "You need to specify the notification system to use (EMail or Stream" ) elif args.notifier == "via-stream": notifier: Notify = NotifyViaStream(task=args.script, stream=args.stream) elif args.notifier == "via-email": if not args.recipients: notify_via_email.error( "Make sure to include at least one recipient via the .pytb.conf \ or via the --recipients option\n") notifier = NotifyViaEmail( task=args.script, email_addresses=args.recipients, sender=args.sender, smtp_host=args.smtp_host, smtp_port=args.smtp_port, smtp_ssl=args.use_ssl, ) # assemble the execution environemnt for the script to run script_globals = {"__name__": "__main__"} if args.run_as_module: _, mod_spec, code = runpy._get_module_details( # type: ignore # pylint: disable=protected-access args.script) script_globals.update({ "__file__": code.co_filename, "__package__": mod_spec.parent, "__loader__": mod_spec.loader, "__spec__": mod_spec, }) else: mainpyfile = Path(args.script) # Replace this modules dir with script's dir in front of module search path. sys.path[0] = str(mainpyfile.parent) with mainpyfile.open("rb") as scriptfile: code = compile(scriptfile.read(), args.script, "exec") script_globals.update({"__file__": args.script}) # assemble a dummy frame with the compiled code_object # to use for the code_block creation of the notifier_context dummy_frame = type(FrameType.__name__, (), {})() setattr(dummy_frame, "f_code", code) setattr(dummy_frame, "f_lineno", 0) notifier_context = [] if args.when_stalled is not None: notifier_context.append( notifier.when_stalled(args.when_stalled, caller_frame=dummy_frame)) if args.every is not None: notifier_context.append( notifier.every(args.every, caller_frame=dummy_frame)) if args.when_done: notifier_context.append( notifier.when_done(caller_frame=dummy_frame)) # overwrite the arguments with the user provided args sys.argv = [str(args.script)] + args.args with ExitStack() as notifiers: for context in notifier_context: notifiers.enter_context(context) exec(code, script_globals, script_globals) # pylint: disable=exec-used
def load(self, clear=False): """ Loads all the config plugin modules to build a working configuration. If there is a ``localconfig`` module on the python path, it will be loaded last, overriding other settings. :param bool clear: Clear out the previous settings before loading """ if clear: self.settings = {} defer = [] # Load all config plugins for conf in pkg_resources.iter_entry_points('pyconfig'): if conf.attrs: raise RuntimeError("config must be a module") mod_name = conf.module_name base_name = conf.name if conf.name != 'any' else None log.info("Loading module '%s'", mod_name) mod_dict = runpy.run_module(mod_name) # If this module wants to be deferred, save it for later if mod_dict.get('deferred', None) is deferred: log.info("Deferring module '%s'", mod_name) mod_dict.pop('deferred') defer.append((mod_name, base_name, mod_dict)) continue self._update(mod_dict, base_name) # Load deferred modules for mod_name, base_name, mod_dict in defer: log.info("Loading deferred module '%s'", mod_name) self._update(mod_dict, base_name) # Allow localconfig overrides mod_dict = None try: mod_dict = runpy.run_module('localconfig') except ImportError: pass except ValueError as err: if getattr(err, 'message') != '__package__ set to non-string': raise # This is a bad work-around to make this work transparently... # shouldn't really access core stuff like this, but F**k It[tm] mod_name = 'localconfig' if sys.version_info < (2, 7): loader, code, fname = runpy._get_module_details(mod_name) else: _, loader, code, fname = runpy._get_module_details(mod_name) mod_dict = runpy._run_code(code, {}, {}, mod_name, fname, loader, pkg_name=None) if mod_dict: log.info("Loading module 'localconfig'") self._update(mod_dict) self.call_reload_hooks()
def main(args=None) -> int: # from . import bytecode_reconstructor # logging.getLogger(bytecode_reconstructor.__name__).setLevel(logging.INFO) if args is None: # first element in argv is program name args = sys.argv[1:] opts = cmdline.parse(args) settings.DEBUG = opts.debug setup_logging(opts.debug) # These details of setting up the program to be run is very much inspired by `trace` # from the standard library if opts.module: import runpy module_name = opts.progname _mod_name, mod_spec, code = runpy._get_module_details(module_name) sys.argv = [code.co_filename, *opts.arguments] globs = { "__name__": "__main__", "__file__": code.co_filename, "__package__": mod_spec.parent, "__loader__": mod_spec.loader, "__spec__": mod_spec, "__cached__": None, } else: sys.argv = [opts.progname, *opts.arguments] sys.path[0] = os.path.dirname(opts.progname) with open(opts.progname) as fp: code = compile(fp.read(), opts.progname, "exec") # try to emulate __main__ namespace as much as possible globs = { "__file__": opts.progname, "__name__": "__main__", "__package__": None, "__cached__": None, } start = time.time() recorded_calls, captured_stdout, captured_stderr, exit_status = record_calls( code, globs) end = time.time() elapsed_formatted = f"{end-start:.2f} seconds" if opts.xml: XMLExporter.export( opts.xml, recorded_calls, info={ "cg_trace_version": __version__, "args": " ".join(args), "exit_status": exit_status, "elapsed": elapsed_formatted, "utctimestamp": datetime.utcnow().replace(microsecond=0).isoformat(), }, ) else: print(f"--- Recorded calls (in {elapsed_formatted}) ---") for (call, callee) in recorded_calls: print(f"{call} --> {callee}") print("--- captured stdout ---") print(captured_stdout.getvalue(), end="") print("--- captured stderr ---") print(captured_stderr.getvalue(), end="") return 0