pytest-fastcollect 0.5.2__cp312-cp312-musllinux_1_2_i686.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,204 @@
1
+ """Test filtering logic for selective import optimization."""
2
+
3
+ import re
4
+ from typing import Dict, List, Set, Any, Optional
5
+
6
+
7
+ class TestFilter:
8
+ """Filter tests based on pytest's -k and -m options."""
9
+
10
+ def __init__(self, keyword_expr: Optional[str] = None, marker_expr: Optional[str] = None):
11
+ """Initialize filter with keyword and marker expressions.
12
+
13
+ Args:
14
+ keyword_expr: The -k expression (e.g., "test_foo and not slow")
15
+ marker_expr: The -m expression (e.g., "smoke and not slow")
16
+ """
17
+ self.keyword_expr = keyword_expr
18
+ self.marker_expr = marker_expr
19
+
20
+ def matches(self, test_item: Dict[str, Any]) -> bool:
21
+ """Check if a test item matches the filter criteria.
22
+
23
+ Args:
24
+ test_item: Test item dict with keys: name, markers, class, etc.
25
+
26
+ Returns:
27
+ True if the test matches the filter
28
+ """
29
+ # If no filters, everything matches
30
+ if not self.keyword_expr and not self.marker_expr:
31
+ return True
32
+
33
+ # Check keyword filter
34
+ if self.keyword_expr:
35
+ if not self._matches_keyword(test_item):
36
+ return False
37
+
38
+ # Check marker filter
39
+ if self.marker_expr:
40
+ if not self._matches_marker(test_item):
41
+ return False
42
+
43
+ return True
44
+
45
+ def _matches_keyword(self, test_item: Dict[str, Any]) -> bool:
46
+ """Check if test matches keyword expression (-k).
47
+
48
+ The keyword expression can match against:
49
+ - Test function name
50
+ - Test class name
51
+ - Test file path
52
+
53
+ Supports: 'and', 'or', 'not', and parentheses.
54
+ """
55
+ # Build a searchable string from the test item
56
+ parts = [test_item['name']]
57
+
58
+ if 'class' in test_item and test_item['class'] is not None:
59
+ parts.append(test_item['class'])
60
+
61
+ # Add file path components
62
+ file_path = test_item.get('file_path', '')
63
+ if file_path:
64
+ # Extract file name without extension
65
+ import os
66
+ filename = os.path.splitext(os.path.basename(file_path))[0]
67
+ parts.append(filename)
68
+
69
+ search_text = ' '.join(parts).lower()
70
+
71
+ # Evaluate the keyword expression
72
+ return self._evaluate_expression(self.keyword_expr, search_text)
73
+
74
+ def _matches_marker(self, test_item: Dict[str, Any]) -> bool:
75
+ """Check if test matches marker expression (-m).
76
+
77
+ Supports: 'and', 'or', 'not', and parentheses.
78
+ """
79
+ markers = test_item.get('markers', [])
80
+ marker_set = set(m.lower() for m in markers)
81
+
82
+ # Evaluate the marker expression
83
+ return self._evaluate_marker_expression(self.marker_expr, marker_set)
84
+
85
+ def _evaluate_expression(self, expr: str, search_text: str) -> bool:
86
+ """Evaluate a keyword expression against search text.
87
+
88
+ Simple implementation that handles common cases:
89
+ - Single keywords: "test_foo"
90
+ - AND: "test_foo and test_bar"
91
+ - OR: "test_foo or test_bar"
92
+ - NOT: "not slow"
93
+ - Combinations: "test_foo and not slow"
94
+ """
95
+ expr = expr.lower().strip()
96
+
97
+ # Handle simple case: single keyword
98
+ if ' and ' not in expr and ' or ' not in expr and not expr.startswith('not '):
99
+ return expr in search_text
100
+
101
+ # Handle NOT
102
+ if expr.startswith('not '):
103
+ keyword = expr[4:].strip()
104
+ return keyword not in search_text
105
+
106
+ # Handle AND
107
+ if ' and ' in expr:
108
+ parts = expr.split(' and ')
109
+ return all(self._evaluate_expression(p.strip(), search_text) for p in parts)
110
+
111
+ # Handle OR
112
+ if ' or ' in expr:
113
+ parts = expr.split(' or ')
114
+ return any(self._evaluate_expression(p.strip(), search_text) for p in parts)
115
+
116
+ return expr in search_text
117
+
118
+ def _evaluate_marker_expression(self, expr: str, markers: Set[str]) -> bool:
119
+ """Evaluate a marker expression against a set of markers.
120
+
121
+ Args:
122
+ expr: Marker expression like "smoke and not slow"
123
+ markers: Set of marker names on this test
124
+
125
+ Returns:
126
+ True if the expression matches
127
+ """
128
+ expr = expr.lower().strip()
129
+
130
+ # Handle simple case: single marker
131
+ if ' and ' not in expr and ' or ' not in expr and not expr.startswith('not '):
132
+ return expr in markers
133
+
134
+ # Handle NOT
135
+ if expr.startswith('not '):
136
+ marker = expr[4:].strip()
137
+ return marker not in markers
138
+
139
+ # Handle AND
140
+ if ' and ' in expr:
141
+ parts = expr.split(' and ')
142
+ return all(self._evaluate_marker_expression(p.strip(), markers) for p in parts)
143
+
144
+ # Handle OR
145
+ if ' or ' in expr:
146
+ parts = expr.split(' or ')
147
+ return any(self._evaluate_marker_expression(p.strip(), markers) for p in parts)
148
+
149
+ return expr in markers
150
+
151
+
152
+ def filter_collected_data(
153
+ collected_data: Dict[str, List[Dict[str, Any]]],
154
+ keyword_expr: Optional[str] = None,
155
+ marker_expr: Optional[str] = None
156
+ ) -> Dict[str, List[Dict[str, Any]]]:
157
+ """Filter collected test data based on keyword and marker expressions.
158
+
159
+ Args:
160
+ collected_data: Dict mapping file paths to lists of test items
161
+ keyword_expr: Optional -k expression
162
+ marker_expr: Optional -m expression
163
+
164
+ Returns:
165
+ Filtered dict with only matching tests and their files
166
+ """
167
+ # If no filters, return everything
168
+ if not keyword_expr and not marker_expr:
169
+ return collected_data
170
+
171
+ test_filter = TestFilter(keyword_expr, marker_expr)
172
+ filtered_data = {}
173
+
174
+ for file_path, test_items in collected_data.items():
175
+ # Filter test items in this file
176
+ matching_items = [item for item in test_items if test_filter.matches(item)]
177
+
178
+ # Only include file if it has matching tests
179
+ if matching_items:
180
+ filtered_data[file_path] = matching_items
181
+
182
+ return filtered_data
183
+
184
+
185
+ def get_files_with_matching_tests(
186
+ collected_data: Dict[str, List[Dict[str, Any]]],
187
+ keyword_expr: Optional[str] = None,
188
+ marker_expr: Optional[str] = None
189
+ ) -> Set[str]:
190
+ """Get the set of file paths that contain matching tests.
191
+
192
+ This is used to populate _test_files_cache with only files
193
+ that have tests matching the filter criteria.
194
+
195
+ Args:
196
+ collected_data: Dict mapping file paths to lists of test items
197
+ keyword_expr: Optional -k expression
198
+ marker_expr: Optional -m expression
199
+
200
+ Returns:
201
+ Set of file paths containing matching tests
202
+ """
203
+ filtered_data = filter_collected_data(collected_data, keyword_expr, marker_expr)
204
+ return set(filtered_data.keys())