Skip to content

single-arg __exit__ function #550

@iritkatriel

Description

@iritkatriel

I went over the __exit__ functions in the stdlib and the vast majority take *args as input and do nothing with it. So every time an __exit__ is called, the interpreter either pushes 3 Nones to the stack and calls a 3-arg method, or (if there is an exception) constructs the (exc, val tb) from the exception, pushes the 3 values to the stack and then calls the 3-arg method. If __exit__ took just the exception object, the interpreter would not need to construct these tuples, and it would just call a single-arg function, which is faster.

Microbenchmarks showed this makes entering and exiting a context manager about 13% faster when no exception is raised.

If this was just about performance I probably wouldn't be that bothered. But the current __exit__ API is a wart in the language that confuses programmers, particularly beginners. As of Python 3.11 it is completely redundant, and it would be nice to tidy this up.

In this branch, I made the interpreter introspect __exit__ and apply the single-arg API if it is either a method with co_argcount=2 or a C Function with flags == METH_O. In all other cases it applies the 3-arg api.

I added a bytecode for the no-exception exit case (previously we pushed 3 Nones and called __exit__, but now we don't know at compile time how many Nones we will need).

Benchmark results:

  1. Just adding the new bytecode had no impact.

  2. Also introspecting the function in each __exit__ call was 1% slower. This can be mitigated by: (*) specialising so that introspection is done only once. (2) we will get some of it back once the stdlib's __exit__s are rewritten to take one arg.

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.12Things we intend to do for 3.12

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions