@@ -16,6 +16,13 @@ def is_hypothesis_test(fn):
1616 return False
1717
1818
19+ try :
20+ from hypothesis import __version_info__ as hypothesis_version
21+ except ImportError :
22+ hypothesis_version = (0 , 0 , 0 )
23+
24+ HYPOTHESIS_THREADSAFE_VERSION = (6 , 136 , 3 )
25+
1926WARNINGS_IS_THREADSAFE = bool (
2027 getattr (sys .flags , "context_aware_warnings" , 0 )
2128 and getattr (sys .flags , "thread_inherit_context" , 0 )
@@ -47,7 +54,9 @@ def construct_base_blocklist(unsafe_warnings, unsafe_ctypes):
4754
4855
4956class ThreadUnsafeNodeVisitor (ast .NodeVisitor ):
50- def __init__ (self , fn , skip_set , unsafe_warnings , unsafe_ctypes , level = 0 ):
57+ def __init__ (
58+ self , fn , skip_set , unsafe_warnings , unsafe_ctypes , unsafe_hypothesis , level = 0
59+ ):
5160 self .thread_unsafe = False
5261 self .thread_unsafe_reason = None
5362 blocklist = construct_base_blocklist (unsafe_warnings , unsafe_ctypes )
@@ -64,6 +73,7 @@ def __init__(self, fn, skip_set, unsafe_warnings, unsafe_ctypes, level=0):
6473 self .skip_set = skip_set
6574 self .unsafe_warnings = unsafe_warnings
6675 self .unsafe_ctypes = unsafe_ctypes
76+ self .unsafe_hypothesis = unsafe_hypothesis
6777 self .level = level
6878 self .modules_aliases = {}
6979 self .func_aliases = {}
@@ -141,6 +151,7 @@ def _get_child_fn(mod, node):
141151 self .skip_set ,
142152 self .unsafe_warnings ,
143153 self .unsafe_ctypes ,
154+ self .unsafe_hypothesis ,
144155 self .level + 1 ,
145156 )
146157 )
@@ -192,6 +203,7 @@ def _recursive_analyze_name(self, node):
192203 self .skip_set ,
193204 self .unsafe_warnings ,
194205 self .unsafe_ctypes ,
206+ self .unsafe_hypothesis ,
195207 self .level + 1 ,
196208 )
197209 )
@@ -236,10 +248,22 @@ def visit(self, node):
236248
237249
238250def _identify_thread_unsafe_nodes (
239- fn , skip_set , unsafe_warnings , unsafe_ctypes , level = 0
251+ fn , skip_set , unsafe_warnings , unsafe_ctypes , unsafe_hypothesis , level = 0
240252):
241253 if is_hypothesis_test (fn ):
242- return True , "uses hypothesis"
254+ if hypothesis_version < HYPOTHESIS_THREADSAFE_VERSION :
255+ return (
256+ True ,
257+ f"uses hypothesis v{ '.' .join (map (str , hypothesis_version ))} , which "
258+ "is before the first thread-safe version "
259+ f"(v{ '.' .join (map (str , HYPOTHESIS_THREADSAFE_VERSION ))} )" ,
260+ )
261+ if unsafe_hypothesis :
262+ return (
263+ True ,
264+ "uses Hypothesis, and pytest-run-parallel was run with "
265+ "--mark-hypothesis-as-unsafe" ,
266+ )
243267
244268 try :
245269 src = inspect .getsource (fn )
@@ -252,7 +276,7 @@ def _identify_thread_unsafe_nodes(
252276 return False , None
253277
254278 visitor = ThreadUnsafeNodeVisitor (
255- fn , skip_set , unsafe_warnings , unsafe_ctypes , level = level
279+ fn , skip_set , unsafe_warnings , unsafe_ctypes , unsafe_hypothesis , level = level
256280 )
257281 visitor .visit (tree )
258282 return visitor .thread_unsafe , visitor .thread_unsafe_reason
@@ -261,15 +285,11 @@ def _identify_thread_unsafe_nodes(
261285cached_thread_unsafe_identify = functools .lru_cache (_identify_thread_unsafe_nodes )
262286
263287
264- def identify_thread_unsafe_nodes (fn , skip_set , unsafe_warnings , unsafe_ctypes , level = 0 ):
288+ def identify_thread_unsafe_nodes (* args , ** kwargs ):
265289 try :
266- return cached_thread_unsafe_identify (
267- fn , skip_set , unsafe_warnings , unsafe_ctypes , level = level
268- )
290+ return cached_thread_unsafe_identify (* args , ** kwargs )
269291 except TypeError :
270- return _identify_thread_unsafe_nodes (
271- fn , skip_set , unsafe_warnings , unsafe_ctypes , level = level
272- )
292+ return _identify_thread_unsafe_nodes (* args , ** kwargs )
273293
274294
275295def construct_thread_unsafe_fixtures (config ):
0 commit comments