[pytest-dev] solving the "too static" fixture scope problem

Vladimir Keleshev vladimir at keleshev.com
Fri Oct 11 13:14:00 CEST 2013


> If you refer to tmpdir/monkeypatch and potentially others, i agree
> that the current behaviour is more surprising (sharing the tmpdir
> across multiple fixtures which don't even neccessarily know about each
> other).

Absolutely. Was a major WAT for me.

—Vladimir


11.10.2013, 12:57, "holger krekel" <holger at merlinux.eu>:
> On Fri, Oct 11, 2013 at 12:51 +0200, Florian Schulze wrote:
>
>>  I would expect the behaviour you describe for scope="each" to be the default for a fixture without a scope. Why introduce a new scope for that? Am I missing something? I haven't used pytest for such complex things yet.
>
> Defaulting to scope="each" is an interesting proposition but it
> would be largely backward-compatible and i think in real situations.
> Consider a fixture like this:
>
>     @pytest.fixture
>     def db(...):
>         return DbInstance()
>
> and then a test and other fixtures using this "db" fixture.  They
> would all get distinct instances (people using fixtures more extensively
> implicitly rely on the current per-test "sharing" of resources and i think
> it makes sense, just not for all resources).
>
>>  Regarding the backward compatibility issue you described, I would expect the new behaviour instead of the current one. But maybe I'm missing something, because of the abstract example.
>
> If you refer to tmpdir/monkeypatch and potentially others, i agree
> that the current behaviour is more surprising (sharing the tmpdir
> across multiple fixtures which don't even neccessarily know about each
> other).
>
> cheers,
> holger
>
>>  Regards,
>>  Florian Schulze
>>
>>  On 11.10.2013, at 10:40, holger krekel <holger at merlinux.eu> wrote:
>>>  Hi pytest users and developers,
>>>
>>>  I'd like to discuss and get feedback on
>>>  extending the fixture mechanism and fix what
>>>  i consider a biggest limitation at the moment.
>>>
>>>  Problem: fixtures are tied statically to a scope
>>>  =================================================
>>>
>>>  For example, you cannot use monkeypatch in a higher
>>>  than "function" scoped fixture. Same is true for
>>>  tmpdir and probably also many user-defined fixtures.
>>>  I've certainly had this problem myself many times
>>>  that i had a fixture function that didn't really
>>>  care in what scope it was used.  There are
>>>  ways to get around this problem but they are not
>>>  pretty:
>>>
>>>     @pytest.fixture(scope="module")
>>>     def myfix_module(request
>>>         return _myfix(request)
>>>
>>>     @pytest.fixture(scope="function")
>>>     def myfix_function(request
>>>         return _myfix(request)
>>>
>>>  where _myfix is the function that doesn't
>>>  care about the actual scope.  Even then, you
>>>  can't use builtin fixtures like "monkeypatch",
>>>  "tmpdir", etc.
>>>
>>>  Solution Idea: introduce "each" scoped fixtures
>>>  =====================================================
>>>
>>>  The idea is allow a fixture function to declare it wants
>>>  to be used in the scope of the requesting fixture function
>>>  (or at function-scope if used from a test).
>>>
>>>  This is how "monkeypatch" would be implemented then:
>>>
>>>     @pytest.fixture(scope="each")
>>>     def monkeypatch(request):
>>>         ...  # same implementation as in _pytest/monkeypatch.py
>>>
>>>  The new "each" scope means that each fixture/test requesting
>>>  the "monkeypatch" fixture would receive its own fixture instance.
>>>
>>>  So a session-scoped fixture could naturally use it like this:
>>>
>>>     @pytest.fixture(scope="session")
>>>     def myfix(monkeypatch):
>>>         monkeypatch.setattr(...)
>>>         return some_value
>>>
>>>  The passed in monkeypatch object here is a specific instance just
>>>  for the ``myfix`` fixture function: "each" fixture function
>>>  requesting ``monkeypatch`` gets a new instance of it.
>>>  If e.g. a test uses another module-scoped fixture defined like this:
>>>
>>>     @pytest.fixture(scope="module")
>>>     def myfix2(monkeypatch):
>>>         mp.setattr(...)
>>>         return some_value
>>>
>>>  this would invoke the ``monkeypatch`` fixture function a second time,
>>>  resulting in a new instance for use by the ``myfix2`` instance.
>>>
>>>  The same logic could be applied to other fixtures
>>>  like "tmpdir" or user-defined ones.
>>>
>>>  Do you like this idea? Would you find it helpful for your test suites?
>>>
>>>  There is one issue i am not sure about yet, however.  Currently,
>>>  when a test requires fixture A and B, and B requires C and C requires A,
>>>  then the two "A" would be exactly the same object, independently of what
>>>  which scopes are declared.  If A=="tmpdir", then the test's tmpdir and
>>>  C's tmpdir would be the same directory.  I often don't find this desirable.
>>>  If tmpdir would be an "each" scoped fixture, then C and the test would
>>>  each receive a clean new tmpdir.  If that is a backward-compat issue,
>>>  we could introduce another name for the new "each" scoped tmpdir.
>>>  I usually find myself working around the problem of a "tmpdir"
>>>  shared by multiple different fixtures, though.
>>>
>>>  cheers,
>>>  holger
>>>  _______________________________________________
>>>  Pytest-dev mailing list
>>>  Pytest-dev at python.org
>>>  https://mail.python.org/mailman/listinfo/pytest-dev
>
> _______________________________________________
> Pytest-dev mailing list
> Pytest-dev at python.org
> https://mail.python.org/mailman/listinfo/pytest-dev


More information about the Pytest-dev mailing list