Created
August 18, 2012 03:07
-
-
Save syshack/3384085 to your computer and use it in GitHub Desktop.
wingide远程调试
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| ######################################################################### | |
| """ wingdb.py -- Top-level command used internally by Wing IDE to | |
| start a debug process. | |
| Copyright (c) 2000-2012, Archaeopteryx Software, Inc. All rights reserved. | |
| Written by Stephan R.A. Deibel and John P. Ehresman | |
| """ | |
| ######################################################################### | |
| # Only import sys at the top-level because sys.path needs to be modified | |
| # in some contexts | |
| import sys | |
| # Start without translation -- this gets changed once netserver is found | |
| _ = lambda x: x | |
| # For trouble-shooting, set environment variable or uncomment line below | |
| def _GetDefaultPrintAllTracebacks(): | |
| import os | |
| return os.environ.get('WINGDB_PRINT_ALL_TRACEBACKS', 0) | |
| kPrintAllTracebacks = _GetDefaultPrintAllTracebacks() | |
| if kPrintAllTracebacks: | |
| import os | |
| os.environ['WINGDB_PRINT_ALL_TRACEBACKS'] = '1' | |
| # Wing version & build numbers | |
| kVersion = "4.1.7" | |
| kBuild = "1" | |
| # Utils for dealing w/ Python 2.x vs. 3.x | |
| if sys.hexversion >= 0x03000000: | |
| def has_key(o, key): | |
| return key in o | |
| else: | |
| def has_key(o, key): | |
| return o.has_key(key) | |
| # Set __file__ if not already set; this is an internal value so is kosher | |
| try: | |
| __file__ | |
| except NameError: | |
| __file__ = sys.argv[0] | |
| def _GetWingDirs(): | |
| """ Gets winghome & usersettings dir if __name__ is __main__. Returns | |
| (None, None) if unable to retrieve the dirs for some reason. """ | |
| if __name__ != '__main__': | |
| return None, None | |
| import os | |
| if has_key(os.environ, 'WINGDB_WINGHOME'): | |
| try: | |
| winghome = os.environ['WINGDB_WINGHOME'] | |
| del os.environ['WINGDB_WINGHOME'] | |
| user_settings = os.environ.get('WINGDB_USERSETTINGS', None) | |
| if user_settings is not None: | |
| del os.environ['WINGDB_USERSETTINGS'] | |
| return winghome, user_settings | |
| except: | |
| if kPrintAllTracebacks: | |
| import traceback | |
| traceback.print_exc(file=sys.__stderr__) | |
| try: | |
| winghome = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) | |
| return winghome, None | |
| except: | |
| if kPrintAllTracebacks: | |
| sys.__stderr__.write('WINGDB ARGS:' + str(sys.argv)) | |
| sys.__stderr__.write('\n') | |
| import traceback | |
| traceback.print_exc(file=sys.__stderr__) | |
| return None, None | |
| ######################################################################### | |
| # Temporary, in-memory logger; used until we know where to send the messages | |
| ######################################################################### | |
| class CTempLog: | |
| """ Temporarily log messages to a list. """ | |
| def __init__(self): | |
| self.fEntries = [] | |
| def out(self, *args): | |
| """ Save msg in fEntries """ | |
| args = map(str, args) | |
| first = 1 | |
| for s in args: | |
| if first: | |
| first = 0 | |
| else: | |
| self.write(' ') | |
| self.write(s) | |
| def write(self, msg): | |
| """ Save msg in fEntries """ | |
| if msg[-1:] == '\n': | |
| msg = msg[:-1] | |
| self.fEntries.append(msg) | |
| def write_entries(self, log): | |
| """ Write fEntries to out object via it's out method. """ | |
| for entry in self.fEntries: | |
| log.out(entry) | |
| def clear(self): | |
| """ Clear all entries. """ | |
| self.fEntries = [] | |
| ######################################################################### | |
| # Argument parsing | |
| ######################################################################### | |
| def _ParseSingleArg(err, arg_index, func = None, choices = None): | |
| """ Parse a single arg from argv; print usage if arg is incorrect. Applies | |
| func to the value if func is not None and look up value in choices map | |
| if choices is not None. """ | |
| try: | |
| value = sys.argv[arg_index] | |
| # Transform value if function is provided | |
| if func != None: | |
| value = func(value) | |
| # Look up value in choices if choices is not None | |
| if choices != None: | |
| value = choices[value] | |
| return value | |
| except: | |
| if kPrintAllTracebacks: | |
| import traceback | |
| traceback.print_exc(file=sys.__stderr__) | |
| sys.exit(2) | |
| def _LogfileTransform(value): | |
| """ Transformation function for logfile arg. """ | |
| value = eval(value) | |
| if value == '<none>': | |
| return None | |
| else: | |
| return value | |
| def _HostportTransform(hostport): | |
| """ Transformation function for host:port arg. """ | |
| colonpos = hostport.index(':') | |
| host = hostport[0:colonpos] | |
| if host == '': | |
| host = '127.0.0.1' | |
| port = int(hostport[colonpos+1:]) | |
| return host, port | |
| def _ParseArgv(err): | |
| """ Parses sys.argv, which contains parameters encoded by position, and | |
| returns dictionary of values. | |
| The parameters allowed are defined as follows by position in sys.argv: | |
| 0) This script's name | |
| 1) host:port indicates where Wing IDE is listening for reverse connection | |
| from this debug process. | |
| 2) attachport, where the debug process will listen for attach requests | |
| when not connected to a debug process. | |
| 3) One of --first-stop to stop on the first line of the debug program, | |
| or --no-first-stop to run to first breakpoint or completion. | |
| 4) logfile for debug server internals. One of <none> for no logging, | |
| <stderr>, <stdout>, or a file name in which to log extra error output. | |
| The parameter should be encoded as a Python expression that can | |
| be parsed by eval(). This avoids problems with special chars | |
| in file names. For example, "r'mylog'" is a valid value. | |
| 5) optional --very-verbose-log to turn on core logging support if it | |
| is present | |
| 6) One of --wait-on-exit or --nowait-on-exit. When set to wait-on-exit, | |
| the debugger will wait for user to hit a key before exiting entirely. | |
| 7) Optional --stdout-encoding=<encoding>. Sets sys.stdout.encoding & | |
| sys.stderr.encoding to given <encoding> string iff encoding is valid | |
| 8) Optional --stdin-encoding=<encoding>. Sets sys.stdin.encoding | |
| to given <encoding> string iff encoding is valid | |
| 7) filename, which is a Python expression that can be passed to eval() | |
| that evaluates to the name of the file to debug. | |
| The dictionary returned from this function contains: | |
| host: host to connect back to | |
| port: port to connect back to | |
| attachport: port # to listen far attach requests on | |
| firststop: whether to stop on first line | |
| logfile: <none>, <stderr>, <stdout>, or a file name | |
| veryverboselog: whether very verbose is on | |
| waitonexit: whether to wait for a keystroke when the program exits | |
| stdoutencoding: output encoding if specified or None | |
| stdinencoding: input encoding if specified or None | |
| filename: name of the python file to debug | |
| """ | |
| import os | |
| args = {} | |
| # Parse args and store value in dictionary to return | |
| args['host'], args['port'] = _ParseSingleArg(err, 1, _HostportTransform) | |
| args['attachport'] = _ParseSingleArg(err, 2, int) | |
| args['firststop'] = _ParseSingleArg(err, 3, choices = {"--no-first-stop": 0, | |
| "--first-stop": 1}) | |
| args['logfile'] = _ParseSingleArg(err, 4, _LogfileTransform) | |
| args['veryverboselog'] = (sys.argv[5] == '--very-verbose-log') | |
| next = 5 | |
| if args['veryverboselog']: | |
| next = next + 1 | |
| args['waitonexit'] = _ParseSingleArg(err, next, choices = {"--nowait-on-exit": 0, | |
| "--wait-on-exit": 1}) | |
| next = next + 1 | |
| if sys.argv[next].find('--stdout-encoding=') == 0: | |
| args['stdoutencoding'] = sys.argv[next][len('--stdout-encoding='):] | |
| next = next + 1 | |
| else: | |
| args['stdoutencoding'] = None | |
| if sys.argv[next].find('--stdin-encoding=') == 0: | |
| args['stdinencoding'] = sys.argv[next][len('--stdin-encoding='):] | |
| next = next + 1 | |
| else: | |
| args['stdinencoding'] = None | |
| args['filename'] = filename = _ParseSingleArg(err, next, eval) | |
| first_addtl_arg = next + 1 | |
| # Check if debug file exists and is a file | |
| if not os.path.exists(filename): | |
| err.write(_('wingdb.py: Error: Debug file does not exist:')) | |
| err.write(filename) | |
| sys.exit(1) | |
| if not os.path.isfile(filename): | |
| err.write(_('wingdb.py: Error: Debug file is not a file:')) | |
| err.write(filename) | |
| sys.exit(1) | |
| # Prune args down to just args for the debugged program | |
| del sys.argv[:first_addtl_arg] | |
| return args | |
| ######################################################################### | |
| # Debug server access | |
| ######################################################################### | |
| def GetVersionTriple(): | |
| """ Return 3 element tuple (major, minor, micro) for the version of | |
| the python interpreter we're running in. """ | |
| if sys.hexversion >= 0x02030000 and sys.hexversion < 0x02040000: | |
| ff000000_mask = eval('int("4278190080")') | |
| else: | |
| ff000000_mask = eval('0xff000000') | |
| return ((sys.hexversion & ff000000_mask) >> 24, | |
| (sys.hexversion & 0x00ff0000) >> 16, | |
| (sys.hexversion & 0x0000ff00) >> 8) | |
| def GetPossibleServerLocations(winghome, user_settings=None): | |
| """ Return sequence of locations to look for netserver in. Each location is | |
| either a directory or a zip file. Look in patch dirs first, then in primary | |
| bin dir, and finally in source dir. """ | |
| import os | |
| server_pkg = 'tserver' | |
| major, minor, release = GetVersionTriple() | |
| interp_id = '%d.%d' % (major, minor) | |
| # Get patch dirs w/ bin/major.minor | |
| try: | |
| exec_dict = {} | |
| f = open(os.path.join(winghome, 'bin', '_patchsupport.py')) | |
| try: | |
| exec(f.read(), exec_dict) | |
| finally: | |
| f.close() | |
| find_matching = exec_dict['FindMatching'] | |
| bin_list = find_matching(interp_id, winghome, user_settings) | |
| except Exception: | |
| if kPrintAllTracebacks: | |
| import traceback | |
| traceback.print_exc(file = sys.__stderr__) | |
| bin_list = [] | |
| # Find dirs with debug server dir | |
| dir_list = [] | |
| for dirname in bin_list: | |
| server_dir = os.path.join(dirname, 'src', 'debug', server_pkg) | |
| if os.path.isdir(server_dir): | |
| dir_list.append(server_dir) | |
| # Finally append default bin & src dirs | |
| zip_name = os.path.join(winghome, 'bin', interp_id, 'src.zip') | |
| if os.path.isfile(zip_name): | |
| dir_list.append(os.path.join(zip_name, 'debug', server_pkg)) | |
| else: | |
| dir_list.append(os.path.join(winghome, 'bin', interp_id, 'src', 'debug', server_pkg)) | |
| dir_list.append(os.path.join(winghome, 'src', 'debug', server_pkg)) | |
| return dir_list | |
| def FindNetServerModule(winghome, user_settings=None): | |
| """ Finds wing's netserver module given winghome path name. Does not write | |
| to log so it can be called from wingdbstub. """ | |
| # Work around win32 path joining problems | |
| if sys.platform == 'win32' and winghome[-1] == '\\': | |
| winghome = winghome[:-1] | |
| if sys.hexversion < 0x03000000: | |
| import copy_reg # cPickle is screwed up if we don't hold onto this | |
| try: | |
| import encodings # encodings needs to be shared if it's available | |
| except ImportError: | |
| encodings = None | |
| prev_mods = list(sys.modules.keys()) | |
| orig_path = list(sys.path) | |
| try: | |
| for path in GetPossibleServerLocations(winghome, user_settings): | |
| sys.path = [path] + orig_path | |
| try: | |
| import netserver | |
| global _ | |
| _ = netserver.abstract._ | |
| except ImportError: | |
| if has_key(sys.modules, 'netserver'): | |
| del sys.modules['netserver'] | |
| if kPrintAllTracebacks: | |
| import traceback | |
| sys.__stderr__.write('Trying to import netserver from %s\n' % path) | |
| traceback.print_exc(file = sys.__stderr__) | |
| else: | |
| netserver.abstract._SetWingHome(winghome) | |
| return netserver | |
| # If reach here, all imports have failed | |
| raise ImportError('Could not import netserver') | |
| # Restore sys.path and unload modules we added to sys.modules | |
| finally: | |
| sys.path = orig_path | |
| for key in list(sys.modules.keys()): | |
| if not key in prev_mods: | |
| del sys.modules[key] | |
| def CreateServer(host, port, attachport, firststop, err, netserver, | |
| pwfile_path): | |
| """ Creates server. Writes traceback to err and returns None if creation | |
| fails. """ | |
| # Create the server | |
| try: | |
| err.out(_("Network peer is "), host, "port", port) | |
| err.out(_("Attach port = "), attachport) | |
| err.out(_("Initial stop = "), firststop) | |
| # Only listen locally if attachport is an int or doesn't contain ':' | |
| if type(attachport) == type(1) or attachport.find(':') == -1: | |
| attachport = '127.0.0.1:' + str(attachport) | |
| internal_modules = [] | |
| mod = sys.modules.get(__name__) | |
| if mod is not None and mod.__dict__ is globals(): | |
| internal_modules.append(mod) | |
| return netserver.CNetworkServer(host, port, attachport, err, firststop, | |
| pwfile_path, internal_modules=tuple(internal_modules)) | |
| # Cook exceptions for better display | |
| except: | |
| err.out(_("wingdb.py: Could not create debug server")) | |
| raise | |
| def DebugFile(server, filename, err, fs_encoding, sys_path=None): | |
| """ Debug the given file. Writes any exception to err. """ | |
| import os | |
| # Run the session | |
| try: | |
| filename = os.path.abspath(filename) | |
| try: | |
| pfilename = unicode(filename, fs_encoding) | |
| except: | |
| pfilename = filename | |
| err.out(_("wingdb.py: Running %s") % pfilename) | |
| os.environ['WINGDB_ACTIVE'] = "1" | |
| if sys_path is not None: | |
| sys.path = sys_path | |
| server.Run(filename, sys.argv) | |
| # Cook exceptions for better display | |
| except: | |
| err.out(_("wingdb.py: Server exiting abnormally on exception")) | |
| raise | |
| def CreateErrStream(netserver, logfile, very_verbose=0): | |
| """ Creates error stream for debugger. """ | |
| file_list = [] | |
| if logfile != None: | |
| file_list.append(logfile) | |
| err = netserver.abstract.CErrStream(file_list, very_verbose=very_verbose) | |
| return err | |
| def ModifySysPath(): | |
| """ Insert std lib directory at the start of sys.path. This is usually | |
| sys.prefix/[Ll]ib but it may not be. Need w/ Python 2.3 | |
| and 2.4 on win32 which put current dir in the path before the | |
| std lib -- the bug was fixed in 2.5 """ | |
| if sys.platform == 'win32': | |
| libdir = '%s\\Lib' % sys.prefix | |
| else: | |
| libdir = '%s/lib' % sys.prefix | |
| sys.path.insert(0, libdir) | |
| import os | |
| # Adjust sys.path[0] if os is not in sys.prefix\Lib | |
| if os.path.dirname(os.__file__) != libdir: | |
| sys.path[0] = os.path.dirname(os.__file__) | |
| if sys.platform != 'win32': | |
| sys.path.insert(1, '%s/lib-dynload' % os.__file__) | |
| def SetEncodingPython2(encoding, file_list, netserver): | |
| """ Set encoding of all file objects in file_list (Python 2.x only)""" | |
| if sys.hexversion >= 0x03000000: | |
| raise NotImplementedError | |
| if encoding is None or netserver is None: | |
| return | |
| try: | |
| import codecs | |
| codec_info = codecs.lookup(encoding) | |
| except: | |
| return | |
| try: | |
| set_file_encoding = netserver.dbgserver.dbgtracer.PyFile_SetEncoding | |
| except AttributeError: | |
| return | |
| for single_file in file_list: | |
| set_file_encoding(single_file, encoding) | |
| def main(): | |
| """ Parse args and then run program. """ | |
| # Make sure we always give opportunity to see output when wait-on-exit is true! | |
| waitonexit = 0 | |
| server = None | |
| err = None | |
| tmp_log = CTempLog() | |
| try: | |
| # Pick up exceptions | |
| args = {} | |
| try: | |
| # Delete sys.path[0] because it refers to this file's directory then save sys.path | |
| # for future use | |
| del sys.path[0] | |
| orig_sys_path = sys.path[:] | |
| # Modify sys.path on win32 python 2.0, 2.1, 2.3 & 2.4 versions to put stdlib before cwd | |
| interp_version = GetVersionTriple() | |
| if (sys.platform == 'win32' and interp_version[0] == 2 | |
| and interp_version[1] in [0, 1, 3, 4]): | |
| ModifySysPath() | |
| # Find directories | |
| winghome, user_settings = _GetWingDirs() | |
| # Parse args | |
| args = _ParseArgv(tmp_log) | |
| waitonexit = args.get('waitonexit', waitonexit) | |
| # Find netserver | |
| try: | |
| netserver = FindNetServerModule(winghome, user_settings) | |
| except: | |
| import traceback | |
| if kPrintAllTracebacks: | |
| traceback.print_exc(file=sys.__stderr__) | |
| traceback.print_exc(file=tmp_log) | |
| tmp_log.out(_("wingdb.py: Error: Failed to start the debug server")) | |
| tmp_log.out(_("wingdb.py: Error: You may be running an unsupported version of Python")) | |
| tmp_log.out(_("wingdb.py: Python version = %s") % sys.version) | |
| tmp_log.out("wingdb.py: WINGHOME=%s" % repr(winghome)) | |
| sys.exit(-1) | |
| # Sanity check: Debugging in optimized mode makes no sense | |
| if __debug__ == 0: | |
| tmp_log.out(_("wingdb.py: Error: Cannot run a debug process with optimized python")) | |
| tmp_log.out(_("wingdb.py: Error: You must omit the -O or -OO command line option, or")) | |
| tmp_log.out(_("wingdb.py: Error: undefine environment variable PYTHONOPTIMIZE")) | |
| sys.exit(-1) | |
| out_encoding = args.get('stdoutencoding') | |
| in_encoding = args.get('stdinencoding') | |
| tmp_log.out("Setting stdoutencoding=%s" % str(out_encoding)) | |
| tmp_log.out("Setting stdinencoding=%s" % str(in_encoding)) | |
| if sys.hexversion < 0x02060000: | |
| SetEncodingPython2(out_encoding, [sys.stdout, sys.stderr], netserver) | |
| SetEncodingPython2(in_encoding, [sys.stdin], netserver) | |
| # Create error log and write tmp entries to it | |
| err = CreateErrStream(netserver, args['logfile'], args['veryverboselog']) | |
| tmp_log.write_entries(err) | |
| tmp_log.clear() | |
| # Create the server and run | |
| err.out("sys.path=%s" % repr(sys.path)) | |
| err.out("sys.argv=%s" % repr(sys.argv)) | |
| pwfile_path = [netserver.abstract.kPWFilePathUserProfileDir] | |
| server = CreateServer(args['host'], args['port'], args['attachport'], | |
| args['firststop'], err, netserver, pwfile_path) | |
| DebugFile(server, args['filename'], err, netserver.abstract.kFileSystemEncoding, | |
| orig_sys_path) | |
| # Handle any exception that caused debug to fail to start up | |
| except: | |
| if kPrintAllTracebacks: | |
| import traceback | |
| traceback.print_exc(file=sys.__stderr__) | |
| # Find log file, if any was specified | |
| logfile = args.get('logfile') | |
| opened_file = 0 | |
| if logfile is None or logfile == '<none>' or logfile == '<stderr>': | |
| file = sys.stderr | |
| elif logfile == '<stdout>': | |
| file = sys.stdout | |
| else: | |
| try: | |
| file = open(logfile, "a") | |
| opened_file = 1 | |
| except (IOError, OSError): | |
| file = sys.stderr | |
| # Write log/exception info if we have a log file | |
| if file != None: | |
| # Write any stored up log entries | |
| for entry in tmp_log.fEntries: | |
| file.write(entry + '\n') | |
| # Also print traceback to log file, which can raise exceptions | |
| import traceback | |
| try: | |
| traceback.print_exc(file=file) | |
| except: | |
| file.write('Exception raised while printing exc') | |
| # Close file if we opened it | |
| if opened_file: | |
| file.close() | |
| # Always stop server and wait for user to exit if requested | |
| finally: | |
| if server != None: | |
| try: server.Stop() | |
| except: pass | |
| if waitonexit: | |
| line = raw_input(_("-- Type return or enter to exit --\n")) | |
| ######################################################################### | |
| # Execution starts here | |
| ######################################################################### | |
| if __name__ == '__main__': | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment