123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- #########################################################################
- """ wingdbstub.py -- Start debugging Python programs from outside of Wing
-
- Copyright (c) 1999-2018, Archaeopteryx Software, Inc. All rights reserved.
-
- Written by Stephan R.A. Deibel and John P. Ehresman
-
- Usage:
- -----
-
- This file is used to initiate debug in Python code without launching
- that code from Wing. To use it, edit the configuration values below,
- start Wing and configure it to listen for outside debug connections,
- and then add the following line to your code:
-
- import wingdbstub
-
- Debugging will start immediately after this import statement is reached.
-
- For details, see Debugging Externally Launched Code in the manual.
-
- """
- #########################################################################
-
- import sys
- orig_sys_modules = list(sys.modules.keys())
- orig_sys_path = list(sys.path)
- orig_meta_path = list(sys.meta_path)
-
- import os
- if sys.version_info >= (3, 7):
- import importlib
- else:
- import imp
-
- #------------------------------------------------------------------------
- # Required configuration values (some installers set this automatically)
-
- # This should be the full path to your Wing installation. On OS X, use
- # the full path of the Wing application bundle, for example
- # /Applications/WingPro.app. When set to None, the environment variable
- # WINGHOME is used instead.
- WINGHOME = r"C:\Program Files (x86)\Wing Pro 7.0"
-
- #------------------------------------------------------------------------
- # Optional configuration values: The named environment variables, if set,
- # will override these settings.
-
- # Set this to 1 to disable all debugging; 0 to enable debugging
- # (WINGDB_DISABLED environment variable)
- kWingDebugDisabled = 0
-
- # Host:port of the IDE within which to debug: As configured in the IDE
- # with the Server Port preference
- # (WINGDB_HOSTPORT environment variable)
- kWingHostPort = 'localhost:50005'
-
- # Port on which to listen for connection requests, so that the
- # IDE can attach or reattach to the debug process after it has started.
- # Set this to '-1' to disable listening for connection requests.
- # This is only used when the debug process is not attached to
- # an IDE or the IDE has dropped its connection. The configured
- # port can optionally be added to the IDE's Common Attach Hosts
- # preference. Note that a random port is used instead if the given
- # port is already in use.
- # (WINGDB_ATTACHPORT environment variable)
- kAttachPort = '50015'
-
- # Set this to a filename to log verbose information about the debugger
- # internals to a file. If the file does not exist, it will be created
- # as long as its enclosing directory exists and is writeable. Use
- # "<stderr>" or "<stdout>" to write to stderr or stdout. Note that
- # "<stderr>" may cause problems on Windows if the debug process is not
- # running in a console.
- # (WINGDB_LOGFILE environment variable)
- kLogFile = None
-
- # Produce a tremendous amount of logging from the debugger internals.
- # Do not set this unless instructed to do so by Wingware support. It
- # will slow the debugger to a crawl.
- # (WINGDB_LOGVERYVERBOSE environment variable)
- kLogVeryVerbose = 0
-
- # Set this to 1 when debugging embedded scripts in an environment that
- # creates and reuses a Python instance across multiple script invocations.
- # It turns off automatic detection of program quit so that the debug session
- # can span multiple script invocations. When this is turned on, you may
- # need to call ProgramQuit() on the debugger object to shut down the
- # debugger cleanly when your application exits or discards the Python
- # instance. If multiple Python instances are created in the same run,
- # only the first one will be able to debug unless it terminates debug
- # and the environment variable WINGDB_ACTIVE is unset before importing
- # this module in the second or later Python instance. See the Wing
- # manual for details.
- # (WINGDB_EMBEDDED environment variable)
- kEmbedded = 0
-
- # Path to search for the debug password file and the name of the file
- # to use. The password file contains a security token for all
- # connections to the IDE and must match the wingdebugpw file in the
- # User Settngs directory used by the IDE. '$<winguserprofile>'
- # is replaced by the User Settings directory for the user that
- # is running the process.
- # (WINGDB_PWFILEPATH environment variable)
- kPWFilePath = [os.path.dirname(__file__), '$<winguserprofile>']
- kPWFileName = 'wingdebugpw'
-
- # Whether to exit when the debugger fails to run or to connect with the IDE
- # By default, execution continues without debug or without connecting to
- # the IDE.
- # (WINGDB_EXITONFAILURE environment variable)
- kExitOnFailure = 0
-
- #------------------------------------------------------------------------
- # Find Wing debugger installation location
-
- # Check environment: Must have WINGHOME defined if still == None
- if WINGHOME == None:
- if 'WINGHOME' in os.environ:
- WINGHOME=os.environ['WINGHOME']
- else:
- msg = '\n'.join(["*******************************************************************",
- "Error: Could not find Wing installation! You must set WINGHOME or edit",
- "wingdbstub.py where indicated to point it to the location where",
- "Wing is installed.\n"])
- sys.stderr.write(msg)
- raise ImportError(msg)
-
- WINGHOME = os.path.expanduser(WINGHOME)
-
- def NP_FindActualWingHome(winghome):
- """ Find the actual directory to use for winghome. Needed on OS X
- where the .app directory is the preferred dir to use for WINGHOME and
- .app/Contents/MacOS is accepted for backward compatibility. """
-
- if sys.platform != 'darwin':
- return winghome
-
- app_dir = None
- if os.path.isdir(winghome):
- if winghome.endswith('/'):
- wo_slash = winghome[:-1]
- else:
- wo_slash = winghome
-
- if wo_slash.endswith('.app'):
- app_dir = wo_slash
- elif wo_slash.endswith('.app/Contents/MacOS'):
- app_dir = wo_slash[:-len('/Contents/MacOS')]
-
- if app_dir and os.path.isdir(os.path.join(app_dir, 'Contents', 'Resources')):
- return os.path.join(app_dir, 'Contents', 'Resources')
-
- return winghome
-
- WINGHOME = NP_FindActualWingHome(WINGHOME)
- os.environ['WINGHOME'] = WINGHOME
-
- # Path used to find the wingdebugpw file
- kPWFilePath.append(WINGHOME)
-
- #-----------------------------------------------------------------------
- def NP_LoadModuleFromBootstrap(winghome, modname):
- """Load a module from the installation bootstrap directory. Assumes that
- imports don't change sys.path. The modules are unloaded from sys.modules
- so they can be loaded again later from an update."""
-
- # Limited to simple module loads
- assert '.' not in modname
-
- orig_sys_path = sys.path[:]
- orig_modules = set(sys.modules)
-
- dirname = winghome + '/bootstrap'
- sys.path.insert(0, dirname)
-
- code = 'import %s' % modname
- exec(code)
-
- new_modules = set(sys.modules)
- new_modules.difference_update(orig_modules)
- for mod in new_modules:
- del sys.modules[mod]
-
- sys.path = orig_sys_path
-
- return locals()[modname]
-
- #------------------------------------------------------------------------
- # Set debugger if it hasn't been set -- this is to handle module reloading
- # In the reload case, the debugger variable will be set to something
- try:
- debugger
- except NameError:
- debugger = None
-
- #-----------------------------------------------------------------------
- # Unset WINGDB_ACTIVE env if it was inherited from another process
- # XXX Would be better to be able to call getpid() on dbgtracer but can't access it yet
- if 'WINGDB_ACTIVE' in os.environ and os.environ['WINGDB_ACTIVE'] != str(os.getpid()):
- del os.environ['WINGDB_ACTIVE']
-
- #-----------------------------------------------------------------------
- # Start debugging if not disabled and this module has never been imported
- # before
- if (not kWingDebugDisabled and debugger is None and
- 'WINGDB_DISABLED' not in os.environ and
- 'WINGDB_ACTIVE' not in os.environ):
-
- exit_on_fail = 0
-
- try:
- # Obtain exit if fails value
- exit_on_fail = os.environ.get('WINGDB_EXITONFAILURE', kExitOnFailure)
-
- # Obtain configuration for log file to use, if any
- logfile = os.environ.get('WINGDB_LOGFILE', kLogFile)
- if logfile == '-' or logfile == None or len(logfile.strip()) == 0:
- logfile = None
-
- very_verbose_log = os.environ.get('WINGDB_LOGVERYVERBOSE', kLogVeryVerbose)
- if type(very_verbose_log) == type('') and very_verbose_log.strip() == '':
- very_verbose_log = 0
-
- # Determine remote host/port where the IDE is running
- hostport = os.environ.get('WINGDB_HOSTPORT', kWingHostPort)
- colonpos = hostport.find(':')
- host = hostport[:colonpos]
- port = int(hostport[colonpos+1:])
-
- # Determine port to listen on locally for attach requests
- attachport = int(os.environ.get('WINGDB_ATTACHPORT', kAttachPort))
-
- # Check if running embedded script
- embedded = int(os.environ.get('WINGDB_EMBEDDED', kEmbedded))
-
- # Obtain debug password file search path
- if 'WINGDB_PWFILEPATH' in os.environ:
- pwfile_path = os.environ['WINGDB_PWFILEPATH'].split(os.pathsep)
- else:
- pwfile_path = kPWFilePath
-
- # Obtain debug password file name
- if 'WINGDB_PWFILENAME' in os.environ:
- pwfile_name = os.environ['WINGDB_PWFILENAME']
- else:
- pwfile_name = kPWFileName
-
- # Set up temporary log for errors from merge importer Setup
- class CTmpLog:
- def __init__(self):
- self.fErrors = []
- def write(self, s):
- self.fErrors.append(s)
- tmp_log = CTmpLog()
-
- # Set up the meta importer; everything after this point can be update
- bootstrap_utils = NP_LoadModuleFromBootstrap(WINGHOME, 'bootstrap_utils')
- winghome, user_settings = bootstrap_utils.NP_SetupWingHomeModule(WINGHOME)
- meta = bootstrap_utils.NP_CreateMetaImporter(WINGHOME, user_settings, 'dbg',
- tmp_log)
- import _winghome
- _winghome.kWingHome = winghome
- _winghome.kUserSettings = user_settings
-
- # Find the netserver module and create an error stream
- from debug.tserver import startdebug
- netserver = startdebug.FindNetServerModule(WINGHOME, user_settings)
- err = startdebug.CreateErrStream(netserver, logfile, very_verbose_log)
-
- # Write any temporary log entries
- for s in tmp_log.fErrors:
- err.write(s)
-
- # Create debug server
- debugger = netserver.CNetworkServer(host, port, attachport, err,
- pwfile_path=pwfile_path,
- pwfile_name=pwfile_name,
- autoquit=not embedded)
-
- # Restore module and path environment
- from debug.tserver import startdebug
- startdebug.RestoreEnvironment(orig_sys_modules, orig_sys_path, orig_meta_path)
-
- # Start debugging
- debugger.StartDebug(stophere=0)
- os.environ['WINGDB_ACTIVE'] = str(os.getpid())
- if debugger.ChannelClosed():
- raise ValueError('Not connected')
-
- except:
- if exit_on_fail:
- raise
- else:
- pass
-
- #-----------------------------------------------------------------------
- def Ensure(require_connection=1, require_debugger=1):
- """ Ensure the debugger is started and attempt to connect to the IDE if
- not already connected. Will raise a ValueError if:
-
- * the require_connection arg is true and the debugger is unable to connect
- * the require_debugger arg is true and the debugger cannot be loaded
-
- If SuspendDebug() has been called through the low-level API, calling
- Ensure() resets the suspend count to zero and additional calls to
- ResumeDebug() will be ignored until SuspendDebug() is called again.
-
- Note that a change to the host & port to connect to will only
- be use if a new connection is made.
-
- """
-
- if debugger is None:
- if require_debugger:
- raise ValueError("No debugger")
- return
-
- hostport = os.environ.get('WINGDB_HOSTPORT', kWingHostPort)
- colonpos = hostport.find(':')
- host = hostport[:colonpos]
- port = int(hostport[colonpos+1:])
-
- resumed = debugger.ResumeDebug()
- while resumed > 0:
- resumed = debugger.ResumeDebug()
-
- debugger.SetClientAddress((host, port))
-
- if not debugger.DebugActive():
- debugger.StartDebug()
- elif debugger.ChannelClosed():
- debugger.ConnectToClient()
-
- if require_connection and debugger.ChannelClosed():
- raise ValueError('Not connected')
-
|