Coverage for sources/dynadoc/introspection.py: 100%

210 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2025-05-25 22:29 +0000

1# vim: set filetype=python fileencoding=utf-8: 

2# -*- coding: utf-8 -*- 

3 

4#============================================================================# 

5# # 

6# Licensed under the Apache License, Version 2.0 (the "License"); # 

7# you may not use this file except in compliance with the License. # 

8# You may obtain a copy of the License at # 

9# # 

10# http://www.apache.org/licenses/LICENSE-2.0 # 

11# # 

12# Unless required by applicable law or agreed to in writing, software # 

13# distributed under the License is distributed on an "AS IS" BASIS, # 

14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

15# See the License for the specific language governing permissions and # 

16# limitations under the License. # 

17# # 

18#============================================================================# 

19 

20 

21''' Introspection of argument, attribute, and return annotations. ''' 

22 

23 

24from . import __ 

25from . import context as _context 

26from . import interfaces as _interfaces 

27from . import nomina as _nomina 

28 

29 

30_default_default = _interfaces.Default( ) 

31_default_suppress = _interfaces.Default( 

32 mode = _interfaces.ValuationModes.Suppress ) 

33 

34 

35IntrospectIntrospectionArgument: __.typx.TypeAlias = __.typx.Annotated[ 

36 _context.IntrospectionControl, 

37 _interfaces.Doc( 

38 ''' Control settings for introspection behavior. ''' ), 

39] 

40 

41 

42def introspect( 

43 possessor: _interfaces.PossessorArgument, /, 

44 context: _context.ContextArgument, 

45 introspection: _context.IntrospectionArgument, 

46 cache: _interfaces.AnnotationsCacheArgument, 

47 table: _interfaces.FragmentsTableArgument, 

48) -> __.cabc.Sequence[ _interfaces.InformationBase ]: 

49 ''' Introspects object to extract documentable information. 

50 

51 Dispatches to appropriate introspection function based on the type 

52 of the object being introspected (class, function, or module). 

53 ''' 

54 if __.inspect.isclass( possessor ): 

55 return _introspect_class( 

56 possessor, context, introspection, cache, table ) 

57 if __.inspect.isfunction( possessor ) and possessor.__name__ != '<lambda>': 

58 return _introspect_function( 

59 possessor, context, cache, table ) 

60 if __.inspect.ismodule( possessor ): 

61 return _introspect_module( 

62 possessor, context, introspection, cache, table ) 

63 return ( ) 

64 

65 

66def introspect_special_classes( # noqa: PLR0913 

67 possessor: _interfaces.PossessorClassArgument, /, 

68 context: _context.ContextArgument, 

69 introspection: _context.IntrospectionArgument, 

70 annotations: _interfaces.AnnotationsArgument, 

71 cache: _interfaces.AnnotationsCacheArgument, 

72 table: _interfaces.FragmentsTableArgument, 

73) -> __.typx.Optional[ _interfaces.Informations ]: 

74 ''' Introspects special classes in Python standard library. 

75 

76 E.g., enum members are collected as class variables. 

77 ''' 

78 informations: list[ _interfaces.InformationBase ] = [ ] 

79 if isinstance( possessor, __.enum.EnumMeta ): 

80 informations.extend( 

81 _interfaces.AttributeInformation( 

82 name = name, 

83 annotation = possessor, 

84 description = None, 

85 association = _interfaces.AttributeAssociations.Class, 

86 default = _default_suppress ) 

87 for name in possessor.__members__ ) 

88 return informations 

89 return None 

90 

91 

92def is_attribute_visible( 

93 possessor: _interfaces.PossessorArgument, 

94 name: str, 

95 annotation: __.typx.Any, 

96 description: __.typx.Optional[ str ], 

97) -> bool: 

98 ''' Determines if attribute should be visible in documentation. 

99 

100 Default visibility predicate that considers attribute with 

101 description or public name (not starting with underscore) as visible. 

102 

103 If attribute possessor is module, then ``__all__`` is considered, 

104 if it exists. 

105 ''' 

106 if __.inspect.ismodule( possessor ): 

107 publics = getattr( possessor, '__all__', None ) 

108 if publics is not None: return name in publics 

109 return bool( description ) or not name.startswith( '_' ) 

110 

111 

112def reduce_annotation( 

113 annotation: __.typx.Any, 

114 context: _context.Context, 

115 adjuncts: _interfaces.AdjunctsData, 

116 cache: _interfaces.AnnotationsCache, 

117) -> __.typx.Any: 

