Skip to content

SEA binary resolution uses an expired importlib.resources temporary path #354

Description

@abhinavkr26104

Description

_resource_binary_path() returns the path yielded by importlib.resources.as_file() after leaving the context manager. For resources backed by a ZIP importer, that path is a temporary extraction which is deleted when the context exits. resolve_binary_path() then passes the expired path to _copy_to_cache(), where read_bytes() fails.

The cache copy appears to be the intended mechanism for non-filesystem package resources, but it currently happens too late.

Reproduction

The lifetime problem can be reproduced with any ZIP-backed package resource:

import importlib
import sys
import tempfile
import zipfile
from pathlib import Path
import importlib.resources as resources

with tempfile.TemporaryDirectory() as directory:
    archive = Path(directory) / "package.zip"
    with zipfile.ZipFile(archive, "w") as output:
        output.writestr("example_package/__init__.py", "")
        output.writestr("example_package/binary", b"binary")

    sys.path.insert(0, str(archive))
    importlib.invalidate_caches()

    resource = resources.files("example_package").joinpath("binary")
    with resources.as_file(resource) as extracted:
        returned = extracted
        print(returned.exists())  # True

    print(returned.exists())      # False

Stagehand follows this exact pattern at src/stagehand/_custom/sea_binary.py:60-61, then attempts to read the returned path at line 73.

Expected behavior

The packaged binary should be copied into Stagehand's persistent cache while the as_file() context is still active, or the context lifetime should otherwise cover the copy.

Actual behavior

For a ZIP-backed Traversable, _resource_binary_path() returns a deleted temporary path and local-mode resolution fails before startup.

Why this matters

Normal wheel installs are usually unpacked, which hides the defect. ZIP imports, zipapps, and packaging/deployment systems that expose resources through a non-filesystem loader exercise the temporary-resource path that this code is intended to support.

Prior-art check

I searched open/closed issues and PRs for zipimport, importlib.resources.as_file, packaged resource lifetime, and _resource_binary_path and found no existing report.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions