Coverage for human_requests/pytest_plugin/_config.py: 85%

67 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-05-28 00:39 +0000

1from __future__ import annotations 

2 

3import importlib 

4import inspect 

5from typing import Any 

6 

7import pytest 

8 

9from ._constants import ( 

10 AUTOTEST_INI_KEY, 

11 AUTOTEST_TRACE_LIMIT_INI_KEY, 

12 AUTOTEST_TRUNCATION_CONTEXT_LINES_INI_KEY, 

13 AUTOTEST_TYPECHECK_INI_KEY, 

14 VALID_TYPECHECK_MODES, 

15) 

16 

17 

18def register_ini_options(parser: pytest.Parser) -> None: 

19 parser.addini( 

20 AUTOTEST_INI_KEY, 

21 default="", 

22 help="Dotted import path to the root API class, e.g. package_name.StartClass", 

23 ) 

24 parser.addini( 

25 AUTOTEST_TYPECHECK_INI_KEY, 

26 default="off", 

27 help="Autotest params type checking mode: off, warn, strict.", 

28 ) 

29 parser.addini( 

30 AUTOTEST_TRACE_LIMIT_INI_KEY, 

31 default="3", 

32 help=( 

33 "Maximum number of Source blocks shown in crash reports. Use 0 to " 

34 "hide the source chain." 

35 ), 

36 ) 

37 parser.addini( 

38 AUTOTEST_TRUNCATION_CONTEXT_LINES_INI_KEY, 

39 default="3", 

40 help=( 

41 "How many source context lines to keep on each side of a split crash " 

42 "report excerpt before the … skipped N lines … marker." 

43 ), 

44 ) 

45 

46 

47def get_start_class_path(config: pytest.Config) -> str: 

48 return str(config.getini(AUTOTEST_INI_KEY)).strip() 

49 

50 

51def get_typecheck_mode(config: pytest.Config) -> str: 

52 raw = str(config.getini(AUTOTEST_TYPECHECK_INI_KEY)).strip().lower() 

53 if not raw: 

54 return "off" 

55 if raw in VALID_TYPECHECK_MODES: 

56 return raw 

57 

58 expected = ", ".join(sorted(VALID_TYPECHECK_MODES)) 

59 raise pytest.UsageError( 

60 f"Invalid {AUTOTEST_TYPECHECK_INI_KEY} value {raw!r}. Expected one of: {expected}." 

61 ) 

62 

63 

64def get_trace_limit(config: pytest.Config) -> int: 

65 raw = str(config.getini(AUTOTEST_TRACE_LIMIT_INI_KEY)).strip() 

66 if not raw: 

67 return 3 

68 

69 try: 

70 value = int(raw) 

71 except ValueError as error: 

72 raise pytest.UsageError( 

73 f"Invalid {AUTOTEST_TRACE_LIMIT_INI_KEY} value {raw!r}. " 

74 "Expected a non-negative integer." 

75 ) from error 

76 

77 if value < 0: 

78 raise pytest.UsageError( 

79 f"Invalid {AUTOTEST_TRACE_LIMIT_INI_KEY} value {raw!r}. " 

80 "Expected a non-negative integer." 

81 ) 

82 return value 

83 

84 

85def get_truncation_context_lines(config: pytest.Config) -> int: 

86 raw = str(config.getini(AUTOTEST_TRUNCATION_CONTEXT_LINES_INI_KEY)).strip() 

87 if not raw: 

88 return 3 

89 

90 try: 

91 value = int(raw) 

92 except ValueError as error: 

93 raise pytest.UsageError( 

94 f"Invalid {AUTOTEST_TRUNCATION_CONTEXT_LINES_INI_KEY} value {raw!r}. " 

95 "Expected a non-negative integer." 

96 ) from error 

97 

98 if value < 0: 

99 raise pytest.UsageError( 

100 f"Invalid {AUTOTEST_TRUNCATION_CONTEXT_LINES_INI_KEY} value {raw!r}. " 

101 "Expected a non-negative integer." 

102 ) 

103 return value 

104 

105 

106def resolve_runtime_dependencies(request: pytest.FixtureRequest) -> tuple[object, Any]: 

107 start_class_path = get_start_class_path(request.config) 

108 if not start_class_path: 

109 raise pytest.UsageError( 

110 f"{AUTOTEST_INI_KEY} must be configured when autotest plugin is enabled." 

111 ) 

112 

113 start_class = import_start_class(start_class_path) 

114 

115 api = request.getfixturevalue("api") 

116 if not isinstance(api, start_class): 

117 expected = f"{start_class.__module__}.{start_class.__qualname__}" 

118 actual = f"{type(api).__module__}.{type(api).__qualname__}" 

119 raise TypeError(f"`api` fixture must return an instance of {expected}. " f"Got {actual}.") 

120 

121 schemashot = request.getfixturevalue("schemashot") 

122 return api, schemashot 

123 

124 

125def import_start_class(dotted_path: str) -> type[Any]: 

126 module_name, separator, class_name = dotted_path.rpartition(".") 

127 if not separator or not module_name or not class_name: 

128 raise pytest.UsageError( 

129 f"Invalid {AUTOTEST_INI_KEY} value {dotted_path!r}. Expected format: module.StartClass" 

130 ) 

131 

132 try: 

133 module = importlib.import_module(module_name) 

134 except Exception as error: # pragma: no cover - importlib already provides full details 

135 raise pytest.UsageError( 

136 f"Cannot import module {module_name!r} from {AUTOTEST_INI_KEY}={dotted_path!r}." 

137 ) from error 

138 

139 if not hasattr(module, class_name): 

140 raise pytest.UsageError( 

141 f"Class {class_name!r} was not found in module {module_name!r} " 

142 f"from {AUTOTEST_INI_KEY}={dotted_path!r}." 

143 ) 

144 

145 start_class = getattr(module, class_name) 

146 if not inspect.isclass(start_class): 

147 raise pytest.UsageError(f"{AUTOTEST_INI_KEY}={dotted_path!r} must point to a class.") 

148 

149 return start_class