alt-stack-openapi-pydantic 1.6.2__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.
@@ -0,0 +1,141 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from typing import Any, TypedDict
6
+
7
+ from .types import AnySchema
8
+
9
+
10
+ def _sort_object_deep(obj: Any) -> Any:
11
+ if obj is None or not isinstance(obj, (dict, list)):
12
+ return obj
13
+ if isinstance(obj, list):
14
+ return [_sort_object_deep(item) for item in obj]
15
+ sorted_obj: dict[str, Any] = {}
16
+ for key in sorted(obj.keys()):
17
+ sorted_obj[key] = _sort_object_deep(obj[key])
18
+ return sorted_obj
19
+
20
+
21
+ def get_schema_fingerprint(schema: AnySchema) -> str:
22
+ return json.dumps(_sort_object_deep(schema), sort_keys=True)
23
+
24
+
25
+ @dataclass
26
+ class SchemaRegistry:
27
+ fingerprint_to_name: dict[str, str]
28
+
29
+
30
+ def create_schema_registry() -> SchemaRegistry:
31
+ return SchemaRegistry(fingerprint_to_name={})
32
+
33
+
34
+ @dataclass
35
+ class RegisterSchemaResult:
36
+ is_new: bool
37
+ canonical_name: str
38
+
39
+
40
+ def register_schema(
41
+ registry: SchemaRegistry,
42
+ name: str,
43
+ schema: AnySchema,
44
+ ) -> RegisterSchemaResult:
45
+ fingerprint = get_schema_fingerprint(schema)
46
+ existing = registry.fingerprint_to_name.get(fingerprint)
47
+ if existing:
48
+ return RegisterSchemaResult(is_new=False, canonical_name=existing)
49
+ registry.fingerprint_to_name[fingerprint] = name
50
+ return RegisterSchemaResult(is_new=True, canonical_name=name)
51
+
52
+
53
+ def pre_register_schema(
54
+ registry: SchemaRegistry,
55
+ name: str,
56
+ fingerprint: str,
57
+ ) -> None:
58
+ registry.fingerprint_to_name[fingerprint] = name
59
+
60
+
61
+ def extract_error_code(schema: AnySchema) -> str | None:
62
+ props = schema.get("properties")
63
+ if not isinstance(props, dict):
64
+ return None
65
+ error_obj = props.get("error")
66
+ if not isinstance(error_obj, dict):
67
+ return None
68
+ error_props = error_obj.get("properties")
69
+ if not isinstance(error_props, dict):
70
+ return None
71
+ code_schema = error_props.get("code")
72
+ if not isinstance(code_schema, dict):
73
+ return None
74
+ code_enum = code_schema.get("enum")
75
+ if isinstance(code_enum, list) and len(code_enum) == 1:
76
+ return str(code_enum[0])
77
+ return None
78
+
79
+
80
+ def error_code_to_pascal_case(code: str) -> str:
81
+ return "".join(part.capitalize() for part in code.split("_"))
82
+
83
+
84
+ def generate_common_error_schema_name(error_code: str) -> str:
85
+ return f"{error_code_to_pascal_case(error_code)}Error"
86
+
87
+
88
+ @dataclass
89
+ class CommonSchema:
90
+ name: str
91
+ schema: AnySchema
92
+ fingerprint: str
93
+ count: int
94
+
95
+
96
+ class NamedSchema(TypedDict):
97
+ name: str
98
+ schema: AnySchema
99
+
100
+
101
+ def find_common_schemas(
102
+ schemas: list[NamedSchema],
103
+ min_count: int = 2,
104
+ ) -> list[CommonSchema]:
105
+ fingerprints: dict[str, dict[str, Any]] = {}
106
+
107
+ for item in schemas:
108
+ name = item["name"]
109
+ schema = item["schema"]
110
+ fingerprint = get_schema_fingerprint(schema)
111
+ existing = fingerprints.get(fingerprint)
112
+ if existing:
113
+ existing["names"].append(name)
114
+ else:
115
+ fingerprints[fingerprint] = {
116
+ "schema": schema,
117
+ "names": [name],
118
+ "error_code": extract_error_code(schema),
119
+ }
120
+
121
+ common: list[CommonSchema] = []
122
+ for fingerprint, data in fingerprints.items():
123
+ names = data["names"]
124
+ if len(names) < min_count:
125
+ continue
126
+ error_code = data["error_code"]
127
+ name = (
128
+ generate_common_error_schema_name(error_code)
129
+ if isinstance(error_code, str)
130
+ else names[0]
131
+ )
132
+ common.append(
133
+ CommonSchema(
134
+ name=name,
135
+ schema=data["schema"],
136
+ fingerprint=fingerprint,
137
+ count=len(names),
138
+ )
139
+ )
140
+
141
+ return sorted(common, key=lambda item: item.count, reverse=True)
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from .emission import openapi_to_pydantic_code
4
+ from .rendering import schema_to_type_expr
5
+ from .types import AnySchema
6
+
7
+
8
+ def convert_schema_to_pydantic_string(schema: AnySchema | None) -> str:
9
+ return schema_to_type_expr(schema)
10
+
11
+
12
+ __all__ = ["convert_schema_to_pydantic_string", "openapi_to_pydantic_code"]
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ def format_openapi_metadata(meta: dict[str, Any]) -> str:
7
+ ordered = {key: meta[key] for key in sorted(meta)}
8
+ return f"json_schema_extra={{'openapi': {ordered!r}}}"
9
+
10
+
11
+ def wrap_annotated(base: str, metadata: list[str]) -> str:
12
+ if not metadata:
13
+ return base
14
+ parts = ", ".join(metadata)
15
+ return f"Annotated[{base}, {parts}]"
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from .types import AnySchema, OpenAPIObjectSchema
4
+
5
+ __all__ = ["AnySchema", "OpenAPIObjectSchema"]
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable
4
+
5
+ from ..type_render import format_openapi_metadata, wrap_annotated
6
+
7
+
8
+ def convert_openapi_array_to_pydantic(
9
+ schema: dict[str, Any],
10
+ convert_schema: Callable[[dict[str, Any]], str],
11
+ ) -> str:
12
+ items = schema.get("items")
13
+ item_type = "Any"
14
+ if isinstance(items, dict):
15
+ item_type = convert_schema(items)
16
+
17
+ base = f"list[{item_type}]"
18
+
19
+ min_items = schema.get("minItems")
20
+ max_items = schema.get("maxItems")
21
+
22
+ field_args: list[str] = []
23
+ if isinstance(min_items, int):
24
+ field_args.append(f"min_length={min_items}")
25
+ if isinstance(max_items, int):
26
+ field_args.append(f"max_length={max_items}")
27
+
28
+ meta: dict[str, Any] = {}
29
+ if isinstance(min_items, int):
30
+ meta["minItems"] = min_items
31
+ if isinstance(max_items, int):
32
+ meta["maxItems"] = max_items
33
+ if meta:
34
+ field_args.append(format_openapi_metadata(meta))
35
+
36
+ if field_args:
37
+ return wrap_annotated(base, [f"Field({', '.join(field_args)})"])
38
+
39
+ return base
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from ..registry import get_schema_exported_variable_name_for_primitive_type
6
+ from ..type_render import wrap_annotated
7
+
8
+
9
+ def convert_openapi_boolean_to_pydantic(schema: dict[str, Any]) -> str:
10
+ _ = schema
11
+ custom = get_schema_exported_variable_name_for_primitive_type("boolean")
12
+ if custom:
13
+ return custom
14
+ return wrap_annotated("bool", ["Field(strict=True)"])
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable
4
+
5
+
6
+ def convert_openapi_intersection_to_pydantic(
7
+ schema: dict[str, Any],
8
+ convert_schema: Callable[[dict[str, Any]], str],
9
+ ) -> str:
10
+ items = schema.get("allOf")
11
+ if not isinstance(items, list):
12
+ return "Any"
13
+
14
+ parts = [convert_schema(item) for item in items if isinstance(item, dict)]
15
+ if not parts:
16
+ return "Any"
17
+ if len(parts) == 1:
18
+ return parts[0]
19
+
20
+ return f"all_of({', '.join(parts)})"
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from ..registry import get_schema_exported_variable_name_for_primitive_type
6
+ from ..type_render import format_openapi_metadata, wrap_annotated
7
+
8
+
9
+ def convert_openapi_number_to_pydantic(schema: dict[str, Any]) -> str:
10
+ if isinstance(schema.get("enum"), list):
11
+ values = ", ".join(repr(value) for value in schema["enum"])
12
+ return f"Literal[{values}]"
13
+
14
+ schema_type = schema.get("type")
15
+ if schema_type in {"number", "integer", "boolean"}:
16
+ custom = get_schema_exported_variable_name_for_primitive_type(schema_type)
17
+ if custom:
18
+ return custom
19
+
20
+ base = "int" if schema.get("type") == "integer" else "float"
21
+
22
+ minimum = schema.get("minimum")
23
+ maximum = schema.get("maximum")
24
+
25
+ field_args: list[str] = ["strict=True"]
26
+ if isinstance(minimum, (int, float)):
27
+ field_args.append(f"ge={minimum}")
28
+ if isinstance(maximum, (int, float)):
29
+ field_args.append(f"le={maximum}")
30
+
31
+ meta: dict[str, Any] = {}
32
+ if isinstance(minimum, (int, float)):
33
+ meta["minimum"] = minimum
34
+ if isinstance(maximum, (int, float)):
35
+ meta["maximum"] = maximum
36
+ if meta:
37
+ field_args.append(format_openapi_metadata(meta))
38
+
39
+ return wrap_annotated(base, [f"Field({', '.join(field_args)})"])
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable
4
+
5
+
6
+ def convert_openapi_object_to_pydantic(
7
+ schema: dict[str, Any],
8
+ convert_schema: Callable[[dict[str, Any]], str],
9
+ ) -> str:
10
+ additional = schema.get("additionalProperties")
11
+
12
+ if isinstance(additional, dict):
13
+ return f"dict[str, {convert_schema(additional)}]"
14
+
15
+ return "dict[str, Any]"
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from ..registry import get_schema_exported_variable_name_for_string_format
6
+ from ..type_render import format_openapi_metadata, wrap_annotated
7
+
8
+
9
+ def convert_openapi_string_to_pydantic(schema: dict[str, Any]) -> str:
10
+ if isinstance(schema.get("enum"), list):
11
+ values = ", ".join(repr(value) for value in schema["enum"])
12
+ return f"Literal[{values}]"
13
+
14
+ fmt = schema.get("format")
15
+ if isinstance(fmt, str) and fmt:
16
+ custom_schema = get_schema_exported_variable_name_for_string_format(fmt)
17
+ if custom_schema:
18
+ return custom_schema
19
+ else:
20
+ fmt = None
21
+
22
+ base = "str"
23
+ if fmt == "email":
24
+ base = "EmailStr"
25
+ elif fmt in {"url", "uri"}:
26
+ base = "AnyUrl"
27
+ elif fmt == "uuid":
28
+ base = "UUID"
29
+
30
+ min_length = schema.get("minLength")
31
+ max_length = schema.get("maxLength")
32
+ pattern = schema.get("pattern")
33
+ pattern_is_explicit = isinstance(pattern, str)
34
+
35
+ if fmt == "color-hex" and not pattern_is_explicit:
36
+ pattern = "^[a-fA-F0-9]{6}$"
37
+
38
+ field_args: list[str] = []
39
+ if base == "str":
40
+ field_args.append("strict=True")
41
+ if isinstance(min_length, int):
42
+ field_args.append(f"min_length={min_length}")
43
+ if isinstance(max_length, int):
44
+ field_args.append(f"max_length={max_length}")
45
+ if isinstance(pattern, str):
46
+ field_args.append(f"pattern={pattern!r}")
47
+
48
+ meta: dict[str, Any] = {}
49
+ if isinstance(fmt, str):
50
+ meta["format"] = fmt
51
+ if isinstance(pattern, str) and pattern_is_explicit:
52
+ meta["pattern"] = pattern
53
+ if isinstance(min_length, int):
54
+ meta["minLength"] = min_length
55
+ if isinstance(max_length, int):
56
+ meta["maxLength"] = max_length
57
+
58
+ if meta:
59
+ field_args.append(format_openapi_metadata(meta))
60
+
61
+ if field_args:
62
+ return wrap_annotated(base, [f"Field({', '.join(field_args)})"])
63
+
64
+ return base
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ AnySchema = dict[str, Any]
6
+ OpenAPIObjectSchema = dict[str, Any]
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable
4
+
5
+ from ..type_render import format_openapi_metadata, wrap_annotated
6
+
7
+
8
+ def convert_openapi_union_to_pydantic(
9
+ schema: dict[str, Any],
10
+ convert_schema: Callable[[dict[str, Any]], str],
11
+ ) -> str:
12
+ items = schema.get("oneOf")
13
+ if not isinstance(items, list):
14
+ return "Any"
15
+
16
+ item_types = [convert_schema(item) for item in items if isinstance(item, dict)]
17
+ if not item_types:
18
+ return "Any"
19
+
20
+ union_expr = f"Union[{', '.join(item_types)}]" if len(item_types) > 1 else item_types[0]
21
+
22
+ discriminator = schema.get("discriminator")
23
+ field_args: list[str] = []
24
+ meta: dict[str, Any] = {}
25
+ if isinstance(discriminator, dict):
26
+ property_name = discriminator.get("propertyName")
27
+ if isinstance(property_name, str) and property_name:
28
+ field_args.append(f"discriminator={property_name!r}")
29
+ meta["discriminator"] = discriminator
30
+
31
+ if meta:
32
+ field_args.append(format_openapi_metadata(meta))
33
+
34
+ if field_args:
35
+ return wrap_annotated(union_expr, [f"Field({', '.join(field_args)})"])
36
+
37
+ return union_expr
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ import keyword
4
+ import re
5
+
6
+ _VALID_IDENTIFIER = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
7
+
8
+
9
+ def to_pascal_case(value: str) -> str:
10
+ parts = re.split(r"[^A-Za-z0-9]+", value)
11
+ return "".join(part[:1].upper() + part[1:] for part in parts if part)
12
+
13
+
14
+ def to_snake_case(value: str) -> str:
15
+ value = re.sub(r"[^A-Za-z0-9]+", "_", value).strip("_")
16
+ if not value:
17
+ return "field"
18
+ value = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", value)
19
+ return value.lower()
20
+
21
+
22
+ def safe_identifier(name: str, used: set[str]) -> tuple[str, bool]:
23
+ candidate = to_snake_case(name)
24
+ if not _VALID_IDENTIFIER.match(candidate) or keyword.iskeyword(candidate):
25
+ candidate = f"_{candidate}"
26
+ base = candidate
27
+ idx = 1
28
+ while candidate in used:
29
+ idx += 1
30
+ candidate = f"{base}_{idx}"
31
+ used.add(candidate)
32
+ return candidate, candidate != name
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: alt-stack-openapi-pydantic
3
+ Version: 1.6.2
4
+ Summary: Generate Pydantic models, typed route maps, and an asyncio client from OpenAPI
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/anthonyaltieri/alt-stack
7
+ Project-URL: Repository, https://github.com/anthonyaltieri/alt-stack
8
+ Project-URL: Issues, https://github.com/anthonyaltieri/alt-stack/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Typing :: Typed
13
+ Requires-Python: >=3.11
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: pydantic[email]>=2.7.0
17
+ Provides-Extra: dev
18
+ Requires-Dist: alt-stack-http-client-httpx; extra == "dev"
19
+ Requires-Dist: pyrefly>=1.0.0; extra == "dev"
20
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
21
+ Requires-Dist: ruff>=0.6.0; extra == "dev"
22
+ Requires-Dist: ty>=0.0.78; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # `alt-stack-openapi-pydantic`
26
+
27
+ Generate Python 3.11+ Pydantic 2 models, statically typed `Request`/`Response` route maps, and an asyncio `HttpxApiClient` from OpenAPI JSON. This is the Python counterpart of `@alt-stack/zod-openapi`; the generated client runs on `alt-stack-http-client-httpx`.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ python -m pip install alt-stack-openapi-pydantic
33
+ ```
34
+
35
+ Consumers of a generated SDK install the runtime package instead:
36
+
37
+ ```bash
38
+ python -m pip install alt-stack-http-client-httpx
39
+ ```
40
+
41
+ ## Generate
42
+
43
+ ```bash
44
+ alt-stack-openapi-pydantic ./openapi.json --output ./generated_types.py
45
+ ```
46
+
47
+ `input` may be a local JSON path or HTTP(S) URL. Use `--registry` to execute custom type mappings and `--include` to insert imports/definitions into output.
48
+
49
+ ```python
50
+ from generated_types import User
51
+
52
+ user = User.model_validate({"id": "u_1", "name": "Ada"})
53
+ ```
54
+
55
+ The CLI enables route generation and emits `Request`/`Response` dictionaries whose leaves are Pydantic model classes. The dictionaries carry generated `TypedDict` annotations, so type checkers resolve literal lookups to the exact model and reject unknown paths, methods, request parts, and status codes:
56
+
57
+ ```python
58
+ from generated_types import Request, Response
59
+
60
+ Request["/users"]["POST"]["body"] # type[CreateUserBody]
61
+ Response["/users/{id}"]["GET"]["200"] # type[User]
62
+ Response["/users/{id}"]["GET"]["999"] # static error: unknown status
63
+ ```
64
+
65
+ Generated modules also include an asyncio `HttpxApiClient` with one typed method per route, built on `alt_stack_http_client_httpx`:
66
+
67
+ ```python
68
+ from alt_stack_http_client_httpx import ApiSuccess
69
+
70
+ from generated_types import GetUsersIdParams, HttpxApiClient, Request, Response
71
+
72
+ async with HttpxApiClient(
73
+ "https://api.example.com", request_map=Request, response_map=Response
74
+ ) as client:
75
+ result = await client.get("/users/{id}", params=GetUsersIdParams(id="u_1"))
76
+
77
+ if isinstance(result, ApiSuccess):
78
+ result.body # User; unknown paths, missing params, and wrong models are static errors
79
+ ```
80
+
81
+ Generated modules that use non-object intersections inline a small `all_of` validator, so they depend only on `pydantic` and `alt-stack-http-client-httpx` at runtime.
82
+
83
+ ## Development
84
+
85
+ ```bash
86
+ uv run --project packages/python-pydantic-openapi --extra dev pytest packages/python-pydantic-openapi/tests
87
+ uv run --project packages/python-pydantic-openapi --extra dev ruff check packages/python-pydantic-openapi
88
+ uv run --project packages/python-pydantic-openapi --extra dev ty check packages/python-pydantic-openapi
89
+ ```
90
+
91
+ ## Documentation
92
+
93
+ - [Code generation Quickstart](../../apps/docs/docs/codegen/quickstart.md)
94
+ - [Common Patterns](../../apps/docs/docs/codegen/common-patterns.md)
95
+ - [Python/Pydantic API Documentation](../../apps/docs/docs/codegen/api/pydantic-openapi.md)
@@ -0,0 +1,28 @@
1
+ alt_stack_openapi_pydantic/__init__.py,sha256=fFC0seuk0nMLp7ocTUuHyvTR3RJoPK8dLCPDWQ7a8_s,665
2
+ alt_stack_openapi_pydantic/cli.py,sha256=wyjMEXo4mHTcmrQzBLz0vMEopKZ152Mqi9QoRP99TGY,1909
3
+ alt_stack_openapi_pydantic/dependencies.py,sha256=27kkVQo8l5jSfytoXpPW6k_gJu_QAL-KSKS1B0ABGMY,3192
4
+ alt_stack_openapi_pydantic/emission.py,sha256=z5O1j8-q9FoZbzMdt9ib3JzrpCLO8m08-pWNr3P3tqI,27463
5
+ alt_stack_openapi_pydantic/lowering.py,sha256=p9fOcDPCNwNiYvsTNnxXHbF30ytx6KusPmW3Y9R-kns,3721
6
+ alt_stack_openapi_pydantic/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
+ alt_stack_openapi_pydantic/registry.py,sha256=11v1K_6yAKjfMp4hFTNFD7VIxV1LMNFgHbESZBoiL6k,5085
8
+ alt_stack_openapi_pydantic/rendering.py,sha256=Tx1YvO9npLMtmeQ8sR-UR24YqAFlyFRceNT-aDdisJw,6264
9
+ alt_stack_openapi_pydantic/routes.py,sha256=A_NPx6zZGKpZbTl5rIDdf7YR083_vHNg49eMRCalKpk,5814
10
+ alt_stack_openapi_pydantic/schema_dedup.py,sha256=nR8VrXFooeV02xDDDdRDCD5FsMYLFq2kMpdz8KqOo28,3751
11
+ alt_stack_openapi_pydantic/to_python.py,sha256=x4wDyXvaOV_I4UjmpeYEF_72AL3G5NGrusn1ZncJWSk,346
12
+ alt_stack_openapi_pydantic/type_render.py,sha256=n2j0iPy5daNPMOWQwwYo5hF3ok2vuSf0Ls8FGiVhoiI,408
13
+ alt_stack_openapi_pydantic/utils.py,sha256=91xMmdRwQP0WMPJSg7n2GrQq5Uqk2cLI3auq3Uq2n4g,907
14
+ alt_stack_openapi_pydantic/types/__init__.py,sha256=MUlvT6Woc4YFbN82M7-k7_3k5rXztfdfPw3TTNzoVwM,134
15
+ alt_stack_openapi_pydantic/types/array.py,sha256=KXsC_l2kz4sUMIaTIWZiZKDasYK7cuJ9yvkPNv1w8LI,1071
16
+ alt_stack_openapi_pydantic/types/boolean.py,sha256=otAaBJmsaZTBV-8-gVDS9OIqIaacz3hlbO5nO6PTq6o,438
17
+ alt_stack_openapi_pydantic/types/intersection.py,sha256=iGGVPzbJAYMhG3ktalWPgm1hbicN528ZgzoRTAnxrvI,506
18
+ alt_stack_openapi_pydantic/types/number.py,sha256=39lakX2P-6V1Ipf-qrY5jJ2mnSEyjBRpYDYShQ2e1IU,1323
19
+ alt_stack_openapi_pydantic/types/object.py,sha256=BDQyu77Ouz2M-bj7YLkNDsTxzPyg4Ap_7iMNtlr7wXg,379
20
+ alt_stack_openapi_pydantic/types/string.py,sha256=qNsNXCbJNmzjebkqOYUKi0tapiZ5USqCUT7Tv6fWkXE,1962
21
+ alt_stack_openapi_pydantic/types/types.py,sha256=2rbEHIxFMrRFHw72tUuOCim2vzIUErfJuclliUUahiQ,124
22
+ alt_stack_openapi_pydantic/types/union.py,sha256=qWIMXaqN4D4x2PnYEu0w-H3TOHG5VkXMZbF6-7F6Agc,1158
23
+ alt_stack_openapi_pydantic-1.6.2.dist-info/licenses/LICENSE,sha256=OvI3tgOAzCyCFOjcQ42mFXk6SKQTicTDrUgbqofOXGY,1072
24
+ alt_stack_openapi_pydantic-1.6.2.dist-info/METADATA,sha256=0imf_TzJfH8e-3ySDrSgHxeHlYFLe61cirWQgnuWrOg,3765
25
+ alt_stack_openapi_pydantic-1.6.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
26
+ alt_stack_openapi_pydantic-1.6.2.dist-info/entry_points.txt,sha256=n_oPzTHD1-LolT8dLBmubt9ajr9-A-rokIx-CpT6Nlg,83
27
+ alt_stack_openapi_pydantic-1.6.2.dist-info/top_level.txt,sha256=qUyCFcP-72UcOp06tPOxJLqxzRXMr7OOhenSuZKIaqo,27
28
+ alt_stack_openapi_pydantic-1.6.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ alt-stack-openapi-pydantic = alt_stack_openapi_pydantic.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Anthony Altieri
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ alt_stack_openapi_pydantic