Skip to content

Commit f04acd7

Browse files
committed
Python: Treat .dev0 versions as local dev builds in bootstrap
The .dev0 suffix indicates a local dev build that isn't published to PyPI, so attempting `pip install openrewrite==X.Y.Z.dev0` will always fail. Restore the dev build check so these versions require the interpreter to already have the package (e.g. via editable install). Also improve pip install error messages to include the version being installed and capture pip output on failure.
1 parent 746fe6e commit f04acd7

1 file changed

Lines changed: 27 additions & 25 deletions

File tree

rewrite-python/src/main/java/org/openrewrite/python/rpc/PythonRewriteRpc.java

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -491,10 +491,11 @@ public Builder workingDirectory(@Nullable Path workingDirectory) {
491491

492492
/**
493493
* Set the base pip packages directory.
494-
* When set and the required version is not already available in the Python
495-
* interpreter, a version-specific subdirectory (e.g., {@code <pipPackagesPath>/8.74.1/}
496-
* or {@code <pipPackagesPath>/8.75.0.dev0/}) is resolved and the openrewrite
497-
* package is automatically installed there via pip.
494+
* When set and the required release version is not already available in the
495+
* Python interpreter, a version-specific subdirectory (e.g.,
496+
* {@code <pipPackagesPath>/8.74.1/}) is resolved and the openrewrite package is
497+
* automatically installed there via pip. Dev builds ({@code .dev0}) are not
498+
* installed this way and require the interpreter to already have the package.
498499
*
499500
* @param pipPackagesPath The base directory under which version-specific pip packages are installed
500501
* @return This builder
@@ -538,10 +539,11 @@ public Builder pythonVersion(String pythonVersion) {
538539
public PythonRewriteRpc get() {
539540
String version = StringUtils.readFully(
540541
PythonRewriteRpc.class.getResourceAsStream("/META-INF/rewrite-python-version.txt")).trim();
542+
boolean isDevBuild = version.isEmpty() || version.endsWith(".dev0");
541543

542544
Path resolvedPipPackagesPath = null;
543-
if (!version.isEmpty()) {
544-
// Known version (release or dev pre-release) — try to find or install it.
545+
if (!isDevBuild) {
546+
// Release version — try to find or install the pinned version.
545547
// 1. Check pipPackagesPath for an existing install
546548
// 2. Check if the interpreter already has the right version
547549
// 3. Install to pipPackagesPath if available, otherwise fail
@@ -559,8 +561,8 @@ public PythonRewriteRpc get() {
559561
"or install the package manually: pip install openrewrite==" + version);
560562
}
561563
} else {
562-
// No version info (local dev build) — require the interpreter to already
563-
// have the rewrite package (e.g., from a venv with an editable install).
564+
// Local dev build — require the interpreter to already have the rewrite
565+
// package (e.g., from a venv with an editable install).
564566
if (!canImportRewrite(pythonPath)) {
565567
throw new IllegalStateException(
566568
"The Python interpreter at " + pythonPath + " cannot import the 'rewrite' package. " +
@@ -609,9 +611,9 @@ public PythonRewriteRpc get() {
609611
// If debug source path is set, use it
610612
if (debugRewriteSourcePath != null) {
611613
pythonPathParts.add(debugRewriteSourcePath.toAbsolutePath().normalize().toString());
612-
} else if (version.isEmpty()) {
613-
// For local dev builds without a version file, try to find the Python
614-
// source in the project structure (as a fallback for PYTHONPATH)
614+
} else if (isDevBuild) {
615+
// For local dev builds, try to find the Python source in the project
616+
// structure (as a fallback for PYTHONPATH)
615617
Path basePath = workingDirectory != null ? workingDirectory : Paths.get(System.getProperty("user.dir"));
616618

617619
// Check common locations
@@ -730,30 +732,30 @@ private void bootstrapOpenrewrite(Path pipPackagesPath, String version) {
730732
pb.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile));
731733
}
732734
Process process = pb.start();
735+
String pipOutput = "";
733736
if (log == null) {
734-
// Drain stdout+stderr to prevent pipe buffer from filling and blocking
735-
Thread drainer = new Thread(() -> {
736-
try (InputStream is = process.getInputStream()) {
737-
byte[] buf = new byte[4096];
738-
//noinspection StatementWithEmptyBody
739-
while (is.read(buf) != -1) {
740-
}
741-
} catch (IOException ignored) {
742-
}
743-
});
744-
drainer.setDaemon(true);
745-
drainer.start();
737+
// Capture stdout+stderr so we can include it in error messages
738+
try (InputStream is = process.getInputStream()) {
739+
pipOutput = StringUtils.readFully(is);
740+
}
746741
}
747742
boolean completed = process.waitFor(2, TimeUnit.MINUTES);
748743

749744
if (!completed) {
750745
process.destroyForcibly();
751-
throw new RuntimeException("Timed out bootstrapping openrewrite package");
746+
throw new RuntimeException("Timed out bootstrapping openrewrite==" + version);
752747
}
753748

754749
int exitCode = process.exitValue();
755750
if (exitCode != 0) {
756-
throw new RuntimeException("Failed to bootstrap openrewrite package, pip install exited with code " + exitCode);
751+
String message = "Failed to install openrewrite==" + version +
752+
" (pip exited with code " + exitCode + ")";
753+
if (!pipOutput.isEmpty()) {
754+
message += ":\n" + pipOutput.trim();
755+
} else if (log != null) {
756+
message += ". See " + log.toAbsolutePath().normalize() + " for details";
757+
}
758+
throw new RuntimeException(message);
757759
}
758760

759761
Files.write(pipPackagesPath.resolve(".openrewrite-version"), version.getBytes(StandardCharsets.UTF_8));

0 commit comments

Comments
 (0)