118 ''' Reduces a complex type annotation to a simpler form. 

119 

120 Processes type annotations, extracting metadata from Annotated types 

121 and simplifying complex generic types. Uses cache to avoid redundant 

122 processing and prevent infinite recursion from reference cycles. 

123 ''' 

124 annotation_r = cache.access( annotation ) 

125 # Avoid infinite recursion from reference cycles. 

126 if annotation_r is _interfaces.incomplete: 

127 emessage = ( 

128 f"Annotation with circular reference {annotation!r}; " 

129 "returning Any." ) 

130 context.notifier( 'admonition', emessage ) 

131 return cache.enter( annotation, __.typx.Any ) 

132 # TODO: Short-circuit on cache hit. 

133 # Need to ensure copy of adjuncts data is retrieved too. 

134 # if annotation_r is not _interfaces.absent: return annotation_r 

135 cache.enter( annotation ) # mark as incomplete 

136 return cache.enter( 

137 annotation, 

138 _reduce_annotation_core( annotation, context, adjuncts, cache ) ) 

139 

140 

141def _access_annotations( 

142 possessor: _nomina.Documentable, /, context: _context.Context 

143) -> __.cabc.Mapping[ str, __.typx.Any ]: 

144 ''' Accesses annotations from a documentable object. 

145 

146 Retrieves annotations with appropriate resolver settings from the 

147 context. Handles errors gracefully. 

148 ''' 

149 nomargs: _nomina.Variables = dict( eval_str = True ) 

150 nomargs[ 'globals' ] = context.resolver_globals 

151 nomargs[ 'locals' ] = context.resolver_locals 

152 try: 

153 return __.types.MappingProxyType( 

154 __.inspect.get_annotations( possessor, **nomargs ) ) 

155 except TypeError as exc: 

156 emessage = f"Cannot access annotations for {possessor!r}: {exc}" 

157 context.notifier( 'error', emessage ) 

158 return __.dictproxy_empty 

159 

160 

161def _classes_sequence_to_union( 

162 annotation: type | __.cabc.Sequence[ type ] 

163) -> __.typx.Any: 

164 ''' Converts a sequence of exception classes to a Union type. 

165 

166 Used for Raises annotations to convert a sequence of exception 

167 classes into a Union type for documentation. 

168 ''' 

169 if not isinstance( annotation, __.cabc.Sequence ): 

170 return annotation 

171 return __.funct.reduce( __.operator.or_, annotation ) 

172 

173 

174def _compile_description( 

175 context: _context.Context, 

176 adjuncts: _interfaces.AdjunctsData, 

177 table: _nomina.FragmentsTable, 

178) -> str: 

179 ''' Compiles a description from adjuncts data. 

180 

181 Processes Doc objects and Findex references in adjuncts data 

182 to create a combined description string with proper formatting. 

183 ''' 

184 fragments: list[ str ] = [ ] 

185 for extra in adjuncts.extras: 

186 if isinstance( extra, _interfaces.Doc ): 

187 fragments.append( extra.documentation ) 

188 elif isinstance( extra, _interfaces.Fname ): 

189 name = extra.name 

190 if name not in table: 

191 emessage = f"Fragment '{name}' not in provided table." 

192 context.notifier( 'error', emessage ) 

193 else: fragments.append( table[ name ] ) 

194 return '\n\n'.join( 

195 context.fragment_rectifier( 

196 fragment, source = _interfaces.FragmentSources.Annotation ) 

197 for fragment in fragments ) 

198 

199 

200def _determine_default_valuator( 

201 context: _context.Context, 

202 adjuncts: _interfaces.AdjunctsData, 

203) -> _interfaces.Default: 

204 ''' Determines how default values should be handled. 

205 

206 Extracts the Default object from adjuncts data or falls back 

207 to the default Default settings. 

208 ''' 

209 return next( 

210 ( extra for extra in adjuncts.extras 

211 if isinstance( extra, _interfaces.Default ) ), 

212 _default_default ) 

213 

214 

215def _filter_reconstitute_annotation( 

216 origin: __.typx.Any, 

217 arguments: __.cabc.Sequence[ __.typx.Any ], 

218 context: _context.Context, 

219 adjuncts: _interfaces.AdjunctsData, 

220 cache: _interfaces.AnnotationsCache, 

221) -> __.typx.Any: 

222 ''' Filters and reconstitutes a generic type annotation. 

223 

224 After reducing the arguments of a generic type, this function 

225 reconstitutes the type with the reduced arguments, potentially 

226 applying transformations based on context. 

227 ''' 

228 adjuncts.traits.add( origin.__name__ ) 

229 arguments_r: list[ __.typx.Any ] = [ ] 

230 match len( arguments ): 

231 case 1: 

232 arguments_r.append( reduce_annotation( 

233 arguments[ 0 ], context, adjuncts, cache ) ) 

234 case _: 

235 # upward propagation is ambiguous, so sever adjuncts data 

236 adjuncts_ = _interfaces.AdjunctsData( ) 

237 adjuncts_.traits.add( origin.__name__ ) 

238 arguments_r.extend( _reduce_annotation_arguments( 

239 origin, arguments, context, adjuncts_.copy( ), cache ) ) 

240 # TODO: Apply filters from context, replacing origin as necessary. 

241 # E.g., ClassVar -> Union 

242 # (Union with one argument returns the argument.) 

243 try: 

244 if origin in ( __.types.UnionType, __.typx.Union ): 

245 # Unions cannot be reconstructed from sequences. 

246 # TODO: Python 3.11: Unpack into subscript. 

247 annotation = __.funct.reduce( __.operator.or_, arguments_r ) 

248 else: 

249 match len( arguments_r ): 

250 case 1: annotation = origin[ arguments_r[ 0 ] ] 

251 case _: annotation = origin[ tuple( arguments_r ) ] 

252 except TypeError as exc: 

253 emessage = ( 

254 f"Cannot reconstruct {origin.__name__!r} " 

255 f"with reduced annotations for arguments. Reason: {exc}" ) 

256 context.notifier( 'error', emessage ) 

257 return origin 

258 return annotation 

259 

260 

261def _introspect_class( 

262 possessor: type, /, 

263 context: _context.Context, 

264 introspection: _context.IntrospectionControl, 

265 cache: _interfaces.AnnotationsCache, 

266 table: _nomina.FragmentsTable, 

267) -> __.cabc.Sequence[ _interfaces.InformationBase ]: 

268 ''' Introspects a class to extract documentable information. 

269 

270 Gathers information about class annotations, potentially considering 

271 inherited annotations based on introspection control settings. Tries 

272 special class introspectors first, then falls back to standard 

273 introspection. 

274 ''' 

275 annotations_: dict[ str, __.typx.Any ] = { } 

276 if introspection.class_control.inheritance: 

277 # Descendant annotations override ancestor annotations. 

278 for class_ in reversed( possessor.__mro__ ): 

279 annotations_b = _access_annotations( class_, context ) 

280 annotations_.update( annotations_b ) 

281 annotations = annotations_ 

282 else: annotations = _access_annotations( possessor, context ) 

283 informations: list[ _interfaces.InformationBase ] = [ ] 

284 for introspector in introspection.class_control.introspectors: 

285 informations_ = introspector( 

286 possessor, 

287 context = context, introspection = introspection, 

288 annotations = annotations, cache = cache, table = table ) 

289 if informations_ is not None: 

290 informations.extend( informations_ ) 

291 break 

292 else: 

293 informations.extend( _introspect_class_annotations( 

294 possessor, context, annotations, cache, table ) ) 

295 if introspection.class_control.scan_attributes: 

296 informations.extend( _introspect_class_attributes( 

297 possessor, context, annotations ) ) 

298 return tuple( informations ) 

299 

300 

301def _introspect_class_annotations( 

302 possessor: type, /, 

303 context: _context.Context, 

304 annotations: __.cabc.Mapping[ str, __.typx.Any ], 

305 cache: _interfaces.AnnotationsCache, 

306 table: _nomina.FragmentsTable, 

307) -> __.cabc.Sequence[ _interfaces.InformationBase ]: 

308 ''' Introspects annotations of a class. 

309 

310 Processes class annotations to extract information about class 

311 attributes, including their types, descriptions from Doc objects, 

312 and whether they are class or instance variables. 

313 ''' 

314 informations: list[ _interfaces.InformationBase ] = [ ] 

315 for name, annotation in annotations.items( ): 

316 adjuncts = _interfaces.AdjunctsData( ) 

317 annotation_ = reduce_annotation( 

318 annotation, context, adjuncts, cache ) 

319 description = _compile_description( context, adjuncts, table ) 

320 if not _is_attribute_visible( 

321 possessor, name, annotation_, context, adjuncts, description 

322 ): continue 

323 association = ( 

324 _interfaces.AttributeAssociations.Class 

325 if 'ClassVar' in adjuncts.traits 

326 else _interfaces.AttributeAssociations.Instance ) 

327 default = _determine_default_valuator( context, adjuncts ) 

328 informations.append( _interfaces.AttributeInformation( 

329 name = name, 

330 annotation = annotation_, 

331 description = description, 

332 association = association, 

333 default = default ) ) 

334 return informations 

335 

336 

337def _introspect_class_attributes( 

338 possessor: type, /, 

339 context: _context.Context, 

340 annotations: __.cabc.Mapping[ str, __.typx.Any ], 

341) -> __.cabc.Sequence[ _interfaces.InformationBase ]: 

342 ''' Introspects attributes of a class not covered by annotations. 

343 

344 Examines class attributes that do not have corresponding annotations 

345 and creates attribute information for those that should be visible. 

346 ''' 

347 informations: list[ _interfaces.InformationBase ] = [ ] 

348 adjuncts = _interfaces.AdjunctsData( ) # dummy value 

349 for name, attribute in __.inspect.getmembers( possessor ): 

350 if name in annotations: continue # already processed 

351 if not _is_attribute_visible( 

352 possessor, name, _interfaces.absent, context, adjuncts, None 

353 ): continue 

354 if callable( attribute ): continue # separately documented 

355 informations.append( _interfaces.AttributeInformation( 

356 name = name, 

357 annotation = _interfaces.absent, 

358 description = None, 

359 association = _interfaces.AttributeAssociations.Class, 

360 default = _default_default ) ) 

361 return informations 

362 

363 

364def _introspect_function( 

365 possessor: __.cabc.Callable[ ..., __.typx.Any ], /, 

366 context: _context.Context, 

367 cache: _interfaces.AnnotationsCache, 

368 table: _nomina.FragmentsTable, 

369) -> __.cabc.Sequence[ _interfaces.InformationBase ]: 

370 ''' Introspects a function to extract documentable information. 

371 

372 Gathers information about function arguments and return value 

373 from annotations and signature analysis. 

374 ''' 

375 annotations = _access_annotations( possessor, context ) 

376 if not annotations: return ( ) 

377 informations: list[ _interfaces.InformationBase ] = [ ] 

378 try: signature = __.inspect.signature( possessor ) 

379 except ValueError as exc: 

380 context.notifier( 

381 'error', 

382 f"Could not assess signature for {possessor.__qualname__!r}. " 

383 f"Reason: {exc}" ) 

384 return ( ) 

385 if signature.parameters: 

386 informations.extend( _introspect_function_valences( 

387 annotations, signature, context, cache, table ) ) 

388 if 'return' in annotations: 

389 informations.extend( _introspect_function_return( 

390 annotations[ 'return' ], context, cache, table ) ) 

391 return tuple( informations ) 

392 

393 

394def _introspect_function_return( 

395 annotation: __.typx.Any, 

396 context: _context.Context, 

397 cache: _interfaces.AnnotationsCache, 

398 table: _nomina.FragmentsTable, 

399) -> __.cabc.Sequence[ _interfaces.InformationBase ]: 

400 ''' Introspects function return annotation. 

401 

402 Processes function return annotation to extract return type information 

403 and possible exception information from Raises annotations. 

404 ''' 

405 informations: list[ _interfaces.InformationBase ] = [ ] 

406 adjuncts = _interfaces.AdjunctsData( ) 

407 annotation_ = reduce_annotation( annotation, context, adjuncts, cache ) 

408 description = _compile_description( context, adjuncts, table ) 

409 informations.append( 

410 _interfaces.ReturnInformation( 

411 annotation = annotation_, description = description ) ) 

412 informations.extend( 

413 _interfaces.ExceptionInformation( 

414 annotation = _classes_sequence_to_union( extra.classes ), 

415 description = extra.description ) 

416 for extra in adjuncts.extras 

417 if isinstance( extra, _interfaces.Raises ) ) 

418 return tuple( informations ) 

419 

420 

421def _introspect_function_valences( 

422 annotations: __.cabc.Mapping[ str, __.typx.Any ], 

423 signature: __.inspect.Signature, 

424 context: _context.Context, 

425 cache: _interfaces.AnnotationsCache, 

426 table: _nomina.FragmentsTable, 

427) -> __.cabc.Sequence[ _interfaces.ArgumentInformation ]: 

428 ''' Introspects function parameters to extract argument information. 

429 

430 Processes function signature and annotations to create information 

431 about function arguments, including their types, descriptions, and 

432 default value handling. 

433 ''' 

434 informations: list[ _interfaces.ArgumentInformation ] = [ ] 

435 for name, param in signature.parameters.items( ): 

436 annotation = annotations.get( name, param.annotation ) 

437 adjuncts = _interfaces.AdjunctsData( ) 

438 if annotation is param.empty: 

439 annotation_ = _interfaces.absent 

440 description = None 

441 else: 

442 annotation_ = reduce_annotation( 

443 annotation, context, adjuncts, cache ) 

444 description = _compile_description( context, adjuncts, table ) 

445 if param.default is param.empty: default = _default_suppress 

446 else: default = _determine_default_valuator( context, adjuncts ) 

447 informations.append( _interfaces.ArgumentInformation( 

448 name = name, 

449 annotation = annotation_, 

450 description = description, 

451 paramspec = param, 

452 default = default ) ) 

453 return tuple( informations ) 

454 

455 

456def _introspect_module( 

457 possessor: __.types.ModuleType, /, 

458 context: _context.Context, 

459 introspection: _context.IntrospectionControl, 

460 cache: _interfaces.AnnotationsCache, 

461 table: _nomina.FragmentsTable, 

462) -> __.cabc.Sequence[ _interfaces.InformationBase ]: 

463 ''' Introspects a module to extract documentable information. 

464 

465 Gathers information about module annotations and potentially about 

466 module attributes based on introspection control settings. 

467 ''' 

468 annotations = _access_annotations( possessor, context ) 

469 if not annotations: return ( ) 

470 informations: list[ _interfaces.InformationBase ] = [ ] 

471 informations.extend( _introspect_module_annotations( 

472 possessor, context, annotations, cache, table ) ) 

473 if introspection.module_control.scan_attributes: 

474 informations.extend( _introspect_module_attributes( 

475 possessor, context, annotations ) ) 

476 return tuple( informations ) 

477 

478 

479def _introspect_module_annotations( 

480 possessor: __.types.ModuleType, /, 

481 context: _context.Context, 

482 annotations: __.cabc.Mapping[ str, __.typx.Any ], 

483 cache: _interfaces.AnnotationsCache, 

484 table: _nomina.FragmentsTable, 

485) -> __.cabc.Sequence[ _interfaces.InformationBase ]: 

486 ''' Introspects annotations of a module. 

487 

488 Processes module annotations to extract information about module 

489 attributes, including their types and descriptions from Doc objects. 

490 ''' 

491 informations: list[ _interfaces.InformationBase ] = [ ] 

492 for name, annotation in annotations.items( ): 

493 adjuncts = _interfaces.AdjunctsData( ) 

494 annotation_ = reduce_annotation( 

495 annotation, context, adjuncts, cache ) 

496 description = _compile_description( context, adjuncts, table ) 

497 if not _is_attribute_visible( 

498 possessor, name, annotation_, context, adjuncts, description 

499 ): continue 

500 default = _determine_default_valuator( context, adjuncts ) 

501 informations.append( _interfaces.AttributeInformation( 

502 name = name, 

503 annotation = annotation_, 

504 description = description, 

505 association = _interfaces.AttributeAssociations.Module, 

506 default = default ) ) 

507 return informations 

508 

509 

510def _introspect_module_attributes( 

511 possessor: __.types.ModuleType, /, 

512 context: _context.Context, 

513 annotations: __.cabc.Mapping[ str, __.typx.Any ], 

514) -> __.cabc.Sequence[ _interfaces.InformationBase ]: 

515 ''' Introspects attributes of a module not covered by annotations. 

516 

517 Examines module attributes that do not have corresponding annotations 

518 and creates attribute information for those that should be visible. 

519 ''' 

520 informations: list[ _interfaces.InformationBase ] = [ ] 

521 adjuncts = _interfaces.AdjunctsData( ) # dummy value 

522 attribute: object 

523 for name, attribute in __.inspect.getmembers( possessor ): 

524 if name in annotations: continue # already processed 

525 if not _is_attribute_visible( 

526 possessor, name, _interfaces.absent, context, adjuncts, None 

527 ): continue 

528 if callable( attribute ): continue # separately documented 

