mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
PY-60971 Create new API in PythonHelpersLocator that alerts about I/O
The deprecated `PythonHelpersLocator.getHelperPath` used to be cheap and had no I/O, but since bd5183320a43878de861fd11ae1f9bf79710290a it may start to copy many files to another directory. It becomes dangerous to call it from EDT. GitOrigin-RevId: 98a913e68671b38299791fde7f267a396bd4faa0
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c67b9317a0
commit
a277ed82d3
@@ -17,11 +17,12 @@ package com.jetbrains.python;
|
||||
|
||||
import com.intellij.execution.process.ProcessIOExecutorService;
|
||||
import com.intellij.ide.plugins.PluginManagerCore;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationInfo;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.openapi.util.BuildNumber;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.io.NioFiles;
|
||||
import com.intellij.util.LazyInitializer;
|
||||
@@ -41,29 +42,100 @@ public final class PythonHelpersLocator {
|
||||
private static final Logger LOG = Logger.getInstance(PythonHelpersLocator.class);
|
||||
private static final String PROPERTY_HELPERS_LOCATION = "idea.python.helpers.path";
|
||||
|
||||
private static final LazyInitializer.LazyValue<@Nullable File> maybeCopiedHelpersRoot = new LazyInitializer.LazyValue<>(() -> {
|
||||
try {
|
||||
if (ApplicationInfo.getInstance().getBuild().isSnapshot()) {
|
||||
// Although it runs not directly from the IDE, it's still built locally from sources, and it's supposed that
|
||||
// copied code may change between runs.
|
||||
return FileUtil.createTempDirectory("python-helpers", null, true);
|
||||
}
|
||||
else {
|
||||
return new File(PathManager.getSystemPath());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to create temporary directory for helpers", e);
|
||||
}
|
||||
});
|
||||
|
||||
private PythonHelpersLocator() { }
|
||||
|
||||
/**
|
||||
* @return the base directory under which various scripts, etc. are stored.
|
||||
* @return the base directory under which various Python scripts and other auxiliary files are stored.
|
||||
* @deprecated This method used to be cheap, but now it may invoke I/O. Consider choosing between
|
||||
* {@link #predictHelpersPath()} and {@link #getCopiedHelpersPath()}. If you're not sure, prefer {@link #getCopiedHelpersPath()} and watch
|
||||
* for exceptions.
|
||||
*/
|
||||
@Deprecated
|
||||
public static @NotNull File getHelpersRoot() {
|
||||
String property = System.getProperty(PROPERTY_HELPERS_LOCATION);
|
||||
if (property != null) {
|
||||
return new File(property);
|
||||
}
|
||||
return assertHelpersLayout(getHelperRoot(ModuleHelpers.COMMUNITY));
|
||||
return assertHelpersLayout(getCopiedHelpersPath(ModuleHelpers.COMMUNITY));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the base directory under which various scripts, various Python scripts and other auxiliary files are supposed to be stored.
|
||||
* The helper scripts may not be stored there at the time of calling this function.
|
||||
*/
|
||||
public static @NotNull File predictHelpersPath() {
|
||||
String property = System.getProperty(PROPERTY_HELPERS_LOCATION);
|
||||
if (property != null) {
|
||||
return new File(property);
|
||||
}
|
||||
return predictHelpersPath(ModuleHelpers.COMMUNITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the base directory under which various scripts, various Python scripts and other auxiliary files are stored.
|
||||
* If the files haven't been copied yet, the functions does that blocking the current thread.
|
||||
*/
|
||||
public static @NotNull File getCopiedHelpersPath() {
|
||||
logErrorOnEdt();
|
||||
String property = System.getProperty(PROPERTY_HELPERS_LOCATION);
|
||||
if (property != null) {
|
||||
return new File(property);
|
||||
}
|
||||
return assertHelpersLayout(getCopiedHelpersPath(ModuleHelpers.COMMUNITY));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method used to be cheap, but now it may invoke I/O. Consider choosing between
|
||||
* {@link #predictHelpersProPath()} and {@link #getCopiedHelpersProPath()}. If you're unsure, prefer {@link #getCopiedHelpersProPath()}
|
||||
* and watch for exceptions.
|
||||
*/
|
||||
@Deprecated
|
||||
public static @NotNull Path getHelpersProRoot() {
|
||||
return assertHelpersProLayout(getHelperRoot(ModuleHelpers.PRO)).toPath().normalize();
|
||||
return assertHelpersProLayout(getCopiedHelpersPath(ModuleHelpers.PRO)).toPath().normalize();
|
||||
}
|
||||
|
||||
private static @NotNull File getHelperRoot(@NotNull ModuleHelpers moduleHelpers) {
|
||||
/**
|
||||
* See {@link #predictHelpersPath()}.
|
||||
*/
|
||||
public static @NotNull Path predictHelpersProPath() {
|
||||
return predictHelpersPath(ModuleHelpers.PRO).toPath().normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #getCopiedHelpersPath()}.
|
||||
*/
|
||||
public static @NotNull Path getCopiedHelpersProPath() {
|
||||
logErrorOnEdt();
|
||||
return assertHelpersProLayout(getCopiedHelpersPath(ModuleHelpers.PRO)).toPath().normalize();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static File predictHelpersPath(@NotNull ModuleHelpers moduleHelpers) {
|
||||
return new File(maybeCopiedHelpersRoot.get(), moduleHelpers.getSubDirectory());
|
||||
}
|
||||
|
||||
private static @NotNull File getCopiedHelpersPath(@NotNull ModuleHelpers moduleHelpers) {
|
||||
if (PluginManagerCore.isRunningFromSources()) {
|
||||
return new File(PathManager.getCommunityHomePath(), moduleHelpers.myCommunityRepoRelativePath);
|
||||
}
|
||||
else {
|
||||
@Nullable File helpersRootDir = ProgressIndicatorUtils.awaitWithCheckCanceled(moduleHelpers.copyRoot.get());
|
||||
@Nullable File helpersRootDir = ProgressIndicatorUtils.awaitWithCheckCanceled(moduleHelpers.copiedHelpersRoot.get());
|
||||
if (helpersRootDir != null) {
|
||||
return helpersRootDir;
|
||||
}
|
||||
@@ -74,6 +146,18 @@ public final class PythonHelpersLocator {
|
||||
}
|
||||
}
|
||||
|
||||
private static void logErrorOnEdt() {
|
||||
try {
|
||||
Application app = ApplicationManager.getApplication();
|
||||
if (app != null) {
|
||||
app.assertIsNonDispatchThread();
|
||||
}
|
||||
}
|
||||
catch (AssertionError err) {
|
||||
Logger.getInstance(PythonHelpersLocator.class).error(err);
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable File getPluginBaseDir(@NonNls String jarPath) {
|
||||
if (jarPath.endsWith(".jar")) {
|
||||
final File jarFile = new File(jarPath);
|
||||
@@ -145,51 +229,36 @@ public final class PythonHelpersLocator {
|
||||
* There is no check for macOS though, since such problems may appear in other operating systems as well, and since it's a bad
|
||||
* idea to modify the IDE distributive during running in general.
|
||||
*/
|
||||
final LazyInitializer.LazyValue<@NotNull CompletableFuture<@Nullable File>> copyRoot;
|
||||
final LazyInitializer.LazyValue<@NotNull CompletableFuture<@Nullable File>> copiedHelpersRoot;
|
||||
|
||||
ModuleHelpers(@NotNull String moduleName, @NotNull String pluginRelativePath, @NotNull String communityRelativePath) {
|
||||
myModuleName = moduleName;
|
||||
myCommunityRepoRelativePath = communityRelativePath;
|
||||
|
||||
copyRoot = new LazyInitializer.LazyValue<>(() -> {
|
||||
copiedHelpersRoot = new LazyInitializer.LazyValue<>(() -> {
|
||||
String jarPath = PathUtil.getJarPathForClass(PythonHelpersLocator.class);
|
||||
final File pluginBaseDir = getPluginBaseDir(jarPath);
|
||||
if (pluginBaseDir == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
try {
|
||||
BuildNumber build = ApplicationInfo.getInstance().getBuild();
|
||||
File systemRootDir;
|
||||
if (build.isSnapshot()) {
|
||||
// Although it runs not directly from the IDE, it's still built locally from sources, and it's supposed that
|
||||
// copied code may change between runs.
|
||||
systemRootDir = FileUtil.createTempDirectory("python-helpers", null, true);
|
||||
}
|
||||
else {
|
||||
systemRootDir = new File(PathManager.getSystemPath());
|
||||
}
|
||||
File helpersRootDir = new File(
|
||||
new File(
|
||||
systemRootDir,
|
||||
"python-helpers-" + build.asStringWithoutProductCode()
|
||||
),
|
||||
moduleName
|
||||
);
|
||||
if (!helpersRootDir.isDirectory()) {
|
||||
NioFiles.createDirectories(helpersRootDir.toPath().getParent());
|
||||
FileUtil.copyDir(new File(pluginBaseDir, pluginRelativePath), helpersRootDir, true);
|
||||
}
|
||||
return helpersRootDir;
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
final File helpersRootDir = predictHelpersPath(this);
|
||||
if (!helpersRootDir.isDirectory()) {
|
||||
NioFiles.createDirectories(helpersRootDir.toPath().getParent());
|
||||
FileUtil.copyDir(new File(pluginBaseDir, pluginRelativePath), helpersRootDir, true);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to create temporary directory for helpers", e);
|
||||
}
|
||||
},
|
||||
ProcessIOExecutorService.INSTANCE
|
||||
);
|
||||
return helpersRootDir;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to create temporary directory for helpers", e);
|
||||
}
|
||||
}, ProcessIOExecutorService.INSTANCE);
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull String getSubDirectory() {
|
||||
return "python-helpers-" + ApplicationInfo.getInstance().getBuild().asStringWithoutProductCode() + File.separator + myModuleName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user