RustPython/RustPython · error · RuntimeError

Cannot exit %r without entering first

Error message

Cannot exit %r without entering first

What it means

warnings.catch_warnings.__exit__ raises RuntimeError when the instance was never entered, because there is no saved filters/showwarning state to restore. It fires when __exit__ is invoked manually or out of order -- i.e. the with-protocol pairing of __enter__/__exit__ was broken by hand-rolled code.

Source

Thrown at Lib/_py_warnings.py:670

            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
                    # that _showwarnmsg() calls _showwarnmsg_impl()
                    self._module.showwarning = self._module._showwarning_orig
            else:
                log = None
        if self._filter is not None:
            self._module.simplefilter(*self._filter)
        return log

    def __exit__(self, *exc_info):
        if not self._entered:
            raise RuntimeError("Cannot exit %r without entering first" % self)
        with _wm._lock:
            if _use_context:
                self._module._warnings_context.set(self._saved_context)
            else:
                self._module.filters = self._filters
                self._module.showwarning = self._showwarning
                self._module._showwarnmsg_impl = self._showwarnmsg_impl
            self._module._filters_mutated_lock_held()


class deprecated:
    """Indicate that a class, function or overload is deprecated.

    When this decorator is applied to an object, the type checker
    will generate a diagnostic on usage of the deprecated object.

    Usage:

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Use the with statement instead of calling __enter__/__exit__ manually
  2. If manual pairing is unavoidable, set a flag and only call __exit__ after a successful __enter__
  3. Audit custom wrappers so exit runs exactly once per enter, including on exceptions

Example fix

# before
cm = warnings.catch_warnings()
cm.__exit__(None, None, None)  # RuntimeError: Cannot exit ... without entering first

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

Strategy: validation

Validate before calling

if getattr(cm, "_entered", False):
    cm.__exit__(None, None, None)  # only exit what was actually entered

Type guard

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

Try / catch

try:
    cm.__exit__(None, None, None)
except RuntimeError as e:
    if "without entering first" in str(e):
        pass  # nothing was saved; nothing to restore
    else:
        raise

Prevention

When it happens

Trigger: Calling cm.__exit__(None, None, None) without a prior cm.__enter__(); framework or fixture code that pairs enter/exit incorrectly; cleanup paths that call exit a second time unguarded.

Common situations: Custom context-manager wrappers or test fixtures that drive __enter__/__exit__ by hand; try/finally blocks imitating the with statement; double-cleanup on exception paths.

Related errors


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