Skip to content

Commit 0d4f740

Browse files
add type annotations
1 parent e8a6830 commit 0d4f740

File tree

1 file changed

+64
-49
lines changed

1 file changed

+64
-49
lines changed

tests/profiling_v2/collector/test_threading.py

Lines changed: 64 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import sys
44
import threading
5-
from typing import Any
5+
from typing import Any, Optional, Type, Union
66
import uuid
77

88
import mock
@@ -17,36 +17,43 @@
1717
from 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

2835
init_linenos(__file__)
2936

3037

3138
# Helper classes for testing lock collector
3239
class 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

4148
class 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

264274
class 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

897912
class 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

Comments
 (0)