Skip to content

fix(runtime): isolate packaged Python from user site-packages#92

Closed
zouyonghe wants to merge 3 commits intoAstrBotDevs:mainfrom
zouyonghe:fix/packaged-python-user-site
Closed

fix(runtime): isolate packaged Python from user site-packages#92
zouyonghe wants to merge 3 commits intoAstrBotDevs:mainfrom
zouyonghe:fix/packaged-python-user-site

Conversation

@zouyonghe
Copy link
Member

@zouyonghe zouyonghe commented Mar 14, 2026

Summary

  • disable Python user site-packages when launching the packaged backend runtime so user-installed packages cannot shadow bundled dependencies
  • add a regression test covering packaged launch environment sanitization to prevent bundled pydantic-settings from importing an incompatible user-installed pydantic

Test Plan

  • cargo test sanitize_packaged_python_environment
  • cargo test
  • cargo fmt --check

Summary by Sourcery

Ensure the packaged backend Python runtime is isolated from user-installed packages and make Windows ARM64 runtime architecture selection explicit and configurable.

Bug Fixes:

  • Disable Python user site-packages for the packaged backend runtime to prevent user-installed dependencies from shadowing bundled ones.

Enhancements:

  • Introduce shared utilities for resolving the bundled backend runtime architecture across platforms, including configurable overrides for Windows ARM64.
  • Update backend runtime preparation to record and reuse the resolved bundled runtime architecture, and adjust Windows ARM64 detection in backend build scripts to use this shared logic.

CI:

  • Pass explicit desktop target architecture and optional Windows ARM64 backend runtime architecture overrides through the Tauri desktop build workflow.

Documentation:

  • Document new environment variables for configuring desktop target architecture and Windows ARM64 backend runtime architecture, and refresh the environment variable reference update date.

Tests:

  • Add Rust tests to verify that the packaged Python environment disables user site-packages.
  • Add Node.js tests to validate bundled runtime architecture resolution and python-build-standalone target mapping, including Windows ARM64 and override scenarios.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the robustness of the application's Python backend by ensuring a clean and isolated runtime environment. It addresses potential conflicts arising from user-installed Python packages by explicitly disabling them, thereby guaranteeing that only bundled dependencies are used. Furthermore, it improves the build process's flexibility by allowing explicit specification of target architectures, which is crucial for cross-compilation and supporting diverse environments like Windows ARM64.

Highlights

  • Python Environment Isolation: Disabled Python user site-packages when launching the packaged backend runtime to prevent user-installed packages from shadowing bundled dependencies.
  • Architecture Resolution: Introduced new utility functions and environment variables to explicitly control and resolve target architectures for bundled runtimes, especially for Windows ARM64 builds.
  • Regression Testing: Added a regression test to ensure the packaged launch environment correctly sanitizes Python's module search path by disabling user site-packages.
Changelog
  • docs/environment-variables.md
    • Updated the document's last modified timestamp.
    • Introduced ASTRBOT_DESKTOP_TARGET_ARCH and ASTRBOT_DESKTOP_WINDOWS_ARM_BACKEND_ARCH environment variables for explicit architecture targeting during resource preparation and Windows ARM64 backend runtime configuration.
  • scripts/backend/build-backend.mjs
    • Refactored the detection of Windows ARM64 for bundled runtime to use a new shared utility function.
  • scripts/backend/runtime-arch-utils.mjs
    • Added a new utility module to centralize logic for resolving desktop target architecture and bundled runtime architecture based on environment variables and process.arch.
  • scripts/backend/runtime-arch-utils.test.mjs
    • Added comprehensive unit tests for the architecture resolution utilities, including default behaviors and explicit overrides.
  • scripts/prepare-resources/backend-runtime.mjs
    • Integrated the new architecture resolution utilities to determine the correct Python-build-standalone target, ensuring proper handling of Windows ARM64 and explicit target architecture overrides.
  • scripts/prepare-resources/backend-runtime.test.mjs
    • Added unit tests for resolvePbsTarget to verify its behavior with various platform, architecture, and environment variable combinations.
  • src-tauri/Cargo.lock
    • Incremented the version of the astrbot-desktop-tauri package.
  • src-tauri/src/backend/launch.rs
    • Modified the sanitize_packaged_python_environment function to set PYTHONNOUSERSITE=1, preventing the Python interpreter from loading user-installed packages.
    • Added a new regression test to confirm that user site-packages are correctly disabled in the packaged Python environment.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/build-desktop-tauri.yml
Activity
  • The author has confirmed that cargo test sanitize_packaged_python_environment passes.
  • The author has confirmed that all cargo tests pass.
  • The author has confirmed that cargo fmt --check passes.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="scripts/backend/runtime-arch-utils.mjs" line_range="60-64" />
