haiway 0.10.11__py3-none-any.whl → 0.10.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. {haiway-0.10.11.dist-info → haiway-0.10.14.dist-info}/METADATA +1 -1
  2. haiway-0.10.14.dist-info/RECORD +4 -0
  3. haiway/__init__.py +0 -111
  4. haiway/context/__init__.py +0 -27
  5. haiway/context/access.py +0 -615
  6. haiway/context/disposables.py +0 -78
  7. haiway/context/identifier.py +0 -92
  8. haiway/context/logging.py +0 -176
  9. haiway/context/metrics.py +0 -165
  10. haiway/context/state.py +0 -113
  11. haiway/context/tasks.py +0 -64
  12. haiway/context/types.py +0 -12
  13. haiway/helpers/__init__.py +0 -21
  14. haiway/helpers/asynchrony.py +0 -225
  15. haiway/helpers/caching.py +0 -326
  16. haiway/helpers/metrics.py +0 -459
  17. haiway/helpers/retries.py +0 -223
  18. haiway/helpers/throttling.py +0 -133
  19. haiway/helpers/timeouted.py +0 -112
  20. haiway/helpers/tracing.py +0 -136
  21. haiway/py.typed +0 -0
  22. haiway/state/__init__.py +0 -12
  23. haiway/state/attributes.py +0 -741
  24. haiway/state/path.py +0 -524
  25. haiway/state/requirement.py +0 -229
  26. haiway/state/structure.py +0 -414
  27. haiway/state/validation.py +0 -468
  28. haiway/types/__init__.py +0 -14
  29. haiway/types/default.py +0 -108
  30. haiway/types/frozen.py +0 -5
  31. haiway/types/missing.py +0 -95
  32. haiway/utils/__init__.py +0 -28
  33. haiway/utils/always.py +0 -61
  34. haiway/utils/collections.py +0 -185
  35. haiway/utils/env.py +0 -230
  36. haiway/utils/freezing.py +0 -28
  37. haiway/utils/logs.py +0 -57
  38. haiway/utils/mimic.py +0 -77
  39. haiway/utils/noop.py +0 -24
  40. haiway/utils/queue.py +0 -82
  41. haiway-0.10.11.dist-info/RECORD +0 -42
  42. {haiway-0.10.11.dist-info → haiway-0.10.14.dist-info}/WHEEL +0 -0
  43. {haiway-0.10.11.dist-info → haiway-0.10.14.dist-info}/licenses/LICENSE +0 -0
