Schema-First 0.4.0__tar.gz → 0.5.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.
- {schema_first-0.4.0/src/Schema_First.egg-info → schema_first-0.5.0}/PKG-INFO +2 -2
- {schema_first-0.4.0 → schema_first-0.5.0}/pyproject.toml +2 -2
- {schema_first-0.4.0 → schema_first-0.5.0/src/Schema_First.egg-info}/PKG-INFO +2 -2
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/schema_object_schema.py +49 -7
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/specification/__init__.py +26 -5
- {schema_first-0.4.0 → schema_first-0.5.0}/tests/test_specification.py +5 -15
- {schema_first-0.4.0 → schema_first-0.5.0}/LICENSE +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/README.md +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/setup.cfg +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/Schema_First.egg-info/SOURCES.txt +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/Schema_First.egg-info/dependency_links.txt +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/Schema_First.egg-info/requires.txt +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/Schema_First.egg-info/top_level.txt +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/__init__.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/exceptions.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/loaders/__init__.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/loaders/exc.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/loaders/yaml_loader.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/__init__.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/exc.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/__init__.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/base.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/constants.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/fields.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/components_object_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/contact_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/info_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/license_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/media_type_object_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/operation_object_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/path_item_object_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/reference_object_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/request_body_object_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/responses_object_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/root_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/server_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/server_variable_object_schema.py +0 -0
- {schema_first-0.4.0 → schema_first-0.5.0}/tests/test_validator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Schema-First
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: OpenAPI specification validator and converter to Marshmallow schemas.
|
|
5
5
|
Author-email: Konstantin Fadeev <fadeev@legalact.pro>
|
|
6
6
|
License: MIT License
|
|
@@ -30,7 +30,7 @@ Project-URL: repository, https://github.com/flask-pro/schema-first
|
|
|
30
30
|
Classifier: License :: OSI Approved :: MIT License
|
|
31
31
|
Classifier: Operating System :: OS Independent
|
|
32
32
|
Classifier: Programming Language :: Python :: 3
|
|
33
|
-
Requires-Python: >=3.
|
|
33
|
+
Requires-Python: >=3.13
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
35
35
|
License-File: LICENSE
|
|
36
36
|
Requires-Dist: marshmallow>=4.0.0
|
|
@@ -19,8 +19,8 @@ description = "OpenAPI specification validator and converter to Marshmallow sche
|
|
|
19
19
|
license = {file = "LICENSE"}
|
|
20
20
|
name = "Schema-First"
|
|
21
21
|
readme = "README.md"
|
|
22
|
-
requires-python = ">=3.
|
|
23
|
-
version = "0.
|
|
22
|
+
requires-python = ">=3.13"
|
|
23
|
+
version = "0.5.0"
|
|
24
24
|
|
|
25
25
|
[project.optional-dependencies]
|
|
26
26
|
dev = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Schema-First
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: OpenAPI specification validator and converter to Marshmallow schemas.
|
|
5
5
|
Author-email: Konstantin Fadeev <fadeev@legalact.pro>
|
|
6
6
|
License: MIT License
|
|
@@ -30,7 +30,7 @@ Project-URL: repository, https://github.com/flask-pro/schema-first
|
|
|
30
30
|
Classifier: License :: OSI Approved :: MIT License
|
|
31
31
|
Classifier: Operating System :: OS Independent
|
|
32
32
|
Classifier: Programming Language :: Python :: 3
|
|
33
|
-
Requires-Python: >=3.
|
|
33
|
+
Requires-Python: >=3.13
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
35
35
|
License-File: LICENSE
|
|
36
36
|
Requires-Dist: marshmallow>=4.0.0
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
|
1
2
|
import re
|
|
3
|
+
import typing
|
|
2
4
|
|
|
3
5
|
from marshmallow import fields
|
|
6
|
+
from marshmallow import types
|
|
4
7
|
from marshmallow import validate
|
|
5
8
|
from marshmallow import validates
|
|
6
9
|
from marshmallow import validates_schema
|
|
@@ -12,6 +15,12 @@ from ..constants import TYPES
|
|
|
12
15
|
from ..fields import DESCRIPTION_FIELD
|
|
13
16
|
|
|
14
17
|
|
|
18
|
+
class BaseSchemaField(BaseSchema):
|
|
19
|
+
type = fields.String(required=True, validate=validate.OneOf(TYPES))
|
|
20
|
+
description = DESCRIPTION_FIELD
|
|
21
|
+
nullable = fields.Boolean()
|
|
22
|
+
|
|
23
|
+
|
|
15
24
|
class FormatBinarySchema(BaseSchema):
|
|
16
25
|
default = fields.String()
|
|
17
26
|
|
|
@@ -61,11 +70,12 @@ format_schemas = {
|
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
|
|
64
|
-
class StringFieldSchema(
|
|
73
|
+
class StringFieldSchema(BaseSchemaField):
|
|
65
74
|
format = fields.String(validate=validate.OneOf(FORMATS))
|
|
66
75
|
minLength = fields.Integer(validate=[validate.Range(min=0)])
|
|
67
76
|
maxLength = fields.Integer(validate=[validate.Range(min=0)])
|
|
68
77
|
pattern = fields.String()
|
|
78
|
+
default = fields.String()
|
|
69
79
|
|
|
70
80
|
@validates('pattern')
|
|
71
81
|
def validate_pattern(self, value: str, data_key: str) -> None:
|
|
@@ -93,15 +103,11 @@ class StringFieldSchema(BaseSchema):
|
|
|
93
103
|
if 'minLength' in data and 'maxLength' in data:
|
|
94
104
|
if data['minLength'] > data['maxLength']:
|
|
95
105
|
raise ValidationError(
|
|
96
|
-
f'<{data[
|
|
106
|
+
f'<{data["minLength"]}> cannot be greater than <{data["maxLength"]}>'
|
|
97
107
|
)
|
|
98
108
|
|
|
99
109
|
|
|
100
|
-
class
|
|
101
|
-
type = fields.String(required=True, validate=validate.OneOf(TYPES))
|
|
102
|
-
default = fields.String()
|
|
103
|
-
description = DESCRIPTION_FIELD
|
|
104
|
-
nullable = fields.Boolean()
|
|
110
|
+
class ObjectFieldSchema(BaseSchemaField):
|
|
105
111
|
required = fields.List(fields.String())
|
|
106
112
|
additionalProperties = fields.Boolean()
|
|
107
113
|
|
|
@@ -118,3 +124,39 @@ class SchemaObjectSchema(StringFieldSchema):
|
|
|
118
124
|
raise ValidationError(
|
|
119
125
|
f'Required field <{field_name}> not in <data["properties"]>'
|
|
120
126
|
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class BooleanFieldSchema(BaseSchemaField):
|
|
130
|
+
default = fields.Boolean(truthy=[True], falsy=[False])
|
|
131
|
+
|
|
132
|
+
@validates_schema
|
|
133
|
+
def validate_default(self, data, **kwargs):
|
|
134
|
+
if 'default' in data:
|
|
135
|
+
if not isinstance(data['default'], bool):
|
|
136
|
+
raise ValidationError(f'<{data["default"]}> is not boolean.')
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
field_schemas = {
|
|
140
|
+
'boolean': BooleanFieldSchema,
|
|
141
|
+
'object': ObjectFieldSchema,
|
|
142
|
+
'string': StringFieldSchema,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class SchemaObjectSchema(BaseSchema):
|
|
147
|
+
type = fields.String(required=True, validate=validate.OneOf(TYPES))
|
|
148
|
+
|
|
149
|
+
def load(
|
|
150
|
+
self,
|
|
151
|
+
data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
|
|
152
|
+
*,
|
|
153
|
+
many: bool | None = None,
|
|
154
|
+
partial: bool | types.StrSequenceOrSet | None = None,
|
|
155
|
+
unknown: types.UnknownOption | None = None,
|
|
156
|
+
):
|
|
157
|
+
try:
|
|
158
|
+
return field_schemas[data['type']]().load(
|
|
159
|
+
data, many=many, partial=partial, unknown=unknown
|
|
160
|
+
)
|
|
161
|
+
except KeyError:
|
|
162
|
+
raise ValidationError(f'Data type <{data["type"]}> not supported.')
|
|
@@ -71,11 +71,32 @@ class Specification:
|
|
|
71
71
|
initialized_schema.required = required
|
|
72
72
|
return initialized_schema
|
|
73
73
|
|
|
74
|
-
def
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
raise NotImplementedError(
|
|
74
|
+
def _convert_boolean_field(self, field_schema: dict, required: bool = False):
|
|
75
|
+
try:
|
|
76
|
+
schema = FIELDS_VIA_TYPES[field_schema['type']]
|
|
77
|
+
except KeyError:
|
|
78
|
+
raise NotImplementedError(
|
|
79
|
+
f'Schema <{field_schema}> for type <{field_schema["type"]}> not implemented.'
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
initialized_schema = schema()
|
|
83
|
+
initialized_schema.allow_none = field_schema.get('nullable', False)
|
|
84
|
+
initialized_schema.required = required
|
|
85
|
+
return initialized_schema
|
|
86
|
+
|
|
87
|
+
def _convert_field_any_type(self, field_schema: dict, required: bool = False):
|
|
88
|
+
field_schema_converters = {
|
|
89
|
+
'string': self._convert_string_field,
|
|
90
|
+
'boolean': self._convert_boolean_field,
|
|
91
|
+
}
|
|
92
|
+
try:
|
|
93
|
+
converted_field_schema = field_schema_converters[field_schema['type']](
|
|
94
|
+
field_schema, required=required
|
|
95
|
+
)
|
|
96
|
+
except KeyError:
|
|
97
|
+
raise NotImplementedError(
|
|
98
|
+
f'Schema <{field_schema}> for type <{field_schema["type"]}> not be converted.'
|
|
99
|
+
)
|
|
79
100
|
|
|
80
101
|
return converted_field_schema
|
|
81
102
|
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
from marshmallow import fields
|
|
2
2
|
from marshmallow import Schema
|
|
3
|
+
import pytest
|
|
3
4
|
|
|
4
5
|
from src.schema_first.openapi import OpenAPI
|
|
5
6
|
from src.schema_first.specification import Specification
|
|
6
7
|
from tests.utils import get_schema_from_request
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
@pytest.mark.parametrize('fx', ['fx_spec_required', 'fx_spec_full'])
|
|
11
|
+
def test_specification(request, fx, fx_spec_as_file):
|
|
12
|
+
spec_file = fx_spec_as_file(request.getfixturevalue(fx))
|
|
11
13
|
spec = Specification(spec_file)
|
|
12
14
|
|
|
13
15
|
assert isinstance(spec.openapi, OpenAPI)
|
|
@@ -16,18 +18,6 @@ def test_specification__required(fx_spec_required, fx_spec_as_file):
|
|
|
16
18
|
spec.load()
|
|
17
19
|
|
|
18
20
|
request_schema = get_schema_from_request(spec.reassembly_spec, '/endpoint', '200')
|
|
21
|
+
assert isinstance(request_schema(), Schema)
|
|
19
22
|
assert isinstance(request_schema().fields['message'], fields.String)
|
|
20
23
|
assert request_schema().load({'message': 'Valid string'})
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def test_specification__full(fx_spec_full, fx_spec_as_file):
|
|
24
|
-
spec_file = fx_spec_as_file(fx_spec_full)
|
|
25
|
-
spec = Specification(spec_file)
|
|
26
|
-
|
|
27
|
-
assert isinstance(spec.openapi, OpenAPI)
|
|
28
|
-
assert spec.reassembly_spec is None
|
|
29
|
-
|
|
30
|
-
spec.load()
|
|
31
|
-
|
|
32
|
-
request_schema = get_schema_from_request(spec.reassembly_spec, '/endpoint', '200')
|
|
33
|
-
assert isinstance(request_schema(), Schema)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/contact_schema.py
RENAMED
|
File without changes
|
{schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/info_schema.py
RENAMED
|
File without changes
|
{schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/license_schema.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/root_schema.py
RENAMED
|
File without changes
|
{schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/server_schema.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|