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

Add a helper to FixtureRequest for adding context managers #12994

Open
jan-hudec opened this issue Nov 25, 2024 · 4 comments
Open

Add a helper to FixtureRequest for adding context managers #12994

jan-hudec opened this issue Nov 25, 2024 · 4 comments

Comments

@jan-hudec
Copy link

What's the problem this feature will solve?

Standard way to work with things that need finalization in python is the context manager. But when composing multiple context managers in a fixture, it either gets nested, like

@fixture
def open_all_the_files():
    with open('one') as one:
        with open('two') as two:
            with open('three') as three:
                yield one, two, three

or the contextlib.ExitStack can be used like

@fixture
def open_all_the_files():
    with contextlib.ExitStack() as stack:
        files = [open(name) for name in ('one', 'two', 'three')]
        yield files

It's not bad, but it could be simplified a bit further.

Describe the solution you'd like

The request can already register finalizers, so it would be nice to have an enter_context on it, so the above case could be further simplified to

@fixture
def open_all_the_files(request):
    return [request.enter_context(open(name) for name in ('one', 'two', 'three')]

With the caveat that handling exceptions with the context managers wouldn't be supported this way, but most context managers don't care.

Alternative Solutions

Well, one can always use contextlib.ExitStack(), this would be just a convenience wrapper. Or one can monkey-patch it into the class, that's not hard either.

@graingert
Copy link
Member

graingert commented Nov 25, 2024

You don't need any nesting:

@fixture
def open_all_the_files():
    with (
        open('one') as one,
        open('two') as two,
        open('three') as three
    ):
        yield one, two, three

@RonnyPfannschmidt
Copy link
Member

personally i'd currently recommend directly using exitstack

eventually we need to integrate more robust cleanup addition/registration - however that api must necessarily be placed and given a sensible outline

i want to avoid expanding the surface of request itself with all necessary methods - and i want to avoid the repercussions of not thinking of async in a reasonable manner - so currently its best to make a own stack

@The-Compiler
Copy link
Member

IMHO the contextlib.ExitStack solution and the alternative shown by @graingert are both perfectly fine here.

I'd say normally a fixture should (more or less) represent one thing rather than three separate things in a tuple, so this seems like a quite exotic use-case, and thus not something that needs its own helper (especially given the caveats shown by @RonnyPfannschmidt).

Maybe I'd reconsider if you can show a real-world use-case that doesn't work well with the alternatives above, and does not benefit from actually splitting the three things into three separate fixtures - but as-is, I'm -1 on this.

@RonnyPfannschmidt
Copy link
Member

i recon that theres certain setups where the "single thing" is set up with a chain of resources such that adding cleanups is the most "sensible" to ensure correct teardown if setup fails mid-way

currently having a dedicated owned exit stack is the most pythonic way to set those up correctly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants