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

211 statements  

« prev     ^ index     » next       coverage.py v7.10.1, created at 2025-07-29 05:16 +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 if isinstance( annotation, str ): # Cannot do much with unresolved strings. 

136 return cache.enter( annotation, annotation ) 

137 if isinstance( annotation, __.typx.ForwardRef ): # Extract string. 

138 return cache.enter( annotation, annotation.__forward_arg__ ) 

139 cache.enter( annotation ) # mark as incomplete 

140 return cache.enter( 

141 annotation, 

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

143 

144 

145def _access_annotations( 

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

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

148 # TODO? Option to attempt resolution of strings. 

149 # Probably after retrieval of annotations dictionary 

150 # to prevent 'NameError' from ruining everything. 

151 # Would leave unresolvable strings as strings. 

152 # TODO? Option 'strict' to force resolution of all strings. 

153 # TODO: Switch to '__.typx.get_annotations'. 

154 ''' Accesses annotations from documentable object. ''' 

155 # nomargs: _nomina.Variables = dict( eval_str = True ) 

156 # nomargs[ 'globals' ] = context.resolver_globals 

157 # nomargs[ 'locals' ] = context.resolver_locals 

158 try: 

159 # return __.types.MappingProxyType( 

160 # __.inspect.get_annotations( possessor, **nomargs ) ) 

161 return __.types.MappingProxyType( 

162 __.inspect.get_annotations( possessor ) ) 

163 except ( NameError, TypeError ) as exc: 

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

165 context.notifier( 'error', emessage ) 

166 return __.dictproxy_empty 

167 

168 

169def _classes_sequence_to_union( 

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

171) -> __.typx.Any: 

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

173 

174 Used for Raises annotations to convert a sequence of exception 

175 classes into a Union type for documentation. 

176 ''' 

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

178 return annotation 

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

180 

181 

182def _compile_description( 

183 context: _context.Context, 

184 adjuncts: _interfaces.AdjunctsData, 

185 table: _nomina.FragmentsTable, 

186) -> str: 

187 ''' Compiles a description from adjuncts data. 

188 

189 Processes Doc objects and Findex references in adjuncts data 

190 to create a combined description string with proper formatting. 

191 ''' 

192 fragments: list[ str ] = [ ] 

193 for extra in adjuncts.extras: 

194 if isinstance( extra, _interfaces.Doc ): 

195 fragments.append( extra.documentation ) 

196 elif isinstance( extra, _interfaces.Fname ): 

197 name = extra.name 

198 if name not in table: 

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

200 context.notifier( 'error', emessage ) 

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

202 return '\n\n'.join( 

203 context.fragment_rectifier( 

204 fragment, source = _interfaces.FragmentSources.Annotation ) 

205 for fragment in fragments ) 

206 

207 

208def _determine_default_valuator( 

209 context: _context.Context, 

210 adjuncts: _interfaces.AdjunctsData, 

211) -> _interfaces.Default: 

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

213 

214 Extracts the Default object from adjuncts data or falls back 

215 to the default Default settings. 

216 ''' 

217 return next( 

218 ( extra for extra in adjuncts.extras 

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

220 _default_default ) 

221 

222 

223def _filter_reconstitute_annotation( 

224 origin: __.typx.Any, 

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

226 context: _context.Context, 

227 adjuncts: _interfaces.AdjunctsData, 

228 cache: _interfaces.AnnotationsCache, 

229) -> __.typx.Any: 

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

231 

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

233 reconstitutes the type with the reduced arguments, potentially 

234 applying transformations based on context. 

235 

236 Note that any type-adjacent information on arguments is not propagated 

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

238 type-adjacent information on the annotation origin. 

239 ''' 

240 adjuncts.traits.add( origin.__name__ ) 

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

242 adjuncts_ = _interfaces.AdjunctsData( ) 

243 adjuncts_.traits.add( origin.__name__ ) 

244 match len( arguments ): 

245 case 1: 

246 arguments_r.append( reduce_annotation( 

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

248 case _: 

249 arguments_r.extend( _reduce_annotation_arguments( 

250 origin, arguments, context, adjuncts_, cache ) ) 

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

252 # E.g., ClassVar -> Union 

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

254 try: 

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

256 # Unions cannot be reconstructed from sequences. 

257 # TODO: Python 3.11: Unpack into subscript. 

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

259 else: 

260 match len( arguments_r ): 

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

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

263 except TypeError as exc: 

264 emessage = ( 

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

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

267 context.notifier( 'error', emessage ) 

268 return origin 

269 return annotation 

270 

271 

272def _introspect_class( 

273 possessor: type, /, 

274 context: _context.Context, 

275 introspection: _context.IntrospectionControl, 

276 cache: _interfaces.AnnotationsCache, 

277 table: _nomina.FragmentsTable, 

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

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

280 

281 Gathers information about class annotations, potentially considering 

282 inherited annotations based on introspection control settings. Tries 

283 special class introspectors first, then falls back to standard 

284 introspection. 

285 ''' 

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

287 if introspection.class_control.inheritance: 

288 # Descendant annotations override ancestor annotations. 

289 for class_ in reversed( possessor.__mro__ ): 

290 annotations_b = _access_annotations( class_, context ) 

291 annotations_.update( annotations_b ) 

292 annotations = annotations_ 

293 else: annotations = _access_annotations( possessor, context ) 

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

295 for introspector in introspection.class_control.introspectors: 

296 informations_ = introspector( 

297 possessor, 

298 context = context, introspection = introspection, 

299 annotations = annotations, cache = cache, table = table ) 

300 if informations_ is not None: 

301 informations.extend( informations_ ) 

302 break 

303 else: 

304 informations.extend( _introspect_class_annotations( 

305 possessor, context, annotations, cache, table ) ) 

306 if introspection.class_control.scan_attributes: 

307 informations.extend( _introspect_class_attributes( 

308 possessor, context, annotations ) ) 

309 return tuple( informations ) 

310 

311 

312def _introspect_class_annotations( 

313 possessor: type, /, 

314 context: _context.Context, 

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

316 cache: _interfaces.AnnotationsCache, 

317 table: _nomina.FragmentsTable, 

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

319 ''' Introspects annotations of a class. 

320 

321 Processes class annotations to extract information about class 

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

323 and whether they are class or instance variables. 

324 ''' 

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

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

327 adjuncts = _interfaces.AdjunctsData( ) 

328 annotation_ = reduce_annotation( 

329 annotation, context, adjuncts, cache ) 

330 description = _compile_description( context, adjuncts, table ) 

331 if not _is_attribute_visible( 

332 possessor, name, annotation_, context, adjuncts, description 

333 ): continue 

334 association = ( 

335 _interfaces.AttributeAssociations.Class 

336 if 'ClassVar' in adjuncts.traits 

337 else _interfaces.AttributeAssociations.Instance ) 

338 default = _determine_default_valuator( context, adjuncts ) 

339 informations.append( _interfaces.AttributeInformation( 

340 name = name, 

341 annotation = annotation_, 

342 description = description, 

343 association = association, 

344 default = default ) ) 

345 return informations 

346 

347 

348def _introspect_class_attributes( 

349 possessor: type, /, 

350 context: _context.Context, 

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

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

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

354 

355 Examines class attributes that do not have corresponding annotations 

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

357 ''' 

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

359 adjuncts = _interfaces.AdjunctsData( ) # dummy value 

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

361 if name in annotations: continue # already processed 

362 if not _is_attribute_visible( 

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

364 ): continue 

365 if callable( attribute ): continue # separately documented 

366 informations.append( _interfaces.AttributeInformation( 

367 name = name, 

368 annotation = _interfaces.absent, 

369 description = None, 

370 association = _interfaces.AttributeAssociations.Class, 

371 default = _default_default ) ) 

372 return informations 

373 

374 

375def _introspect_function( 

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

377 context: _context.Context, 

378 cache: _interfaces.AnnotationsCache, 

379 table: _nomina.FragmentsTable, 

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

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

382 

383 Gathers information about function arguments and return value 

384 from annotations and signature analysis. 

385 ''' 

386 annotations = _access_annotations( possessor, context ) 

387 if not annotations: return ( ) 

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

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

390 except ValueError as exc: 

391 context.notifier( 

392 'error', 

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

394 f"Reason: {exc}" ) 

395 return ( ) 

396 if signature.parameters: 

397 informations.extend( _introspect_function_valences( 

398 annotations, signature, context, cache, table ) ) 

399 if 'return' in annotations: 

400 informations.extend( _introspect_function_return( 

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

402 return tuple( informations ) 

403 

404 

405def _introspect_function_return( 

406 annotation: __.typx.Any, 

407 context: _context.Context, 

408 cache: _interfaces.AnnotationsCache, 

409 table: _nomina.FragmentsTable, 

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

411 ''' Introspects function return annotation. 

412 

413 Processes function return annotation to extract return type information 

414 and possible exception information from Raises annotations. 

415 ''' 

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

417 adjuncts = _interfaces.AdjunctsData( ) 

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

419 description = _compile_description( context, adjuncts, table ) 

420 informations.append( 

421 _interfaces.ReturnInformation( 

422 annotation = annotation_, description = description ) ) 

423 informations.extend( 

424 _interfaces.ExceptionInformation( 

425 annotation = _classes_sequence_to_union( extra.classes ), 

426 description = extra.description ) 

427 for extra in adjuncts.extras 

428 if isinstance( extra, _interfaces.Raises ) ) 

429 return tuple( informations ) 

430 

431 

432def _introspect_function_valences( 

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

434 signature: __.inspect.Signature, 

435 context: _context.Context, 

436 cache: _interfaces.AnnotationsCache, 

437 table: _nomina.FragmentsTable, 

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

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

440 

441 Processes function signature and annotations to create information 

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

443 default value handling. 

444 ''' 

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

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

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

448 adjuncts = _interfaces.AdjunctsData( ) 

449 if annotation is param.empty: 

450 annotation_ = _interfaces.absent 

451 description = None 

452 else: 

453 annotation_ = reduce_annotation( 

454 annotation, context, adjuncts, cache ) 

455 description = _compile_description( context, adjuncts, table ) 

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

457 else: default = _determine_default_valuator( context, adjuncts ) 

458 informations.append( _interfaces.ArgumentInformation( 

459 name = name, 

460 annotation = annotation_, 

461 description = description, 

462 paramspec = param, 

463 default = default ) ) 

464 return tuple( informations ) 

465 

466 

467def _introspect_module( 

468 possessor: __.types.ModuleType, /, 

469 context: _context.Context, 

470 introspection: _context.IntrospectionControl, 

471 cache: _interfaces.AnnotationsCache, 

472 table: _nomina.FragmentsTable, 

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

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

475 

476 Gathers information about module annotations and potentially about 

477 module attributes based on introspection control settings. 

478 ''' 

479 annotations = _access_annotations( possessor, context ) 

480 if not annotations: return ( ) 

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

482 informations.extend( _introspect_module_annotations( 

483 possessor, context, annotations, cache, table ) ) 

484 if introspection.module_control.scan_attributes: 

485 informations.extend( _introspect_module_attributes( 

486 possessor, context, annotations ) ) 

487 return tuple( informations ) 

488 

489 

490def _introspect_module_annotations( 

491 possessor: __.types.ModuleType, /, 

492 context: _context.Context, 

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

494 cache: _interfaces.AnnotationsCache, 

495 table: _nomina.FragmentsTable, 

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

497 ''' Introspects annotations of a module. 

498 

499 Processes module annotations to extract information about module 

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

501 ''' 

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

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

504 adjuncts = _interfaces.AdjunctsData( ) 

505 annotation_ = reduce_annotation( 

506 annotation, context, adjuncts, cache ) 

507 description = _compile_description( context, adjuncts, table ) 

508 if not _is_attribute_visible( 

509 possessor, name, annotation_, context, adjuncts, description 

510 ): continue 

511 default = _determine_default_valuator( context, adjuncts ) 

512 informations.append( _interfaces.AttributeInformation( 

513 name = name, 

514 annotation = annotation_, 

515 description = description, 

516 association = _interfaces.AttributeAssociations.Module, 

517 default = default ) ) 

518 return informations 

519 

520 

521def _introspect_module_attributes( 

522 possessor: __.types.ModuleType, /, 

523 context: _context.Context, 

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

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

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

527 

528 Examines module attributes that do not have corresponding annotations 

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

530 ''' 

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

532 adjuncts = _interfaces.AdjunctsData( ) # dummy value 

533 attribute: object 

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

535 if name in annotations: continue # already processed 

536 if not _is_attribute_visible( 

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

538 ): continue 

539 if callable( attribute ): continue # separately documented 

540 informations.append( _interfaces.AttributeInformation( 

541 name = name, 

542 annotation = _interfaces.absent, 

543 description = None, 

544 association = _interfaces.AttributeAssociations.Module, 

545 default = _default_default ) ) 

546 return informations 

547 

548 

549def _is_attribute_visible( # noqa: PLR0913 

550 possessor: _nomina.Documentable, 

551 name: str, 

552 annotation: __.typx.Any, 

553 context: _context.Context, 

554 adjuncts: _interfaces.AdjunctsData, 

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

556) -> bool: 

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

558 

559 Checks for explicit visibility settings in adjuncts data and falls 

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

561 to Default. 

562 ''' 

563 visibility = next( 

564 ( extra for extra in adjuncts.extras 

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

566 _interfaces.Visibilities.Default ) 

567 match visibility: 

568 case _interfaces.Visibilities.Conceal: return False 

569 case _interfaces.Visibilities.Reveal: return True 

570 case _: 

571 return context.visibility_decider( 

572 possessor, name, annotation, description ) 

573 

574 

575def _reduce_annotation_arguments( 

576 origin: __.typx.Any, 

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

578 context: _context.Context, 

579 adjuncts: _interfaces.AdjunctsData, 

580 cache: _interfaces.AnnotationsCache, 

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

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

583 

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

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

586 for Callable types. 

587 ''' 

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

589 return _reduce_annotation_for_callable( 

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

591 return tuple( 

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

593 for argument in arguments ) 

594 

595 

596def _reduce_annotation_core( 

597 annotation: __.typx.Any, 

598 context: _context.Context, 

599 adjuncts: _interfaces.AdjunctsData, 

600 cache: _interfaces.AnnotationsCache, 

601) -> __.typx.Any: 

602 ''' Core implementation of annotation reduction. 

603 

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

605 extracting metadata from Annotated types and processing generic types. 

606 Returns the reduced annotation. 

607 ''' 

608 origin = __.typx.get_origin( annotation ) 

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

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

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

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

613 arguments = __.typx.get_args( annotation ) 

614 if not arguments: return annotation 

615 if origin is __.typx.Annotated: 

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

617 return reduce_annotation( 

618 annotation.__origin__, context, adjuncts, cache ) 

619 return _filter_reconstitute_annotation( 

620 origin, arguments, context, adjuncts, cache ) 

621 

622 

623def _reduce_annotation_for_callable( 

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

625 context: _context.Context, 

626 adjuncts: _interfaces.AdjunctsData, 

627 cache: _interfaces.AnnotationsCache, 

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

629 ''' Reduces annotations for Callable types. 

630 

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

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

633 type separately and returns the reduced forms. 

634 ''' 

635 farguments, freturn = arguments 

636 if farguments is Ellipsis: 

637 farguments_r = Ellipsis 

638 else: 

639 farguments_r = [ 

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

641 for element in farguments ] 

642 freturn_r = ( 

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

644 return ( farguments_r, freturn_r )