Produce a branded schema for *ty*, or `None`.
(ty: Any)
| 77 | return origin is Union or origin is getattr(_types, "UnionType", None) |
| 78 | |
| 79 | def resolve(ty: Any) -> dict[str, Any] | None: |
| 80 | """Produce a branded schema for *ty*, or `None`.""" |
| 81 | if ty in branded: |
| 82 | return make_ref(branded[ty][0]) |
| 83 | |
| 84 | origin = typing.get_origin(ty) |
| 85 | args = typing.get_args(ty) |
| 86 | |
| 87 | # Union / Optional |
| 88 | if _is_union(origin): |
| 89 | non_none = [a for a in args if a is not type(None)] |
| 90 | has_none = len(non_none) < len(args) |
| 91 | if len(non_none) == 1: |
| 92 | inner = resolve(non_none[0]) |
| 93 | if inner is not None: |
| 94 | if has_none: |
| 95 | return {"anyOf": [inner, {"type": "null"}]} |
| 96 | return inner |
| 97 | return None |
| 98 | |
| 99 | # list[T] |
| 100 | if origin is list: |
| 101 | if args: |
| 102 | inner = resolve(args[0]) |
| 103 | if inner is not None: |
| 104 | return {"type": "array", "items": inner} |
| 105 | return None |
| 106 | |
| 107 | # tuple[T, ...] |
| 108 | if origin is tuple: |
| 109 | if len(args) == 2 and args[1] is Ellipsis: |
| 110 | inner = resolve(args[0]) |
| 111 | if inner is not None: |
| 112 | return {"type": "array", "items": inner} |
| 113 | return None |
| 114 | |
| 115 | # dict[K, V] — brand the value type (keys are always strings in JSON) |
| 116 | if origin is dict and len(args) == 2: |
| 117 | val = resolve(args[1]) |
| 118 | if val is not None: |
| 119 | return {"type": "object", "additionalProperties": val} |
| 120 | |
| 121 | return None |
| 122 | |
| 123 | # Step 2 — collect *all* reachable struct types (not just MODELS, |
| 124 | # since msgspec pulls in transitively-referenced structs too). |
no test coverage detected
searching dependent graphs…