diff --git a/python/python-sdk/intellij.python.sdk.iml b/python/python-sdk/intellij.python.sdk.iml
index a05a8bcc2f67..df5ebed6a41f 100644
--- a/python/python-sdk/intellij.python.sdk.iml
+++ b/python/python-sdk/intellij.python.sdk.iml
@@ -5,6 +5,7 @@
+
@@ -25,5 +26,7 @@
+
+
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/sdk/flavors/WinAppxTools.kt b/python/python-sdk/src/com/jetbrains/python/sdk/WinAppxTools.kt
similarity index 85%
rename from python/src/com/jetbrains/python/sdk/flavors/WinAppxTools.kt
rename to python/python-sdk/src/com/jetbrains/python/sdk/WinAppxTools.kt
index 2d216a28babc..19664617dcdf 100644
--- a/python/src/com/jetbrains/python/sdk/flavors/WinAppxTools.kt
+++ b/python/python-sdk/src/com/jetbrains/python/sdk/WinAppxTools.kt
@@ -1,16 +1,17 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package com.jetbrains.python.sdk.flavors
+package com.jetbrains.python.sdk
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.SystemInfo
+import com.intellij.util.io.isAncestor
import com.sun.jna.platform.win32.Kernel32
import com.sun.jna.platform.win32.Kernel32.*
import com.sun.jna.platform.win32.Ntifs
import com.sun.jna.platform.win32.WinioctlUtil
import com.sun.jna.ptr.IntByReference
-import java.io.File
-import java.io.FilenameFilter
import java.nio.ByteBuffer
+import java.nio.file.Path
+import kotlin.io.path.*
/**
* AppX packages installed to AppX volume (see ``Get-AppxDefaultVolume``, ``Get-AppxPackage``).
@@ -26,6 +27,8 @@ import java.nio.ByteBuffer
* There is no Java API to see reparse point destination, so we use JNA.
* This tool returns AppX name (either ``PythonSoftwareFoundation...`` or ``DesktopAppInstaller..``).
* We use it to check if ``python.exe`` is real python or WindowsStore mock.
+ *
+ * See [WinAppxTest]
*/
/**
@@ -39,22 +42,24 @@ private const val storeMarker = "DesktopAppInstaller"
* There may be several files linked to this product, we need only first.
* And for 3.7 there could be ``PythonSoftwareFoundation.Python.3.7_(SOME_OTHER_UID)``.
*/
-fun getAppxFiles(expectedProduct: String, filePattern: Regex): Collection =
- userAppxFolder?.listFiles(FilenameFilter { _, name -> filePattern.matches(name) })
+fun getAppxFiles(expectedProduct: String?, filePattern: Regex): Collection =
+ userAppxFolder?.listDirectoryEntries()
+ ?.filter { filePattern.matches(it.name) }
?.sortedBy { it.nameWithoutExtension }
?.mapNotNull { file -> file.appxProduct?.let { product -> Pair(product, file) } }
?.toMap()
- ?.filterKeys { expectedProduct in it }
+ ?.filterKeys { expectedProduct == null || expectedProduct in it }
?.values ?: emptyList()
/**
* If file is AppX reparse point link -- return its product name
*/
-val File.appxProduct: String?
+val Path.appxProduct: String?
get() {
- if (parentFile?.equals(userAppxFolder) != true) return null
- return getAppxTag(absolutePath)?.let {
+ val userAppxFolder = userAppxFolder ?: return null
+ if (!userAppxFolder.isAncestor(this)) return null
+ return getAppxTag(this)?.let {
if (storeMarker !in it) it else null
}
}
@@ -63,13 +68,13 @@ val File.appxProduct: String?
/**
* Path to ``%LOCALAPPDATA%\Microsoft\WindowsApps``
*/
-private val userAppxFolder =
+private val userAppxFolder: Path? =
if (!SystemInfo.isWin10OrNewer) {
null
}
else {
System.getenv("LOCALAPPDATA")?.let { localappdata ->
- val appsPath = File(localappdata, "Microsoft//WindowsApps")
+ val appsPath = Path.of(localappdata, "Microsoft//WindowsApps")
if (appsPath.exists()) appsPath else null
}
}
@@ -99,11 +104,11 @@ See https://youtrack.jetbrains.com/issue/PY-43082
Output is unicode 16-LE
*/
-private fun getAppxTag(path: String): String? {
+private fun getAppxTag(path: Path): String? {
if (!SystemInfo.isWin10OrNewer) return null
val kernel = INSTANCE
val logger = Logger.getInstance(Kernel32::class.java)
- val file = kernel.CreateFile(path, GENERIC_READ, FILE_SHARE_READ, null, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, null)
+ val file = kernel.CreateFile(path.pathString, GENERIC_READ, FILE_SHARE_READ, null, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, null)
if (file == INVALID_HANDLE_VALUE) {
logger.warn("Invalid handle for $path")
return null
diff --git a/python/python-sdk/tests/com/jetbrains/python/tests/WinAppxTest.kt b/python/python-sdk/tests/com/jetbrains/python/tests/WinAppxTest.kt
new file mode 100644
index 000000000000..c1a3cc05e33a
--- /dev/null
+++ b/python/python-sdk/tests/com/jetbrains/python/tests/WinAppxTest.kt
@@ -0,0 +1,20 @@
+package com.jetbrains.python.tests
+
+import com.intellij.openapi.util.SystemInfoRt
+import com.jetbrains.python.sdk.appxProduct
+import com.jetbrains.python.sdk.getAppxFiles
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.Test
+
+class WinAppxTest {
+ @Test
+ fun testAppx() {
+ Assume.assumeTrue("Win only", SystemInfoRt.isWindows)
+ val appxFiles = getAppxFiles(null, Regex(".+"))
+ Assume.assumeTrue("No appx apps found", appxFiles.isNotEmpty())
+ for (appxFile in appxFiles) {
+ Assert.assertNotNull("No appx product product for $appxFile", appxFile.appxProduct)
+ }
+ }
+}
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java
index cc94a0e9f00a..4f2f1be61eb3 100644
--- a/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java
+++ b/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java
@@ -26,8 +26,8 @@ import java.io.File;
import java.nio.file.Path;
import java.util.*;
-import static com.jetbrains.python.sdk.flavors.WinAppxToolsKt.getAppxFiles;
-import static com.jetbrains.python.sdk.flavors.WinAppxToolsKt.getAppxProduct;
+import static com.jetbrains.python.sdk.WinAppxToolsKt.getAppxFiles;
+import static com.jetbrains.python.sdk.WinAppxToolsKt.getAppxProduct;
/**
* This class knows how to find python in Windows Registry according to
@@ -100,7 +100,6 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor {
}
var path = sdk.getHomePath();
return path != null && isLocalPathValidPython(Path.of(path));
-
}
@Override
@@ -117,8 +116,7 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor {
return true;
}
- final File file = path.toFile();
- return StringUtils.contains(getAppxProduct(file), APPX_PRODUCT) && isValidSdkPath(file);
+ return StringUtils.contains(getAppxProduct(path), APPX_PRODUCT) && isValidSdkPath(path.toFile());
}
@Override
@@ -160,7 +158,7 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor {
@NotNull
private static Set getPythonsFromStore() {
- return ContainerUtil.map2Set(getAppxFiles(APPX_PRODUCT, PYTHON_EXE), file -> file.getAbsolutePath());
+ return ContainerUtil.map2Set(getAppxFiles(APPX_PRODUCT, PYTHON_EXE), file -> file.toAbsolutePath().toString());
}
@NotNull