Exemple #1
0
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()
Exemple #2
0
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()
Exemple #3
0
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()
Exemple #4
0
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")
Exemple #5
0
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)
Exemple #6
0
 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_)
Exemple #7
0
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)
    )
Exemple #9
0
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)
Exemple #10
0
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)
Exemple #11
0
 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)
Exemple #12
0
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
Exemple #13
0
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
Exemple #14
0
 def update_event(self, inp=-1):
     self.set_output_val(0, runpy._get_module_details(self.input(0), self.input(1)))
Exemple #15
0

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:
Exemple #16
0
    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()
Exemple #17
0
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)
Exemple #18
0
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
Exemple #19
0
    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