<code_context>
+  return normalizeDesktopArch(windowsArmBackendArch, WINDOWS_ARM_BACKEND_ARCH_ENV);
+};
+
+export const isWindowsArm64BundledRuntime = ({
+  platform = process.platform,
+  arch = process.arch,
+  env = process.env,
+} = {}) => platform === 'win32' && resolveBundledRuntimeArch({ platform, arch, env }) === 'arm64';
</code_context>
<issue_to_address>
**issue:** Calling `resolveBundledRuntimeArch` unconditionally here can now throw on unsupported `process.arch`, where the previous logic would just return `false`.

The previous inlined check `process.platform === 'win32' && process.arch === 'arm64'` could not throw. Now `isWindowsArm64BundledRuntime()` always calls `resolveBundledRuntimeArch()`, which can throw for unknown/unsupported arches (e.g. `ia32`) because `resolveDesktopTargetArch` only handles `x64` and `arm64`. To keep the old non‑throwing behavior, short‑circuit on `platform !== 'win32'` (and possibly on unsupported `arch`) before calling `resolveBundledRuntimeArch()`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +60 to +64
export const isWindowsArm64BundledRuntime = ({
platform = process.platform,
arch = process.arch,
env = process.env,
} = {}) => platform === 'win32' && resolveBundledRuntimeArch({ platform, arch, env }) === 'arm64';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Calling resolveBundledRuntimeArch unconditionally here can now throw on unsupported process.arch, where the previous logic would just return false.

The previous inlined check process.platform === 'win32' && process.arch === 'arm64' could not throw. Now isWindowsArm64BundledRuntime() always calls resolveBundledRuntimeArch(), which can throw for unknown/unsupported arches (e.g. ia32) because resolveDesktopTargetArch only handles x64 and arm64. To keep the old non‑throwing behavior, short‑circuit on platform !== 'win32' (and possibly on unsupported arch) before calling resolveBundledRuntimeArch().

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively isolates the packaged Python runtime from user site-packages by setting the PYTHONNOUSERSITE environment variable, which is a solid approach to prevent dependency conflicts. The addition of a regression test for this change is commendable. Furthermore, the extensive refactoring to centralize and improve architecture resolution logic, especially for Windows on ARM, significantly enhances the build process's robustness and configurability. The new utility scripts are well-structured and thoroughly tested. I've identified a few minor robustness issues in the new runtime-arch-utils.mjs script related to handling null values from environment variables and have suggested improvements.

Comment on lines +25 to +27
if (overrideRaw !== undefined && String(overrideRaw).trim()) {
return normalizeDesktopArch(overrideRaw, DESKTOP_TARGET_ARCH_ENV);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current check for a non-empty environment variable is not robust against null values. If overrideRaw were null, String(overrideRaw) would evaluate to the string "null", which is truthy, leading to an error in normalizeDesktopArch. Using optional chaining (?.) with trim() provides a more concise and robust way to check for a non-empty string.

Suggested change
if (overrideRaw !== undefined && String(overrideRaw).trim()) {
return normalizeDesktopArch(overrideRaw, DESKTOP_TARGET_ARCH_ENV);
}
if (overrideRaw?.trim()) {
return normalizeDesktopArch(overrideRaw, DESKTOP_TARGET_ARCH_ENV);
}

Comment on lines +43 to +45
if (explicitBundledRuntimeArch !== undefined && String(explicitBundledRuntimeArch).trim()) {
return normalizeDesktopArch(explicitBundledRuntimeArch, BUNDLED_RUNTIME_ARCH_ENV);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the previous comment, this check is not robust against null values. Using optional chaining (?.) with trim() is a safer and more concise approach.

Suggested change
if (explicitBundledRuntimeArch !== undefined && String(explicitBundledRuntimeArch).trim()) {
return normalizeDesktopArch(explicitBundledRuntimeArch, BUNDLED_RUNTIME_ARCH_ENV);
}
if (explicitBundledRuntimeArch?.trim()) {
return normalizeDesktopArch(explicitBundledRuntimeArch, BUNDLED_RUNTIME_ARCH_ENV);
}

Comment on lines +53 to +55
if (windowsArmBackendArch === undefined || !String(windowsArmBackendArch).trim()) {
return 'amd64';
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This check for a missing or empty environment variable can also be simplified and made more robust against null values by using optional chaining (?.). The expression !windowsArmBackendArch?.trim() correctly handles undefined, null, and empty/whitespace strings.

Suggested change
if (windowsArmBackendArch === undefined || !String(windowsArmBackendArch).trim()) {
return 'amd64';
}
if (!windowsArmBackendArch?.trim()) {
return 'amd64';
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant