Hi,

I order to do some remote debugging, I recently copied pysrc to the
server and modified pydevd_file_utils.py as described in
http://pydev.org/manual_adv_remote_debugger.html. As far as I
understand, there is no separation between the (potentially) system-wide
module pysrc and its user-specific or project-specific configuration.

I would like to suggest refactoring the configuration variables out of
pydevd_file_utils.py into a new file pydevd_config.py. That way, it
should be possible to package pysrc and install it as a system-wide
module on a remote debugging server. Each user or project could specify
an appropriate configuration in a copy of pydevd_config.py. Of course,
that copy must be placed where it is found and imported before the
generic version in the system-wide package is found.

I'm attaching my versions of pydevd_file_utils.py and pydevd_config.py,
based on pydev 2.7.1, which I created for testing this approach.

Malte

'''
    This module provides utilities to get the absolute filenames so that we can 
be sure that:
        - The case of a file will match the actual file in the filesystem 
(otherwise breakpoints won't be hit).
        - Providing means for the user to make path conversions when doing a 
remote debugging session in
          one machine and debugging in another.

'''

from pydevd_constants import * #@UnusedWildImport
from pydevd_config import PATHS_FROM_ECLIPSE_TO_PYTHON, 
DEBUG_CLIENT_SERVER_TRANSLATION
import os.path
import sys
import traceback


normcase = os.path.normcase
basename = os.path.basename
exists = os.path.exists
join = os.path.join

try:
    rPath = os.path.realpath #@UndefinedVariable
except:
    # jython does not support os.path.realpath
    # realpath is a no-op on systems without islink support
    rPath = os.path.abspath 
  

#caches filled as requested during the debug session
NORM_FILENAME_CONTAINER = {}
NORM_FILENAME_AND_BASE_CONTAINER = {}
NORM_FILENAME_TO_SERVER_CONTAINER = {}
NORM_FILENAME_TO_CLIENT_CONTAINER = {}

def _NormFile(filename):
    try:
        return NORM_FILENAME_CONTAINER[filename]
    except KeyError:
        r = normcase(rPath(filename))
        #cache it for fast access later
        NORM_FILENAME_CONTAINER[filename] = r
        return r

    
#Now, let's do a quick test to see if we're working with a version of python 
that has no problems
#related to the names generated...
try:
    try:
        code = rPath.func_code
    except AttributeError:
        code = rPath.__code__
    if not exists(_NormFile(code.co_filename)):
        
