-
Notifications
You must be signed in to change notification settings - Fork 7.2k
NOMRG Convert temp_video into a fixture #3979
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
Conversation
def temp_video(num_frames, height, width, fps, lossless=False, video_codec=None, options=None): | ||
@pytest.fixture(scope='module') | ||
def temp_video(request): | ||
print(f"fixture called once with {request.param}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to work as expected as I only see 2 print outputs over all tests:
fixture called once with {'num_frames': 10, 'height': 300, 'width': 300, 'fps': 5, 'lossless': True}
fixture called once with {'num_frames': 20, 'height': 30, 'width': 30, 'fps': 10, 'lossless': True}
assert info["video_fps"] == 10 | ||
|
||
|
||
# @pytest.mark.skipif(not io._HAS_VIDEO_OPT, reason="video_reader backend is not chosen") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just commented out the rest to avoid errors
I'm not sure if they are "better", but I can think of two more options:
In both cases we have the option to not specifying any special attributes, but go with sensible defaults instead. I like option 2 better, since it works well with other parametrizations: @pytest.mark.parametrize("num_frames", [10, 20])
def test_bar(temp_video_factory, num_frames):
temp_video = temp_video_factory(num_frames=num_frames)
... |
On a side note: I dislike the name |
Thanks for looking into this @pmeier ! The problem with option 2 is that it will not make use of the fixture caching, so the video will be created multiple times: it will cache the outer factory but not the inner video creation. Also, the inner function Basically I'm looking for a way to have a "parametrized" fixture ("parametrized" not in the pytest sense here, just the regular sense), that:
My version in this diff currently fails on the last one ^^ |
True, but we have the @pytest.fixture(scope="module")
def special_temp_video(temp_video_factory):
return temp_video_factory(num_frames=42)
My bad, I was under the impression @pytest.fixture(scope="session")
def temp_video_factory():
temp_videos = set()
def temp_video_factory_(num_frames: int = 10, ...):
...
temp_videos.add(temp_video)
return temp_video
yield temp_video_factory_
for temp_video in temp_videos:
os.remove(temp_video) If we use the |
I'm not sure what you mean by that. No matter the scope, the inner function never gets cached, it's only the outer one
In this specific case there are only a small set of parameters with which we want to call the fixture so that is doable, but not in general. |
|
Here is a minimal example using the factory strategy: import pytest
import functools
@pytest.fixture(scope="session")
def video_factory(tmp_path_factory):
@functools.lru_cache()
def video_factory_(*, num_frames: int, extension: str):
name = f"video-{num_frames}.{extension}"
print(f"Creating {name}")
return tmp_path_factory.mktemp(name)
# we cannot cache this directly, since functools.lru_cache is not able to handle default values.
# Thus, calling video_factory_with_defaults() and video_factory_with_defaults(num_frames=10)
# would count as two separate calls, which is not desired here.
def video_factory_with_defaults(num_frames: int = 10, extension="avi"):
return video_factory_(num_frames=num_frames, extension=extension)
return video_factory_with_defaults
@pytest.fixture(scope="module")
def video(video_factory):
return video_factory()
def test_foo(video):
assert video.exists()
def test_bar(video):
assert video.exists()
@pytest.mark.parametrize("num_frames", [10, 20])
def test_spam(video_factory, num_frames):
video = video_factory(num_frames=num_frames)
assert video.exists()
@pytest.mark.parametrize("num_frames", [20, 30])
def test_ham(video_factory, num_frames):
video = video_factory(num_frames=num_frames)
assert video.exists() If you run this with the |
Thanks @pmeier ! This seems to address most use-cases for this specific file. As discussed offline, there are a few caveats for a more general case though:
I feel like none of the solutions are worth merging at this point, as here the video creation is really cheap. But this was a good exercise that might be useful in the future! Thanks again |
Depending on the usage of a "default" video throughout the test suite, for example Line 76 in 964ce1e
we might still create a @pytest.fixture
def temp_video_fixture():
with temp_video(10, 300, 300, 5, lossless=True) as (f_name, data):
yield to adhere a little more to |
Attempt at #3922 (comment)
It's a bit tricky as the fixture needs to be called with different parameters. I'm not sure how to best do this, but this solution is inspired from https://docs.pytest.org/en/latest/example/parametrize.html#indirect-parametrization.
In this specific case, the fixture doesn't bring much to the table (there's no significant time saving), and makes the tests a bit cryptic, so I wouldn't merge this.
@pmeier perhaps you're aware of a better solution for this?