1
Vote

Symbolic links cause VS to open a multiple copies of each file when debugging

description

If Python loads a script via a symbolic link, VS will think there are multiple versions of it. Currently I have these documents open:
F:\Users\Tom\Documents\Mods\blender-smd\src\io_scene_valvesource\export_smd.py
C:\Users\Tom\AppData\Roaming\Blender Foundation\Blender\2.66\scripts\addons\io_scene_valvesource\export_smd.py
C:\Users\Tom\AppData\Roaming\Blender Foundation\Blender\2.69\scripts\addons\io_scene_valvesource\export_smd.py
The first document represents the actual location of the file and was created when I opened it from Solution Explorer.

The other two were opened by the debugger and represent the location at which Python thought the file existed. (Yes, I have a user folder on two different drives!)

PyTools should examine the path of any file it wants to open for symlinks and ensure that it opens the real location.

comments

pminaev wrote Feb 11 at 5:21 AM

This is actually more of a debugging issue - we should leave the editor as is (to match the behavior for all other file types that VS handles), but when getting a filename reported from debugger, do symlink resolution on it.

Artfunkel wrote Aug 5 at 9:28 PM

I've fixed this myself. The change consists of a static utility class to handle OS calls plus a couple of calls to a method of that class.

The utility class: https://gist.github.com/Artfunkel/c5a642b90a3e7a65eb7d

The constructors of classes Microsoft.PythonTools.Debugger.DebugEngine.AD7DocumentContext and Microsoft.PythonTools.Debugger.PythonBreakpoint should be modified so that the filename argument is assigned like this:
_filename = Analysis.SymbolicLink.ConvertToHardPath(filename);
Ideally symlinks wouldn't be resolved on paths that are under the project's root or that are directly included in the project. I'm not sure how that would be implemented though - over to you!

pminaev wrote Aug 5 at 11:44 PM

Thanks! We actually just checked in a bunch of stuff to resolve symlinks to NTVS (to fix another bug that we had around them, that being a crash when moving a directory onto a symlink to itself), which is most of the same P/Invoke goo.

Why do you prefer links to not be resolved inside the project folder? It sounds like such cases would still potentially present the same problem.

Artfunkel wrote Aug 6 at 7:46 AM

Awesome! The next dev build will make me very happy. :)

The goal, for me, is to ensure that the file PTVS opens on hitting a breakpoint is always the same file that the user would open by clicking in Solution Explorer.

Let's say we have the symlink C:\MyPyProj -> \\dev\09aaf4\. If a breakpoint is hit in C:\MyPrProj\__init__.py, the file PTVS opens should be C:\MyPyProj\__init__.py and not \\dev\09aaf4\__init__.py.

It's not so clear what should happen for symlinks inside the project folder. PTVS should copy whatever Solution Explorer does, which I suspect is nothing.

pminaev wrote Aug 6 at 5:07 PM

Ah, I see. But that would require some more work in more complicated scenarios. Suppose, for example, that the breakpoint is actually hit (according to Python's __file__) in \dev\09aaf4 - because the loader was configured that way, or because someone used eval, or for any other reason. I think that, from this perspective, you'd still want to open the file inside the project, regardless of which way aliasing goes.

I think this is doable, too. Basically, it would add one more step to symlink resolution, where after getting the canonical path it would also check whether any project item is an alias for that path. It would require maintaining a map of aliases, probably on the project node, and update it whenever nodes are added or removed, but it should be pretty straightforward.