def compare_code_with_srcfile(pyc_filename, src_filename, verify): """Compare a .pyc with a source code file. If everything is okay, None is returned. Otherwise a string message describing the mismatch is returned. """ ( version, timestamp, magic_int, code_obj1, is_pypy, source_size, sip_hash, ) = load_module(pyc_filename) if magic_int != PYTHON_MAGIC_INT: msg = ( "Can't compare code - Python is running with magic %s, but code is magic %s " % (PYTHON_MAGIC_INT, magic_int) ) return msg try: code_obj2 = load_file(src_filename) except SyntaxError, e: # src_filename can be the first of a group sometimes if version == 2.4: print(pyc_filename) return str(e).replace(src_filename, pyc_filename)
def decompile_file( filename: str, outstream=None, showasm=None, showast={}, showgrammar=False, source_encoding=None, mapstream=None, do_fragments=False, ) -> Any: """ decompile Python byte-code file (.pyc). Return objects to all of the deparsed objects found in `filename`. """ filename = check_object_path(filename) code_objects = {} (version, timestamp, magic_int, co, is_pypy, source_size, sip_hash) = load_module(filename, code_objects) if isinstance(co, list): deparsed = [] for con in co: deparsed.append( decompile( version, con, outstream, showasm, showast, timestamp, showgrammar, source_encoding, code_objects=code_objects, is_pypy=is_pypy, magic_int=magic_int, mapstream=mapstream, ), ) else: deparsed = [ decompile( version, co, outstream, showasm, showast, timestamp, showgrammar, source_encoding, code_objects=code_objects, source_size=source_size, is_pypy=is_pypy, magic_int=magic_int, mapstream=mapstream, do_fragments=do_fragments, compile_mode="exec", ) ] co = None return deparsed
def compare_code_with_srcfile(pyc_filename, src_filename, verify): """Compare a .pyc with a source code file. If everything is okay, None is returned. Otherwise a string message describing the mismatch is returned. """ ( version, timestamp, magic_int, code_obj1, is_pypy, source_size, sip_hash, ) = load_module(pyc_filename) if magic_int != PYTHON_MAGIC_INT: msg = ( "Can't compare code - Python is running with magic %s, but code is magic %s " % (PYTHON_MAGIC_INT, magic_int)) return msg try: code_obj2 = load_file(src_filename) except SyntaxError as e: # src_filename can be the first of a group sometimes return str(e).replace(src_filename, pyc_filename) cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify) if verify == "verify-run": try: retcode = call("%s %s" % (sys.executable, src_filename), shell=True) if retcode != 0: return "Child was terminated by signal %d" % retcode pass except OSError as e: return "Execution failed: %s" % e pass return None
def copy_magic_into_pyc(input_pyc, output_pyc, src_version, dest_version): """Bytecodes are the same except the magic number, so just change that""" (version, timestamp, magic_int, co, is_pypy, source_size) = load_module(input_pyc) assert version == float( src_version ), "Need Python %s bytecode; got bytecode for version %s" % (src_version, version) magic_int = magic2int(magics.magics[dest_version]) write_bytecode_file(output_pyc, co, magic_int) print("Wrote %s" % output_pyc) return
def line_number_mapping(pyc_filename, src_filename): ( version, timestamp, magic_int, code1, is_pypy, source_size, sip_hash, ) = load_module(pyc_filename) try: code2 = load_file(src_filename) except SyntaxError, e: return str(e)
def disassemble_file(filename, outstream=None): """ disassemble Python byte-code file (.pyc) If given a Python source file (".py") file, we'll try to find the corresponding compiled object. """ filename = check_object_path(filename) (version, timestamp, magic_int, co, is_pypy, source_size, sip_hash) = load_module(filename) if type(co) == list: for con in co: disco(version, con, outstream) else: disco(version, co, outstream, is_pypy=is_pypy) co = None
def frame2filesize(frame): if '__cached__' in frame.f_globals: bc_path = frame.f_globals['__cached__'] else: bc_path = None path = frame.f_globals['__file__'] fs_size = os.stat(path).st_size if bc_path: (version, timestamp, magic_int, co, is_pypy, bc_source_size) = xdis.load_module(bc_path, fast_load=True, get_code=False) return fs_size, bc_source_size elif os.path.exists(path): return fs_size, None else: return None, None
def assert_runs_ok(self, path_or_code, raises=None, arg_type="string"): """Run `code` in our VM.""" if arg_type == "bytecode-file": self.version, timestamp, magic_int, code, pypy, source_size, sip_hash = load_module( path_or_code) else: self.version = PYTHON_VERSION if arg_type == "source": code_str = open(path_or_code, "r").read() else: assert arg_type == "string", ( "arg_type parameter needs to be either: bytecode-file, source or string; got %s" % arg_type) code_str = textwrap.dedent(path_or_code) code = compile(code_str, "<%s>" % self.id(), "exec", 0, 1) print("%s bytecode %s for %s %s " % (LINE_STR, self.version, code.co_filename, LINE_STR)) vm = PyVM(python_version=self.version) vm_value = vm_exc = None try: vm_value = vm.run_code(code) except PyVMError: # pragma: no cover # If the VM code raises an error, show it. self.assertTrue(False) raise except AssertionError: # pragma: no cover # If test code fails an assert, show it. raise except Exception as e: # Otherwise, keep the exception for comparison later. if not CAPTURE_EXCEPTION: # pragma: no cover raise vm_exc = e else: self.assertTrue(True)
def line_number_mapping(pyc_filename, src_filename): ( version, timestamp, magic_int, code1, is_pypy, source_size, sip_hash, ) = load_module(pyc_filename) try: code2 = load_file(src_filename) except SyntaxError as e: return str(e) queue = deque([code1, code2]) mappings = [] opc = get_opcode(version, is_pypy) number_loop(queue, mappings, opc) return sorted(mappings, key=lambda x: x[1])
def frame2filesize(frame): if "__cached__" in frame.f_globals: bc_path = frame.f_globals["__cached__"] else: bc_path = None path = frame.f_globals["__file__"] source_path = getsourcefile(path) fs_size = os.stat(source_path).st_size if bc_path: ( version, timestamp, magic_int, co, is_pypy, bc_source_size, sip_hash, ) = xdis.load_module(bc_path, fast_load=True, get_code=False) return fs_size, bc_source_size elif osp.exists(path): return fs_size, None else: return None, None
def test_roundtrip3(): if not PYTHON3: print( "test skipped because Python 2.x has problems creating Python 3.x files" ) return fp = NamedTemporaryFile(mode="wb+", suffix=".pyc", prefix="test_pyc-", delete=False) orig_path = "testdata/test_pyc.pyc" version, timestamp, magic_int, co, is_pypy, source_size, sip_hash = load_module( orig_path) write_pycfile(fp, [co], timestamp, version) new_path = fp.name size = fp.tell() fp.close() print("Wrote Python %s bytecode file %s; %d bytes" % (version, fp.name, size)) old_fp = open(orig_path, "rb") new_fp = open(new_path, "rb") compare_size = 590 assert old_fp.read(compare_size) == new_fp.read(compare_size) os.unlink(new_path)
def main(dbg=None, sys_argv=list(sys.argv)): """Routine which gets run if we were invoked directly""" global __title__ # Save the original just for use in the restart that works via exec. orig_sys_argv = list(sys_argv) opts, dbg_opts, sys_argv = Moptions.process_options( __title__, VERSION, sys_argv) if opts.server is not None: if opts.server == 'tcp': connection_opts = {'IO': 'TCP', 'PORT': opts.port} else: connection_opts = {'IO': 'FIFO'} intf = Mserver.ServerInterface(connection_opts=connection_opts) dbg_opts['interface'] = intf if 'FIFO' == intf.server_type: print('Starting FIFO server for process %s.' % os.getpid()) elif 'TCP' == intf.server_type: print('Starting TCP server listening on port %s.' % intf.inout.PORT) pass elif opts.client: Mclient.main(opts, sys_argv) return dbg_opts['orig_sys_argv'] = orig_sys_argv if dbg is None: dbg = Mdebugger.Trepan(dbg_opts) dbg.core.add_ignore(main) pass Moptions._postprocess_options(dbg, opts) # process_options has munged sys.argv to remove any options that # options that belong to this debugger. The original options to # invoke the debugger and script are in global sys_argv if len(sys_argv) == 0: # No program given to debug. Set to go into a command loop # anyway mainpyfile = None else: mainpyfile = sys_argv[0] # Get script filename. if not osp.isfile(mainpyfile): mainpyfile = Mclifns.whence_file(mainpyfile) is_readable = Mfile.readable(mainpyfile) if is_readable is None: print("%s: Python script file '%s' does not exist" % ( __title__, mainpyfile, )) sys.exit(1) elif not is_readable: print("%s: Can't read Python script file '%s'" % ( __title__, mainpyfile, )) sys.exit(1) return if Mfile.is_compiled_py(mainpyfile): try: from xdis import load_module, PYTHON_VERSION, IS_PYPY (python_version, timestamp, magic_int, co, is_pypy, source_size) = load_module(mainpyfile, code_objects=None, fast_load=True) assert is_pypy == IS_PYPY assert python_version == PYTHON_VERSION, \ "bytecode is for version %s but we are version %s" % ( python_version, PYTHON_VERSION) # We should we check version magic_int py_file = co.co_filename if osp.isabs(py_file): try_file = py_file else: mainpydir = osp.dirname(mainpyfile) tag = sys.implementation.cache_tag dirnames = [ osp.join(mainpydir, tag), mainpydir ] + os.environ['PATH'].split(osp.pathsep) + ['.'] try_file = Mclifns.whence_file(py_file, dirnames) if osp.isfile(try_file): mainpyfile = try_file pass else: # Move onto the except branch raise IOError( "Python file name embedded in code %s not found" % try_file) except IOError: try: from uncompyle6 import decompile_file except ImportError: print( "%s: Compiled python file '%s', but uncompyle6 not found" % (__title__, mainpyfile), file=sys.stderr) sys.exit(1) return short_name = osp.basename(mainpyfile).strip('.pyc') fd = tempfile.NamedTemporaryFile(suffix='.py', prefix=short_name + "_", delete=False) old_write = fd.file.write def write_wrapper(*args, **kwargs): if isinstance(args[0], str): new_args = list(args) new_args[0] = args[0].encode('utf-8') old_write(*new_args, **kwargs) else: old_write(*args, **kwargs) fd.file.write = write_wrapper # from io import StringIO # linemap_io = StringIO() try: decompile_file(mainpyfile, fd.file, mapstream=fd) except: print("%s: error decompiling '%s'" % (__title__, mainpyfile), file=sys.stderr) sys.exit(1) return # # Get the line associations between the original and # # decompiled program # mapline = linemap_io.getvalue() # fd.write(mapline + "\n\n") # linemap = eval(mapline[3:]) mainpyfile = fd.name fd.close() # Since we are actually running the recreated source, # there is little no need to remap line numbers. # The mapping is given at the end of the file. # However we should consider adding this information # and original file name. print( "%s: couldn't find Python source so we recreated it at '%s'" % (__title__, mainpyfile), file=sys.stderr) pass # If mainpyfile is an optimized Python script try to find and # use non-optimized alternative. mainpyfile_noopt = pyficache.pyc2py(mainpyfile) if mainpyfile != mainpyfile_noopt \ and Mfile.readable(mainpyfile_noopt): print("%s: Compiled Python script given and we can't use that." % __title__) print("%s: Substituting non-compiled name: %s" % ( __title__, mainpyfile_noopt, )) mainpyfile = mainpyfile_noopt pass # Replace trepan's dir with script's dir in front of # module search path. sys.path[0] = dbg.main_dirname = osp.dirname(mainpyfile) # XXX If a signal has been received we continue in the loop, otherwise # the loop exits for some reason. dbg.sig_received = False # if not mainpyfile: # print('For now, you need to specify a Python script name!') # sys.exit(2) # pass while True: # Run the debugged script over and over again until we get it # right. try: if dbg.program_sys_argv and mainpyfile: normal_termination = dbg.run_script(mainpyfile) if not normal_termination: break else: dbg.core.execution_status = 'No program' dbg.core.processor.process_commands() pass dbg.core.execution_status = 'Terminated' dbg.intf[-1].msg("The program finished - quit or restart") dbg.core.processor.process_commands() except Mexcept.DebuggerQuit: break except Mexcept.DebuggerRestart: dbg.core.execution_status = 'Restart requested' if dbg.program_sys_argv: sys.argv = list(dbg.program_sys_argv) part1 = ('Restarting %s with arguments:' % dbg.core.filename(mainpyfile)) args = ' '.join(dbg.program_sys_argv[1:]) dbg.intf[-1].msg( Mmisc.wrapped_lines(part1, args, dbg.settings['width'])) else: break except SystemExit: # In most cases SystemExit does not warrant a post-mortem session. break pass # Restore old sys.argv sys.argv = orig_sys_argv return
def main(dbg=None, sys_argv=list(sys.argv)): """Routine which gets run if we were invoked directly""" # Save the original just for use in the restart that works via exec. orig_sys_argv = list(sys_argv) opts, dbg_opts, sys_argv = Moptions.process_options( __title__, __version__, sys_argv) if opts.server: connection_opts = {"IO": "TCP", "PORT": opts.port} intf = Mserver.ServerInterface(connection_opts=connection_opts) dbg_opts["interface"] = intf if "FIFO" == intf.server_type: print("Starting FIFO server for process %s." % os.getpid()) elif "TCP" == intf.server_type: print("Starting TCP server listening on port %s." % intf.inout.PORT) pass elif opts.client: Mclient.main(opts, sys_argv) return dbg_opts["orig_sys_argv"] = orig_sys_argv if dbg is None: dbg = Mdebugger.Debugger(dbg_opts) dbg.core.add_ignore(main) pass Moptions._postprocess_options(dbg, opts) # process_options has munged sys.argv to remove any options that # options that belong to this debugger. The original options to # invoke the debugger and script are in global sys_argv if len(sys_argv) == 0: # No program given to debug. Set to go into a command loop # anyway mainpyfile = None else: mainpyfile = sys_argv[0] # Get script filename. if not osp.isfile(mainpyfile): mainpyfile = Mclifns.whence_file(mainpyfile) is_readable = Mfile.readable(mainpyfile) if is_readable is None: sys.stderr.write( "%s: Python script file '%s' does not exist\n" % (__title__, mainpyfile)) sys.exit(1) elif not is_readable: sys.stderr.write("%s: Can't read Python script file '%s'\n" % ( __title__, mainpyfile, )) sys.exit(1) return if Mfile.is_compiled_py(mainpyfile): try: from xdis import load_module from xdis_version import load_module, PYTHON_VERSION_TRIPLE, IS_PYPY, version_tuple_to_str ( python_version, timestamp, magic_int, co, is_pypy, source_size, ) = load_module(mainpyfile, code_objects=None, fast_load=True) assert is_pypy == IS_PYPY assert ( python_version == PYTHON_VERSION_TRIPLE ), "bytecode is for version %s but we are version %s" % ( python_version, version_tuple_to_str(), ) # We should we check version magic_int py_file = co.co_filename if osp.isabs(py_file): try_file = py_file else: mainpydir = osp.dirname(mainpyfile) dirnames = ([mainpydir] + os.environ["PATH"].split(os.pathsep) + ["."]) try_file = Mclifns.whence_file(py_file, dirnames) if osp.isfile(try_file): mainpyfile = try_file pass else: # Move onto the except branch raise IOError( "Python file name embedded in code %s not found" % try_file) except: try: from uncompyle6 import decompile_file except ImportError: sys.stderr.write( "%s: Compiled python file '%s', but uncompyle6 not found\n" % (__title__, mainpyfile)) sys.exit(1) return short_name = osp.basename(mainpyfile).strip(".pyc") fd = tempfile.NamedTemporaryFile( suffix=".py", prefix=short_name + "_", dir=dbg.settings["tempdir"], delete=False, ) try: decompile_file(mainpyfile, outstream=fd) mainpyfile = fd.name fd.close() except: sys.stderr.write("%s: error uncompyling '%s'\n" % (__title__, mainpyfile)) sys.exit(1) pass # If mainpyfile is an optimized Python script try to find and # use non-optimized alternative. mainpyfile_noopt = pyficache.resolve_name_to_path(mainpyfile) if mainpyfile != mainpyfile_noopt \ and Mfile.readable(mainpyfile_noopt): sys.stderr.write( "%s: Compiled Python script given and we can't use that.\n" % __title__) sys.stderr.write("%s: Substituting non-compiled name: %s\n" % (__title__, mainpyfile_noopt)) mainpyfile = mainpyfile_noopt pass # Replace trepan's dir with script's dir in front of # module search path. sys.path[0] = dbg.main_dirname = osp.dirname(mainpyfile) # XXX If a signal has been received we continue in the loop, otherwise # the loop exits for some reason. dbg.sig_received = False # if not mainpyfile: # print('For now, you need to specify a Python script name!') # sys.exit(2) # pass while True: # Run the debugged script over and over again until we get it # right. try: if dbg.program_sys_argv and mainpyfile: normal_termination = dbg.run_script(mainpyfile) if not normal_termination: break else: dbg.core.execution_status = "No program" dbg.core.processor.process_commands() pass dbg.core.execution_status = "Terminated" dbg.intf[-1].msg("The program finished - quit or restart") dbg.core.processor.process_commands() except Mexcept.DebuggerQuit: break except Mexcept.DebuggerRestart: dbg.core.execution_status = "Restart requested" if dbg.program_sys_argv: sys.argv = list(dbg.program_sys_argv) part1 = "Restarting %s with arguments:" % dbg.core.filename( mainpyfile) args = " ".join(dbg.program_sys_argv[1:]) dbg.intf[-1].msg( Mmisc.wrapped_lines(part1, args, dbg.settings["width"])) else: break except SystemExit: # In most cases SystemExit does not warrant a post-mortem session. break except: # FIXME: Should be handled above without this mess exception_name = str(sys.exc_info()[0]) if exception_name == str(Mexcept.DebuggerQuit): break elif exception_name == str(Mexcept.DebuggerRestart): dbg.core.execution_status = "Restart requested" if dbg.program_sys_argv: sys.argv = list(dbg.program_sys_argv) part1 = "Restarting %s with arguments:" % dbg.core.filename( mainpyfile) args = " ".join(dbg.program_sys_argv[1:]) dbg.intf[-1].msg( Mmisc.wrapped_lines(part1, args, dbg.settings["width"])) pass else: raise pass # Restore old sys.argv sys.argv = orig_sys_argv return
def assert_ok(self, path_or_code, raises=None, arg_type="string"): """Run `code` in our VM and in real Python: they behave the same.""" if arg_type == "bytecode-file": self.version, timestamp, magic_int, code, pypy, source_size, sip_hash = load_module( path_or_code) else: self.version = PYTHON_VERSION if arg_type == "source": code_str = open(path_or_code, "r").read() else: assert arg_type == "string", "arg_type parameter needs to be either: bytecode-file, source or string; got %s" % arg_type code_str = textwrap.dedent(path_or_code) code = compile(code_str, "<%s>" % self.id(), "exec", 0, 1) real_stdout = sys.stdout # Run the code through our VM. vm_stdout = six.StringIO() if CAPTURE_STDOUT: # pragma: no branch sys.stdout = vm_stdout vm = PyVM(vmtest_testing=True) vm_value = vm_exc = None try: vm_value = vm.run_code(code) except PyVMError: # pragma: no cover # If the VM code raises an error, show it. raise except AssertionError: # pragma: no cover # If test code fails an assert, show it. raise except Exception as e: # Otherwise, keep the exception for comparison later. if not CAPTURE_EXCEPTION: # pragma: no cover raise vm_exc = e finally: real_stdout.write("\n%s %s output %s\n\n" % (LINE_STR, code.co_filename, LINE_STR)) real_stdout.write(vm_stdout.getvalue()) # Run the code through the real Python interpreter, for comparison. if self.version != PYTHON_VERSION: return py_stdout = six.StringIO() sys.stdout = py_stdout py_value = py_exc = None globs = {} try: py_value = eval(code, globs, globs) except AssertionError: # pragma: no cover raise except Exception as e: py_exc = e sys.stdout = real_stdout self.assert_same_exception(vm_exc, py_exc) self.assertEqual(vm_stdout.getvalue(), py_stdout.getvalue()) self.assertEqual(vm_value, py_value) if raises: self.assertIsInstance(vm_exc, raises) else: self.assertIsNone(vm_exc)
def asm_file(path): offset = 0 methods = {} method_name = None asm = None backpatch_inst = set([]) label = {} python_version = None lines = open(path).readlines() i = 0 bytecode_seen = False while i < len(lines): line = lines[i] i += 1 if line.startswith(".READ"): match = re.match("^.READ (.+)$", line) if match: input_pyc = match.group(1) print(f"Reading {input_pyc}") (version, timestamp, magic_int, co, is_pypy, source_size, sip_hash) = load_module(input_pyc) if python_version and python_version != version: TypeError( f"We previously saw Python version {python_version} but we just loaded {version}.\n" ) python_version = version # FIXME: extract all code options below the top-level and asm.code_list elif line.startswith("#"): match = re.match("^# (Pypy )?Python bytecode ", line) if match: if match.group(1): pypy_str = match.group(1) is_pypy = len(pypy_str) else: is_pypy = False pypy_str = "" version = (line[len("# Python bytecode " + pypy_str):].strip().split()[0]) python_version_pair = version_str_to_tuple(version, len=2) asm = Assembler(python_version_pair, is_pypy) if python_version_pair >= (3, 10): TypeError( f"Creating Python version {python_version} not supported yet. Feel free to fix and put in a PR.\n" ) asm.code_init(python_version_pair) bytecode_seen = True elif line.startswith("# Timestamp in code: "): text = line[len("# Timestamp in code: "):].strip() time_str = text.split()[0] if is_int(time_str): asm.timestamp = int(time_str) elif line.startswith("# Method Name: "): if method_name: co = create_code(asm, label, backpatch_inst) asm.update_lists(co, label, backpatch_inst) label = {} backpatch_inst = set([]) methods[method_name] = co offset = 0 python_version_pair = version_str_to_tuple(version, len=2) asm.code_init(python_version_pair) asm.code.co_name = line[len("# Method Name: "):].strip() method_name = asm.code.co_name elif line.startswith("# SipHash: "): siphash = line[len("# ShipHash: "):].strip().split()[0] asm.siphash = ast.literal_eval(siphash) if asm.siphash != 0: raise TypeError( "SIP hashes not supported yet. Feel free to fix and in a PR.\n" ) elif line.startswith("# Filename: "): asm.code.co_filename = line[len("# Filename: "):].strip() elif line.startswith("# First Line: "): s = line[len("# First Line: "):].strip() first_lineno = int(s) asm.code.co_firstlineno = first_lineno elif line.startswith("# Argument count: "): argc = line[len("# Argument count: "):].strip().split()[0] elif line.startswith("# Position-only argument count: "): argc = (line[len("# Position-only argument count: "):].strip(). split()[0]) asm.code.co_posonlyargcount = ast.literal_eval(argc) elif line.startswith("# Keyword-only argument count: "): argc = line[len("# Keyword-only argument count: "):].strip( ).split()[0] asm.code.co_kwonlyargcount = ast.literal_eval(argc) elif line.startswith("# Number of locals: "): l_str = line[len("# Number of locals: "):].strip() asm.code.co_nlocals = int(l_str) elif line.startswith("# Source code size mod 2**32: "): l_str = line[len("# Source code size mod 2**32: " ):-len(" bytes")].strip() asm.size = int(l_str) elif line.startswith("# Stack size: "): l_str = line[len("# Stack size: "):].strip() asm.code.co_stacksize = int(l_str) elif line.startswith("# Flags: "): flags = line[len("# Flags: "):].strip().split()[0] asm.code.co_flags = ast.literal_eval(flags) elif line.startswith("# Constants:"): count = 0 while i < len(lines): line = lines[i] i += 1 match = re.match(r"^#\s+(\d+): (.+)$", line) if match: index = int(match.group(1)) assert index == count, ( "Constant index {%d} found on line {%d}" "doesn't match expected constant index {%d}." % (index, i, count)) expr = match.group(2) match = re.match( r"<(?:Code\d+ )?code object (\S+) at (0x[0-f]+)", expr) if match: name = match.group(1) m2 = re.match("^<(.+)>$", name) if m2: name = "%s_%s" % (m2.group(1), match.group(2)) if name in methods: asm.code.co_consts.append(methods[name]) else: print( f"line {i} ({asm.code.co_filename}, {method_name}): can't find method {name}" ) bogus_name = f"**bogus {name}**" print( f"\t appending {bogus_name} to list of constants" ) asm.code.co_consts.append(bogus_name) else: asm.code.co_consts.append(ast.literal_eval(expr)) count += 1 else: i -= 1 break pass pass elif line.startswith("# Cell variables:"): i = update_code_tuple_field("co_cellvars", asm.code, lines, i) elif line.startswith("# Free variables:"): i = update_code_tuple_field("co_freevars", asm.code, lines, i) elif line.startswith("# Names:"): i = update_code_tuple_field("co_names", asm.code, lines, i) elif line.startswith("# Varnames:"): line = lines[i] asm.code.co_varnames = line[1:].strip().split(", ") i += 1 elif line.startswith("# Positional arguments:"): line = lines[i] args = line[1:].strip().split(", ") asm.code.co_argcount = len(args) i += 1 else: if not line.strip(): continue match = re.match(r"^([^\s]+):$", line) if match: label[match.group(1)] = offset continue match = re.match(r"^\s*([\d]+):\s*$", line) if match: line_no = int(match.group(1)) asm.code.co_lnotab[offset] = line_no continue # Opcode section assert ( bytecode_seen ), "File needs to start out with: # Python bytecode <version>" fields = line.strip().split() line_no = None num_fields = len(fields) if num_fields > 1: if fields[0] == ">>": fields = fields[1:] num_fields -= 1 if is_lineno(fields[0]) and is_int(fields[1]): line_no = int(fields[0][:-1]) opname, operand = get_opname_operand(asm.opc, fields[2:]) elif is_lineno(fields[0]): line_no = int(fields[0][:-1]) fields = fields[1:] if fields[0] == ">>": fields = fields[1:] if is_int(fields[0]): fields = fields[1:] opname, operand = get_opname_operand(asm.opc, fields) elif is_int(fields[0]): opname, operand = get_opname_operand(asm.opc, fields[1:]) else: opname, operand = get_opname_operand(asm.opc, fields) else: opname, _ = get_opname_operand(asm.opc, fields) if opname in asm.opc.opname: inst = Instruction() inst.opname = opname.replace("+", "_") inst.opcode = asm.opc.opmap[inst.opname] if xdis.op_has_argument(inst.opcode, asm.opc): inst.arg = operand else: inst.arg = None inst.line_no = line_no asm.code.instructions.append(inst) if inst.opcode in asm.opc.JUMP_OPS: if not is_int(operand): backpatch_inst.add(inst) offset += xdis.op_size(inst.opcode, asm.opc) else: raise RuntimeError("Illegal opname %s in:\n%s" % (opname, line)) pass pass # print(asm.code.co_lnotab) if asm: co = create_code(asm, label, backpatch_inst) asm.update_lists(co, label, backpatch_inst) asm.code_list.reverse() asm.status = "finished" return asm
def main(dbg=None, sys_argv=list(sys.argv)): """Routine which gets run if we were invoked directly""" # Save the original just for use in the restart that works via exec. orig_sys_argv = list(sys_argv) opts, dbg_opts, sys_argv = Moptions.process_options(__title__, __version__, sys_argv) if opts.server: connection_opts={'IO': 'TCP', 'PORT': opts.port} intf = Mserver.ServerInterface(connection_opts=connection_opts) dbg_opts['interface'] = intf if 'FIFO' == intf.server_type: print('Starting FIFO server for process %s.' % os.getpid()) elif 'TCP' == intf.server_type: print('Starting TCP server listening on port %s.' % intf.inout.PORT) pass elif opts.client: Mclient.main(opts, sys_argv) return dbg_opts['orig_sys_argv'] = orig_sys_argv if dbg is None: dbg = Mdebugger.Debugger(dbg_opts) dbg.core.add_ignore(main) pass Moptions._postprocess_options(dbg, opts) # process_options has munged sys.argv to remove any options that # options that belong to this debugger. The original options to # invoke the debugger and script are in global sys_argv if len(sys_argv) == 0: # No program given to debug. Set to go into a command loop # anyway mainpyfile = None else: mainpyfile = sys_argv[0] # Get script filename. if not osp.isfile(mainpyfile): mainpyfile=Mclifns.whence_file(mainpyfile) is_readable = Mfile.readable(mainpyfile) if is_readable is None: print("%s: Python script file '%s' does not exist" % (__title__, mainpyfile,), file=sys.stderr) sys.exit(1) elif not is_readable: print("%s: Can't read Python script file '%s'" % (__title__, mainpyfile, ), file=sys.stderr) sys.exit(1) return if Mfile.is_compiled_py(mainpyfile): try: from xdis import load_module, PYTHON_VERSION, IS_PYPY (python_version, timestamp, magic_int, co, is_pypy, source_size) = load_module(mainpyfile, code_objects=None, fast_load=True) assert is_pypy == IS_PYPY assert python_version == PYTHON_VERSION, \ "bytecode is for version %s but we are version %s" % ( python_version, PYTHON_VERSION) # We should we check version magic_int py_file = co.co_filename if osp.isabs(py_file): try_file = py_file else: mainpydir = osp.dirname(mainpyfile) dirnames = [mainpydir] + os.environ['PATH'].split(os.pathsep) + ['.'] try_file = Mclifns.whence_file(py_file, dirnames) if osp.isfile(try_file): mainpyfile = try_file pass else: # Move onto the except branch raise IOError("Python file name embedded in code %s not found" % try_file) except: try: from uncompyle6 import uncompyle_file except ImportError: print("%s: Compiled python file '%s', but uncompyle6 not found" % (__title__, mainpyfile), file=sys.stderr) sys.exit(1) return short_name = osp.basename(mainpyfile).strip('.pyc') fd = tempfile.NamedTemporaryFile(suffix='.py', prefix=short_name + "_", delete=False) try: uncompyle_file(mainpyfile, fd) mainpyfile = fd.name fd.close() except: print("%s: error uncompiling '%s'" % (__title__, mainpyfile), file=sys.stderr) fd.close() os.unlink(fd.name) # FIXME: remove the below line and continue with just the # bytecode sys.exit(1) pass # If mainpyfile is an optimized Python script try to find and # use non-optimized alternative. mainpyfile_noopt = pyficache.pyc2py(mainpyfile) if mainpyfile != mainpyfile_noopt \ and Mfile.readable(mainpyfile_noopt): print("%s: Compiled Python script given and we can't use that." % __title__, file=sys.stderr) print("%s: Substituting non-compiled name: %s" % ( __title__, mainpyfile_noopt,), file=sys.stderr) mainpyfile = mainpyfile_noopt pass # Replace trepan's dir with script's dir in front of # module search path. sys.path[0] = dbg.main_dirname = osp.dirname(mainpyfile) # XXX If a signal has been received we continue in the loop, otherwise # the loop exits for some reason. dbg.sig_received = False # if not mainpyfile: # print('For now, you need to specify a Python script name!') # sys.exit(2) # pass while True: # Run the debugged script over and over again until we get it # right. try: if dbg.program_sys_argv and mainpyfile: normal_termination = dbg.run_script(mainpyfile) if not normal_termination: break else: dbg.core.execution_status = 'No program' dbg.core.processor.process_commands() pass dbg.core.execution_status = 'Terminated' dbg.intf[-1].msg("The program finished - quit or restart") dbg.core.processor.process_commands() except Mexcept.DebuggerQuit: break except Mexcept.DebuggerRestart: dbg.core.execution_status = 'Restart requested' if dbg.program_sys_argv: sys.argv = list(dbg.program_sys_argv) part1 = ('Restarting %s with arguments:' % dbg.core.filename(mainpyfile)) args = ' '.join(dbg.program_sys_argv[1:]) dbg.intf[-1].msg(Mmisc.wrapped_lines(part1, args, dbg.settings['width'])) else: break except SystemExit: # In most cases SystemExit does not warrant a post-mortem session. break except: # FIXME: Should be handled above without this mess exception_name = str(sys.exc_info()[0]) if exception_name == str(Mexcept.DebuggerQuit): break elif exception_name == str(Mexcept.DebuggerRestart): dbg.core.execution_status = 'Restart requested' if dbg.program_sys_argv: sys.argv = list(dbg.program_sys_argv) part1 = ('Restarting %s with arguments:' % dbg.core.filename(mainpyfile)) args = ' '.join(dbg.program_sys_argv[1:]) dbg.intf[-1].msg( Mmisc.wrapped_lines(part1, args, dbg.settings['width'])) pass else: raise pass # Restore old sys.argv sys.argv = orig_sys_argv return
def _load_pyc(self, filename): return xdis.load_module(filename)
def run_python_file(filename, args, package=None, callback=None, format_instruction=format_instruction): """Run a python file as if it were the main program on the command line. `filename` is the path to the file to execute, it need not be a .py file. `args` is the argument array to present as sys.argv, including the first element naming the file being executed. `package` is the name of the enclosing package, if any. If `callback` is not None, it is a function which is called back as the execution progresses. This can be used for example in a debugger, or for custom tracing or statistics gathering. """ # Create a module to serve as __main__ old_main_mod = sys.modules["__main__"] main_mod = imp.new_module("__main__") sys.modules["__main__"] = main_mod main_mod.__file__ = filename if package: main_mod.__package__ = package main_mod.__builtins__ = BUILTINS # set sys.argv and the first path element properly. old_argv = sys.argv old_path0 = sys.path[0] # note: the type of args is na tuple; we want type(sys.argv) == list sys.argv = [filename] + list(args) if package: sys.path[0] = "" else: sys.path[0] = os.path.abspath(os.path.dirname(filename)) is_pypy = IS_PYPY try: # Open the source or bytecode file. try: mime = mimetypes.guess_type(filename) if mime == ("application/x-python-code", None): ( python_version, timestamp, magic_int, code, is_pypy, source_size, sip_hash, ) = load_module(filename) supported_versions, mess = get_supported_versions( is_pypy, is_bytecode=True) if python_version not in supported_versions: raise WrongBytecodeError( "We only support byte code for %s: %r is %2.1f bytecode" % (mess, filename, python_version)) pass else: source_file = open_source(filename) try: source = source_file.read() finally: source_file.close() supported_versions, mess = get_supported_versions( IS_PYPY, is_bytecode=False) if PYTHON_VERSION not in supported_versions: raise CannotCompileError( "We need %s to compile source code; you are running Python %s" % (mess, PYTHON_VERSION)) # We have the source. `compile` still needs the last line to be clean, # so make sure it is, then compile a code object from it. if not source or source[-1] != "\n": source += "\n" code = compile(source, filename, "exec") python_version = PYTHON_VERSION except (IOError, ImportError): raise NoSourceError("No file to run: %r" % filename) # Execute the source file. exec_code_object( code, main_mod.__dict__, python_version, is_pypy, callback, format_instruction=format_instruction, ) finally: # Restore the old __main__ sys.modules["__main__"] = old_main_mod # Restore the old argv and path sys.argv = old_argv sys.path[0] = old_path0