From 7b9931e2a63dacb8a17ddfe428044e20acbfc113 Mon Sep 17 00:00:00 2001 From: Shunping Huang Date: Fri, 16 Jan 2026 14:17:25 -0500 Subject: [PATCH 1/2] Disable build isolation for workflow tarball by default. Add an env var to enable it if needed. --- sdks/python/container/boot.go | 7 ++++++- sdks/python/container/piputil.go | 17 +++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/sdks/python/container/boot.go b/sdks/python/container/boot.go index 847325d4f83c..6b479626a0bf 100644 --- a/sdks/python/container/boot.go +++ b/sdks/python/container/boot.go @@ -390,6 +390,11 @@ func installSetupPackages(ctx context.Context, logger *tools.Logger, files []str bufLogger.Printf(ctx, "Failed to setup acceptable wheel specs, leave it as empty: %v", err) } + useBuildIsolationForWorkflow := false + if os.Getenv("USE_BUILD_ISOLATION_FOR_WORKFLOW") != "" { + useBuildIsolationForWorkflow = true + } + // Install the Dataflow Python SDK if one was staged. In released // container images, SDK is already installed, but can be overriden // using the --sdk_location pipeline option. @@ -411,7 +416,7 @@ func installSetupPackages(ctx context.Context, logger *tools.Logger, files []str if err := installExtraPackages(ctx, logger, files, extraPackagesFile, workDir); err != nil { return fmt.Errorf("failed to install extra packages: %v", err) } - if err := pipInstallPackage(ctx, logger, files, workDir, workflowFile, false, true, nil); err != nil { + if err := pipInstallPackage(ctx, logger, files, workDir, workflowFile, false, true, useBuildIsolationForWorkflow, nil); err != nil { return fmt.Errorf("failed to install workflow: %v", err) } if err := logRuntimeDependencies(ctx, bufLogger); err != nil { diff --git a/sdks/python/container/piputil.go b/sdks/python/container/piputil.go index d6250ad2fdcd..0c8bf4297801 100644 --- a/sdks/python/container/piputil.go +++ b/sdks/python/container/piputil.go @@ -81,7 +81,7 @@ func isPackageInstalled(pkgName string) bool { } // pipInstallPackage installs the given package, if present. -func pipInstallPackage(ctx context.Context, logger *tools.Logger, files []string, dir, name string, force, optional bool, extras []string) error { +func pipInstallPackage(ctx context.Context, logger *tools.Logger, files []string, dir, name string, force, optional, useBuildIsolation bool, extras []string) error { pythonVersion, err := expansionx.GetPythonVersion() if err != nil { return err @@ -112,6 +112,9 @@ func pipInstallPackage(ctx context.Context, logger *tools.Logger, files []string // installed if necessary. This achieves our goal outlined above. args := []string{"-m", "pip", "install", "--no-cache-dir", "--disable-pip-version-check", "--upgrade", "--force-reinstall", "--no-deps", filepath.Join(dir, packageSpec)} + if !useBuildIsolation { + args = append(args, "--no-build-isolation") + } err := execx.ExecuteEnvWithIO(nil, os.Stdin, bufLogger, bufLogger, pythonVersion, args...) if err != nil { bufLogger.FlushAtError(ctx) @@ -120,6 +123,9 @@ func pipInstallPackage(ctx context.Context, logger *tools.Logger, files []string bufLogger.FlushAtDebug(ctx) } args = []string{"-m", "pip", "install", "--no-cache-dir", "--disable-pip-version-check", filepath.Join(dir, packageSpec)} + if !useBuildIsolation { + args = append(args, "--no-build-isolation") + } err = execx.ExecuteEnvWithIO(nil, os.Stdin, bufLogger, bufLogger, pythonVersion, args...) if err != nil { bufLogger.FlushAtError(ctx) @@ -131,6 +137,9 @@ func pipInstallPackage(ctx context.Context, logger *tools.Logger, files []string // Case when we do not perform a forced reinstall. args := []string{"-m", "pip", "install", "--no-cache-dir", "--disable-pip-version-check", filepath.Join(dir, packageSpec)} + if !useBuildIsolation { + args = append(args, "--no-build-isolation") + } err := execx.ExecuteEnvWithIO(nil, os.Stdin, bufLogger, bufLogger, pythonVersion, args...) if err != nil { bufLogger.FlushAtError(ctx) @@ -168,7 +177,7 @@ func installExtraPackages(ctx context.Context, logger *tools.Logger, files []str for s.Scan() { extraPackage := s.Text() bufLogger.Printf(ctx, "Installing extra package: %s", extraPackage) - if err = pipInstallPackage(ctx, logger, files, dir, extraPackage, true, false, nil); err != nil { + if err = pipInstallPackage(ctx, logger, files, dir, extraPackage, true, false, true, nil); err != nil { return fmt.Errorf("failed to install extra package %s: %v", extraPackage, err) } } @@ -203,7 +212,7 @@ func installSdk(ctx context.Context, logger *tools.Logger, files []string, workD if sdkWhlFile != "" { // by default, pip rejects to install wheel if same version already installed isDev := strings.Contains(sdkWhlFile, ".dev") - err := pipInstallPackage(ctx, logger, files, workDir, sdkWhlFile, isDev, false, []string{"gcp"}) + err := pipInstallPackage(ctx, logger, files, workDir, sdkWhlFile, isDev, false, true, []string{"gcp"}) if err == nil { return nil } @@ -215,6 +224,6 @@ func installSdk(ctx context.Context, logger *tools.Logger, files []string, workD return nil } } - err := pipInstallPackage(ctx, logger, files, workDir, sdkSrcFile, false, false, []string{"gcp"}) + err := pipInstallPackage(ctx, logger, files, workDir, sdkSrcFile, false, false, true, []string{"gcp"}) return err } From 42768bd8c37b3775349d8fd75f4f0c758965d9ce Mon Sep 17 00:00:00 2001 From: Shunping Huang Date: Wed, 21 Jan 2026 16:21:49 -0500 Subject: [PATCH 2/2] Apply build isolation setting to all pip install commands. Use experiment to control. --- sdks/python/container/boot.go | 13 +++++++------ sdks/python/container/piputil.go | 19 ++++++++++++------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/sdks/python/container/boot.go b/sdks/python/container/boot.go index 6b479626a0bf..1be1268a4465 100644 --- a/sdks/python/container/boot.go +++ b/sdks/python/container/boot.go @@ -155,6 +155,12 @@ func launchSDKProcess() error { logger.Fatalf(ctx, "Failed to convert pipeline options: %v", err) } + pipNoBuildIsolation = false + if strings.Contains(options, "pip_no_build_isolation") { + pipNoBuildIsolation = true + logger.Printf(ctx, "Disabled build isolation when installing packages with pip") + } + // (2) Retrieve and install the staged packages. // // No log.Fatalf() from here on, otherwise deferred cleanups will not be called! @@ -390,11 +396,6 @@ func installSetupPackages(ctx context.Context, logger *tools.Logger, files []str bufLogger.Printf(ctx, "Failed to setup acceptable wheel specs, leave it as empty: %v", err) } - useBuildIsolationForWorkflow := false - if os.Getenv("USE_BUILD_ISOLATION_FOR_WORKFLOW") != "" { - useBuildIsolationForWorkflow = true - } - // Install the Dataflow Python SDK if one was staged. In released // container images, SDK is already installed, but can be overriden // using the --sdk_location pipeline option. @@ -416,7 +417,7 @@ func installSetupPackages(ctx context.Context, logger *tools.Logger, files []str if err := installExtraPackages(ctx, logger, files, extraPackagesFile, workDir); err != nil { return fmt.Errorf("failed to install extra packages: %v", err) } - if err := pipInstallPackage(ctx, logger, files, workDir, workflowFile, false, true, useBuildIsolationForWorkflow, nil); err != nil { + if err := pipInstallPackage(ctx, logger, files, workDir, workflowFile, false, true, nil); err != nil { return fmt.Errorf("failed to install workflow: %v", err) } if err := logRuntimeDependencies(ctx, bufLogger); err != nil { diff --git a/sdks/python/container/piputil.go b/sdks/python/container/piputil.go index 0c8bf4297801..1faf8421a02d 100644 --- a/sdks/python/container/piputil.go +++ b/sdks/python/container/piputil.go @@ -32,6 +32,11 @@ import ( "github.com/apache/beam/sdks/v2/go/pkg/beam/util/execx" ) +var ( + // Whether to append "--no-build-isolation" flag to pip install command + pipNoBuildIsolation bool +) + const pipLogFlushInterval time.Duration = 15 * time.Second const unrecoverableURL string = "https://beam.apache.org/documentation/sdks/python-unrecoverable-errors/index.html#pip-dependency-resolution-failures" @@ -81,7 +86,7 @@ func isPackageInstalled(pkgName string) bool { } // pipInstallPackage installs the given package, if present. -func pipInstallPackage(ctx context.Context, logger *tools.Logger, files []string, dir, name string, force, optional, useBuildIsolation bool, extras []string) error { +func pipInstallPackage(ctx context.Context, logger *tools.Logger, files []string, dir, name string, force, optional bool, extras []string) error { pythonVersion, err := expansionx.GetPythonVersion() if err != nil { return err @@ -112,7 +117,7 @@ func pipInstallPackage(ctx context.Context, logger *tools.Logger, files []string // installed if necessary. This achieves our goal outlined above. args := []string{"-m", "pip", "install", "--no-cache-dir", "--disable-pip-version-check", "--upgrade", "--force-reinstall", "--no-deps", filepath.Join(dir, packageSpec)} - if !useBuildIsolation { + if pipNoBuildIsolation { args = append(args, "--no-build-isolation") } err := execx.ExecuteEnvWithIO(nil, os.Stdin, bufLogger, bufLogger, pythonVersion, args...) @@ -123,7 +128,7 @@ func pipInstallPackage(ctx context.Context, logger *tools.Logger, files []string bufLogger.FlushAtDebug(ctx) } args = []string{"-m", "pip", "install", "--no-cache-dir", "--disable-pip-version-check", filepath.Join(dir, packageSpec)} - if !useBuildIsolation { + if pipNoBuildIsolation { args = append(args, "--no-build-isolation") } err = execx.ExecuteEnvWithIO(nil, os.Stdin, bufLogger, bufLogger, pythonVersion, args...) @@ -137,7 +142,7 @@ func pipInstallPackage(ctx context.Context, logger *tools.Logger, files []string // Case when we do not perform a forced reinstall. args := []string{"-m", "pip", "install", "--no-cache-dir", "--disable-pip-version-check", filepath.Join(dir, packageSpec)} - if !useBuildIsolation { + if pipNoBuildIsolation { args = append(args, "--no-build-isolation") } err := execx.ExecuteEnvWithIO(nil, os.Stdin, bufLogger, bufLogger, pythonVersion, args...) @@ -177,7 +182,7 @@ func installExtraPackages(ctx context.Context, logger *tools.Logger, files []str for s.Scan() { extraPackage := s.Text() bufLogger.Printf(ctx, "Installing extra package: %s", extraPackage) - if err = pipInstallPackage(ctx, logger, files, dir, extraPackage, true, false, true, nil); err != nil { + if err = pipInstallPackage(ctx, logger, files, dir, extraPackage, true, false, nil); err != nil { return fmt.Errorf("failed to install extra package %s: %v", extraPackage, err) } } @@ -212,7 +217,7 @@ func installSdk(ctx context.Context, logger *tools.Logger, files []string, workD if sdkWhlFile != "" { // by default, pip rejects to install wheel if same version already installed isDev := strings.Contains(sdkWhlFile, ".dev") - err := pipInstallPackage(ctx, logger, files, workDir, sdkWhlFile, isDev, false, true, []string{"gcp"}) + err := pipInstallPackage(ctx, logger, files, workDir, sdkWhlFile, isDev, false, []string{"gcp"}) if err == nil { return nil } @@ -224,6 +229,6 @@ func installSdk(ctx context.Context, logger *tools.Logger, files []string, workD return nil } } - err := pipInstallPackage(ctx, logger, files, workDir, sdkSrcFile, false, false, true, []string{"gcp"}) + err := pipInstallPackage(ctx, logger, files, workDir, sdkSrcFile, false, false, []string{"gcp"}) return err }