Prefer venv Python and normalize venv extraction
This commit is contained in:
parent
9064d3ea10
commit
b5baaa38c3
|
|
@ -298,6 +298,7 @@ public class ResourceExtractor {
|
||||||
Files.createDirectories(targetDir);
|
Files.createDirectories(targetDir);
|
||||||
copyDirectoryFromClasspath("python-runtime/venv-offline", targetDir);
|
copyDirectoryFromClasspath("python-runtime/venv-offline", targetDir);
|
||||||
}
|
}
|
||||||
|
normalizeVenvDir(targetDir.getParent(), targetDir);
|
||||||
makeExecutable(targetDir.resolve("bin/python3.10"));
|
makeExecutable(targetDir.resolve("bin/python3.10"));
|
||||||
makeExecutable(targetDir.resolve("bin/python3"));
|
makeExecutable(targetDir.resolve("bin/python3"));
|
||||||
log.info(" Done");
|
log.info(" Done");
|
||||||
|
|
@ -468,4 +469,28 @@ public class ResourceExtractor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void normalizeVenvDir(Path parentDir, Path expectedDir) throws IOException {
|
||||||
|
if (Files.isDirectory(expectedDir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Path altLinux = parentDir.resolve("venv-linux-offline");
|
||||||
|
if (Files.isDirectory(altLinux)) {
|
||||||
|
log.info(" Renaming venv directory: {} -> {}", altLinux.getFileName(), expectedDir.getFileName());
|
||||||
|
Files.move(altLinux, expectedDir, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Path altVenv = parentDir.resolve("venv");
|
||||||
|
if (Files.isDirectory(altVenv)) {
|
||||||
|
log.info(" Renaming venv directory: {} -> {}", altVenv.getFileName(), expectedDir.getFileName());
|
||||||
|
Files.move(altVenv, expectedDir, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Path altOff = parentDir.resolve("venv-offline");
|
||||||
|
if (Files.isDirectory(altOff)) {
|
||||||
|
// already present under expected name in parent, but expectedDir path differs
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.warn(" Venv directory not found after extraction under {}", parentDir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,10 +153,20 @@ public class FlaskProcessManager implements ApplicationListener<ContextRefreshed
|
||||||
if (libPath != null) {
|
if (libPath != null) {
|
||||||
env.put("PYTHONPATH", libPath.toString());
|
env.put("PYTHONPATH", libPath.toString());
|
||||||
}
|
}
|
||||||
// Prefer embedded Python home when using standalone runtime
|
Path venvRoot = resolveEmbeddedVenvRoot();
|
||||||
Path pythonHome = resolveEmbeddedPythonHome();
|
boolean usingVenv = venvRoot != null && embeddedPython != null &&
|
||||||
if (pythonHome != null) {
|
embeddedPython.toAbsolutePath().startsWith(venvRoot.toAbsolutePath());
|
||||||
env.put("PYTHONHOME", pythonHome.toString());
|
if (usingVenv) {
|
||||||
|
Path venvBin = venvRoot.resolve(isWindows() ? "Scripts" : "bin");
|
||||||
|
env.put("VIRTUAL_ENV", venvRoot.toString());
|
||||||
|
String oldPath = env.getOrDefault("PATH", "");
|
||||||
|
env.put("PATH", venvBin.toString() + File.pathSeparator + oldPath);
|
||||||
|
} else {
|
||||||
|
// Prefer embedded Python home when using standalone runtime
|
||||||
|
Path pythonHome = resolveEmbeddedPythonHome();
|
||||||
|
if (pythonHome != null) {
|
||||||
|
env.put("PYTHONHOME", pythonHome.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use bundled models
|
// Use bundled models
|
||||||
|
|
@ -338,7 +348,40 @@ public class FlaskProcessManager implements ApplicationListener<ContextRefreshed
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path resolveEmbeddedPython() {
|
private Path resolveEmbeddedPython() {
|
||||||
// Prefer embedded standalone runtime first
|
// Prefer venv interpreter first (ensures Flask and deps are available)
|
||||||
|
Path venvRoot = resolveEmbeddedVenvRoot();
|
||||||
|
if (venvRoot != null) {
|
||||||
|
Path linuxPython310 = venvRoot.resolve("bin/python3.10");
|
||||||
|
if (Files.exists(linuxPython310)) {
|
||||||
|
return linuxPython310;
|
||||||
|
}
|
||||||
|
Path linuxPython311 = venvRoot.resolve("bin/python3.11");
|
||||||
|
if (Files.exists(linuxPython311)) {
|
||||||
|
return linuxPython311;
|
||||||
|
}
|
||||||
|
Path linuxPython39 = venvRoot.resolve("bin/python3.9");
|
||||||
|
if (Files.exists(linuxPython39)) {
|
||||||
|
return linuxPython39;
|
||||||
|
}
|
||||||
|
Path linuxPython38 = venvRoot.resolve("bin/python3.8");
|
||||||
|
if (Files.exists(linuxPython38)) {
|
||||||
|
return linuxPython38;
|
||||||
|
}
|
||||||
|
Path linuxPythonVenv = venvRoot.resolve("bin/python3");
|
||||||
|
if (Files.exists(linuxPythonVenv)) {
|
||||||
|
return linuxPythonVenv;
|
||||||
|
}
|
||||||
|
Path linuxPythonAlt = venvRoot.resolve("bin/python");
|
||||||
|
if (Files.exists(linuxPythonAlt)) {
|
||||||
|
return linuxPythonAlt;
|
||||||
|
}
|
||||||
|
Path windowsPython = venvRoot.resolve("Scripts/python.exe");
|
||||||
|
if (Files.exists(windowsPython)) {
|
||||||
|
return windowsPython;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to embedded standalone runtime
|
||||||
Path linuxPython310 = Paths.get(resourceDir, "python-runtime/python/bin/python3.10");
|
Path linuxPython310 = Paths.get(resourceDir, "python-runtime/python/bin/python3.10");
|
||||||
if (Files.exists(linuxPython310)) {
|
if (Files.exists(linuxPython310)) {
|
||||||
return linuxPython310;
|
return linuxPython310;
|
||||||
|
|
@ -351,35 +394,6 @@ public class FlaskProcessManager implements ApplicationListener<ContextRefreshed
|
||||||
if (Files.exists(linuxPython)) {
|
if (Files.exists(linuxPython)) {
|
||||||
return linuxPython;
|
return linuxPython;
|
||||||
}
|
}
|
||||||
// Fallback to venv interpreter if bundled (still embedded)
|
|
||||||
linuxPython310 = Paths.get(resourceDir, "python-runtime/venv-offline/bin/python3.10");
|
|
||||||
if (Files.exists(linuxPython310)) {
|
|
||||||
return linuxPython310;
|
|
||||||
}
|
|
||||||
Path linuxPython311 = Paths.get(resourceDir, "python-runtime/venv-offline/bin/python3.11");
|
|
||||||
if (Files.exists(linuxPython311)) {
|
|
||||||
return linuxPython311;
|
|
||||||
}
|
|
||||||
Path linuxPython39 = Paths.get(resourceDir, "python-runtime/venv-offline/bin/python3.9");
|
|
||||||
if (Files.exists(linuxPython39)) {
|
|
||||||
return linuxPython39;
|
|
||||||
}
|
|
||||||
Path linuxPython38 = Paths.get(resourceDir, "python-runtime/venv-offline/bin/python3.8");
|
|
||||||
if (Files.exists(linuxPython38)) {
|
|
||||||
return linuxPython38;
|
|
||||||
}
|
|
||||||
Path linuxPythonVenv = Paths.get(resourceDir, "python-runtime/venv-offline/bin/python3");
|
|
||||||
if (Files.exists(linuxPythonVenv)) {
|
|
||||||
return linuxPythonVenv;
|
|
||||||
}
|
|
||||||
Path linuxPythonAlt = Paths.get(resourceDir, "python-runtime/venv-offline/bin/python");
|
|
||||||
if (Files.exists(linuxPythonAlt)) {
|
|
||||||
return linuxPythonAlt;
|
|
||||||
}
|
|
||||||
Path windowsPython = Paths.get(resourceDir, "python-runtime/venv-offline/Scripts/python.exe");
|
|
||||||
if (Files.exists(windowsPython)) {
|
|
||||||
return windowsPython;
|
|
||||||
}
|
|
||||||
Path windowsPythonAlt = Paths.get(resourceDir, "python-runtime/python.exe");
|
Path windowsPythonAlt = Paths.get(resourceDir, "python-runtime/python.exe");
|
||||||
if (Files.exists(windowsPythonAlt)) {
|
if (Files.exists(windowsPythonAlt)) {
|
||||||
return windowsPythonAlt;
|
return windowsPythonAlt;
|
||||||
|
|
@ -397,7 +411,8 @@ public class FlaskProcessManager implements ApplicationListener<ContextRefreshed
|
||||||
|
|
||||||
private Path resolveEmbeddedSitePackages() {
|
private Path resolveEmbeddedSitePackages() {
|
||||||
// Linux venvs use lib/pythonX.Y/site-packages
|
// Linux venvs use lib/pythonX.Y/site-packages
|
||||||
Path libRoot = Paths.get(resourceDir, "python-runtime/venv-offline/lib");
|
Path venvRoot = resolveEmbeddedVenvRoot();
|
||||||
|
Path libRoot = venvRoot != null ? venvRoot.resolve("lib") : Paths.get(resourceDir, "python-runtime/venv-offline/lib");
|
||||||
if (Files.isDirectory(libRoot)) {
|
if (Files.isDirectory(libRoot)) {
|
||||||
try {
|
try {
|
||||||
try (java.nio.file.DirectoryStream<Path> stream = Files.newDirectoryStream(libRoot)) {
|
try (java.nio.file.DirectoryStream<Path> stream = Files.newDirectoryStream(libRoot)) {
|
||||||
|
|
@ -415,10 +430,28 @@ public class FlaskProcessManager implements ApplicationListener<ContextRefreshed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Windows venvs
|
// Windows venvs
|
||||||
Path venvLibWin = Paths.get(resourceDir, "python-runtime/venv-offline/Lib/site-packages");
|
Path venvLibWin = venvRoot != null ? venvRoot.resolve("Lib/site-packages") :
|
||||||
|
Paths.get(resourceDir, "python-runtime/venv-offline/Lib/site-packages");
|
||||||
if (Files.exists(venvLibWin)) {
|
if (Files.exists(venvLibWin)) {
|
||||||
return venvLibWin;
|
return venvLibWin;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Path resolveEmbeddedVenvRoot() {
|
||||||
|
Path venv = Paths.get(resourceDir, "python-runtime/venv-offline");
|
||||||
|
if (Files.isDirectory(venv)) {
|
||||||
|
return venv;
|
||||||
|
}
|
||||||
|
Path alt = Paths.get(resourceDir, "python-runtime/venv-linux-offline");
|
||||||
|
if (Files.isDirectory(alt)) {
|
||||||
|
return alt;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWindows() {
|
||||||
|
String os = System.getProperty("os.name").toLowerCase();
|
||||||
|
return os.contains("windows");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue