Export types of builtin fixture for type annotations

In order to allow users to type annotate fixtures they request, the
types need to be imported from the `pytest` namespace. They are/were
always available to import from the `_pytest` namespace, but that is
not guaranteed to be stable.

These types are only exported for the purpose of typing. Specifically,
the following are *not* public:

- Construction (`__init__`)
- Subclassing
- staticmethods and classmethods

We try to combat them being used anyway by:

- Marking the classes as `@final` when possible (already done).

- Not documenting private stuff in the API Reference.

- Using `_`-prefixed names or marking as `:meta private:` for private
  stuff.

- Adding a keyword-only `_ispytest=False` to private constructors,
  warning if False, and changing pytest itself to pass True. In the
  future it will (hopefully) become a hard error.

Hopefully that will be enough.
This commit is contained in:
Ran Benita
2020-09-27 22:20:31 +03:00
parent b050578882
commit f1e6fdcddb
19 changed files with 292 additions and 126 deletions

View File

@@ -621,7 +621,7 @@ class TestRequestBasic:
def test_func(something): pass
"""
)
req = fixtures.FixtureRequest(item)
req = fixtures.FixtureRequest(item, _ispytest=True)
assert req.function == item.obj
assert req.keywords == item.keywords
assert hasattr(req.module, "test_func")
@@ -661,7 +661,9 @@ class TestRequestBasic:
)
(item1,) = testdir.genitems([modcol])
assert item1.name == "test_method"
arg2fixturedefs = fixtures.FixtureRequest(item1)._arg2fixturedefs
arg2fixturedefs = fixtures.FixtureRequest(
item1, _ispytest=True
)._arg2fixturedefs
assert len(arg2fixturedefs) == 1
assert arg2fixturedefs["something"][0].argname == "something"
@@ -910,7 +912,7 @@ class TestRequestBasic:
def test_request_getmodulepath(self, testdir):
modcol = testdir.getmodulecol("def test_somefunc(): pass")
(item,) = testdir.genitems([modcol])
req = fixtures.FixtureRequest(item)
req = fixtures.FixtureRequest(item, _ispytest=True)
assert req.fspath == modcol.fspath
def test_request_fixturenames(self, testdir):
@@ -1052,7 +1054,7 @@ class TestRequestMarking:
pass
"""
)
req1 = fixtures.FixtureRequest(item1)
req1 = fixtures.FixtureRequest(item1, _ispytest=True)
assert "xfail" not in item1.keywords
req1.applymarker(pytest.mark.xfail)
assert "xfail" in item1.keywords
@@ -3882,7 +3884,7 @@ class TestScopeOrdering:
"""
)
items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0])
request = FixtureRequest(items[0], _ispytest=True)
assert request.fixturenames == "m1 f1".split()
def test_func_closure_with_native_fixtures(self, testdir, monkeypatch) -> None:
@@ -3928,7 +3930,7 @@ class TestScopeOrdering:
"""
)
items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0])
request = FixtureRequest(items[0], _ispytest=True)
# order of fixtures based on their scope and position in the parameter list
assert (
request.fixturenames == "s1 my_tmpdir_factory p1 m1 f1 f2 my_tmpdir".split()
@@ -3954,7 +3956,7 @@ class TestScopeOrdering:
"""
)
items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0])
request = FixtureRequest(items[0], _ispytest=True)
assert request.fixturenames == "m1 f1".split()
def test_func_closure_scopes_reordered(self, testdir):
@@ -3987,7 +3989,7 @@ class TestScopeOrdering:
"""
)
items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0])
request = FixtureRequest(items[0], _ispytest=True)
assert request.fixturenames == "s1 m1 c1 f2 f1".split()
def test_func_closure_same_scope_closer_root_first(self, testdir):
@@ -4027,7 +4029,7 @@ class TestScopeOrdering:
}
)
items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0])
request = FixtureRequest(items[0], _ispytest=True)
assert request.fixturenames == "p_sub m_conf m_sub m_test f1".split()
def test_func_closure_all_scopes_complex(self, testdir):
@@ -4071,7 +4073,7 @@ class TestScopeOrdering:
"""
)
items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0])
request = FixtureRequest(items[0], _ispytest=True)
assert request.fixturenames == "s1 p1 m1 m2 c1 f2 f1".split()
def test_multiple_packages(self, testdir):