Merge branch 'pydevd-winpaths' of https://github.com/fitermay/intellij-community
authorElizaveta Shashkova <Elizaveta.Shashkova@jetbrains.com>
Tue, 19 Apr 2016 12:45:18 +0000 (15:45 +0300)
committerElizaveta Shashkova <Elizaveta.Shashkova@jetbrains.com>
Tue, 19 Apr 2016 12:45:18 +0000 (15:45 +0300)
python/helpers/pydev/pydevd_file_utils.py
python/src/com/jetbrains/python/debugger/PyLocalPositionConverter.java
python/testData/debug/long_name_win_test.py [new file with mode: 0644]
python/testData/debug/test_winegg.py [new file with mode: 0644]
python/testData/debug/wintestegg-0.1.egg [new file with mode: 0644]
python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java

index 2e63f4b26569dac1e613cbb0f5f8385830126ca5..d0237d2247456863e55a6ff6c59a3b74d2ee455c 100644 (file)
@@ -46,6 +46,7 @@ from _pydev_bundle._pydev_filesystem_encoding import getfilesystemencoding
 import os.path
 import sys
 import traceback
+import types
 
 os_normcase = os.path.normcase
 basename = os.path.basename
@@ -72,11 +73,31 @@ PATHS_FROM_ECLIPSE_TO_PYTHON = []
 
 
 normcase = os_normcase # May be rebound on set_ide_os
-
+import types
+str_to_unicode = types.UnicodeType
+def convert_to_long_pathname(filename):
+    return filename
+if os.name == 'nt':
+    try:
+        import ctypes
+    except ImportError:
+        pass
+    else:
+        def convert_to_long_pathname(filename):
+            buf = ctypes.create_unicode_buffer(260)
+            GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
+            rv = GetLongPathName(str_to_unicode(filename), buf , 260)
+            if rv == 0 or rv > 260:
+                return filename
+            else:
+                return buf.value.encode(getfilesystemencoding())
 
 def norm_case(filename):
     # `normcase` doesn't lower case on Python 2 for non-English locale, but Java side does it,
     # so we should do it manually
+    if '~' in filename:
+        filename = convert_to_long_pathname(filename)
+
     filename = os_normcase(filename)
     enc = getfilesystemencoding()
     if IS_PY3K or enc is None or enc.lower() == "utf-8":
@@ -143,7 +164,7 @@ def _NormPaths(filename):
 
 
 def _NormPath(filename, normpath):
-    r = normcase(normpath(filename))
+    r = normpath(filename)
     #cache it for fast access later
     ind = r.find('.zip')
     if ind == -1:
@@ -156,7 +177,9 @@ def _NormPath(filename, normpath):
         inner_path = r[ind:]
         if inner_path.startswith('/') or inner_path.startswith('\\'):
             inner_path = inner_path[1:]
-        r = join(zip_path, inner_path)
+        r = join(normcase(zip_path), inner_path)
+    else:
+        r = normcase(r)
     return r
 
 
@@ -366,11 +389,11 @@ def get_abs_path_real_path_and_base_from_frame(frame):
     except:
         #This one is just internal (so, does not need any kind of client-server translation)
         f = frame.f_code.co_filename
-        if f is not None and f.startswith('build/bdist.'):
+        if f is not None and f.startswith (('build/bdist.','build\\bdist.')):
             # files from eggs in Python 2.7 have paths like build/bdist.linux-x86_64/egg/<path-inside-egg>
             f = frame.f_globals['__file__']
-            if f.endswith('.pyc'):
-                f = f[:-1]
+        if f is not None and f.endswith('.pyc'):
+            f = f[:-1]
         ret = get_abs_path_real_path_and_base_from_file(f)
         # Also cache based on the frame.f_code.co_filename (if we had it inside build/bdist it can make a difference).
         NORM_PATHS_AND_BASE_CONTAINER[frame.f_code.co_filename] = ret
index e071549a44c69c601576692a0df48a125df131d5..1afaec0a7179c15d3fd74aeaf33b8d41934b67aa 100644 (file)
@@ -47,7 +47,7 @@ public class PyLocalPositionConverter implements PyPositionConverter {
         return null;
       }
       if (SystemInfo.isWindows) {
-        file = file.toLowerCase();
+        file = winNormCase(file);
       }
       return super.normalize(file);
     }
@@ -64,7 +64,7 @@ public class PyLocalPositionConverter implements PyPositionConverter {
         return null;
       }
       if (SystemInfo.isWindows && isWindowsPath(file)) {
-        file = file.toLowerCase();
+        file = winNormCase(file);
       }
       return super.normalize(file);
     }
