RustPython/RustPython · error · RuntimeError

Cannot enter %r twice

Error message

Cannot enter %r twice

What it means

warnings.catch_warnings is a one-shot context manager: __enter__ sets an internal _entered flag and raises RuntimeError if the same instance is entered again. Entering twice would save the already-swapped filter state as the 'original', making restoration impossible, so reuse is rejected outright.

Source

Thrown at Lib/_py_warnings.py:641

        self._module = sys.modules['warnings'] if module is None else module
        self._entered = False
        if action is None:
            self._filter = None
        else:
            self._filter = (action, category, lineno, append)

    def __repr__(self):
        args = []
        if self._record:
            args.append("record=True")
        if self._module is not sys.modules['warnings']:
            args.append("module=%r" % self._module)
        name = type(self).__name__
        return "%s(%s)" % (name, ", ".join(args))

    def __enter__(self):
        if self._entered:
            raise RuntimeError("Cannot enter %r twice" % self)
        self._entered = True
        with _wm._lock:
            if _use_context:
                self._saved_context, context = self._module._new_context()
            else:
                context = None
                self._filters = self._module.filters
                self._module.filters = self._filters[:]
                self._showwarning = self._module.showwarning
                self._showwarnmsg_impl = self._module._showwarnmsg_impl
            self._module._filters_mutated_lock_held()
            if self._record:
                if _use_context:
                    context.log = log = []
                else:
                    log = []
                    self._module._showwarnmsg_impl = log.append
                    # Reset showwarning() to the default implementation to make sure

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Construct a fresh warnings.catch_warnings() for each with statement
  2. In test fixtures, create the instance inside the test or per-test setup, never once at module import
  3. Wrap creation in a small factory function if you need repeated enter/exit cycles

Example fix

# before
cm = warnings.catch_warnings()
with cm:
    ...
with cm:  # RuntimeError: Cannot enter ... twice
    ...

# after
with warnings.catch_warnings():
    ...
with warnings.catch_warnings():
    ...
Defensive patterns

Strategy: validation

Validate before calling

if getattr(cm, "_entered", False):
    cm = warnings.catch_warnings()  # previous use exhausted it; start fresh
with cm:
    ...

Type guard

def is_fresh_catch_warnings(cm) -> bool:
    return not getattr(cm, "_entered", False)

Try / catch

try:
    with cm:
        ...
except RuntimeError as e:
    if "Cannot enter" in str(e):
        with warnings.catch_warnings():
            ...
    else:
        raise

Prevention

When it happens

Trigger: cm = warnings.catch_warnings(); with cm: ... followed later by with cm: ... -- reusing a stored instance; a module-level catch_warnings object entered by two different tests.

Common situations: Test suites that create the context manager once in a fixture and enter it per test; refactoring a with-block into a helper that receives the cm object; sharing one cm across setup/teardown.

Related errors


AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17). Data as JSON: /api/errors/f259d6927b55a572. Report an issue: GitHub.