529 informations.append( _interfaces.AttributeInformation( 

530 name = name, 

531 annotation = _interfaces.absent, 

532 description = None, 

533 association = _interfaces.AttributeAssociations.Module, 

534 default = _default_default ) ) 

535 return informations 

536 

537 

538def _is_attribute_visible( # noqa: PLR0913 

539 possessor: _nomina.Documentable, 

540 name: str, 

541 annotation: __.typx.Any, 

542 context: _context.Context, 

543 adjuncts: _interfaces.AdjunctsData, 

544 description: __.typx.Optional[ str ], 

545) -> bool: 

546 ''' Determines if an attribute should be visible in documentation. 

547 

548 Checks for explicit visibility settings in adjuncts data and falls 

549 back to the context's visibility decider if the visibility is set 

550 to Default. 

551 ''' 

552 visibility = next( 

553 ( extra for extra in adjuncts.extras 

554 if isinstance( extra, _interfaces.Visibilities ) ), 

555 _interfaces.Visibilities.Default ) 

556 match visibility: 

557 case _interfaces.Visibilities.Conceal: return False 

558 case _interfaces.Visibilities.Reveal: return True 

559 case _: 

560 return context.visibility_decider( 

561 possessor, name, annotation, description ) 

562 

563 

564def _reduce_annotation_arguments( 

565 origin: __.typx.Any, 

566 arguments: __.cabc.Sequence[ __.typx.Any ], 

567 context: _context.Context, 

568 adjuncts: _interfaces.AdjunctsData, 

569 cache: _interfaces.AnnotationsCache, 

570) -> __.cabc.Sequence[ __.typx.Any ]: 

571 ''' Reduces the arguments of a generic type annotation. 

572 

573 Processes the arguments of a generic type like List[T] or Dict[K, V] 

574 and returns the reduced forms of those arguments. Special handling 

575 for Callable types. 

576 ''' 

577 if __.inspect.isclass( origin ) and issubclass( origin, __.cabc.Callable ): 

578 return _reduce_annotation_for_callable( 

579 arguments, context, adjuncts.copy( ), cache ) 

580 return tuple( 

581 reduce_annotation( argument, context, adjuncts.copy( ), cache ) 

582 for argument in arguments ) 

583 

584 

585def _reduce_annotation_core( 

586 annotation: __.typx.Any, 

587 context: _context.Context, 

588 adjuncts: _interfaces.AdjunctsData, 

589 cache: _interfaces.AnnotationsCache, 

590) -> __.typx.Any: 

591 ''' Core implementation of annotation reduction. 

592 

593 Handles the reduction of complex type annotations into simpler forms, 

594 extracting metadata from Annotated types and processing generic types. 

595 Returns the reduced annotation. 

596 ''' 

597 origin = __.typx.get_origin( annotation ) 

598 # bare types, Ellipsis, typing.Any, typing.LiteralString, typing.Never, 

599 # typing.TypeVar have no origin; taken as-is 

600 # typing.Literal is considered fully reduced; taken as-is 

601 if origin in ( None, __.typx.Literal ): return annotation 

602 arguments = __.typx.get_args( annotation ) 

603 if not arguments: return annotation 

604 if origin is __.typx.Annotated: 

605 adjuncts.extras.extend( arguments[ 1 : ] ) 

606 return reduce_annotation( 

607 annotation.__origin__, context, adjuncts, cache ) 

608 return _filter_reconstitute_annotation( 

609 origin, arguments, context, adjuncts, cache ) 

610 

611 

612def _reduce_annotation_for_callable( 

613 arguments: __.cabc.Sequence[ __.typx.Any ], 

614 context: _context.Context, 

615 adjuncts: _interfaces.AdjunctsData, 

616 cache: _interfaces.AnnotationsCache, 

617) -> tuple[ list[ __.typx.Any ] | __.types.EllipsisType, __.typx.Any ]: 

618 ''' Reduces annotations for Callable types. 

619 

620 Special handling for Callable type annotations, which have a tuple 

621 of (arguments, return_type). Processes the arguments list and return 

622 type separately and returns the reduced forms. 

623 ''' 

624 farguments, freturn = arguments 

625 if farguments is Ellipsis: 

626 farguments_r = Ellipsis 

627 else: 

628 farguments_r = [ 

629 reduce_annotation( element, context, adjuncts.copy( ), cache ) 

630 for element in farguments ] 

631 freturn_r = ( 

632 reduce_annotation( freturn, context, adjuncts.copy( ), cache ) ) 

633 return ( farguments_r, freturn_r )