11# -*- encoding: utf-8 -*-
22from functools import partial
33import sys
4- import types
4+ from types import FunctionType
55from types import ModuleType
66import typing
77
2929ASYNCIO_IMPORTED : bool = False
3030
3131
32+ # AIDEV-NOTE: Structural Protocols for introspected asyncio/uvloop classes. getattr() returns Any, so
33+ # these annotations document the contract we actually depend on rather than accepting any `type`.
34+ class _HasSetEventLoop (typing .Protocol ):
35+ set_event_loop : typing .Callable [..., typing .Any ]
36+
37+
38+ class _HasCreateTask (typing .Protocol ):
39+ create_task : typing .Callable [..., typing .Any ]
40+
41+
3242def current_task () -> typing .Optional ["asyncio.Task[typing.Any]" ]:
3343 return None
3444
@@ -136,7 +146,7 @@ def _get_running_loop() -> typing.Optional["aio.AbstractEventLoop"]:
136146 # replaced — the policy hook won't exist. Use asyncio.run(loop_factory=...) instead.
137147 # See docs/contributing-profiling-new-cpython.rst for migration guidance.
138148 events_module : ModuleType = sys .modules ["asyncio.events" ]
139- policy_class : typing .Optional [type ]
149+ policy_class : typing .Optional [type [ _HasSetEventLoop ] ]
140150 if sys .hexversion >= 0x030E0000 :
141151 # Python 3.14+: Use _BaseDefaultEventLoopPolicy
142152 policy_class = getattr (events_module , "_BaseDefaultEventLoopPolicy" , None )
@@ -292,10 +302,10 @@ def _(
292302 if sys .hexversion >= 0x030B0000 : # Python 3.11+
293303 taskgroups_module : typing .Optional [ModuleType ] = sys .modules .get ("asyncio.taskgroups" )
294304 if taskgroups_module is not None :
295- taskgroup_class : typing .Optional [type ] = getattr (taskgroups_module , "TaskGroup" , None )
305+ taskgroup_class : typing .Optional [type [ _HasCreateTask ] ] = getattr (taskgroups_module , "TaskGroup" , None )
296306 if taskgroup_class is not None and hasattr (taskgroup_class , "create_task" ):
297307
298- @partial (wrap , taskgroup_class .create_task )
308+ @partial (wrap , typing . cast ( FunctionType , taskgroup_class .create_task ) )
299309 def _ (
300310 f : typing .Callable [..., "aio.Task[typing.Any]" ],
301311 args : tuple [typing .Any , ...],
@@ -353,10 +363,12 @@ def _(uvloop: ModuleType) -> None:
353363 init_stack : bool = config .stack .enabled and stack .is_available
354364
355365 # Wrap uvloop.new_event_loop to track loops when they're created
356- new_event_loop_func : typing .Optional [types .FunctionType ] = getattr (uvloop , "new_event_loop" , None )
366+ new_event_loop_func : typing .Optional [typing .Callable [..., "asyncio.AbstractEventLoop" ]] = getattr (
367+ uvloop , "new_event_loop" , None
368+ )
357369 if new_event_loop_func is not None :
358370
359- @partial (wrap , new_event_loop_func )
371+ @partial (wrap , typing . cast ( FunctionType , new_event_loop_func ) )
360372 def _ (
361373 f : typing .Callable [..., "asyncio.AbstractEventLoop" ],
362374 args : tuple [typing .Any , ...],
@@ -374,10 +386,10 @@ def _(
374386 return loop
375387
376388 # Wrap uvloop.EventLoopPolicy.set_event_loop for uvloop.install() + asyncio.run() pattern
377- uvloop_policy_class : typing .Optional [type ] = getattr (uvloop , "EventLoopPolicy" , None )
389+ uvloop_policy_class : typing .Optional [type [ _HasSetEventLoop ] ] = getattr (uvloop , "EventLoopPolicy" , None )
378390 if uvloop_policy_class is not None and hasattr (uvloop_policy_class , "set_event_loop" ):
379391
380- @partial (wrap , uvloop_policy_class .set_event_loop )
392+ @partial (wrap , typing . cast ( FunctionType , uvloop_policy_class .set_event_loop ) )
381393 def _ (
382394 f : typing .Callable [..., typing .Any ], args : tuple [typing .Any , ...], kwargs : dict [str , typing .Any ]
383395 ) -> typing .Any :
0 commit comments