Skip to content

Inspect the project's Python environment instead of the CLI's (support uv tool install) #889

Open

Description

@tdstein

Problem

When rsconnect-python (or posit-cli) is installed with uv tool install, it runs in its own isolated environment. Without --python, rsconnect-python inspects whichever interpreter is running the CLI (which_python() falls back to sys.executable, rsconnect/environment.py:252). So it inspects the tool environment instead of the project's:

Feature Result under uv tool install
--force-generate requirements.txt lists the CLI's own dependencies (click, PyJWT, semver, uv, ...) and none of the project's.
Manifest python.version Records the tool's Python (e.g. 3.13) instead of the project's (e.g. 3.11).
--python .venv/bin/python Fails with No module named 'rsconnect', because the inspector runs as python -m rsconnect.subprocesses.inspect_environment (rsconnect/environment.py:227).
deploy notebook --static Runs <python> -m nbconvert with the same interpreter (rsconnect/main.py:1879), so nbconvert must be in the tool environment.

Running under uv run (which sets VIRTUAL_ENV) doesn't help; the tool environment is still used.

The CLI shouldn't have to share a Python environment with the content it deploys.

Dependencies

Proposal

Keep the inspector running in the CLI's own environment, so it can keep using its dependencies (packaging, and toml on Python < 3.11). Most of what it does never reads the target interpreter: requirements files, pyproject.toml, uv export, rsconnect filtering, editable checks, and locale are all file or string work. Only query the project interpreter for the facts that come from it.

  1. Find the project's interpreter. which_python(python, directory) checks, in order:

    • --python
    • $VIRTUAL_ENV
    • a .venv in the deploy directory or its parents, respecting UV_PROJECT_ENVIRONMENT
    • $CONDA_PREFIX
    • sys.executable

    Log the interpreter that was picked.

  2. Run the inspector with the CLI's interpreter and pass it the target. The parent runs sys.executable -m rsconnect.subprocesses.inspect_environment --python <interp> --uv <uv path> .... Within the inspector, replace every sys.executable that means "the project's Python" with the --python argument.

  3. Query the target interpreter for only these facts:

    • Python version: <interp> -I -c "import sys; print('%d.%d.%d' % sys.version_info[:3])".
    • pip version: <interp> -m pip --version, now optional. uv-created venvs don't include pip. When pip is missing, omit the version instead of erroring; bundle.py:262 already falls back when the package-manager version is absent.
    • --force-generate freeze: <interp> -m pip freeze when pip ≥ 20.3 is present (keeping Fix dependency filtering and require pip 20.3 for inspection #909's version check and --exclude flags), otherwise uv pip freeze --python <interp>. Both outputs go through Fix dependency filtering and require pip 20.3 for inspection #909's filter and editable check.
  4. Use one uv binary. Resolve the bundled uv once in the parent process (the uv dependency's binary, falling back to shutil.which("uv")), pass it to the inspector, and use it for both uv export and uv pip freeze instead of bare uv from PATH.

  5. Static notebooks run <interp> -m nbconvert in the project's environment. When nbconvert is missing, raise a clear RSConnectException naming the interpreter and how to install nbconvert, instead of a raw CalledProcessError.

Decisions

  • Behavior change: a globally installed CLI run inside an activated venv now inspects that venv. Call this out in the changelog.
  • --python becomes a supported explicit override again; remove its deprecation warning.
  • The inspector is not run by file path inside the project's interpreter. That would require it to use only the standard library, and a stdlib replacement for packaging was tried and rejected.

Open questions

  • With uv bundled, should --force-generate always use uv pip freeze --python <interp>? That's one code path, and it makes Fix dependency filtering and require pip 20.3 for inspection #909's pip ≥ 20.3 check irrelevant for generating requirements. The cost is that the manifest records pip as the package manager even though uv produced the freeze.
  • Does Connect accept a manifest with an empty package_manager.version when the project environment has no pip?

Tests

  • Unit tests for the interpreter lookup order: env vars, a temporary .venv, a .venv in a parent directory, UV_PROJECT_ENVIRONMENT, $CONDA_PREFIX, and the sys.executable fallback.
  • An integration-style test that creates a uv venv with neither pip nor rsconnect-python and installs one package into it. --force-generate inspection from the dev environment should report that venv's Python version and package, and none of rsconnect's own dependencies.
  • Run the filtering and editable-check tests against uv pip freeze output too. It formats editables as -e file:///... and has none of pip's # Editable install ... comments.
  • deploy notebook --static with nbconvert missing from the project environment raises the clear error.

Once released and picked up by posit-cli, the Connect docs can keep recommending uv tool install posit-cli.

Activity

  1. added
    enhancementNew feature or request
    ready-for-agentFully specified, ready for an autonomous coding agent
    on Oct 27, 2026
  2. self-assigned this
    on Oct 27, 2026
  3. removed
    ready-for-agentFully specified, ready for an autonomous coding agent
    on Nov 4, 2026
  4. tdstein commented on Nov 5, 2026

    @tdstein
    CollaboratorAuthor

    🤖 > This was generated by AI during triage.

    Agent Brief

    Category: bug
    Summary: Inspect and render with the project's Python environment while keeping the environment inspector and its dependencies in the CLI environment.

    Current behavior:
    which_python() defaults to the CLI interpreter even with VIRTUAL_ENV set. _inspect_environment() runs the inspector in the selected interpreter, requiring the project's environment to contain rsconnect and its dependencies. On current main after #909, a clean venv reproduced inspection failure on missing packaging (the checkout was importable from the working directory). Interpreter selection with VIRTUAL_ENV set also reproduced selection of the CLI interpreter. Static notebook rendering already wraps subprocess failures in RSConnectException, but the message lacks interpreter-specific installation guidance.

    Desired behavior:
    Select the project interpreter in this order: explicit --python, VIRTUAL_ENV, a project environment in the deployment directory or its ancestors (respecting UV_PROJECT_ENVIRONMENT), CONDA_PREFIX, then the CLI interpreter. Log the selected interpreter and support platform-specific executable locations. Explicit invalid overrides must produce actionable errors.

    Run the inspector in the CLI environment and pass the selected project interpreter separately. Read requirements, project metadata, and locale in the inspector; query the target interpreter for its Python version and optional pip version. Resolve the bundled uv executable once in the parent, falling back to PATH, and use that executable for both export and freeze.

    For generated requirements, use target-environment pip when pip >= 20.3 is present, preserving #909's exclusions. Otherwise use uv pip freeze --python <target>. Apply #909's distribution filtering and editable validation to both outputs. Missing pip must not prevent inspection of a project environment. Preserve the pip requirements manifest semantics; verify how Connect handles the unavailable pip version before choosing the final serialized representation. Do not invent a pip version.

    Static notebooks must use the selected project interpreter. Missing nbconvert must raise an actionable RSConnectException naming that interpreter and explaining how to install nbconvert.

    Key interfaces:

    • which_python(python, directory): interpreter resolution with deployment-directory context.
    • Environment._get_python_env_info() and Environment._inspect_environment(): distinguish the CLI interpreter from the project interpreter and pass the resolved uv executable.
    • detect_environment(), get_python_version(), get_version(), pip_freeze(), and uv_export(): obtain target facts without requiring inspector dependencies in the target environment.
    • EnvironmentData and Manifest: represent unavailable pip metadata compatibly with Connect.
    • make_notebook_html_bundle(): use the project interpreter and provide useful missing-nbconvert errors.

    Acceptance criteria:

    • Tests establish the stated interpreter precedence, ancestor lookup, UV_PROJECT_ENVIRONMENT, platform executable locations, explicit override validation, and CLI fallback.
    • An integration test creates a uv venv without pip or rsconnect, installs a package, and confirms force-generated inspection reports that environment's Python version and package rather than CLI dependencies.
    • Pip >= 20.3 uses pip freeze with Fix dependency filtering and require pip 20.3 for inspection #909's exclusions; missing or older pip uses the resolved uv executable. Tests exercise each branch.
    • Pip and uv freeze outputs preserve unrelated distribution names and pass through the same filtering and editable checks, including uv's editable file URLs.
    • uv export and uv freeze use the same parent-resolved executable, including the PATH fallback.
    • Requirements-file inspection succeeds when the target has no pip.
    • Verify Connect accepts the generated manifest when pip is unavailable, recording the Connect version and evidence. An empty package-manager version remains a hypothesis until verified; if rejected, establish a compatible representation and test it. Local serialization tests alone do not establish server acceptance.
    • Static notebooks render with the project interpreter; missing nbconvert gives the interpreter and installation guidance.
    • Remove any remaining --python deprecation warning and document supported explicit overrides. The changelog calls out that a globally installed CLI now selects an activated project environment.
    • Relevant existing environment, manifest, and notebook tests pass, preserving Fix dependency filtering and require pip 20.3 for inspection #909's filtering and editable protections.

    Out of scope:

    • Always using uv freeze when a supported pip is available.
    • Running the inspector inside the target environment or replacing packaging with a standard-library parser.
    • fix: support minor-only Python version overrides #911's independent override-Python-version changes.
    • Publishing posit-cli or changing Connect documentation as part of this implementation.

    Dependency: #909 merged on 2026-11-05; work can proceed. Connect acceptance verification is required before completion; if suitable server access is unavailable, report that specific blocker rather than claiming compatibility.

  5. added
    bugSomething isn't working
    ready-for-agentFully specified, ready for an autonomous coding agent
    and removed
    enhancementNew feature or request
    on Nov 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingready-for-agentFully specified, ready for an autonomous coding agent

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions