Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
pdb.py3679 linesDownload Raw Back to Lib
1"""2The Python Debugger Pdb3=======================4 5To use the debugger in its simplest form:6 7        >>> import pdb8        >>> pdb.run('<a statement>')9 10The debugger's prompt is '(Pdb) '.  This will stop in the first11function call in <a statement>.12 13Alternatively, if a statement terminated with an unhandled exception,14you can use pdb's post-mortem facility to inspect the contents of the15traceback:16 17        >>> <a statement>18        <exception traceback>19        >>> import pdb20        >>> pdb.pm()21 22The commands recognized by the debugger are listed in the next23section.  Most can be abbreviated as indicated; e.g., h(elp) means24that 'help' can be typed as 'h' or 'help' (but not as 'he' or 'hel',25nor as 'H' or 'Help' or 'HELP').  Optional arguments are enclosed in26square brackets.  Alternatives in the command syntax are separated27by a vertical bar (|).28 29A blank line repeats the previous command literally, except for30'list', where it lists the next 11 lines.31 32Commands that the debugger doesn't recognize are assumed to be Python33statements and are executed in the context of the program being34debugged.  Python statements can also be prefixed with an exclamation35point ('!').  This is a powerful way to inspect the program being36debugged; it is even possible to change variables or call functions.37When an exception occurs in such a statement, the exception name is38printed but the debugger's state is not changed.39 40The debugger supports aliases, which can save typing.  And aliases can41have parameters (see the alias help entry) which allows one a certain42level of adaptability to the context under examination.43 44Multiple commands may be entered on a single line, separated by the45pair ';;'.  No intelligence is applied to separating the commands; the46input is split at the first ';;', even if it is in the middle of a47quoted string.48 49If a file ".pdbrc" exists in your home directory or in the current50directory, it is read in and executed as if it had been typed at the51debugger prompt.  This is particularly useful for aliases.  If both52files exist, the one in the home directory is read first and aliases53defined there can be overridden by the local file.  This behavior can be54disabled by passing the "readrc=False" argument to the Pdb constructor.55 56Aside from aliases, the debugger is not directly programmable; but it57is implemented as a class from which you can derive your own debugger58class, which you can make as fancy as you like.59 60 61Debugger commands62=================63 64"""65# NOTE: the actual command documentation is collected from docstrings of the66# commands and is appended to __doc__ after the class has been defined.67 68import os69import io70import re71import sys72import cmd73import bdb74import dis75import code76import glob77import json78import stat79import token80import types81import atexit82import codeop83import pprint84import signal85import socket86import typing87import asyncio88import inspect89import weakref90import builtins91import tempfile92import textwrap93import tokenize94import itertools95import traceback96import linecache97import selectors98import threading99import _colorize100import _pyrepl.utils101 102from contextlib import ExitStack, closing, contextmanager103from types import CodeType104from warnings import deprecated105 106 107class Restart(Exception):108    """Causes a debugger to be restarted for the debugged python program."""109    pass110 111__all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace",112           "post_mortem", "set_default_backend", "get_default_backend", "help"]113 114 115def find_first_executable_line(code):116    """ Try to find the first executable line of the code object.117 118    Equivalently, find the line number of the instruction that's119    after RESUME120 121    Return code.co_firstlineno if no executable line is found.122    """123    prev = None124    for instr in dis.get_instructions(code):125        if prev is not None and prev.opname == 'RESUME':126            if instr.positions.lineno is not None:127                return instr.positions.lineno128            return code.co_firstlineno129        prev = instr130    return code.co_firstlineno131 132def find_function(funcname, filename):133    cre = re.compile(r'def\s+%s(\s*\[.+\])?\s*[(]' % re.escape(funcname))134    try:135        fp = tokenize.open(filename)136    except OSError:137        lines = linecache.getlines(filename)138        if not lines:139            return None140        fp = io.StringIO(''.join(lines))141    funcdef = ""142    funcstart = 0143    # consumer of this info expects the first line to be 1144    with fp:145        for lineno, line in enumerate(fp, start=1):146            if cre.match(line):147                funcstart, funcdef = lineno, line148            elif funcdef:149                funcdef += line150 151            if funcdef:152                try:153                    code = compile(funcdef, filename, 'exec')154                except SyntaxError:155                    continue156                # We should always be able to find the code object here157                funccode = next(c for c in code.co_consts if158                                isinstance(c, CodeType) and c.co_name == funcname)159                lineno_offset = find_first_executable_line(funccode)160                return funcname, filename, funcstart + lineno_offset - 1161    return None162 163def lasti2lineno(code, lasti):164    linestarts = list(dis.findlinestarts(code))165    linestarts.reverse()166    for i, lineno in linestarts:167        if lasti >= i:168            return lineno169    return 0170 171 172class _rstr(str):173    """String that doesn't quote its repr."""174    def __repr__(self):175        return self176 177 178class _ExecutableTarget:179    filename: str180    code: CodeType | str181    namespace: dict182 183 184class _ScriptTarget(_ExecutableTarget):185    def __init__(self, target):186        self._check(target)187        self._target = self._safe_realpath(target)188 189        # If PYTHONSAFEPATH (-P) is not set, sys.path[0] is the directory190        # of pdb, and we should replace it with the directory of the script191        if not sys.flags.safe_path:192            sys.path[0] = os.path.dirname(self._target)193 194    @staticmethod195    def _check(target):196        """197        Check that target is plausibly a script.198        """199        if not os.path.exists(target):200            print(f'Error: {target} does not exist')201            sys.exit(1)202        if os.path.isdir(target):203            print(f'Error: {target} is a directory')204            sys.exit(1)205 206    @staticmethod207    def _safe_realpath(path):208        """209        Return the canonical path (realpath) if it is accessible from the userspace.210        Otherwise (for example, if the path is a symlink to an anonymous pipe),211        return the original path.212 213        See GH-142315.214        """215        realpath = os.path.realpath(path)216        return realpath if os.path.exists(realpath) else path217 218    def __repr__(self):219        return self._target220 221    @property222    def filename(self):223        return self._target224 225    @property226    def code(self):227        # Open the file each time because the file may be modified228        with io.open_code(self._target) as fp:229            return f"exec(compile({fp.read()!r}, {self._target!r}, 'exec'))"230 231    @property232    def namespace(self):233        return dict(234            __name__='__main__',235            __file__=self._target,236            __builtins__=__builtins__,237            __spec__=None,238        )239 240 241class _ModuleTarget(_ExecutableTarget):242    def __init__(self, target):243        self._target = target244 245        import runpy246        try:247            _, self._spec, self._code = runpy._get_module_details(self._target)248        except ImportError as e:249            print(f"ImportError: {e}")250            sys.exit(1)251        except Exception:252            traceback.print_exc()253            sys.exit(1)254 255    def __repr__(self):256        return self._target257 258    @property259    def filename(self):260        return self._code.co_filename261 262    @property263    def code(self):264        return self._code265 266    @property267    def namespace(self):268        return dict(269            __name__='__main__',270            __file__=os.path.normcase(os.path.abspath(self.filename)),271            __package__=self._spec.parent,272            __loader__=self._spec.loader,273            __spec__=self._spec,274            __builtins__=__builtins__,275        )276 277 278class _ZipTarget(_ExecutableTarget):279    def __init__(self, target):280        import runpy281 282        self._target = os.path.realpath(target)283        sys.path.insert(0, self._target)284        try:285            _, self._spec, self._code = runpy._get_main_module_details()286        except ImportError as e:287            print(f"ImportError: {e}")288            sys.exit(1)289        except Exception:290            traceback.print_exc()291            sys.exit(1)292 293    def __repr__(self):294        return self._target295 296    @property297    def filename(self):298        return self._code.co_filename299 300    @property301    def code(self):302        return self._code303 304    @property305    def namespace(self):306        return dict(307            __name__='__main__',308            __file__=os.path.normcase(os.path.abspath(self.filename)),309            __package__=self._spec.parent,310            __loader__=self._spec.loader,311            __spec__=self._spec,312            __builtins__=__builtins__,313        )314 315 316class _PdbInteractiveConsole(code.InteractiveConsole):317    def __init__(self, ns, message):318        self._message = message319        super().__init__(locals=ns, local_exit=True)320 321    def write(self, data):322        self._message(data, end='')323 324 325# Interaction prompt line will separate file and call info from code326# text using value of line_prefix string.  A newline and arrow may327# be to your liking.  You can set it once pdb is imported using the328# command "pdb.line_prefix = '\n% '".329# line_prefix = ': '    # Use this to get the old situation back330line_prefix = '\n-> '   # Probably a better default331 332 333# The default backend to use for Pdb instances if not specified334# Should be either 'settrace' or 'monitoring'335_default_backend = 'settrace'336 337 338def set_default_backend(backend):339    """Set the default backend to use for Pdb instances."""340    global _default_backend341    if backend not in ('settrace', 'monitoring'):342        raise ValueError("Invalid backend: %s" % backend)343    _default_backend = backend344 345 346def get_default_backend():347    """Get the default backend to use for Pdb instances."""348    return _default_backend349 350 351class Pdb(bdb.Bdb, cmd.Cmd):352    _previous_sigint_handler = None353 354    # Limit the maximum depth of chained exceptions, we should be handling cycles,355    # but in case there are recursions, we stop at 999.356    MAX_CHAINED_EXCEPTION_DEPTH = 999357 358    _file_mtime_table = {}359 360    _last_pdb_instance = None361 362    def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None,363                 nosigint=False, readrc=True, mode=None, backend=None, colorize=False):364        bdb.Bdb.__init__(self, skip=skip, backend=backend if backend else get_default_backend())365        cmd.Cmd.__init__(self, completekey, stdin, stdout)366        sys.audit("pdb.Pdb")367        if stdout:368            self.use_rawinput = 0369        self.prompt = '(Pdb) '370        self.aliases = {}371        self.displaying = {}372        self.mainpyfile = ''373        self._wait_for_mainpyfile = False374        self.tb_lineno = {}375        self.mode = mode376        self.colorize = colorize and _colorize.can_colorize(file=stdout or sys.stdout)377        # Try to load readline if it exists378        try:379            import readline380            # remove some common file name delimiters381            readline.set_completer_delims(' \t\n`@#%^&*()=+[{]}\\|;:\'",<>?')382        except ImportError:383            pass384 385        self.allow_kbdint = False386        self.nosigint = nosigint387        # Consider these characters as part of the command so when the users type388        # c.a or c['a'], it won't be recognized as a c(ontinue) command389        self.identchars = cmd.Cmd.identchars + '=.[](),"\'+-*/%@&|<>~^'390 391        # Read ~/.pdbrc and ./.pdbrc392        self.rcLines = []393        if readrc:394            try:395                with open(os.path.expanduser('~/.pdbrc'), encoding='utf-8') as rcFile:396                    self.rcLines.extend(rcFile)397            except OSError:398                pass399            try:400                with open(".pdbrc", encoding='utf-8') as rcFile:401                    self.rcLines.extend(rcFile)402            except OSError:403                pass404 405        self.commands = {} # associates a command list to breakpoint numbers406        self.commands_defining = False # True while in the process of defining407                                       # a command list408        self.commands_bnum = None # The breakpoint number for which we are409                                  # defining a list410 411        self.async_shim_frame = None412        self.async_awaitable = None413 414        self._chained_exceptions = tuple()415        self._chained_exception_index = 0416 417        self._current_task = None418 419    def set_trace(self, frame=None, *, commands=None):420        Pdb._last_pdb_instance = self421        if frame is None:422            frame = sys._getframe().f_back423 424        if commands is not None:425            self.rcLines.extend(commands)426 427        super().set_trace(frame)428 429    async def set_trace_async(self, frame=None, *, commands=None):430        if self.async_awaitable is not None:431            # We are already in a set_trace_async call, do not mess with it432            return433 434        if frame is None:435            frame = sys._getframe().f_back436 437        # We need set_trace to set up the basics, however, this will call438        # set_stepinstr() will we need to compensate for, because we don't439        # want to trigger on calls440        self.set_trace(frame, commands=commands)441        # Changing the stopframe will disable trace dispatch on calls442        self.stopframe = frame443        # We need to stop tracing because we don't have the privilege to avoid444        # triggering tracing functions as normal, as we are not already in445        # tracing functions446        self.stop_trace()447 448        self.async_shim_frame = sys._getframe()449        self.async_awaitable = None450 451        while True:452            self.async_awaitable = None453            # Simulate a trace event454            # This should bring up pdb and make pdb believe it's debugging the455            # caller frame456            self.trace_dispatch(frame, "opcode", None)457            if self.async_awaitable is not None:458                try:459                    if self.breaks:460                        with self.set_enterframe(frame):461                            # set_continue requires enterframe to work462                            self.set_continue()463                        self.start_trace()464                    await self.async_awaitable465                except Exception:466                    self._error_exc()467            else:468                break469 470        self.async_shim_frame = None471 472        # start the trace (the actual command is already set by set_* calls)473        if self.returnframe is None and self.stoplineno == -1 and not self.breaks:474            # This means we did a continue without any breakpoints, we should not475            # start the trace476            return477 478        self.start_trace()479 480    def sigint_handler(self, signum, frame):481        if self.allow_kbdint:482            raise KeyboardInterrupt483        self.message("\nProgram interrupted. (Use 'cont' to resume).")484        self.set_step()485        self.set_trace(frame)486 487    def reset(self):488        bdb.Bdb.reset(self)489        self.forget()490 491    def forget(self):492        self.lineno = None493        self.stack = []494        self.curindex = 0495        if hasattr(self, 'curframe') and self.curframe:496            self.curframe.f_globals.pop('__pdb_convenience_variables', None)497        self.curframe = None498        self.tb_lineno.clear()499 500    def setup(self, f, tb):501        self.forget()502        self.stack, self.curindex = self.get_stack(f, tb)503        while tb:504            # when setting up post-mortem debugging with a traceback, save all505            # the original line numbers to be displayed along the current line506            # numbers (which can be different, e.g. due to finally clauses)507            lineno = lasti2lineno(tb.tb_frame.f_code, tb.tb_lasti)508            self.tb_lineno[tb.tb_frame] = lineno509            tb = tb.tb_next510        self.curframe = self.stack[self.curindex][0]511        self.set_convenience_variable(self.curframe, '_frame', self.curframe)512        if self._current_task:513            self.set_convenience_variable(self.curframe, '_asynctask', self._current_task)514        self._save_initial_file_mtime(self.curframe)515 516        if self._chained_exceptions:517            self.set_convenience_variable(518                self.curframe,519                '_exception',520                self._chained_exceptions[self._chained_exception_index],521            )522 523        if self.rcLines:524            self.cmdqueue = [525                line for line in self.rcLines526                if line.strip() and not line.strip().startswith("#")527            ]528            self.rcLines = []529 530    @property531    @deprecated("The frame locals reference is no longer cached. Use 'curframe.f_locals' instead.")532    def curframe_locals(self):533        return self.curframe.f_locals534 535    @curframe_locals.setter536    @deprecated("Setting 'curframe_locals' no longer has any effect. Update the contents of 'curframe.f_locals' instead.")537    def curframe_locals(self, value):538        pass539 540    # Override Bdb methods541 542    def user_call(self, frame, argument_list):543        """This method is called when there is the remote possibility544        that we ever need to stop in this function."""545        if self._wait_for_mainpyfile:546            return547        if self.stop_here(frame):548            self.message('--Call--')549            self.interaction(frame, None)550 551    def user_line(self, frame):552        """This function is called when we stop or break at this line."""553        if self._wait_for_mainpyfile:554            if (self.mainpyfile != self.canonic(frame.f_code.co_filename)):555                return556            self._wait_for_mainpyfile = False557        if self.trace_opcodes:558            # GH-127321559            # We want to avoid stopping at an opcode that does not have560            # an associated line number because pdb does not like it561            if frame.f_lineno is None:562                self.set_stepinstr()563                return564        self.bp_commands(frame)565        self.interaction(frame, None)566 567    user_opcode = user_line568 569    def bp_commands(self, frame):570        """Call every command that was set for the current active breakpoint571        (if there is one).572 573        Returns True if the normal interaction function must be called,574        False otherwise."""575        # self.currentbp is set in bdb in Bdb.break_here if a breakpoint was hit576        if getattr(self, "currentbp", False) and \577               self.currentbp in self.commands:578            currentbp = self.currentbp579            self.currentbp = 0580            for line in self.commands[currentbp]:581                self.cmdqueue.append(line)582            self.cmdqueue.append(f'_pdbcmd_restore_lastcmd {self.lastcmd}')583 584    def user_return(self, frame, return_value):585        """This function is called when a return trap is set here."""586        if self._wait_for_mainpyfile:587            return588        frame.f_locals['__return__'] = return_value589        self.set_convenience_variable(frame, '_retval', return_value)590        self.message('--Return--')591        self.interaction(frame, None)592 593    def user_exception(self, frame, exc_info):594        """This function is called if an exception occurs,595        but only if we are to stop at or just below this level."""596        if self._wait_for_mainpyfile:597            return598        exc_type, exc_value, exc_traceback = exc_info599        frame.f_locals['__exception__'] = exc_type, exc_value600        self.set_convenience_variable(frame, '_exception', exc_value)601 602        # An 'Internal StopIteration' exception is an exception debug event603        # issued by the interpreter when handling a subgenerator run with604        # 'yield from' or a generator controlled by a for loop. No exception has605        # actually occurred in this case. The debugger uses this debug event to606        # stop when the debuggee is returning from such generators.607        prefix = 'Internal ' if (not exc_traceback608                                    and exc_type is StopIteration) else ''609        self.message('%s%s' % (prefix, self._format_exc(exc_value)))610        self.interaction(frame, exc_traceback)611 612    # General interaction function613    def _cmdloop(self):614        while True:615            try:616                # keyboard interrupts allow for an easy way to cancel617                # the current command, so allow them during interactive input618                self.allow_kbdint = True619                self.cmdloop()620                self.allow_kbdint = False621                break622            except KeyboardInterrupt:623                self.message('--KeyboardInterrupt--')624 625    def _save_initial_file_mtime(self, frame):626        """save the mtime of the all the files in the frame stack in the file mtime table627        if they haven't been saved yet."""628        while frame:629            filename = frame.f_code.co_filename630            if filename not in self._file_mtime_table:631                try:632                    self._file_mtime_table[filename] = os.path.getmtime(filename)633                except Exception:634                    pass635            frame = frame.f_back636 637    def _validate_file_mtime(self):638        """Check if the source file of the current frame has been modified.639        If so, give a warning and reset the modify time to current."""640        try:641            filename = self.curframe.f_code.co_filename642            mtime = os.path.getmtime(filename)643        except Exception:644            return645        if (filename in self._file_mtime_table and646            mtime != self._file_mtime_table[filename]):647            self.message(f"*** WARNING: file '{filename}' was edited, "648                         "running stale code until the program is rerun")649            self._file_mtime_table[filename] = mtime650 651    # Called before loop, handles display expressions652    # Set up convenience variable containers653    def _show_display(self):654        displaying = self.displaying.get(self.curframe)655        if displaying:656            for expr, oldvalue in displaying.items():657                newvalue = self._getval_except(expr)658                # check for identity first; this prevents custom __eq__ to659                # be called at every loop, and also prevents instances whose660                # fields are changed to be displayed661                if newvalue is not oldvalue and newvalue != oldvalue:662                    displaying[expr] = newvalue663                    self.message('display %s: %s  [old: %s]' %664                                 (expr, self._safe_repr(newvalue, expr),665                                  self._safe_repr(oldvalue, expr)))666 667    def _get_tb_and_exceptions(self, tb_or_exc):668        """669        Given a tracecack or an exception, return a tuple of chained exceptions670        and current traceback to inspect.671 672        This will deal with selecting the right ``__cause__`` or ``__context__``673        as well as handling cycles, and return a flattened list of exceptions we674        can jump to with do_exceptions.675 676        """677        _exceptions = []678        if isinstance(tb_or_exc, BaseException):679            traceback, current = tb_or_exc.__traceback__, tb_or_exc680 681            while current is not None:682                if current in _exceptions:683                    break684                _exceptions.append(current)685                if current.__cause__ is not None:686                    current = current.__cause__687                elif (688                    current.__context__ is not None and not current.__suppress_context__689                ):690                    current = current.__context__691 692                if len(_exceptions) >= self.MAX_CHAINED_EXCEPTION_DEPTH:693                    self.message(694                        f"More than {self.MAX_CHAINED_EXCEPTION_DEPTH}"695                        " chained exceptions found, not all exceptions"696                        "will be browsable with `exceptions`."697                    )698                    break699        else:700            traceback = tb_or_exc701        return tuple(reversed(_exceptions)), traceback702 703    @contextmanager704    def _hold_exceptions(self, exceptions):705        """706        Context manager to ensure proper cleaning of exceptions references707 708        When given a chained exception instead of a traceback,709        pdb may hold references to many objects which may leak memory.710 711        We use this context manager to make sure everything is properly cleaned712 713        """714        try:715            self._chained_exceptions = exceptions716            self._chained_exception_index = len(exceptions) - 1717            yield718        finally:719            # we can't put those in forget as otherwise they would720            # be cleared on exception change721            self._chained_exceptions = tuple()722            self._chained_exception_index = 0723 724    def _get_asyncio_task(self):725        try:726            task = asyncio.current_task()727        except RuntimeError:728            task = None729        return task730 731    def interaction(self, frame, tb_or_exc):732        # Restore the previous signal handler at the Pdb prompt.733        if Pdb._previous_sigint_handler:734            try:735                signal.signal(signal.SIGINT, Pdb._previous_sigint_handler)736            except ValueError:  # ValueError: signal only works in main thread737                pass738            else:739                Pdb._previous_sigint_handler = None740 741        self._current_task = self._get_asyncio_task()742 743        _chained_exceptions, tb = self._get_tb_and_exceptions(tb_or_exc)744        if isinstance(tb_or_exc, BaseException):745            assert tb is not None, "main exception must have a traceback"746        with self._hold_exceptions(_chained_exceptions):747            self.setup(frame, tb)748            # We should print the stack entry if and only if the user input749            # is expected, and we should print it right before the user input.750            # We achieve this by appending _pdbcmd_print_frame_status to the751            # command queue. If cmdqueue is not exhausted, the user input is752            # not expected and we will not print the stack entry.753            self.cmdqueue.append('_pdbcmd_print_frame_status')754            self._cmdloop()755            # If _pdbcmd_print_frame_status is not used, pop it out756            if self.cmdqueue and self.cmdqueue[-1] == '_pdbcmd_print_frame_status':757                self.cmdqueue.pop()758            self.forget()759 760    def displayhook(self, obj):761        """Custom displayhook for the exec in default(), which prevents762        assignment of the _ variable in the builtins.763        """764        # reproduce the behavior of the standard displayhook, not printing None765        if obj is not None:766            self.message(repr(obj))767 768    @contextmanager769    def _enable_multiline_input(self):770        try:771            import readline772        except ImportError:773            yield774            return775 776        def input_auto_indent():777            last_index = readline.get_current_history_length()778            last_line = readline.get_history_item(last_index)779            if last_line:780                if last_line.isspace():781                    # If the last line is empty, we don't need to indent782                    return783 784                last_line = last_line.rstrip('\r\n')785                indent = len(last_line) - len(last_line.lstrip())786                if last_line.endswith(":"):787                    indent += 4788                readline.insert_text(' ' * indent)789 790        completenames = self.completenames791        try:792            self.completenames = self.complete_multiline_names793            readline.set_startup_hook(input_auto_indent)794            yield795        finally:796            readline.set_startup_hook()797            self.completenames = completenames798        return799 800    def _exec_in_closure(self, source, globals, locals):801        """ Run source code in closure so code object created within source802            can find variables in locals correctly803 804            returns True if the source is executed, False otherwise805        """806 807        # Determine if the source should be executed in closure. Only when the808        # source compiled to multiple code objects, we should use this feature.809        # Otherwise, we can just raise an exception and normal exec will be used.810 811        code = compile(source, "<string>", "exec")812        if not any(isinstance(const, CodeType) for const in code.co_consts):813            return False814 815        # locals could be a proxy which does not support pop816        # copy it first to avoid modifying the original locals817        locals_copy = dict(locals)818 819        locals_copy["__pdb_eval__"] = {820            "result": None,821            "write_back": {}822        }823 824        # If the source is an expression, we need to print its value825        try:826            compile(source, "<string>", "eval")827        except SyntaxError:828            pass829        else:830            source = "__pdb_eval__['result'] = " + source831 832        # Add write-back to update the locals833        source = ("try:\n" +834                  textwrap.indent(source, "  ") + "\n" +835                  "finally:\n" +836                  "  __pdb_eval__['write_back'] = locals()")837 838        # Build a closure source code with freevars from locals like:839        # def __pdb_outer():840        #   var = None841        #   def __pdb_scope():  # This is the code object we want to execute842        #     nonlocal var843        #     <source>844        #   return __pdb_scope.__code__845        source_with_closure = ("def __pdb_outer():\n" +846                               "\n".join(f"  {var} = None" for var in locals_copy) + "\n" +847                               "  def __pdb_scope():\n" +848                               "\n".join(f"    nonlocal {var}" for var in locals_copy) + "\n" +849                               textwrap.indent(source, "    ") + "\n" +850                               "  return __pdb_scope.__code__"851                               )852 853        # Get the code object of __pdb_scope()854        # The exec fills locals_copy with the __pdb_outer() function and we can call855        # that to get the code object of __pdb_scope()856        ns = {}857        try:858            exec(source_with_closure, {}, ns)859        except Exception:860            return False861        code = ns["__pdb_outer"]()862 863        cells = tuple(types.CellType(locals_copy.get(var)) for var in code.co_freevars)864 865        try:866            exec(code, globals, locals_copy, closure=cells)867        except Exception:868            return False869 870        # get the data we need from the statement871        pdb_eval = locals_copy["__pdb_eval__"]872 873        # __pdb_eval__ should not be updated back to locals874        pdb_eval["write_back"].pop("__pdb_eval__")875 876        # Write all local variables back to locals877        locals.update(pdb_eval["write_back"])878        eval_result = pdb_eval["result"]879        if eval_result is not None:880            self.message(repr(eval_result))881 882        return True883 884    def _exec_await(self, source, globals, locals):885        """ Run source code that contains await by playing with async shim frame"""886        # Put the source in an async function887        source_async = (888            "async def __pdb_await():\n" +889            textwrap.indent(source, "    ") + '\n' +890            "    __pdb_locals.update(locals())"891        )892        ns = globals | locals893        # We use __pdb_locals to do write back894        ns["__pdb_locals"] = locals895        exec(source_async, ns)896        self.async_awaitable = ns["__pdb_await"]()897 898    def _read_code(self, line):899        buffer = line900        is_await_code = False901        code = None902        try:903            if (code := codeop.compile_command(line + '\n', '<stdin>', 'single')) is None:904                # Multi-line mode905                with self._enable_multiline_input():906                    buffer = line907                    continue_prompt = "...   "908                    while (code := codeop.compile_command(buffer, '<stdin>', 'single')) is None:909                        if self.use_rawinput:910                            try:911                                line = input(continue_prompt)912                            except (EOFError, KeyboardInterrupt):913                                self.lastcmd = ""914                                print('\n')915                                return None, None, False916                        else:917                            self.stdout.write(continue_prompt)918                            self.stdout.flush()919                            line = self.stdin.readline()920                            if not len(line):921                                self.lastcmd = ""922                                self.stdout.write('\n')923                                self.stdout.flush()924                                return None, None, False925                            else:926                                line = line.rstrip('\r\n')927                        if line.isspace():928                            # empty line, just continue929                            buffer += '\n'930                        else:931                            buffer += '\n' + line932                    self.lastcmd = buffer933        except SyntaxError as e:934            # Maybe it's an await expression/statement935            if (936                self.async_shim_frame is not None937                and e.msg == "'await' outside function"938            ):939                is_await_code = True940            else:941                raise942 943        return code, buffer, is_await_code944 945    def default(self, line):946        if line[:1] == '!': line = line[1:].strip()947        locals = self.curframe.f_locals948        globals = self.curframe.f_globals949        try:950            code, buffer, is_await_code = self._read_code(line)951            if buffer is None:952                return953            save_stdout = sys.stdout954            save_stdin = sys.stdin955            save_displayhook = sys.displayhook956            try:957                sys.stdin = self.stdin958                sys.stdout = self.stdout959                sys.displayhook = self.displayhook960                if is_await_code:961                    self._exec_await(buffer, globals, locals)962                    return True963                else:964                    if not self._exec_in_closure(buffer, globals, locals):965                        exec(code, globals, locals)966            finally:967                sys.stdout = save_stdout968                sys.stdin = save_stdin969                sys.displayhook = save_displayhook970        except:971            self._error_exc()972 973    def _replace_convenience_variables(self, line):974        """Replace the convenience variables in 'line' with their values.975           e.g. $foo is replaced by __pdb_convenience_variables["foo"].976           Note: such pattern in string literals will be skipped"""977 978        if "$" not in line:979            return line980 981        dollar_start = dollar_end = (-1, -1)982        replace_variables = []983        try:984            for t in tokenize.generate_tokens(io.StringIO(line).readline):985                token_type, token_string, start, end, _ = t986                if token_type == token.OP and token_string == '$':987                    dollar_start, dollar_end = start, end988                elif start == dollar_end and token_type == token.NAME:989                    # line is a one-line command so we only care about column990                    replace_variables.append((dollar_start[1], end[1], token_string))991        except tokenize.TokenError:992            return line993 994        if not replace_variables:995            return line996 997        last_end = 0998        line_pieces = []999        for start, end, name in replace_variables:1000            line_pieces.append(line[last_end:start] + f'__pdb_convenience_variables["{name}"]')1001            last_end = end1002        line_pieces.append(line[last_end:])1003 1004        return ''.join(line_pieces)1005 1006    def precmd(self, line):1007        """Handle alias expansion and ';;' separator."""1008        if not line.strip():1009            return line1010        args = line.split()1011        while args[0] in self.aliases:1012            line = self.aliases[args[0]]1013            for idx in range(1, 10):1014                if f'%{idx}' in line:1015                    if idx >= len(args):1016                        self.error(f"Not enough arguments for alias '{args[0]}'")1017                        # This is a no-op1018                        return "!"1019                    line = line.replace(f'%{idx}', args[idx])1020                elif '%*' not in line:1021                    if idx < len(args):1022                        self.error(f"Too many arguments for alias '{args[0]}'")1023                        # This is a no-op1024                        return "!"1025                    break1026 1027            line = line.replace("%*", ' '.join(args[1:]))1028            args = line.split()1029        # split into ';;' separated commands1030        # unless it's an alias command1031        if args[0] != 'alias':1032            marker = line.find(';;')1033            if marker >= 0:1034                # queue up everything after marker1035                next = line[marker+2:].lstrip()1036                self.cmdqueue.insert(0, next)1037                line = line[:marker].rstrip()1038 1039        # Replace all the convenience variables1040        line = self._replace_convenience_variables(line)1041 1042        return line1043 1044    def onecmd(self, line):1045        """Interpret the argument as though it had been typed in response1046        to the prompt.1047 1048        Checks whether this line is typed at the normal prompt or in1049        a breakpoint command list definition.1050        """1051        if not self.commands_defining:1052            if line.startswith('_pdbcmd'):1053                command, arg, line = self.parseline(line)1054                if hasattr(self, command):1055                    return getattr(self, command)(arg)1056            return cmd.Cmd.onecmd(self, line)1057        else:1058            return self.handle_command_def(line)1059 1060    def handle_command_def(self, line):1061        """Handles one command line during command list definition."""1062        cmd, arg, line = self.parseline(line)1063        if not cmd:1064            return False1065        if cmd == 'end':1066            return True  # end of cmd list1067        elif cmd == 'EOF':1068            self.message('')1069            return True  # end of cmd list1070        cmdlist = self.commands[self.commands_bnum]1071        if cmd == 'silent':1072            cmdlist.append('_pdbcmd_silence_frame_status')1073            return False  # continue to handle other cmd def in the cmd list1074        if arg:1075            cmdlist.append(cmd+' '+arg)1076        else:1077            cmdlist.append(cmd)1078        # Determine if we must stop1079        try:1080            func = getattr(self, 'do_' + cmd)1081        except AttributeError:1082            func = self.default1083        # one of the resuming commands1084        if func.__name__ in self.commands_resuming:1085            return True1086        return False1087 1088    def _colorize_code(self, code):1089        if self.colorize:1090            colors = list(_pyrepl.utils.gen_colors(code))1091            chars, _ = _pyrepl.utils.disp_str(code, colors=colors, force_color=True)1092            code = "".join(chars)1093        return code1094 1095    # interface abstraction functions1096 1097    def message(self, msg, end='\n'):1098        print(msg, end=end, file=self.stdout)1099 1100    def error(self, msg):1101        print('***', msg, file=self.stdout)1102 1103    # convenience variables1104 1105    def set_convenience_variable(self, frame, name, value):1106        if '__pdb_convenience_variables' not in frame.f_globals:1107            frame.f_globals['__pdb_convenience_variables'] = {}1108        frame.f_globals['__pdb_convenience_variables'][name] = value1109 1110    # Generic completion functions.  Individual complete_foo methods can be1111    # assigned below to one of these functions.1112 1113    @property1114    def rlcompleter(self):1115        """Return the `Completer` class from `rlcompleter`, while avoiding the1116        side effects of changing the completer from `import rlcompleter`.1117 1118        This is a compromise between GH-138860 and GH-139289. If GH-139289 is1119        fixed, then we don't need this and we can just `import rlcompleter` in1120        `Pdb.__init__`.1121        """1122        if not hasattr(self, "_rlcompleter"):1123            try:1124                import readline1125            except ImportError:1126                # readline is not available, just get the Completer1127                from rlcompleter import Completer1128                self._rlcompleter = Completer1129            else:1130                # importing rlcompleter could have side effect of changing1131                # the current completer, we need to restore it1132                prev_completer = readline.get_completer()1133                from rlcompleter import Completer1134                self._rlcompleter = Completer1135                readline.set_completer(prev_completer)1136        return self._rlcompleter1137 1138    def completenames(self, text, line, begidx, endidx):1139        # Overwrite completenames() of cmd so for the command completion,1140        # if no current command matches, check for expressions as well1141        commands = super().completenames(text, line, begidx, endidx)1142        for alias in self.aliases:1143            if alias.startswith(text):1144                commands.append(alias)1145        if commands:1146            return commands1147        else:1148            expressions = self._complete_expression(text, line, begidx, endidx)1149            if expressions:1150                return expressions1151            return self.completedefault(text, line, begidx, endidx)1152 1153    def _complete_location(self, text, line, begidx, endidx):1154        # Complete a file/module/function location for break/tbreak/clear.1155        if line.strip().endswith((':', ',')):1156            # Here comes a line number or a condition which we can't complete.1157            return []1158        # First, try to find matching functions (i.e. expressions).1159        try:1160            ret = self._complete_expression(text, line, begidx, endidx)1161        except Exception:1162            ret = []1163        # Then, try to complete file names as well.1164        globs = glob.glob(glob.escape(text) + '*')1165        for fn in globs:1166            if os.path.isdir(fn):1167                ret.append(fn + '/')1168            elif os.path.isfile(fn) and fn.lower().endswith(('.py', '.pyw')):1169                ret.append(fn + ':')1170        return ret1171 1172    def _complete_bpnumber(self, text, line, begidx, endidx):1173        # Complete a breakpoint number.  (This would be more helpful if we could1174        # display additional info along with the completions, such as file/line1175        # of the breakpoint.)1176        return [str(i) for i, bp in enumerate(bdb.Breakpoint.bpbynumber)1177                if bp is not None and str(i).startswith(text)]1178 1179    def _complete_expression(self, text, line, begidx, endidx):1180        # Complete an arbitrary expression.1181        if not self.curframe:1182            return []1183        # Collect globals and locals.  It is usually not really sensible to also1184        # complete builtins, and they clutter the namespace quite heavily, so we1185        # leave them out.1186        ns = {**self.curframe.f_globals, **self.curframe.f_locals}1187        if '.' in text:1188            # Walk an attribute chain up to the last part, similar to what1189            # rlcompleter does.  This will bail if any of the parts are not1190            # simple attribute access, which is what we want.1191            dotted = text.split('.')1192            try:1193                if dotted[0].startswith('$'):1194                    obj = self.curframe.f_globals['__pdb_convenience_variables'][dotted[0][1:]]1195                else:1196                    obj = ns[dotted[0]]1197                for part in dotted[1:-1]:1198                    obj = getattr(obj, part)1199            except (KeyError, AttributeError):1200                return []

Showing the first 1,200 of 3679 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai