22import os
33import sys
44import threading
5- from typing import Any
5+ from typing import Any , Optional , Type , Union
66import uuid
77
88import mock
1717from tests .profiling .collector .lock_utils import init_linenos
1818
1919
20+ # Type aliases for supported classes
21+ LockClass = Union [Type [threading .Lock ], Type [threading .RLock ]]
22+ CollectorClass = Union [Type [collector_threading .ThreadingLockCollector ], Type [collector_threading .ThreadingRLockCollector ]]
2023
21- # Module-level globals for testing global lock profiling
22- _test_global_lock = None
23- _test_global_bar_instance = None
2424
25+ # Type aliases for supported classes
26+ LockClass = Union [Type [threading .Lock ], Type [threading .RLock ]]
27+ CollectorClass = Union [Type [collector_threading .ThreadingLockCollector ], Type [collector_threading .ThreadingRLockCollector ]]
28+
29+ # Module-level globals for testing global lock profiling
30+ _test_global_lock : Optional [Any ] = None
31+ _test_global_bar_instance : Optional [Any ] = None
2532
26- TESTING_GEVENT = os .getenv ("DD_PROFILE_TEST_GEVENT" , False )
33+ TESTING_GEVENT : Union [ str , bool ] = os .getenv ("DD_PROFILE_TEST_GEVENT" , False )
2734
2835init_linenos (__file__ )
2936
3037
3138# Helper classes for testing lock collector
3239class Foo :
33- def __init__ (self , lock_class : Any ) :
40+ def __init__ (self , lock_class : LockClass ) -> None :
3441 self .foo_lock = lock_class () # !CREATE! foolock
3542
36- def foo (self ):
43+ def foo (self ) -> None :
3744 with self .foo_lock : # !RELEASE! !ACQUIRE! foolock
3845 pass
3946
4047
4148class Bar :
42- def __init__ (self , lock_class : Any ) :
49+ def __init__ (self , lock_class : LockClass ) -> None :
4350 self .foo = Foo (lock_class )
4451
45- def bar (self ):
52+ def bar (self ) -> None :
4653 self .foo .foo ()
4754
4855
49- def test_repr ():
56+ def test_repr () -> None :
5057 test_collector ._test_repr (
5158 collector_threading .ThreadingLockCollector ,
5259 "ThreadingLockCollector(status=<ServiceStatus.STOPPED: 'stopped'>, "
@@ -55,7 +62,7 @@ def test_repr():
5562 )
5663
5764
58- def test_patch ():
65+ def test_patch () -> None :
5966 lock = threading .Lock
6067 collector = collector_threading .ThreadingLockCollector ()
6168 collector .start ()
@@ -70,7 +77,7 @@ def test_patch():
7077@pytest .mark .skipif (not sys .platform .startswith ("linux" ), reason = "only works on linux" )
7178@pytest .mark .subprocess (err = None )
7279# For macOS: Could print 'Error uploading' but okay to ignore since we are checking if native_id is set
73- def test_user_threads_have_native_id ():
80+ def test_user_threads_have_native_id () -> None :
7481 from os import getpid
7582 from threading import Thread
7683 from threading import _MainThread
@@ -97,13 +104,16 @@ def test_user_threads_have_native_id():
97104 for _ in range (10 ):
98105 try :
99106 # The TID should be higher than the PID, but not too high
100- assert 0 < t .native_id - getpid () < 100 , (t .native_id , getpid ())
107+ native_id = getattr (t , 'native_id' , None )
108+ if native_id is not None :
109+ assert 0 < native_id - getpid () < 100 , (native_id , getpid ())
110+ break
111+ else :
112+ raise AttributeError ("native_id not set yet" )
101113 except AttributeError :
102114 # The native_id attribute is set by the thread so we might have to
103115 # wait a bit for it to be set.
104116 sleep (0.1 )
105- else :
106- break
107117 else :
108118 raise AssertionError ("Thread.native_id not set" )
109119
@@ -115,7 +125,7 @@ def test_user_threads_have_native_id():
115125@pytest .mark .subprocess (
116126 env = dict (WRAPT_DISABLE_EXTENSIONS = "True" , DD_PROFILING_FILE_PATH = __file__ ),
117127)
118- def test_wrapt_disable_extensions ():
128+ def test_wrapt_disable_extensions () -> None :
119129 import os
120130 import threading
121131
@@ -182,7 +192,7 @@ def test_wrapt_disable_extensions():
182192@pytest .mark .subprocess (
183193 env = dict (DD_PROFILING_FILE_PATH = __file__ ),
184194)
185- def test_lock_gevent_tasks ():
195+ def test_lock_gevent_tasks () -> None :
186196 from gevent import monkey
187197
188198 monkey .patch_all ()
@@ -208,7 +218,7 @@ def test_lock_gevent_tasks():
208218
209219 init_linenos (os .environ ["DD_PROFILING_FILE_PATH" ])
210220
211- def play_with_lock ():
221+ def play_with_lock () -> None :
212222 lock = threading .Lock () # !CREATE! test_lock_gevent_tasks
213223 lock .acquire () # !ACQUIRE! test_lock_gevent_tasks
214224 lock .release () # !RELEASE! test_lock_gevent_tasks
@@ -262,18 +272,22 @@ def play_with_lock():
262272
263273
264274class BaseThreadingLockCollectorTest :
275+ test_name : str
276+ pprof_prefix : str
277+ output_filename : str
278+
265279 # These should be implemented by child classes
266280 @property
267- def collector_class (self ):
281+ def collector_class (self ) -> CollectorClass :
268282 raise NotImplementedError ("Child classes must implement collector_class" )
269283
270284 @property
271- def lock_class (self ):
285+ def lock_class (self ) -> LockClass :
272286 raise NotImplementedError ("Child classes must implement lock_class" )
273287
274288 # setup_method and teardown_method which will be called before and after
275289 # each test method, respectively, part of pytest api.
276- def setup_method (self , method ) :
290+ def setup_method (self , method : Any ) -> None :
277291 self .test_name = method .__name__
278292 self .pprof_prefix = "/tmp" + os .sep + self .test_name
279293 # The output filename will be /tmp/method_name.<pid>.<counter>.
@@ -286,7 +300,7 @@ def setup_method(self, method):
286300 ddup .config (env = "test" , service = self .test_name , version = "my_version" , output_filename = self .pprof_prefix )
287301 ddup .start ()
288302
289- def teardown_method (self , method ) :
303+ def teardown_method (self , method : Any ) -> None :
290304 # might be unnecessary but this will ensure that the file is removed
291305 # after each successful test, and when a test fails it's easier to
292306 # pinpoint and debug.
@@ -296,15 +310,15 @@ def teardown_method(self, method):
296310 except Exception as e :
297311 print ("Error removing file: {}" .format (e ))
298312
299- def test_wrapper (self ):
313+ def test_wrapper (self ) -> None :
300314 # TODO: change to collector_class
301315 collector = collector_threading .ThreadingLockCollector ()
302316 with collector :
303317
304318 class Foobar (object ):
305319 lock_class = threading .Lock
306320
307- def __init__ (self ):
321+ def __init__ (self ) -> None :
308322 lock = self .lock_class ()
309323 assert lock .acquire ()
310324 lock .release ()
@@ -317,7 +331,7 @@ def __init__(self):
317331 Foobar ()
318332
319333 # Tests
320- def test_lock_events (self ):
334+ def test_lock_events (self ) -> None :
321335 # The first argument is the recorder.Recorder which is used for the
322336 # v1 exporter. We don't need it for the v2 exporter.
323337 with self .collector_class (capture_pct = 100 ):
@@ -349,12 +363,12 @@ def test_lock_events(self):
349363 ],
350364 )
351365
352- def test_lock_acquire_events_class (self ):
366+ def test_lock_acquire_events_class (self ) -> None :
353367 with self .collector_class (capture_pct = 100 ):
354368 lock_class = self .lock_class # Capture for inner class
355369
356370 class Foobar (object ):
357- def lockfunc (self ):
371+ def lockfunc (self ) -> None :
358372 lock = lock_class () # !CREATE! test_lock_acquire_events_class
359373 lock .acquire () # !ACQUIRE! test_lock_acquire_events_class
360374
@@ -377,7 +391,7 @@ def lockfunc(self):
377391 ],
378392 )
379393
380- def test_lock_events_tracer (self , tracer ) :
394+ def test_lock_events_tracer (self , tracer : Any ) -> None :
381395 tracer ._endpoint_call_counter_span_processor .enable ()
382396 resource = str (uuid .uuid4 ())
383397 span_type = ext .SpanTypes .WEB
@@ -438,7 +452,7 @@ def test_lock_events_tracer(self, tracer):
438452 ],
439453 )
440454
441- def test_lock_events_tracer_non_web (self , tracer ) :
455+ def test_lock_events_tracer_non_web (self , tracer : Any ) -> None :
442456 tracer ._endpoint_call_counter_span_processor .enable ()
443457 resource = str (uuid .uuid4 ())
444458 span_type = ext .SpanTypes .SQL
@@ -480,7 +494,7 @@ def test_lock_events_tracer_non_web(self, tracer):
480494 ],
481495 )
482496
483- def test_lock_events_tracer_late_finish (self , tracer ) :
497+ def test_lock_events_tracer_late_finish (self , tracer : Any ) -> None :
484498 tracer ._endpoint_call_counter_span_processor .enable ()
485499 resource = str (uuid .uuid4 ())
486500 span_type = ext .SpanTypes .WEB
@@ -535,7 +549,7 @@ def test_lock_events_tracer_late_finish(self, tracer):
535549 ],
536550 )
537551
538- def test_resource_not_collected (self , tracer ) :
552+ def test_resource_not_collected (self , tracer : Any ) -> None :
539553 tracer ._endpoint_call_counter_span_processor .enable ()
540554 resource = str (uuid .uuid4 ())
541555 span_type = ext .SpanTypes .WEB
@@ -596,7 +610,7 @@ def test_resource_not_collected(self, tracer):
596610 ],
597611 )
598612
599- def test_lock_enter_exit_events (self ):
613+ def test_lock_enter_exit_events (self ) -> None :
600614 with self .collector_class (capture_pct = 100 ):
601615 th_lock = self .lock_class () # !CREATE! test_lock_enter_exit_events
602616 with th_lock : # !ACQUIRE! !RELEASE! test_lock_enter_exit_events
@@ -632,7 +646,7 @@ def test_lock_enter_exit_events(self):
632646 "inspect_dir_enabled" ,
633647 [True , False ],
634648 )
635- def test_class_member_lock (self , inspect_dir_enabled ) :
649+ def test_class_member_lock (self , inspect_dir_enabled : bool ) -> None :
636650 with mock .patch ("ddtrace.settings.profiling.config.lock.name_inspect_dir" , inspect_dir_enabled ):
637651 expected_lock_name = "foo_lock" if inspect_dir_enabled else None
638652
@@ -671,12 +685,12 @@ def test_class_member_lock(self, inspect_dir_enabled):
671685 ],
672686 )
673687
674- def test_private_lock (self ):
688+ def test_private_lock (self ) -> None :
675689 class Foo :
676- def __init__ (self , lock_class : Any ) :
690+ def __init__ (self , lock_class : LockClass ) -> None :
677691 self .__lock = lock_class () # !CREATE! test_private_lock
678692
679- def foo (self ):
693+ def foo (self ) -> None :
680694 with self .__lock : # !RELEASE! !ACQUIRE! test_private_lock
681695 pass
682696
@@ -710,12 +724,12 @@ def foo(self):
710724 ],
711725 )
712726
713- def test_inner_lock (self ):
727+ def test_inner_lock (self ) -> None :
714728 class Bar :
715- def __init__ (self , lock_class : Any ) :
729+ def __init__ (self , lock_class : LockClass ) -> None :
716730 self .foo = Foo (lock_class )
717731
718- def bar (self ):
732+ def bar (self ) -> None :
719733 with self .foo .foo_lock : # !RELEASE! !ACQUIRE! test_inner_lock
720734 pass
721735
@@ -750,7 +764,7 @@ def bar(self):
750764 ],
751765 )
752766
753- def test_anonymous_lock (self ):
767+ def test_anonymous_lock (self ) -> None :
754768 with self .collector_class (capture_pct = 100 ):
755769 with self .lock_class (): # !CREATE! !ACQUIRE! !RELEASE! test_anonymous_lock
756770 pass
@@ -777,23 +791,24 @@ def test_anonymous_lock(self):
777791 ],
778792 )
779793
780- def test_global_locks (self ):
794+ def test_global_locks (self ) -> None :
781795 global _test_global_lock , _test_global_bar_instance
782796
783797 with self .collector_class (capture_pct = 100 ):
784798 # Create true module-level globals
785799 _test_global_lock = self .lock_class () # !CREATE! _test_global_lock
786800
787801 class TestBar :
788- def __init__ (self , lock_class : Any ) :
802+ def __init__ (self , lock_class : LockClass ) -> None :
789803 self .bar_lock = lock_class () # !CREATE! bar_lock
790804
791- def bar (self ):
805+ def bar (self ) -> None :
792806 with self .bar_lock : # !ACQUIRE! !RELEASE! bar_lock
793807 pass
794808
795- def foo ():
809+ def foo () -> None :
796810 global _test_global_lock
811+ assert _test_global_lock is not None
797812 with _test_global_lock : # !ACQUIRE! !RELEASE! _test_global_lock
798813 pass
799814
@@ -844,7 +859,7 @@ def foo():
844859 ],
845860 )
846861
847- def test_upload_resets_profile (self ):
862+ def test_upload_resets_profile (self ) -> None :
848863 # This test checks that the profile is cleared after each upload() call
849864 # It is added in test_threading.py as LockCollector can easily be
850865 # configured to be deterministic with capture_pct=100.
@@ -886,21 +901,21 @@ class TestThreadingLockCollector(BaseThreadingLockCollectorTest):
886901 """Test threading.Lock profiling"""
887902
888903 @property
889- def collector_class (self ):
904+ def collector_class (self ) -> Type [ collector_threading . ThreadingLockCollector ] :
890905 return collector_threading .ThreadingLockCollector
891906
892907 @property
893- def lock_class (self ):
908+ def lock_class (self ) -> Type [ threading . Lock ] :
894909 return threading .Lock
895910
896911
897912class TestThreadingRLockCollector (BaseThreadingLockCollectorTest ):
898913 """Test threading.RLock profiling"""
899914
900915 @property
901- def collector_class (self ):
916+ def collector_class (self ) -> Type [ collector_threading . ThreadingRLockCollector ] :
902917 return collector_threading .ThreadingRLockCollector
903918
904919 @property
905- def lock_class (self ):
920+ def lock_class (self ) -> Type [ threading . RLock ] :
906921 return threading .RLock
0 commit comments