Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception context is not suppressed correctly in contextlib.ExitStack.__exit__ #59174

Closed
ncoghlan opened this issue May 31, 2012 · 6 comments
Closed
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@ncoghlan
Copy link
Contributor

BPO 14969
Nosy @ncoghlan
Files
  • 14963.3.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2012-06-01.12:48:50.416>
    created_at = <Date 2012-05-31.13:41:44.047>
    labels = ['interpreter-core', 'type-bug', 'library']
    title = 'Exception context is not suppressed correctly in contextlib.ExitStack.__exit__'
    updated_at = <Date 2012-06-01.12:48:50.393>
    user = 'https://github.com/ncoghlan'

    bugs.python.org fields:

    activity = <Date 2012-06-01.12:48:50.393>
    actor = 'python-dev'
    assignee = 'none'
    closed = True
    closed_date = <Date 2012-06-01.12:48:50.416>
    closer = 'python-dev'
    components = ['Interpreter Core', 'Library (Lib)']
    creation = <Date 2012-05-31.13:41:44.047>
    creator = 'ncoghlan'
    dependencies = []
    files = ['25785']
    hgrepos = []
    issue_num = 14969
    keywords = ['patch']
    message_count = 6.0
    messages = ['161998', '162021', '162037', '162055', '162072', '162073']
    nosy_count = 3.0
    nosy_names = ['ncoghlan', 'alonho', 'python-dev']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue14969'
    versions = ['Python 3.3']

    @ncoghlan
    Copy link
    Contributor Author

    When adding the test case for bpo-14963, I discovered that contextlib.ExitStack can't *quite* reproduce the exception handling of nested with statements.

    The problem arises when the original exception gets suppressed by one of the handlers, but an outer handler raises a *new* exception, then nested with statements will correctly indicate that there was no exception context active when the new exception was raised (since the inner with statement will fully clear the exception state).

    By contrast, when using ExitStack, the interpreter will add the original exception from inside the body of the with statement as the context for the *new* exception, even though the inner exception had been suppressed before the outer one was encountered.

    Restoring sys.exc_clear() *might* allow this discrepancy to be resolved by explicitly clearing the exception state when one of the callbacks indicates that the current exception has been handled (although it might be trickier than that, if the problem is actually due to caching the exception state inside the with cleanup code in the eval loop)

    @ncoghlan ncoghlan added type-feature A feature request or enhancement interpreter-core (Objects, Python, Grammar, and Parser dirs) stdlib Python modules in the Lib dir labels May 31, 2012
    @alonho
    Copy link
    Mannequin

    alonho mannequin commented May 31, 2012

    Another possible solution is to explicitly set an exception's __supress_context__ attribute to False (right now impossible because it's the default value).
    If a user can 'turn on' the flag when attaching a different exception (raise X from Y), why not allow 'turning it off'? (symmetry anyone?) right now it is set to False by default and changed to true when 'raising from'.
    I suggest changing the default to None, allowing the user to explicitly say: I'm no longer in the previous exception's context.

    Feels a bit like solving our hack with another hack (:

    And about the PSF contrib agreement, I'll do it as soon as I'm near a printer. too bad we're using pens and not RSA private keys for signatures (-:

    thanks, Alon

    @ncoghlan
    Copy link
    Contributor Author

    ncoghlan commented Jun 1, 2012

    Electronic contributor forms are actually on the PSF wishlist. We'll get there some day (maybe after the website update).

    Getting back to the problem at hand, I think you might be on to something with the idea of exploiting PEP-409 to handle this. Specifically, where we reraise a caught exception when there are no exception details active, we should be able to replace the bare raise with something like:

        # A containing with statement will automatically add the exception
        # context back in after it gets suppressed. Avoid displaying it.
        if suppressed_exc and exc_details == (None, None, None):
            raise exc from None
        raise

    That way, the exception *display* of escaping exceptions will still match that of nested with statements, even though the attributes are subtly different (suppress_context being set to True, rather than __context__ being None)

    @ncoghlan ncoghlan changed the title Restore sys.exc_clear()? Use __suppress_context__ in contextlib.ExitStack.__exit__ Jun 1, 2012
    @alonho
    Copy link
    Mannequin

    alonho mannequin commented Jun 1, 2012

    Ok, so turns out this was just a stupid bug: we set the __context__ attr only if an exception is raised, but not when an exception has been previously 'cleared'. so the context is filled (by python) with the last exception raised which is the outer one.
    deleting the 'if last context is an exception' solved it.

    This is how I understood it:
    the exception's __context__ is set when it's raised and not in its except clause, meaning there is no way the outer with is mutating our inner exceptions. using pdb I saw the outer exception being explicitly set.

    @ncoghlan
    Copy link
    Contributor Author

    ncoghlan commented Jun 1, 2012

    Ah, you're absolutely right. Interestingly though, the old recursive implementation appeared to have the same bug, and I'd deliberately used the recursive approach precisely so that suppressed exceptions *would* get cleared correctly. I must have managed to leave a loophole somewhere :(

    I also realised simply overwriting the context isn't correct in all cases, and have adjusted the test cases accordingly.

    @ncoghlan ncoghlan changed the title Use __suppress_context__ in contextlib.ExitStack.__exit__ Exception context is not suppressed correctly in contextlib.ExitStack.__exit__ Jun 1, 2012
    @ncoghlan ncoghlan added type-bug An unexpected behavior, bug, or error and removed type-feature A feature request or enhancement labels Jun 1, 2012
    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Jun 1, 2012

    New changeset c108bc96aec6 by Nick Coghlan in branch 'default':
    Close bpo-14969: Improve the handling of exception chaining in contextlib.ExitStack
    http://hg.python.org/cpython/rev/c108bc96aec6

    @python-dev python-dev mannequin closed this as completed Jun 1, 2012
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    interpreter-core (Objects, Python, Grammar, and Parser dirs) stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    1 participant