zopy-takibiapi 0.1.0__tar.gz

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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 - present, zopy-takibiapi Contributors
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,97 @@
1
+ Metadata-Version: 2.4
2
+ Name: zopy-takibiapi
3
+ Version: 0.1.0
4
+ Summary: takibiapi with zopy (Zod for Python) schemas, checked strictly
5
+ Keywords: openapi,asgi,zopy,zod,rest,api,validation
6
+ Author: nakita628
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
17
+ Classifier: Typing :: Typed
18
+ Requires-Dist: takibiapi>=0.1.0,<0.2
19
+ Requires-Dist: zopy>=0.1.1
20
+ Requires-Python: >=3.12
21
+ Project-URL: Repository, https://github.com/nakita628/takibi
22
+ Project-URL: Issues, https://github.com/nakita628/takibi/issues
23
+ Description-Content-Type: text/markdown
24
+
25
+ # zopy-takibiapi
26
+
27
+ [TakibiAPI](../takibiapi) with [zopy](https://github.com/nakita628/zopy) (Zod for Python) schemas, checked strictly: checks, unknown keys and responses included.
28
+
29
+ ```sh
30
+ pip install zopy-takibiapi
31
+ ```
32
+
33
+ ## Quick start
34
+
35
+ ```python
36
+ from zopy_takibiapi import Context, TakibiAPI, createRoute, z
37
+
38
+
39
+ class Message(z.StrictObject):
40
+ message = z.string()
41
+
42
+
43
+ MessageSchema = Message.openapi("Message") # components.schemas.Message, referenced with $ref
44
+
45
+
46
+ class getTakibiRoute(
47
+ createRoute(
48
+ {
49
+ "method": "get",
50
+ "path": "/takibi",
51
+ "responses": {200: {"description": "takibišŸ”„", "content": {"application/json": {"schema": MessageSchema}}}},
52
+ }
53
+ )
54
+ ):
55
+ """Say takibišŸ”„."""
56
+
57
+
58
+ def getTakibiRouteHandler(c: Context[getTakibiRoute]):
59
+ return c.json(Message(message="takibišŸ”„"), 200)
60
+
61
+
62
+ app = TakibiAPI()
63
+ app.openapi(getTakibiRoute, getTakibiRouteHandler)
64
+ app.doc("/doc", {"openapi": "3.0.0", "info": {"title": "takibi API", "version": "1.0.0"}})
65
+ ```
66
+
67
+ ```bash
68
+ granian --interface rsgi myapp:app # or: uvicorn myapp:app
69
+ curl localhost:8000/takibi # {"message":"takibišŸ”„"}
70
+ ```
71
+
72
+ Import `TakibiAPI` and `z` from `zopy_takibiapi`; everything else works as in Takibi.
73
+
74
+ ## Schemas
75
+
76
+ Every object, nested ones included, must be `z.StrictObject`; `app.openapi` raises `TypeError` for a loose object or a non-zopy schema. Parameters arrive as text and are parsed as Zod parses them, so convert explicitly: `todoId = z.coerce.number().int()`.
77
+
78
+ ## Checks
79
+
80
+ - **Requests**: `c.req.valid(...)` gives the parsed value. A failure gets **422** with Zod's issue codes as `code`.
81
+ - **Responses**: `c.json(value, status)` encodes `value` with the schema declared for that status (`safeEncode`: checked, codecs to their wire form). A mismatch raises `ResponseValidationError` and answers **500** (details in the log, not in the response).
82
+
83
+ ```python
84
+ app.onError(lambda c: c.json({"error": str(c.error)}, 500)) # replaces the 500, ResponseValidationError included
85
+ ```
86
+
87
+ ## OpenAPI
88
+
89
+ As in Zod, only `.openapi("Name")` schemas become components; others are written in place. Documents come from zopy's `toJSONSchema` (`openapi-3.0` or JSON Schema 2020-12).
90
+
91
+ ## Development
92
+
93
+ From the repository root: `bazel test //pypi/zopy-takibiapi:test`. See the root README.
94
+
95
+ ## License
96
+
97
+ Distributed under the MIT License. See [LICENSE](https://github.com/nakita628/takibi/blob/main/pypi/zopy-takibiapi/LICENSE) for more information.
@@ -0,0 +1,73 @@
1
+ # zopy-takibiapi
2
+
3
+ [TakibiAPI](../takibiapi) with [zopy](https://github.com/nakita628/zopy) (Zod for Python) schemas, checked strictly: checks, unknown keys and responses included.
4
+
5
+ ```sh
6
+ pip install zopy-takibiapi
7
+ ```
8
+
9
+ ## Quick start
10
+
11
+ ```python
12
+ from zopy_takibiapi import Context, TakibiAPI, createRoute, z
13
+
14
+
15
+ class Message(z.StrictObject):
16
+ message = z.string()
17
+
18
+
19
+ MessageSchema = Message.openapi("Message") # components.schemas.Message, referenced with $ref
20
+
21
+
22
+ class getTakibiRoute(
23
+ createRoute(
24
+ {
25
+ "method": "get",
26
+ "path": "/takibi",
27
+ "responses": {200: {"description": "takibišŸ”„", "content": {"application/json": {"schema": MessageSchema}}}},
28
+ }
29
+ )
30
+ ):
31
+ """Say takibišŸ”„."""
32
+
33
+
34
+ def getTakibiRouteHandler(c: Context[getTakibiRoute]):
35
+ return c.json(Message(message="takibišŸ”„"), 200)
36
+
37
+
38
+ app = TakibiAPI()
39
+ app.openapi(getTakibiRoute, getTakibiRouteHandler)
40
+ app.doc("/doc", {"openapi": "3.0.0", "info": {"title": "takibi API", "version": "1.0.0"}})
41
+ ```
42
+
43
+ ```bash
44
+ granian --interface rsgi myapp:app # or: uvicorn myapp:app
45
+ curl localhost:8000/takibi # {"message":"takibišŸ”„"}
46
+ ```
47
+
48
+ Import `TakibiAPI` and `z` from `zopy_takibiapi`; everything else works as in Takibi.
49
+
50
+ ## Schemas
51
+
52
+ Every object, nested ones included, must be `z.StrictObject`; `app.openapi` raises `TypeError` for a loose object or a non-zopy schema. Parameters arrive as text and are parsed as Zod parses them, so convert explicitly: `todoId = z.coerce.number().int()`.
53
+
54
+ ## Checks
55
+
56
+ - **Requests**: `c.req.valid(...)` gives the parsed value. A failure gets **422** with Zod's issue codes as `code`.
57
+ - **Responses**: `c.json(value, status)` encodes `value` with the schema declared for that status (`safeEncode`: checked, codecs to their wire form). A mismatch raises `ResponseValidationError` and answers **500** (details in the log, not in the response).
58
+
59
+ ```python
60
+ app.onError(lambda c: c.json({"error": str(c.error)}, 500)) # replaces the 500, ResponseValidationError included
61
+ ```
62
+
63
+ ## OpenAPI
64
+
65
+ As in Zod, only `.openapi("Name")` schemas become components; others are written in place. Documents come from zopy's `toJSONSchema` (`openapi-3.0` or JSON Schema 2020-12).
66
+
67
+ ## Development
68
+
69
+ From the repository root: `bazel test //pypi/zopy-takibiapi:test`. See the root README.
70
+
71
+ ## License
72
+
73
+ Distributed under the MIT License. See [LICENSE](https://github.com/nakita628/takibi/blob/main/pypi/zopy-takibiapi/LICENSE) for more information.
@@ -0,0 +1,50 @@
1
+ [project]
2
+ name = "zopy-takibiapi"
3
+ version = "0.1.0"
4
+ description = "takibiapi with zopy (Zod for Python) schemas, checked strictly"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ keywords = [
10
+ "openapi",
11
+ "asgi",
12
+ "zopy",
13
+ "zod",
14
+ "rest",
15
+ "api",
16
+ "validation",
17
+ ]
18
+ classifiers = [
19
+ "Development Status :: 3 - Alpha",
20
+ "Intended Audience :: Developers",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
27
+ "Typing :: Typed",
28
+ ]
29
+ dependencies = [
30
+ "takibiapi>=0.1.0,<0.2",
31
+ "zopy>=0.1.1",
32
+ ]
33
+
34
+ [[project.authors]]
35
+ name = "nakita628"
36
+
37
+ [project.urls]
38
+ Repository = "https://github.com/nakita628/takibi"
39
+ Issues = "https://github.com/nakita628/takibi/issues"
40
+
41
+ [build-system]
42
+ requires = ["uv_build>=0.8,<0.14"]
43
+ build-backend = "uv_build"
44
+
45
+ [tool.uv.build-backend]
46
+ module-root = "python"
47
+ module-name = "zopy_takibiapi"
48
+
49
+ [tool.uv.sources.takibiapi]
50
+ workspace = true
@@ -0,0 +1,37 @@
1
+ [project]
2
+ name = "zopy-takibiapi"
3
+ version = "0.1.0"
4
+ description = "takibiapi with zopy (Zod for Python) schemas, checked strictly"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "nakita628" }]
10
+ keywords = ["openapi", "asgi", "zopy", "zod", "rest", "api", "validation"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Intended Audience :: Developers",
14
+ "Operating System :: OS Independent",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
19
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
20
+ "Typing :: Typed",
21
+ ]
22
+ dependencies = ["takibiapi>=0.1.0,<0.2", "zopy>=0.1.1"]
23
+
24
+ [project.urls]
25
+ Repository = "https://github.com/nakita628/takibi"
26
+ Issues = "https://github.com/nakita628/takibi/issues"
27
+
28
+ [build-system]
29
+ requires = ["uv_build>=0.8,<0.14"]
30
+ build-backend = "uv_build"
31
+
32
+ [tool.uv.build-backend]
33
+ module-root = "python"
34
+ module-name = "zopy_takibiapi"
35
+
36
+ [tool.uv.sources]
37
+ takibiapi = { workspace = true }
@@ -0,0 +1,53 @@
1
+ """zopy-takibiapi: takibi with zopy (Zod for Python) schemas, checked strictly.
2
+
3
+ from zopy_takibiapi import Context, Response, TakibiAPI, createRoute, z
4
+
5
+ Schemas are zopy's (``z.StrictObject`` classes, ``z.string().min(1)``, ...), named for the
6
+ document with ``.openapi("Todo")``. Requests are parsed with them before the handler runs, checks
7
+ included, and responses are encoded with them; every object must reject unknown keys.
8
+ """
9
+
10
+ from zopy import z
11
+
12
+ from takibiapi import (
13
+ Context,
14
+ Middleware,
15
+ Next,
16
+ OpenAPIConfig,
17
+ Request,
18
+ RequestConfig,
19
+ Response,
20
+ ResponseConfig,
21
+ ResponseValidationError,
22
+ Route,
23
+ RouteConfig,
24
+ ValidationError,
25
+ createRoute,
26
+ problem,
27
+ )
28
+ from zopy_takibiapi.app import TakibiAPI
29
+ from zopy_takibiapi.problems import ValidationErrorItem, ValidationProblem
30
+ from zopy_takibiapi.schemas import ZopySchemas
31
+
32
+ __all__ = [
33
+ "Context",
34
+ "Middleware",
35
+ "Next",
36
+ "OpenAPIConfig",
37
+ "Request",
38
+ "RequestConfig",
39
+ "Response",
40
+ "ResponseConfig",
41
+ "ResponseValidationError",
42
+ "Route",
43
+ "RouteConfig",
44
+ "TakibiAPI",
45
+ "ValidationError",
46
+ "ValidationErrorItem",
47
+ "ValidationProblem",
48
+ "ZopySchemas",
49
+ "createRoute",
50
+ "problem",
51
+ "z",
52
+ ]
53
+ __version__ = "0.1.0"
@@ -0,0 +1,21 @@
1
+ """``TakibiAPI``: takibi with zopy (Zod for Python) schemas."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import takibiapi
8
+
9
+ from .schemas import ZopySchemas
10
+
11
+
12
+ class TakibiAPI(takibiapi.TakibiAPI):
13
+ """``takibiapi.TakibiAPI`` whose routes are described with zopy schemas: requests are parsed with
14
+ them before the handler runs (422 when they do not match), and ``c.json(...)`` encodes the
15
+ response with the declared schema (500 when the handler breaks its own contract). Everything
16
+ else (routing, middleware, ASGI / RSGI, the OpenAPI document) is takibi's."""
17
+
18
+ schemas: Any = ZopySchemas()
19
+
20
+
21
+ __all__ = ["TakibiAPI"]
@@ -0,0 +1,38 @@
1
+ """The RFC 9457 Problem Details body answered when a request part does not match its schema."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, cast
6
+
7
+ from zopy import z
8
+
9
+ # "in" is a Python keyword, so this one shape is built from a namespace instead of a class body.
10
+ ValidationErrorItem: Any = cast(Any, type(z.StrictObject))(
11
+ "ValidationErrorItem",
12
+ (z.StrictObject,),
13
+ {
14
+ "in": z.enum(("path", "query", "header", "cookie", "body")).openapi(
15
+ {"description": "Where the value was sent (an OpenAPI parameter location, or body)"}
16
+ ),
17
+ "path": z.string().openapi({"description": "Dot-separated path inside that part", "example": "title"}),
18
+ "code": z.string().openapi({"description": "The Zod issue code", "example": "too_small"}),
19
+ "message": z.string().openapi({"example": "Too small: expected string to have >=1 characters"}),
20
+ },
21
+ ).openapi("ValidationErrorItem")
22
+
23
+
24
+ class ValidationProblemFields(z.StrictObject):
25
+ type = z.literal("/problems/validation-error")
26
+ title = z.literal("Unprocessable Content")
27
+ status = z.literal(422)
28
+ detail = z.string()
29
+ instance = z.string().openapi({"description": "The request path"})
30
+ errors = z.array(ValidationErrorItem)
31
+
32
+
33
+ ValidationProblem = ValidationProblemFields.openapi(
34
+ "ValidationProblem",
35
+ {"description": "RFC 9457 Problem Details for a request part that does not match its schema."},
36
+ )
37
+
38
+ __all__ = ["ValidationErrorItem", "ValidationProblem"]
File without changes
@@ -0,0 +1,187 @@
1
+ """``ZopySchemas``: zopy (Zod for Python) schemas as takibi's schemas (see ``takibiapi.schemas``).
2
+
3
+ Requests are parsed with the route's schemas exactly as Zod parses: checks included, no
4
+ conversion unless the schema asks for it (``z.coerce.number()`` for a numeric path parameter), and
5
+ no unknown keys (every object must be ``z.StrictObject``). Responses are encoded with the schema
6
+ the route declares for the status (``safeEncode``: checked, and codecs turned into their wire
7
+ form) before they are written. As in Zod, ``.openapi("Todo")`` names a schema: it is written once to
8
+ ``components.schemas`` and referenced with ``$ref``; other schemas are written in place.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from collections.abc import Iterator, Mapping
14
+ from typing import Any, Literal
15
+
16
+ from zopy import z
17
+ from zopy.core import ZodType
18
+ from zopy.declarative import Object, ObjectMeta
19
+ from zopy.registries import globalRegistry, openapiParamRegistry
20
+
21
+ from takibiapi import ValidationError
22
+ from takibiapi.schema import dumps, loads
23
+
24
+ from .problems import ValidationProblem
25
+
26
+ COMPONENT_URI = "#/components/schemas/{}"
27
+
28
+
29
+ def is_schema(tp: Any) -> bool:
30
+ return isinstance(tp, (ZodType, ObjectMeta))
31
+
32
+
33
+ def _children(value: Any) -> Iterator[Any]:
34
+ """The schemas directly inside ``value``'s definition (object fields, array items, ...)."""
35
+ if isinstance(value, ObjectMeta):
36
+ yield from value.shape.values()
37
+ for item in value._def.values():
38
+ if is_schema(item):
39
+ yield item
40
+ elif isinstance(item, (list, tuple)):
41
+ yield from (i for i in item if is_schema(i))
42
+ elif isinstance(item, Mapping):
43
+ yield from (i for i in item.values() if is_schema(i))
44
+
45
+
46
+ def strict_error(schema: Any) -> str | None:
47
+ """Why ``schema`` is not strict enough, or None: every object must reject unknown keys."""
48
+ seen: set[int] = set()
49
+ stack = [schema]
50
+ while stack:
51
+ current = stack.pop()
52
+ if id(current) in seen:
53
+ continue
54
+ seen.add(id(current))
55
+ definition = current._def
56
+ if definition.get("type") == "object" and not isinstance(definition.get("catchall"), z.ZodNever):
57
+ name = current.__name__ if isinstance(current, type) else type(current).__name__
58
+ return f"{name} is not strict: unknown keys must be an error (subclass z.StrictObject)"
59
+ stack.extend(_children(current))
60
+ return None
61
+
62
+
63
+ def _checked(tp: Any, target: str) -> Any:
64
+ if not is_schema(tp):
65
+ raise TypeError(f"request {target} / response schemas must be zopy schemas, got {tp!r}")
66
+ error = strict_error(tp)
67
+ if error is not None:
68
+ raise TypeError(error)
69
+ return tp
70
+
71
+
72
+ def _issues(error: Any) -> list[dict[str, Any]]:
73
+ return [{"path": list(i["path"]), "code": i["code"], "message": i["message"]} for i in error.issues]
74
+
75
+
76
+ def _unwrapped(schema: Any) -> Any:
77
+ """The schema inside ``.optional()`` / ``.nullable()`` / ``.default()``."""
78
+ while not isinstance(schema, ObjectMeta) and "innerType" in schema._def:
79
+ schema = schema._def["innerType"]
80
+ return schema
81
+
82
+
83
+ class _Checker:
84
+ __slots__ = ("_schema", "names")
85
+
86
+ def __init__(self, tp: Any, target: str) -> None:
87
+ self._schema = _checked(tp, target)
88
+ if target == "json":
89
+ self.names: list[tuple[str, bool]] = []
90
+ return
91
+ if not isinstance(tp, ObjectMeta):
92
+ raise TypeError(f"request {target} parts must be an object schema (z.StrictObject), got {tp!r}")
93
+ self.names = [(name, isinstance(_unwrapped(field), z.ZodArray)) for name, field in tp.shape.items()]
94
+
95
+ def decode(self, value: Any, *, coerce: bool) -> Any:
96
+ # Parameters arrive as text and are parsed as the schema says (z.coerce converts), as in Zod.
97
+ result = self._schema.safeParse(value)
98
+ if not result.success:
99
+ raise ValidationError(_issues(result.error))
100
+ return result.data
101
+
102
+ def decode_json(self, data: bytes) -> Any:
103
+ return self.decode(loads(data), coerce=False) # loads raises ValueError for malformed JSON
104
+
105
+
106
+ def _wire(value: Any) -> Any:
107
+ """``dumps``'s ``default`` for an encoded result: a z.Object instance as its wire dict (the
108
+ JSON writer then writes its values, calling this again for nested objects)."""
109
+ if isinstance(value, Object):
110
+ # Without optional fields to leave out, the instance's own dict is the wire dict.
111
+ return value._wire_dict() if type(value)._absent_when_none() else vars(value)
112
+ raise TypeError(f"Object of type {type(value).__name__} is not JSON serializable")
113
+
114
+
115
+ class _Encoder:
116
+ __slots__ = ("_schema",)
117
+
118
+ def __init__(self, tp: Any) -> None:
119
+ self._schema = _checked(tp, "response")
120
+
121
+ def encode_json(self, value: Any) -> bytes:
122
+ result = self._schema.safeEncode(value)
123
+ if not result.success:
124
+ raise ValidationError(_issues(result.error))
125
+ return dumps(result.data, default=_wire)
126
+
127
+
128
+ class _DocBuilder:
129
+ """Schemas as zopy's ``toJSONSchema`` writes them, named ones (``.openapi("Todo")``) collected
130
+ into ``components``. Bodies are documented in their wire form (``io: "input"``, a codec's JSON
131
+ side) both ways: requests are parsed from it and responses encoded into it."""
132
+
133
+ def __init__(self, openapi31: bool) -> None:
134
+ self.openapi31 = openapi31
135
+ self.components: dict[str, Any] = {}
136
+
137
+ def of(self, tp: Any, io: Literal["input", "output"] = "input") -> dict[str, Any]:
138
+ params = {
139
+ "target": "draft-2020-12" if self.openapi31 else "openapi-3.0",
140
+ "io": "input",
141
+ "unrepresentable": "any",
142
+ "uri": COMPONENT_URI.format,
143
+ }
144
+ out = z.toJSONSchema(tp, params)
145
+ out.pop("$schema", None)
146
+ for key in ("$defs", "definitions"):
147
+ for name, schema in out.pop(key, {}).items():
148
+ known = self.components.setdefault(name, schema)
149
+ if known != schema:
150
+ raise TypeError(f"two different schemas are named {name!r} (.openapi({name!r}))")
151
+ return out
152
+
153
+ def parameters(self, tp: Any, location: str) -> list[dict[str, Any]]:
154
+ if not isinstance(tp, ObjectMeta):
155
+ raise TypeError(f"{location} parameters must be an object schema, got {tp!r}")
156
+ out: list[dict[str, Any]] = []
157
+ for name, field in tp.shape.items():
158
+ param: dict[str, Any] = {
159
+ "name": name,
160
+ "in": location,
161
+ "required": location == "path" or not field.isOptional(),
162
+ "schema": self.of(field),
163
+ }
164
+ meta = globalRegistry.get(field) or {}
165
+ if "description" in meta:
166
+ param["description"] = meta["description"]
167
+ param.update(openapiParamRegistry.get(field) or {})
168
+ out.append(param)
169
+ return out
170
+
171
+
172
+ class ZopySchemas:
173
+ """zopy schemas as takibi's schemas: request and response checks, Zod's semantics."""
174
+
175
+ validation_problem: Any = ValidationProblem
176
+
177
+ def checker(self, tp: Any, target: str) -> _Checker:
178
+ return _Checker(tp, target)
179
+
180
+ def response_encoder(self, tp: Any) -> _Encoder:
181
+ return _Encoder(tp)
182
+
183
+ def builder(self, openapi31: bool) -> _DocBuilder:
184
+ return _DocBuilder(openapi31)
185
+
186
+
187
+ __all__ = ["ZopySchemas", "is_schema", "strict_error"]