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

210 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2025-06-02 23:37 +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 Note that any type-adjacent information on arguments is not propagated 

229 upwards, due to ambiguity in its insertion order relative to 

230 type-adjacent information on the annotation origin. 

231 ''' 

232 adjuncts.traits.add( origin.__name__ ) 

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

234 adjuncts_ = _interfaces.AdjunctsData( ) 

235 adjuncts_.traits.add( origin.__name__ ) 

236 match len( arguments ): 

237 case 1: 

238 arguments_r.append( reduce_annotation( 

239 arguments[ 0 ], context, adjuncts_, cache ) ) 

240 case _: 

241 arguments_r.extend( _reduce_annotation_arguments( 

242 origin, arguments, context, adjuncts_, cache ) ) 

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

244 # E.g., ClassVar -> Union 

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

246 try: 

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

248 # Unions cannot be reconstructed from sequences. 

249 # TODO: Python 3.11: Unpack into subscript. 

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

251 else: 

252 match len( arguments_r ): 

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

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

255 except TypeError as exc: 

256 emessage = ( 

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

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

259 context.notifier( 'error', emessage ) 

260 return origin 

261 return annotation 

262 

263 

264def _introspect_class( 

265 possessor: type, /, 

266 context: _context.Context, 

267 introspection: _context.IntrospectionControl, 

268 cache: _interfaces.AnnotationsCache, 

269 table: _nomina.FragmentsTable, 

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

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

272 

273 Gathers information about class annotations, potentially considering 

274 inherited annotations based on introspection control settings. Tries 

275 special class introspectors first, then falls back to standard 

276 introspection. 

277 ''' 

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

279 if introspection.class_control.inheritance: 

280 # Descendant annotations override ancestor annotations. 

281 for class_ in reversed( possessor.__mro__ ): 

282 annotations_b = _access_annotations( class_, context ) 

283 annotations_.update( annotations_b ) 

284 annotations = annotations_ 

285 else: annotations = _access_annotations( possessor, context ) 

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

287 for introspector in introspection.class_control.introspectors: 

288 informations_ = introspector( 

289 possessor, 

290 context = context, introspection = introspection, 

291 annotations = annotations, cache = cache, table = table ) 

292 if informations_ is not None: 

293 informations.extend( informations_ ) 

294 break 

295 else: 

296 informations.extend( _introspect_class_annotations( 

297 possessor, context, annotations, cache, table ) ) 

298 if introspection.class_control.scan_attributes: 

299 informations.extend( _introspect_class_attributes( 

300 possessor, context, annotations ) ) 

301 return tuple( informations ) 

302 

303 