sys.stderr.write('-------------------------------------------------------------------------------\n')
        sys.stderr.write('pydev debugger: CRITICAL WARNING: This version of 
python seems to be incorrectly compiled (internal generated filenames are not 
absolute)\n')
        sys.stderr.write('pydev debugger: The debugger may still function, but 
it will work slower and may miss breakpoints.\n')
        sys.stderr.write('pydev debugger: Related bug: 
http://bugs.python.org/issue1666807\n')
        
sys.stderr.write('-------------------------------------------------------------------------------\n')
        
        NORM_SEARCH_CACHE = {}
        
        initial_norm_file = _NormFile
        def _NormFile(filename): #Let's redefine _NormFile to work with paths 
that may be incorrect
            try:
                return NORM_SEARCH_CACHE[filename]
            except KeyError:
                ret = initial_norm_file(filename)
                if not exists(ret):
                    #We must actually go on and check if we can find it as if 
it was a relative path for some of the paths in the pythonpath
                    for path in sys.path:
                        ret = initial_norm_file(join(path, filename))
                        if exists(ret):
                            break
                    else:
                        sys.stderr.write('pydev debugger: Unable to find real 
location for: %s\n' % (filename,))
                        ret = filename
                        
                NORM_SEARCH_CACHE[filename] = ret
                return ret
except:
    #Don't fail if there's something not correct here -- but at least print it 
to the user so that we can correct that
    traceback.print_exc()


if PATHS_FROM_ECLIPSE_TO_PYTHON:
    #Work on the client and server slashes.
    eclipse_sep = None
    python_sep = None
    for eclipse_prefix, server_prefix in PATHS_FROM_ECLIPSE_TO_PYTHON:
        if eclipse_sep is not None and python_sep is not None:
            break
        
        if eclipse_sep is None:
            for c in eclipse_prefix:
                if c in ('/', '\\'):
                    eclipse_sep = c
                    break
                
        if python_sep is None:
            for c in server_prefix:
                if c in ('/', '\\'):
                    python_sep = c
                    break
        
    #If they're the same or one of them cannot be determined, just make it all 
None.
    if eclipse_sep == python_sep or eclipse_sep is None or python_sep is None:
        eclipse_sep = python_sep = None
            
                
    #only setup translation functions if absolutely needed! 
    def NormFileToServer(filename):
        #Eclipse will send the passed filename to be translated to the python 
process
        #So, this would be 'NormFileFromEclipseToPython' 
        try:
            return NORM_FILENAME_TO_SERVER_CONTAINER[filename]
        except KeyError:
            #used to translate a path from the client to the debug server
            translated = normcase(filename)
            for eclipse_prefix, server_prefix in PATHS_FROM_ECLIPSE_TO_PYTHON:
                if translated.startswith(eclipse_prefix):
                    if DEBUG_CLIENT_SERVER_TRANSLATION:
                        sys.stderr.write('pydev debugger: replacing to server: 
%s\n' % (translated,))
                    translated = translated.replace(eclipse_prefix, 
server_prefix)
                    if DEBUG_CLIENT_SERVER_TRANSLATION:
                        sys.stderr.write('pydev debugger: sent to server: %s\n' 
% (translated,))
                    break
            else:
                if DEBUG_CLIENT_SERVER_TRANSLATION:
                    sys.stderr.write('pydev debugger: to server: unable to find 
matching prefix for: %s in %s\n' % \
                        (translated, [x[0] for x in 
PATHS_FROM_ECLIPSE_TO_PYTHON]))
                    
            #Note that when going to the server, we do the replace first and 
only later do the norm file.
            if eclipse_sep is not None:
                translated = translated.replace(eclipse_sep, python_sep)
            translated = _NormFile(translated)
                
            NORM_FILENAME_TO_SERVER_CONTAINER[filename] = translated
            return translated
        
    
    def NormFileToClient(filename): 
        #The result of this method will be passed to eclipse
        #So, this would be 'NormFileFromPythonToEclipse'
        try:
            return NORM_FILENAME_TO_CLIENT_CONTAINER[filename]
        except KeyError:
            #used to translate a path from the debug server to the client
            translated = _NormFile(filename)
            for eclipse_prefix, pyhon_prefix in PATHS_FROM_ECLIPSE_TO_PYTHON:
                if translated.startswith(pyhon_prefix):
                    if DEBUG_CLIENT_SERVER_TRANSLATION:
                        sys.stderr.write('pydev debugger: replacing to client: 
%s\n' % (translated,))
                    translated = translated.replace(pyhon_prefix, 
eclipse_prefix)
                    if DEBUG_CLIENT_SERVER_TRANSLATION:
                        sys.stderr.write('pydev debugger: sent to client: %s\n' 
% (translated,))
                    break
            else:
                if DEBUG_CLIENT_SERVER_TRANSLATION:
                    sys.stderr.write('pydev debugger: to client: unable to find 
matching prefix for: %s in %s\n' % \
                        (translated, [x[1] for x in 
PATHS_FROM_ECLIPSE_TO_PYTHON]))
                        
            if eclipse_sep is not None:
                translated = translated.replace(python_sep, eclipse_sep)
            
            #The resulting path is not in the python process, so, we cannot do 
a _NormFile here,
            #only at the beginning of this method.
            NORM_FILENAME_TO_CLIENT_CONTAINER[filename] = translated
            return translated
        
else:
    #no translation step needed (just inline the calls)
    NormFileToClient = _NormFile
    NormFileToServer = _NormFile
    

def GetFilenameAndBase(frame):
    #This one is just internal (so, does not need any kind of client-server 
translation)
    f = frame.f_code.co_filename
    try:
        return NORM_FILENAME_AND_BASE_CONTAINER[f]
    except KeyError:
        filename = _NormFile(f)
        base = basename(filename)
        NORM_FILENAME_AND_BASE_CONTAINER[f] = filename, base
        return filename, base
'''
    This module provides configuration variables to get the absolute filenames 
so that we can be sure that:
        - The case of a file will match the actual file in the filesystem 
(otherwise breakpoints won't be hit).
        - Providing means for the user to make path conversions when doing a 
remote debugging session in
          one machine and debugging in another.
    
    To do that, the PATHS_FROM_ECLIPSE_TO_PYTHON constant must be filled with 
the appropriate paths.
    
    @note: 
        in this context, the server is where your python process is running 
        and the client is where eclipse is running.
    
    E.g.: 
        If the server (your python process) has the structure
            /user/projects/my_project/src/package/module1.py  
        
        and the client has: 
            c:\my_project\src\package\module1.py  
            
        the PATHS_FROM_ECLIPSE_TO_PYTHON would have to be:
            PATHS_FROM_ECLIPSE_TO_PYTHON = [(r'c:\my_project\src', 
r'/user/projects/my_project/src')]
    
    @note: DEBUG_CLIENT_SERVER_TRANSLATION can be set to True to debug the 
result of those translations
    
    @note: the case of the paths is important! Note that this can be tricky to 
get right when one machine
    uses a case-independent filesystem and the other uses a case-dependent 
filesystem (if the system being
    debugged is case-independent, 'normcase()' should be used on the paths 
defined in PATHS_FROM_ECLIPSE_TO_PYTHON). 
    
    @note: all the paths with breakpoints must be translated (otherwise they 
won't be found in the server)
    
    @note: to enable remote debugging in the target machine (pydev extensions 
in the eclipse installation)
        import pydevd;pydevd.settrace(host, stdoutToServer, stderrToServer, 
port, suspend)
        
        see parameter docs on pydevd.py
        
    @note: for doing a remote debugging session, all the pydevd_ files must be 
on the server accessible 
        through the PYTHONPATH (and the PATHS_FROM_ECLIPSE_TO_PYTHON only needs 
to be set on the target 
        machine for the paths that'll actually have breakpoints).
'''


#defined as a list of tuples where the 1st element of the tuple is the path in 
the client machine
#and the 2nd element is the path in the server machine.
#see module docstring for more details.
PATHS_FROM_ECLIPSE_TO_PYTHON = []

#example:
#PATHS_FROM_ECLIPSE_TO_PYTHON = [
#(normcase(r'd:\temp\temp_workspace_2\test_python\src\yyy\yyy'),
# normcase(r'd:\temp\temp_workspace_2\test_python\src\hhh\xxx'))]

DEBUG_CLIENT_SERVER_TRANSLATION = False


------------------------------------------------------------------------------
Free Next-Gen Firewall Hardware Offer
Buy your Sophos next-gen firewall before the end March 2013 
and get the hardware for free! Learn more.
http://p.sf.net/sfu/sophos-d2d-feb
_______________________________________________
pydev-code mailing list
pydev-code@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/pydev-code

Reply via email to