|
|
""" |
|
|
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 __future__ import annotations |
|
|
|
|
|
from enum import Enum |
|
|
from functools import total_ordering |
|
|
from typing 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 <<<- |
|
|
""" |
|
|
|
|
|
|
|
|
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: str | None = 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: |
|
|
|
|
|
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)} |
|
|
|
|
|
|
|
|
|
|
|
HIGH_SCOPES = [x for x in Scope if x is not Scope.Function] |
|
|
|