92 lines
2.8 KiB
Python
92 lines
2.8 KiB
Python
|
"""
|
||
|
Scope definition and related utilities.
|
||
|
|
||
|
Those are defined here, instead of in the 'fixtures' module because
|
||
|
their use is spread across many other pytest modules, and centralizing it in 'fixtures'
|
||
|
would cause circular references.
|
||
|
|
||
|
Also this makes the module light to import, as it should.
|
||
|
"""
|
||
|
from enum import Enum
|
||
|
from functools import total_ordering
|
||
|
from typing import Optional
|
||
|
from typing import TYPE_CHECKING
|
||
|
|
||
|
if TYPE_CHECKING:
|
||
|
from typing_extensions import Literal
|
||
|
|
||
|
_ScopeName = Literal["session", "package", "module", "class", "function"]
|
||
|
|
||
|
|
||
|
@total_ordering
|
||
|
class Scope(Enum):
|
||
|
"""
|
||
|
Represents one of the possible fixture scopes in pytest.
|
||
|
|
||
|
Scopes are ordered from lower to higher, that is:
|
||
|
|
||
|
->>> higher ->>>
|
||
|
|
||
|
Function < Class < Module < Package < Session
|
||
|
|
||
|
<<<- lower <<<-
|
||
|
"""
|
||
|
|
||
|
# Scopes need to be listed from lower to higher.
|
||
|
Function: "_ScopeName" = "function"
|
||
|
Class: "_ScopeName" = "class"
|
||
|
Module: "_ScopeName" = "module"
|
||
|
Package: "_ScopeName" = "package"
|
||
|
Session: "_ScopeName" = "session"
|
||
|
|
||
|
def next_lower(self) -> "Scope":
|
||
|
"""Return the next lower scope."""
|
||
|
index = _SCOPE_INDICES[self]
|
||
|
if index == 0:
|
||
|
raise ValueError(f"{self} is the lower-most scope")
|
||
|
return _ALL_SCOPES[index - 1]
|
||
|
|
||
|
def next_higher(self) -> "Scope":
|
||
|
"""Return the next higher scope."""
|
||
|
index = _SCOPE_INDICES[self]
|
||
|
if index == len(_SCOPE_INDICES) - 1:
|
||
|
raise ValueError(f"{self} is the upper-most scope")
|
||
|
return _ALL_SCOPES[index + 1]
|
||
|
|
||
|
def __lt__(self, other: "Scope") -> bool:
|
||
|
self_index = _SCOPE_INDICES[self]
|
||
|
other_index = _SCOPE_INDICES[other]
|
||
|
return self_index < other_index
|
||
|
|
||
|
@classmethod
|
||
|
def from_user(
|
||
|
cls, scope_name: "_ScopeName", descr: str, where: Optional[str] = None
|
||
|
) -> "Scope":
|
||
|
"""
|
||
|
Given a scope name from the user, return the equivalent Scope enum. Should be used
|
||
|
whenever we want to convert a user provided scope name to its enum object.
|
||
|
|
||
|
If the scope name is invalid, construct a user friendly message and call pytest.fail.
|
||
|
"""
|
||
|
from _pytest.outcomes import fail
|
||
|
|
||
|
try:
|
||
|
# Holding this reference is necessary for mypy at the moment.
|
||
|
scope = Scope(scope_name)
|
||
|
except ValueError:
|
||
|
fail(
|
||
|
"{} {}got an unexpected scope value '{}'".format(
|
||
|
descr, f"from {where} " if where else "", scope_name
|
||
|
),
|
||
|
pytrace=False,
|
||
|
)
|
||
|
return scope
|
||
|
|
||
|
|
||
|
_ALL_SCOPES = list(Scope)
|
||
|
_SCOPE_INDICES = {scope: index for index, scope in enumerate(_ALL_SCOPES)}
|
||
|
|
||
|
|
||
|
# Ordered list of scopes which can contain many tests (in practice all except Function).
|
||
|
HIGH_SCOPES = [x for x in Scope if x is not Scope.Function]
|