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.
Files changed (38) hide show
  1. {schema_first-0.4.0/src/Schema_First.egg-info → schema_first-0.5.0}/PKG-INFO +2 -2
  2. {schema_first-0.4.0 → schema_first-0.5.0}/pyproject.toml +2 -2
  3. {schema_first-0.4.0 → schema_first-0.5.0/src/Schema_First.egg-info}/PKG-INFO +2 -2
  4. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/schema_object_schema.py +49 -7
  5. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/specification/__init__.py +26 -5
  6. {schema_first-0.4.0 → schema_first-0.5.0}/tests/test_specification.py +5 -15
  7. {schema_first-0.4.0 → schema_first-0.5.0}/LICENSE +0 -0
  8. {schema_first-0.4.0 → schema_first-0.5.0}/README.md +0 -0
  9. {schema_first-0.4.0 → schema_first-0.5.0}/setup.cfg +0 -0
  10. {schema_first-0.4.0 → schema_first-0.5.0}/src/Schema_First.egg-info/SOURCES.txt +0 -0
  11. {schema_first-0.4.0 → schema_first-0.5.0}/src/Schema_First.egg-info/dependency_links.txt +0 -0
  12. {schema_first-0.4.0 → schema_first-0.5.0}/src/Schema_First.egg-info/requires.txt +0 -0
  13. {schema_first-0.4.0 → schema_first-0.5.0}/src/Schema_First.egg-info/top_level.txt +0 -0
  14. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/__init__.py +0 -0
  15. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/exceptions.py +0 -0
  16. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/loaders/__init__.py +0 -0
  17. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/loaders/exc.py +0 -0
  18. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/loaders/yaml_loader.py +0 -0
  19. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/__init__.py +0 -0
  20. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/exc.py +0 -0
  21. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/__init__.py +0 -0
  22. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/base.py +0 -0
  23. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/constants.py +0 -0
  24. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/fields.py +0 -0
  25. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/components_object_schema.py +0 -0
  26. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/contact_schema.py +0 -0
  27. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/info_schema.py +0 -0
  28. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/license_schema.py +0 -0
  29. {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
  30. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/operation_object_schema.py +0 -0
  31. {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
  32. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/reference_object_schema.py +0 -0
  33. {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
  34. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/responses_object_schema.py +0 -0
  35. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/root_schema.py +0 -0
  36. {schema_first-0.4.0 → schema_first-0.5.0}/src/schema_first/openapi/schemas/v3_1_1/server_schema.py +0 -0
  37. {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
  38. {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.4.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.14
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.14"
23
- version = "0.4.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.4.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.14
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(BaseSchema):
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['minLength']}> cannot be greater than <{data['maxLength']}>'
106
+ f'<{data["minLength"]}> cannot be greater than <{data["maxLength"]}>'
97
107
  )
98
108
 
99
109
 
100
- class SchemaObjectSchema(StringFieldSchema):
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 _convert_field_any_type(self, field_schema: dict, required: bool = False) -> dict:
75
- if field_schema['type'] == 'string':
76
- converted_field_schema = self._convert_string_field(field_schema, required=required)
77
- else:
78
- raise NotImplementedError(field_schema)
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
- def test_specification__required(fx_spec_required, fx_spec_as_file):
10
- spec_file = fx_spec_as_file(fx_spec_required)
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