classification
Title: Bdb doesn't find instruction in linecache after pdb.set_trace() following os.chdir("/tmp")
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: prounce, xtreak
Priority: normal Keywords:

Created on 2018-03-25 21:10 by prounce, last changed 2018-07-25 12:47 by xtreak.

Files
File name Uploaded Description Edit
testpdb.py prounce, 2018-03-25 21:10 testpdb.py is test program to generate fault after 2nd pdb.set_trace()
Messages (2)
msg314435 - (view) Author: Peter Rounce (prounce) Date: 2018-03-25 21:10
In my view there is a fault in python3 pdb in that if you use pdb.set_trace() after using os.chdir() to change the cwd to a directory that does not contain the source code being executed, then there is no instruction output on next or step. This is shown in the following code where I have added print lines to bdb.py file to show the errors.
[The tes program is attached.

    python3 testpdb.py
    # To output a line of code the canonic function in bdp.py is called 
    # to build an absolute path to the source code being executed.

    PRINT --> canonic line 32 - canonic = None
    PRINT --> canonic line 36 - canonic_abs = /home/pythontest/Software/python/3/testpdb.py

    # the following  is printed after the call to linecache and shows
    # the file accessed, the line number in the code and
    # the instruction string returned
    PRINT --> filename: /home/pythontest/Software/python/3/testpdb.py - lineno: 11, line: e=d+5
    
    > /home/pythontest/Software/python/3/testpdb.py(11)<module>()
    -> e=d+5
    (Pdb) c
    # The program is continued and os.chdir("/tmp") is executed.
    # Another pdb.set_trace() has been executed, which creates a new Pdb
    # class instance, and thus a new Bdb instance, where Bdb.fncache
    # used by the canonic function is {}. 
    # The canonic function is passed just the filename 'testpdb.py" and
    # canonic uses os.path.abs to get a full path. Of course this gives 
    # the wrong path to testpdb.py since it just prepends the current
    # cwd, thus:-

    PRINT --> canonic line 32 - canonic = None
    PRINT --> canonic line 36 - canonic_abs = /tmp/testpdb.py

    # the call to linecache in format_cache_entry (line 411) doesn't
    # find the source code so returns an empty string.

    PRINT --> filename: /tmp/testpdb.py - lineno: 15, line: 
    > /tmp/testpdb.py(15)<module>()
    (Pdb) c

Why canonic is using os.path.abs is not clear to me: it seems to be a    mistake, but it is surprising that it has not been found, if this is the
 case. It is interesting to note that linecache itself, when reading from a file with just a filename (and not an absolute path) does not try to guess the path with os.path.abs but looks down the python 'sys.path' to find the full path to the file. 
This would look like a reasonable solution, but it might be better to extend the existing code by checking the full path from the 'os.path.abs' instruction with an os.exists call and if this fails doing a search down 'sys.path'.

The modified code in bdb.py for this solution is:-

        def getfullpath(self, basename) :
            for dirname in sys.path:
                try:
                    fullname = os.path.join(dirname, basename)
                except (TypeError, AttributeError):
                    # Not sufficiently string-like to do anything useful with.
                    continue
                try:
                    stat = os.stat(fullname)
                    break
                except OSError:
                    pass
            else:
                return []
            return fullname

        def canonic(self, filename):
            if filename == "<" + filename[1:-1] + ">":
                return filename
            canonic = self.fncache.get(filename)
            if not canon ic:
                canonicabs = canonic = os.path.abspath(filename)
                canonic = os.path.normcase(canonic)
                # if path does not exists look down sys.path
                if not os.path.exists(canonic) :
                    canonic = self.getfullpath(filename)
                    canonic = os.path.normcase(canonic)
                self.fncache[filename] = canonic
            return canonic
msg322350 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python triager) Date: 2018-07-25 12:47
Seems related : https://bugs.python.org/issue31121
History
Date User Action Args
2018-07-25 12:47:35xtreaksetnosy: + xtreak
messages: + msg322350
2018-03-25 21:10:27prouncecreate