@@ -165,6 +165,19 @@ public class PyLocalPositionConverter implements PyPositionConverter {
     }
   }
 
+  private static String winNormCase(String file) {
+    int ind = -1;
+    for (String ext : EGG_EXTENSIONS) {
+      ind = file.indexOf(ext);
+      if (ind != -1) break;
+    }
+    if (ind != -1) {
+      return file.substring(0, ind + 4).toLowerCase() + file.substring(ind + 4);
+    }
+    else {
+      return file.toLowerCase();
+    }
+  }
   @Nullable
   protected static XSourcePosition createXSourcePosition(@Nullable VirtualFile vFile, int line) {
     if (vFile != null) {
diff --git a/python/testData/debug/long_name_win_test.py b/python/testData/debug/long_name_win_test.py
new file mode 100644 (file)
index 0000000..e61b3fc
--- /dev/null
@@ -0,0 +1,3 @@
+##intentionally named differently so that so that short name won't clash with other files
+x = 5 + 5
+print (x)
\ No newline at end of file
diff --git a/python/testData/debug/test_winegg.py b/python/testData/debug/test_winegg.py
new file mode 100644 (file)
index 0000000..092aed5
--- /dev/null
@@ -0,0 +1,8 @@
+from eggxample import MIXED_case, lower_case
+
+x = lower_case.add(7, 9)
+print(x)
+
+x = MIXED_case.add(7, 10)
+print(x)
+
diff --git a/python/testData/debug/wintestegg-0.1.egg b/python/testData/debug/wintestegg-0.1.egg
new file mode 100644 (file)
index 0000000..068e05c
Binary files /dev/null and b/python/testData/debug/wintestegg-0.1.egg differ
index d883defa7b823e58e1c96f1020c1a812e3ab5b7f..2d6364a70f882e83e6f607360c2799a79363e7b3 100644 (file)
@@ -4,6 +4,8 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.testFramework.UsefulTestCase;
 import com.intellij.testFramework.fixtures.IdeaProjectTestFixture;
 import com.intellij.xdebugger.XDebuggerTestUtil;
@@ -23,6 +25,7 @@ import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
 import com.jetbrains.python.sdkTools.SdkCreationType;
 import org.jetbrains.annotations.NotNull;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -539,6 +542,87 @@ public class PythonDebuggerTest extends PyEnvTestCase {
     });
   }
 
+
+  public void testWinEggDebug() throws Exception {
+    if (!SystemInfo.isWindows)
+    {
+       return; // Only needs to run on windows
+    }
+    runPythonTest(new PyDebuggerTask("/debug", "test_winegg.py") {
+      @Override
+      public void before() throws Exception {
+        String egg = getFilePath("wintestegg-0.1.egg");
+        toggleBreakpointInEgg(egg, "eggxample/lower_case.py", 2);
+        toggleBreakpointInEgg(egg, "eggxample/MIXED_case.py", 2);
+
+        PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(getRunConfiguration().getSdkHome());
+        if (flavor != null) {
+          flavor.initPythonPath(Lists.newArrayList(egg), getRunConfiguration().getEnvs());
+        }
+        else {
+          getRunConfiguration().getEnvs().put("PYTHONPATH", egg);
+        }
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        eval("ret").hasValue("16");
+        resume();
+
+        waitForPause();
+        eval("ret").hasValue("17");
+        resume();
+      }
+
+      @NotNull
+      @Override
+      public Set<String> getTags() {
+        return ImmutableSet.of("-jython"); //TODO: fix that for Jython if anybody needs it
+      }
+
+
+    });
+  }
+
+  public void testWinLongName() throws Exception {
+    if (!SystemInfo.isWindows)
+    {
+      return; // Only needs to run on windows
+    }
+    runPythonTest(new PyDebuggerTask("/debug", "long_n~1.py") {
+      @Override
+      public void before() throws Exception {
+
+        String scriptPath = getScriptPath();
+        String longPath = FileUtil
+          .toSystemDependentName((new File(scriptPath).getCanonicalPath()));
+        LocalFileSystem.getInstance().refreshAndFindFileByPath(longPath);
+        toggleBreakpoint(longPath, 2);
+
+
+      }
+
+      @Override
+      public void testing() throws Exception {
+        waitForPause();
+        eval("x").hasValue("10");
+        resume();
+
+      }
+
+      @NotNull
+      @Override
+      public Set<String> getTags() {
+        return ImmutableSet.of("-jython"); //TODO: fix that for Jython if anybody needs it
+      }
+
+
+    });
+  }
+
+
+
   public void testStepOverConditionalBreakpoint() throws Exception {
     runPythonTest(new PyDebuggerTask("/debug", "test_stepOverCondition.py") {
       @Override