Skip to content

PopenPlugin

PopenPlugin

PopenPlugin(verifier)

Bases: StateMachinePlugin

Popen interception plugin.

Replaces subprocess.Popen with _FakePopen at activate() time and restores the original at deactivate() time. Uses reference counting so nested sandboxes work correctly.

States: created -> running -> terminated

Coexists with SubprocessPlugin: SubprocessPlugin patches subprocess.run and shutil.which; PopenPlugin patches subprocess.Popen. Both plugins target independent names in the subprocess module and restore correctly.

Source code in src/bigfoot/plugins/popen_plugin.py
def __init__(self, verifier: "StrictVerifier") -> None:
    super().__init__(verifier)
    self._spawn_sentinel = _StepSentinel(_SOURCE_SPAWN)
    self._communicate_sentinel = _StepSentinel(_SOURCE_COMMUNICATE)
    self._wait_sentinel = _StepSentinel(_SOURCE_WAIT)

activate

activate()

Reference-counted class-level patch installation.

Source code in src/bigfoot/plugins/popen_plugin.py
def activate(self) -> None:
    """Reference-counted class-level patch installation."""
    global _bigfoot_popen_class

    with PopenPlugin._install_lock:
        if PopenPlugin._install_count == 0:
            self._check_conflicts()
            PopenPlugin._original_popen = subprocess.Popen
            _bigfoot_popen_class = _FakePopen
            subprocess.Popen = _FakePopen  # type: ignore[assignment, misc]
        PopenPlugin._install_count += 1

matches

matches(interaction, expected)

Field-by-field comparison with dirty-equals support.

Source code in src/bigfoot/plugins/popen_plugin.py
def matches(self, interaction: Interaction, expected: dict[str, Any]) -> bool:
    """Field-by-field comparison with dirty-equals support."""
    try:
        for key, expected_val in expected.items():
            actual_val = interaction.details.get(key)
            if expected_val != actual_val:
                return False
        return True
    except Exception:
        return False

assertable_fields

assertable_fields(interaction)

Return assertable fields for each step type.

Source code in src/bigfoot/plugins/popen_plugin.py
def assertable_fields(self, interaction: Interaction) -> frozenset[str]:
    """Return assertable fields for each step type."""
    if interaction.source_id == _SOURCE_WAIT:
        return frozenset()
    return frozenset(interaction.details.keys())