@@ -1,741 +0,0 @@
1
- import types
2
- import typing
3
- from collections.abc import Callable, Mapping, MutableMapping, Sequence
4
- from types import GenericAlias, NoneType, UnionType
5
- from typing import (
6
- Any,
7
- ClassVar,
8
- ForwardRef,
9
- Generic,
10
- Literal,
11
- ParamSpec,
12
- Self,
13
- TypeAliasType,
14
- TypeVar,
15
- TypeVarTuple,
16
- _GenericAlias, # pyright: ignore
17
- get_args,
18
- get_origin,
19
- get_type_hints,
20
- is_typeddict,
21
- overload,
22
- )
23
-
24
- from haiway import types as haiway_types
25
- from haiway.types import MISSING, Missing
26
-
27
- __all__ = [
28
- "AttributeAnnotation",
29
- "attribute_annotations",
30
- "resolve_attribute_annotation",
31
- ]
32
-
33
-
34
- class AttributeAnnotation:
35
- def __init__(
36
- self,
37
- *,
38
- origin: Any,
39
- arguments: Sequence[Any] | None = None,
40
- required: bool = True,
41
- extra: Mapping[str, Any] | None = None,
42
- ) -> None:
43
- self.origin: Any = origin
44
- self.arguments: Sequence[Any]
45
- if arguments is None:
46
- self.arguments = ()
47
-
48
- else:
49
- self.arguments = arguments
50
-
51
- self.required: bool = required
52
- self.extra: Mapping[str, Any]
53
- if extra is None:
54
- self.extra = {}
55
-
56
- else:
57
- self.extra = extra
58
-
59
- def update_required(
60
- self,
61
- required: bool,
62
- /,
63
- ) -> Self:
64
- self.required = self.required and required
65
-
66
- return self
67
-
68
- def __str__(self) -> str:
69
- origin_str: str = getattr(self.origin, "__name__", str(self.origin))
70
- arguments_str: str
71
- if self.arguments:
72
- arguments_str = "[" + ", ".join(str(arg) for arg in self.arguments) + "]"
73
-
74
- else:
75
- arguments_str = ""
76
-
77
- if module := getattr(self.origin, "__module__", None):
78
- return f"{module}.{origin_str}{arguments_str}"
79
-
80
- else:
81
- return f"{origin_str}{arguments_str}"
82
-
83
-
84
- def attribute_annotations(
85
- cls: type[Any],
86
- /,
87
- type_parameters: Mapping[str, Any],
88
- ) -> Mapping[str, AttributeAnnotation]:
89
- self_annotation = AttributeAnnotation(
90
- origin=cls,
91
- # ignore arguments here, State (and draive.DataModel) will have them resolved at this stage
92
- arguments=[],
93
- )
94
-
95
- # ignore args_keys here, State (and draive.DataModel) will have them resolved at this stage
96
- recursion_guard: MutableMapping[str, AttributeAnnotation] = {
97
- _recursion_key(cls, default=str(self_annotation)): self_annotation
98
- }
99
-
100
- attributes: dict[str, AttributeAnnotation] = {}
101
- for key, annotation in get_type_hints(cls, localns={cls.__name__: cls}).items():
102
- # do not include private or special items
103
- if key.startswith("_"):
104
- continue
105
-
106
- # do not include ClassVars
107
- if (get_origin(annotation) or annotation) is ClassVar:
108
- continue
109
-
110
- attributes[key] = resolve_attribute_annotation(
111
- annotation,
112
- type_parameters=type_parameters,
113
- module=cls.__module__,
114
- self_annotation=self_annotation,
115
- recursion_guard=recursion_guard,
116
- )
117
-
118
- return attributes
119
-
120
-
121
- def _resolve_none(
122
- annotation: Any,
123
- ) -> AttributeAnnotation:
124
- return AttributeAnnotation(origin=NoneType)
125
-
126
-
127
- def _resolve_missing(
128
- annotation: Any,
129
- ) -> AttributeAnnotation:
130
- # special case - attributes marked as missing are not required
131
- # Missing does not work properly within TypedDict though
132
- return AttributeAnnotation(
133
- origin=Missing,
134
- required=False,
135
- )
136
-
137
-
138
- def _resolve_literal(
139
- annotation: Any,
140
- ) -> AttributeAnnotation:
141
- return AttributeAnnotation(
142
- origin=Literal,
143
- arguments=get_args(annotation),
144
- )
145
-
146
-
147
- def _resolve_forward_ref(
148
- annotation: ForwardRef | str,
149
- /,
150
- module: str,
151
- type_parameters: Mapping[str, Any],
152
- self_annotation: AttributeAnnotation | None,
153
- recursion_guard: MutableMapping[str, AttributeAnnotation],
154
- ) -> AttributeAnnotation:
155
- forward_ref: ForwardRef
156
- match annotation:
157
- case str() as string:
158
- forward_ref = ForwardRef(string, module=module)
159
-
160
- case reference:
161
- forward_ref = reference
162
-
163
- if evaluated := forward_ref._evaluate(
164
- globalns=None,
165
- localns=None,
166
- recursive_guard=frozenset(),
167
- ):
168
- return resolve_attribute_annotation(
169
- evaluated,
170
- type_parameters=type_parameters,
171
- module=module,
172
- self_annotation=self_annotation,
173
- recursion_guard=recursion_guard,
174
- )
175
-
176
- else:
177
- raise RuntimeError(f"Cannot resolve annotation of {annotation}")
178
-
179
-
180
- def _resolve_generic_alias( # noqa: PLR0911, PLR0912
181
- annotation: GenericAlias,
182
- /,
183
- module: str,
184
- type_parameters: Mapping[str, Any],
185
- self_annotation: AttributeAnnotation | None,
186
- recursion_guard: MutableMapping[str, AttributeAnnotation],
187
- ) -> AttributeAnnotation:
188
- match get_origin(annotation):
189
- case TypeAliasType() as alias: # pyright: ignore[reportUnnecessaryComparison]
190
- return _resolve_type_alias(
191
- alias,
192
- type_parameters={
193
- # verify if we should pass all parameters
194
- param.__name__: get_args(annotation)[idx]
195
- for idx, param in enumerate(alias.__type_params__)
196
- },
197
- module=module,
198
- self_annotation=self_annotation,
199
- recursion_guard=recursion_guard,
200
- )
201
-
202
- case origin if issubclass(origin, Generic):
203
- match origin.__class_getitem__( # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
204
- tuple(
205
- type_parameters.get(
206
- arg.__name__,
207
- arg.__bound__ or Any,
208
- )
209
- if isinstance(arg, TypeVar)
210
- else arg
211
- for arg in get_args(annotation)
212
- )
213
- ):
214
- case GenericAlias() as generic_alias:
215
- resolved_attribute = AttributeAnnotation(origin=generic_alias.__origin__)
216
- if recursion_key := _recursion_key(generic_alias):
217
- if recursive := recursion_guard.get(recursion_key):
218
- return recursive
219
-
220
- else:
221
- recursion_guard[recursion_key] = resolved_attribute
222
-
223
- resolved_attribute.arguments = [
224
- resolve_attribute_annotation(
225
- argument,
226
- type_parameters=type_parameters,
227
- module=module,
228
- self_annotation=self_annotation,
229
- recursion_guard=recursion_guard,
230
- )
231
- for argument in get_args(generic_alias)
232
- ]
233
-
234
- return resolved_attribute
235
-
236
- # use resolved type if it is not an alias again
237
- case resolved: # pyright: ignore
238
- resolved_attribute = AttributeAnnotation(origin=resolved)
239
-
240
- if recursion_key := _recursion_key(origin):
241
- if recursive := recursion_guard.get(recursion_key):
242
- return recursive
243
-
244
- else:
245
- recursion_guard[recursion_key] = resolved_attribute
246
-
247
- resolved_attribute.arguments = [
248
- resolve_attribute_annotation(
249
- argument,
250
- type_parameters=type_parameters,
251
- module=module,
252
- self_annotation=self_annotation,
253
- recursion_guard=recursion_guard,
254
- )
255
- for argument in get_args(annotation)
256
- ]
257
-
258
- return resolved_attribute
259
-
260
- case origin:
261
- resolved_attribute = AttributeAnnotation(origin=origin)
262
-
263
- if recursion_key := _recursion_key(origin):
264
- if recursive := recursion_guard.get(recursion_key):
265
- return recursive
266
-
267
- resolved_attribute.arguments = [
268
- resolve_attribute_annotation(
269
- argument,
270
- type_parameters=type_parameters,
271
- module=module,
272
- self_annotation=self_annotation,
273
- recursion_guard=recursion_guard,
274
- )
275
- for argument in get_args(annotation)
276
- ]
277
-
278
- return resolved_attribute
279
-
280
-
281
- def _resolve_special_generic_alias(
282
- annotation: Any,
283
- /,
284
- module: str,
285
- type_parameters: Mapping[str, Any],
286
- self_annotation: AttributeAnnotation | None,
287
- recursion_guard: MutableMapping[str, AttributeAnnotation],
288
- ) -> AttributeAnnotation:
289
- origin: type[Any] = get_origin(annotation)
290
- resolved_attribute = AttributeAnnotation(origin=origin)
291
-
292
- if recursion_key := _recursion_key(origin):
293
- if recursive := recursion_guard.get(recursion_key):
294
- return recursive
295
-
296
- else:
297
- recursion_guard[recursion_key] = resolved_attribute
298
-
299
- resolved_attribute.arguments = [
300
- resolve_attribute_annotation(
301
- argument,
302
- type_parameters=type_parameters,
303
- module=module,
304
- self_annotation=self_annotation,
305
- recursion_guard=recursion_guard,
306
- )
307
- for argument in get_args(annotation)
308
- ]
309
-
310
- return resolved_attribute
311
-
312
-
313
- def _resolve_type_alias(
314
- annotation: TypeAliasType,
315
- /,
316
- module: str,
317
- type_parameters: Mapping[str, Any],
318
- self_annotation: AttributeAnnotation | None,
319
- recursion_guard: MutableMapping[str, AttributeAnnotation],
320
- ) -> AttributeAnnotation:
321
- resolved_attribute = AttributeAnnotation(origin=MISSING)
322
-
323
- if recursion_key := _recursion_key(annotation):
324
- if recursive := recursion_guard.get(recursion_key):
325
- return recursive
326
-
327
- else:
328
- recursion_guard[recursion_key] = resolved_attribute
329
-
330
- resolved: AttributeAnnotation = resolve_attribute_annotation(
331
- annotation.__value__,
332
- module=annotation.__module__ or module,
333
- type_parameters=type_parameters,
334
- self_annotation=self_annotation,
335
- recursion_guard=recursion_guard,
336
- )
337
-
338
- resolved_attribute.origin = resolved.origin
339
- resolved_attribute.arguments = resolved.arguments
340
- resolved_attribute.extra = resolved.extra
341
- resolved_attribute.required = resolved.required
342
-
343
- return resolved_attribute
344
-
345
-
346
- def _resolve_type_var(
347
- annotation: TypeVar,
348
- /,
349
- module: str,
350
- type_parameters: Mapping[str, Any],
351
- self_annotation: AttributeAnnotation | None,
352
- recursion_guard: MutableMapping[str, AttributeAnnotation],
353
- ) -> AttributeAnnotation:
354
- return resolve_attribute_annotation(
355
- type_parameters.get(
356
- annotation.__name__,
357
- # use bound as default or Any otherwise
358
- annotation.__bound__ or Any,
359
- ),
360
- module=module,
361
- type_parameters=type_parameters,
362
- self_annotation=self_annotation,
363
- recursion_guard=recursion_guard,
364
- )
365
-
366
-
367
- def _resolve_type_union(
368
- annotation: UnionType,
369
- /,
370
- module: str,
371
- type_parameters: Mapping[str, Any],
372
- self_annotation: AttributeAnnotation | None,
373
- recursion_guard: MutableMapping[str, AttributeAnnotation],
374
- ) -> AttributeAnnotation:
375
- arguments: Sequence[AttributeAnnotation] = [
376
- resolve_attribute_annotation(
377
- argument,
378
- type_parameters=type_parameters,
379
- module=module,
380
- self_annotation=self_annotation,
381
- recursion_guard=recursion_guard,
382
- )
383
- for argument in get_args(annotation)
384
- ]
385
- return AttributeAnnotation(
386
- origin=UnionType, # pyright: ignore[reportArgumentType]
387
- arguments=arguments,
388
- required=all(argument.required for argument in arguments),
389
- )
390
-
391
-
392
- def _resolve_callable(
393
- annotation: Any,
394
- /,
395
- module: str,
396
- type_parameters: Mapping[str, Any],
397
- self_annotation: AttributeAnnotation | None,
398
- recursion_guard: MutableMapping[str, AttributeAnnotation],
399
- ) -> AttributeAnnotation:
400
- return AttributeAnnotation(
401
- origin=Callable,
402
- arguments=[
403
- resolve_attribute_annotation(
404
- argument,
405
- type_parameters=type_parameters,
406
- module=module,
407
- self_annotation=self_annotation,
408
- recursion_guard=recursion_guard,
409
- )
410
- for argument in get_args(annotation)
411
- ],
412
- )
413
-
414
-
415
- def _resolve_type_box(
416
- annotation: Any,
417
- /,
418
- module: str,
419
- type_parameters: Mapping[str, Any],
420
- self_annotation: AttributeAnnotation | None,
421
- recursion_guard: MutableMapping[str, AttributeAnnotation],
422
- ) -> AttributeAnnotation:
423
- return resolve_attribute_annotation(
424
- get_args(annotation)[0],
425
- type_parameters=type_parameters,
426
- module=module,
427
- self_annotation=self_annotation,
428
- recursion_guard=recursion_guard,
429
- )
430
-
431
-
432
- def _resolve_type_not_required(
433
- annotation: Any,
434
- /,
435
- module: str,
436
- type_parameters: Mapping[str, Any],
437
- self_annotation: AttributeAnnotation | None,
438
- recursion_guard: MutableMapping[str, AttributeAnnotation],
439
- ) -> AttributeAnnotation:
440
- return resolve_attribute_annotation(
441
- get_args(annotation)[0],
442
- type_parameters=type_parameters,
443
- module=module,
444
- self_annotation=self_annotation,
445
- recursion_guard=recursion_guard,
446
- ).update_required(False)
447
-
448
-
449
- def _resolve_type_optional(
450
- annotation: Any,
451
- /,
452
- module: str,
453
- type_parameters: Mapping[str, Any],
454
- self_annotation: AttributeAnnotation | None,
455
- recursion_guard: MutableMapping[str, AttributeAnnotation],
456
- ) -> AttributeAnnotation:
457
- return AttributeAnnotation(
458
- origin=UnionType, # pyright: ignore[reportArgumentType]
459
- arguments=[
460
- resolve_attribute_annotation(
461
- get_args(annotation)[0],
462
- type_parameters=type_parameters,
463
- module=module,
464
- self_annotation=self_annotation,
465
- recursion_guard=recursion_guard,
466
- ),
467
- AttributeAnnotation(origin=NoneType),
468
- ],
469
- )
470
-
471
-
472
- def _resolve_type_typeddict(
473
- annotation: Any,
474
- /,
475
- module: str,
476
- type_parameters: Mapping[str, Any],
477
- self_annotation: AttributeAnnotation | None,
478
- recursion_guard: MutableMapping[str, AttributeAnnotation],
479
- ) -> AttributeAnnotation:
480
- resolved_attribute = AttributeAnnotation(origin=annotation)
481
-
482
- if recursion_key := _recursion_key(annotation):
483
- if recursive := recursion_guard.get(recursion_key):
484
- return recursive
485
-
486
- else:
487
- recursion_guard[recursion_key] = resolved_attribute
488
-
489
- resolved_attribute.arguments = [
490
- resolve_attribute_annotation(
491
- argument,
492
- type_parameters=type_parameters,
493
- module=module,
494
- self_annotation=self_annotation,
495
- recursion_guard=recursion_guard,
496
- )
497
- for argument in get_args(annotation)
498
- ]
499
-
500
- attributes: dict[str, AttributeAnnotation] = {}
501
- for key, element in get_type_hints(
502
- annotation,
503
- localns={annotation.__name__: annotation},
504
- ).items():
505
- attributes[key] = resolve_attribute_annotation(
506
- element,
507
- type_parameters=type_parameters,
508
- module=getattr(annotation, "__module__", module),
509
- self_annotation=resolved_attribute,
510
- recursion_guard=recursion_guard,
511
- ).update_required(key in annotation.__required_keys__)
512
- resolved_attribute.extra = {
513
- "attributes": attributes,
514
- "required": annotation.__required_keys__,
515
- }
516
- return resolved_attribute
517
-
518
-
519
- def _resolve_type(
520
- annotation: Any,
521
- /,
522
- module: str,
523
- type_parameters: Mapping[str, Any],
524
- self_annotation: AttributeAnnotation | None,
525
- recursion_guard: MutableMapping[str, AttributeAnnotation],
526
- ) -> AttributeAnnotation:
527
- if recursion_key := _recursion_key(annotation):
528
- if recursive := recursion_guard.get(recursion_key):
529
- return recursive
530
-
531
- # not updating recursion guard here - it might be a builtin type
532
-
533
- return AttributeAnnotation(
534
- origin=annotation,
535
- arguments=[
536
- resolve_attribute_annotation(
537
- argument,
538
- type_parameters=type_parameters,
539
- module=module,
540
- self_annotation=self_annotation,
541
- recursion_guard=recursion_guard,
542
- )
543
- for argument in get_args(annotation)
544
- ],
545
- )
546
-
547
-
548
- def resolve_attribute_annotation( # noqa: C901, PLR0911, PLR0912
549
- annotation: Any,
550
- /,
551
- module: str,
552
- type_parameters: Mapping[str, Any],
553
- self_annotation: AttributeAnnotation | None,
554
- recursion_guard: MutableMapping[str, AttributeAnnotation],
555
- ) -> AttributeAnnotation:
556
- match get_origin(annotation) or annotation:
557
- case types.NoneType | None:
558
- return _resolve_none(
559
- annotation=annotation,
560
- )
561
-
562
- case haiway_types.Missing:
563
- return _resolve_missing(
564
- annotation=annotation,
565
- )
566
-
567
- case types.UnionType | typing.Union:
568
- return _resolve_type_union(
569
- annotation,
570
- module=module,
571
- type_parameters=type_parameters,
572
- self_annotation=self_annotation,
573
- recursion_guard=recursion_guard,
574
- )
575
-
576
- case typing.Literal:
577
- return _resolve_literal(annotation)
578
-
579
- case typeddict if is_typeddict(typeddict):
580
- return _resolve_type_typeddict(
581
- typeddict,
582
- module=module,
583
- type_parameters=type_parameters,
584
- self_annotation=self_annotation,
585
- recursion_guard=recursion_guard,
586
- )
587
-
588
- case typing.Callable: # pyright: ignore
589
- return _resolve_callable(
590
- annotation,
591
- module=module,
592
- type_parameters=type_parameters,
593
- self_annotation=self_annotation,
594
- recursion_guard=recursion_guard,
595
- )
596
-
597
- case typing.Annotated | typing.Final | typing.Required:
598
- return _resolve_type_box(
599
- annotation,
600
- module=module,
601
- type_parameters=type_parameters,
602
- self_annotation=self_annotation,
603
- recursion_guard=recursion_guard,
604
- )
605
-
606
- case typing.NotRequired:
607
- return _resolve_type_not_required(
608
- annotation,
609
- module=module,
610
- type_parameters=type_parameters,
611
- self_annotation=self_annotation,
612
- recursion_guard=recursion_guard,
613
- )
614
-
615
- case typing.Optional: # optional is a Union[Value, None]
616
- return _resolve_type_optional(
617
- annotation,
618
- module=module,
619
- type_parameters=type_parameters,
620
- self_annotation=self_annotation,
621
- recursion_guard=recursion_guard,
622
- )
623
-
624
- case typing.Self: # pyright: ignore
625
- if self_annotation:
626
- return self_annotation
627
-
628
- else:
629
- raise RuntimeError(f"Unresolved Self annotation: {annotation}")
630
-
631
- case _:
632
- match annotation:
633
- case str() | ForwardRef():
634
- return _resolve_forward_ref(
635
- annotation,
636
- module=module,
637
- type_parameters=type_parameters,
638
- self_annotation=self_annotation,
639
- recursion_guard=recursion_guard,
640
- )
641
-
642
- case GenericAlias():
643
- return _resolve_generic_alias(
644
- annotation,
645
- module=module,
646
- type_parameters=type_parameters,
647
- self_annotation=self_annotation,
648
- recursion_guard=recursion_guard,
649
- )
650
-
651
- case _GenericAlias():
652
- return _resolve_special_generic_alias(
653
- annotation,
654
- module=module,
655
- type_parameters=type_parameters,
656
- self_annotation=self_annotation,
657
- recursion_guard=recursion_guard,
658
- )
659
-
660
- case TypeAliasType():
661
- return _resolve_type_alias(
662
- annotation,
663
- module=module,
664
- type_parameters=type_parameters,
665
- self_annotation=self_annotation,
666
- recursion_guard=recursion_guard,
667
- )
668
-
669
- case TypeVar():
670
- return _resolve_type_var(
671
- annotation,
672
- module=module,
673
- type_parameters=type_parameters,
674
- self_annotation=self_annotation,
675
- recursion_guard=recursion_guard,
676
- )
677
-
678
- case ParamSpec():
679
- raise NotImplementedError(f"Unresolved ParamSpec annotation: {annotation}")
680
-
681
- case TypeVarTuple():
682
- raise NotImplementedError(f"Unresolved TypeVarTuple annotation: {annotation}")
683
-
684
- case _: # finally use whatever there was
685
- return _resolve_type(
686
- annotation,
687
- module=module,
688
- type_parameters=type_parameters,
689
- self_annotation=self_annotation,
690
- recursion_guard=recursion_guard,
691
- )
692
-
693
-
694
- @overload
695
- def _recursion_key(
696
- annotation: Any,
697
- /,
698
- ) -> str | None: ...
699
-
700
-
701
- @overload
702
- def _recursion_key(
703
- annotation: Any,
704
- /,
705
- default: str,
706
- ) -> str: ...
707
-
708
-
709
- def _recursion_key(
710
- annotation: Any,
711
- /,
712
- default: str | None = None,
713
- ) -> str | None:
714
- args_suffix: str
715
- if arguments := get_args(annotation):
716
- arguments_string: str = ", ".join(
717
- _recursion_key(
718
- argument,
719
- default="?",
720
- )
721
- for argument in arguments
722
- )
723
- args_suffix = f"[{arguments_string}]"
724
-
725
- else:
726
- args_suffix = ""
727
-
728
- if qualname := getattr(annotation, "__qualname__", None):
729
- return qualname + args_suffix
730
-
731
- module_prefix: str
732
- if module := getattr(annotation, "__module__", None):
733
- module_prefix = module + "."
734
-
735
- else:
736
- module_prefix = ""
737
-
738
- if name := getattr(annotation, "__name__", None):
739
- return module_prefix + name + args_suffix
740
-
741
- return default