304def _introspect_class_annotations( 

305 possessor: type, /, 

306 context: _context.Context, 

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

308 cache: _interfaces.AnnotationsCache, 

309 table: _nomina.FragmentsTable, 

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

311 ''' Introspects annotations of a class. 

312 

313 Processes class annotations to extract information about class 

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

315 and whether they are class or instance variables. 

316 ''' 

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

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

319 adjuncts = _interfaces.AdjunctsData( ) 

320 annotation_ = reduce_annotation( 

321 annotation, context, adjuncts, cache ) 

322 description = _compile_description( context, adjuncts, table ) 

323 if not _is_attribute_visible( 

324 possessor, name, annotation_, context, adjuncts, description 

325 ): continue 

326 association = ( 

327 _interfaces.AttributeAssociations.Class 

328 if 'ClassVar' in adjuncts.traits 

329 else _interfaces.AttributeAssociations.Instance ) 

330 default = _determine_default_valuator( context, adjuncts ) 

331 informations.append( _interfaces.AttributeInformation( 

332 name = name, 

333 annotation = annotation_, 

334 description = description, 

335 association = association, 

336 default = default ) ) 

337 return informations 

338 

339 

340def _introspect_class_attributes( 

341 possessor: type, /, 

342 context: _context.Context, 

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

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

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

346 

347 Examines class attributes that do not have corresponding annotations 

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

349 ''' 

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

351 adjuncts = _interfaces.AdjunctsData( ) # dummy value 

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

353 if name in annotations: continue # already processed 

354 if not _is_attribute_visible( 

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

356 ): continue 

357 if callable( attribute ): continue # separately documented 

358 informations.append( _interfaces.AttributeInformation( 

359 name = name, 

360 annotation = _interfaces.absent, 

361 description = None, 

362 association = _interfaces.AttributeAssociations.Class, 

363 default = _default_default ) ) 

364 return informations 

365 

366 

367def _introspect_function( 

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

369 context: _context.Context, 

370 cache: _interfaces.AnnotationsCache, 

371 table: _nomina.FragmentsTable, 

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

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

374 

375 Gathers information about function arguments and return value 

376 from annotations and signature analysis. 

377 ''' 

378 annotations = _access_annotations( possessor, context ) 

379 if not annotations: return ( ) 

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

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

382 except ValueError as exc: 

383 context.notifier( 

384 'error', 

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

386 f"Reason: {exc}" ) 

387 return ( ) 

388 if signature.parameters: 

389 informations.extend( _introspect_function_valences( 

390 annotations, signature, context, cache, table ) ) 

391 if 'return' in annotations: 

392 informations.extend( _introspect_function_return( 

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

394 return tuple( informations ) 

395 

396 

397def _introspect_function_return( 

398 annotation: __.typx.Any, 

399 context: _context.Context, 

400 cache: _interfaces.AnnotationsCache, 

401 table: _nomina.FragmentsTable, 

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

403 ''' Introspects function return annotation. 

404 

405 Processes function return annotation to extract return type information 

406 and possible exception information from Raises annotations. 

407 ''' 

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

409 adjuncts = _interfaces.AdjunctsData( ) 

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

411 description = _compile_description( context, adjuncts, table ) 

412 informations.append( 

413 _interfaces.ReturnInformation( 

414 annotation = annotation_, description = description ) ) 

415 informations.extend( 

416 _interfaces.ExceptionInformation( 

417 annotation = _classes_sequence_to_union( extra.classes ), 

418 description = extra.description ) 

419 for extra in adjuncts.extras 

420 if isinstance( extra, _interfaces.Raises ) ) 

421 return tuple( informations ) 

422 

423 

424def _introspect_function_valences( 

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

426 signature: __.inspect.Signature, 

427 context: _context.Context, 

428 cache: _interfaces.AnnotationsCache, 

429 table: _nomina.FragmentsTable, 

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

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

432 

433 Processes function signature and annotations to create information 

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

435 default value handling. 

436 ''' 

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

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

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

440 adjuncts = _interfaces.AdjunctsData( ) 

441 if annotation is param.empty: 

442 annotation_ = _interfaces.absent 

443 description = None 

444 else: 

445 annotation_ = reduce_annotation( 

446 annotation, context, adjuncts, cache ) 

447 description = _compile_description( context, adjuncts, table ) 

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

449 else: default = _determine_default_valuator( context, adjuncts ) 

450 informations.append( _interfaces.ArgumentInformation( 

451 name = name, 

452 annotation = annotation_, 

453 description = description, 

454 paramspec = param, 

455 default = default ) ) 

456 return tuple( informations ) 

457 

458 

459def _introspect_module( 

460 possessor: __.types.ModuleType, /, 

461 context: _context.Context, 

462 introspection: _context.IntrospectionControl, 

463 cache: _interfaces.AnnotationsCache, 

464 table: _nomina.FragmentsTable, 

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

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

467 

468 Gathers information about module annotations and potentially about 

469 module attributes based on introspection control settings. 

470 ''' 

471 annotations = _access_annotations( possessor, context ) 

472 if not annotations: return ( ) 

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

474 informations.extend( _introspect_module_annotations( 

475 possessor, context, annotations, cache, table ) ) 

476 if introspection.module_control.scan_attributes: 

477 informations.extend( _introspect_module_attributes( 

478 possessor, context, annotations ) ) 

479 return tuple( informations ) 

480 

481 

482def _introspect_module_annotations( 

483 possessor: __.types.ModuleType, /, 

484 context: _context.Context, 

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

486 cache: _interfaces.AnnotationsCache, 

487 table: _nomina.FragmentsTable, 

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

489 ''' Introspects annotations of a module. 

490 

491 Processes module annotations to extract information about module 

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

493 ''' 

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

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

496 adjuncts = _interfaces.AdjunctsData( ) 

497 annotation_ = reduce_annotation( 

498 annotation, context, adjuncts, cache ) 

499 description = _compile_description( context, adjuncts, table ) 

500 if not _is_attribute_visible( 

501 possessor, name, annotation_, context, adjuncts, description 

502 ): continue 

503 default = _determine_default_valuator( context, adjuncts ) 

504 informations.append( _interfaces.AttributeInformation( 

505 name = name, 

506 annotation = annotation_, 

507 description = description, 

508 association = _interfaces.AttributeAssociations.Module, 

509 default = default ) ) 

510 return informations 

511 

512 

513def _introspect_module_attributes( 

514 possessor: __.types.ModuleType, /, 

515 context: _context.Context, 

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

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

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

519 

520 Examines module attributes that do not have corresponding annotations 

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

522 ''' 

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

524 adjuncts = _interfaces.AdjunctsData( ) # dummy value 

525 attribute: object 

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

527 if name in annotations: continue # already processed 

528 if not _is_attribute_visible( 

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

530 ): continue 

531 if callable( attribute ): continue # separately documented 

532 informations.append( _interfaces.AttributeInformation( 

533 name = name, 

534 annotation = _interfaces.absent, 

535 description = None, 

536 association = _interfaces.AttributeAssociations.Module, 

537 default = _default_default ) ) 

538 return informations 

539 

540 

541def _is_attribute_visible( # noqa: PLR0913 

542 possessor: _nomina.Documentable, 

543 name: str, 

544 annotation: __.typx.Any, 

545 context: _context.Context, 

546 adjuncts: _interfaces.AdjunctsData, 

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

548) -> bool: 

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

550 

551 Checks for explicit visibility settings in adjuncts data and falls 

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

553 to Default. 

554 ''' 

555 visibility = next( 

556 ( extra for extra in adjuncts.extras 

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

558 _interfaces.Visibilities.Default ) 

559 match visibility: 

560 case _interfaces.Visibilities.Conceal: return False 

561 case _interfaces.Visibilities.Reveal: return True 

562 case _: 

563 return context.visibility_decider( 

564 possessor, name, annotation, description ) 

565 

566 

567def _reduce_annotation_arguments( 

568 origin: __.typx.Any, 

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

570 context: _context.Context, 

571 adjuncts: _interfaces.AdjunctsData, 

572 cache: _interfaces.AnnotationsCache, 

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

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

575 

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

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

578 for Callable types. 

579 ''' 

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

581 return _reduce_annotation_for_callable( 

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

583 return tuple( 

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

585 for argument in arguments ) 

586 

587 

588def _reduce_annotation_core( 

589 annotation: __.typx.Any, 

590 context: _context.Context, 

591 adjuncts: _interfaces.AdjunctsData, 

592 cache: _interfaces.AnnotationsCache, 

593) -> __.typx.Any: 

594 ''' Core implementation of annotation reduction. 

595 

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

597 extracting metadata from Annotated types and processing generic types. 

598 Returns the reduced annotation. 

599 ''' 

600 origin = __.typx.get_origin( annotation ) 

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

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

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

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

605 arguments = __.typx.get_args( annotation ) 

606 if not arguments: return annotation 

607 if origin is __.typx.Annotated: 

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

609 return reduce_annotation( 

610 annotation.__origin__, context, adjuncts, cache ) 

611 return _filter_reconstitute_annotation( 

612 origin, arguments, context, adjuncts, cache ) 

613 

614 

615def _reduce_annotation_for_callable( 

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

617 context: _context.Context, 

618 adjuncts: _interfaces.AdjunctsData, 

619 cache: _interfaces.AnnotationsCache, 

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

621 ''' Reduces annotations for Callable types. 

622 

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

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

625 type separately and returns the reduced forms. 

626 ''' 

627 farguments, freturn = arguments 

628 if farguments is Ellipsis: 

629 farguments_r = Ellipsis 

630 else: 

631 farguments_r = [ 

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

633 for element in farguments ] 

634 freturn_r = ( 

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

636 return ( farguments_r, freturn_r )