1111
1212from ddtrace import ext
1313from ddtrace .internal .datadog .profiling import ddup
14+ from ddtrace .profiling .collector .asyncio import AsyncioBoundedSemaphoreCollector
1415from ddtrace .profiling .collector .asyncio import AsyncioLockCollector
1516from ddtrace .profiling .collector .asyncio import AsyncioSemaphoreCollector
1617from tests .profiling .collector import pprof_utils
2324
2425PY_311_OR_ABOVE = sys .version_info [:2 ] >= (3 , 11 )
2526
26- # Type aliases for collector and lock types
27- CollectorType = Union [
27+ # Type aliases for supported classes
28+ LockTypeClass = Union [Type [asyncio .Lock ], Type [asyncio .Semaphore ], Type [asyncio .BoundedSemaphore ]]
29+ LockTypeInst = Union [asyncio .Lock , asyncio .Semaphore , asyncio .BoundedSemaphore ]
30+
31+ CollectorTypeClass = Union [
2832 Type [AsyncioLockCollector ],
2933 Type [AsyncioSemaphoreCollector ],
34+ Type [AsyncioBoundedSemaphoreCollector ],
3035]
31- LockType = Union [Type [ asyncio . Lock ], Type [ asyncio . Semaphore ] ]
36+ CollectorTypeInst = Union [AsyncioLockCollector , AsyncioSemaphoreCollector , AsyncioBoundedSemaphoreCollector ]
3237
3338
3439@pytest .mark .parametrize (
4247 AsyncioSemaphoreCollector ,
4348 "AsyncioSemaphoreCollector(status=<ServiceStatus.STOPPED: 'stopped'>, capture_pct=1.0, nframes=64, tracer=None)" , # noqa: E501
4449 ),
50+ (
51+ AsyncioBoundedSemaphoreCollector ,
52+ "AsyncioBoundedSemaphoreCollector(status=<ServiceStatus.STOPPED: 'stopped'>, capture_pct=1.0, nframes=64, tracer=None)" ,
53+ ),
4554 ],
4655)
47- def test_collector_repr (collector_class : CollectorType , expected_repr : str ) -> None :
56+ def test_collector_repr (collector_class : CollectorTypeClass , expected_repr : str ) -> None :
4857 test_collector ._test_repr (collector_class , expected_repr )
4958
5059
@@ -59,19 +68,19 @@ class BaseAsyncioLockCollectorTest:
5968 """
6069
6170 @property
62- def collector_class (self ) -> CollectorType :
71+ def collector_class (self ) -> CollectorTypeClass :
6372 raise NotImplementedError ("Child classes must implement collector_class" )
6473
6574 @property
66- def lock_class (self ) -> LockType :
75+ def lock_class (self ) -> LockTypeClass :
6776 raise NotImplementedError ("Child classes must implement lock_class" )
6877
6978 @property
7079 def lock_init_args (self ) -> tuple :
7180 """Arguments to pass to lock constructor. Override for Semaphore-like locks."""
7281 return ()
7382
74- def create_lock (self ) -> Union [ asyncio . Lock , asyncio . Semaphore ] :
83+ def create_lock (self ) -> LockTypeInst :
7584 """Create a lock instance with the appropriate arguments."""
7685 return self .lock_class (* self .lock_init_args )
7786
@@ -242,3 +251,33 @@ def lock_class(self):
242251 @property
243252 def lock_init_args (self ):
244253 return (2 ,) # Initial semaphore value
254+
255+
256+ class TestAsyncioBoundedSemaphoreCollector (BaseAsyncioLockCollectorTest ):
257+ """Test asyncio.BoundedSemaphore profiling."""
258+
259+ @property
260+ def collector_class (self ):
261+ return AsyncioBoundedSemaphoreCollector
262+
263+ @property
264+ def lock_class (self ):
265+ return asyncio .BoundedSemaphore
266+
267+ @property
268+ def lock_init_args (self ):
269+ return (2 ,) # Initial semaphore value
270+
271+ async def test_bounded_behavior_preserved (self ):
272+ """Test that profiling wrapper preserves BoundedSemaphore's bounded behavior.
273+
274+ This verifies the wrapper doesn't interfere with BoundedSemaphore's unique characteristic:
275+ raising ValueError when releasing beyond the initial value.
276+ """
277+ with self .collector_class (capture_pct = 100 ):
278+ bs = asyncio .BoundedSemaphore (1 )
279+ await bs .acquire ()
280+ bs .release ()
281+ # BoundedSemaphore should raise ValueError when releasing more than initial value
282+ with pytest .raises (ValueError , match = "BoundedSemaphore released too many times" ):
283+ bs .release ()
0 commit comments