From 7c9a1af1d79780d643e4dd2a775fbef384c55a0e Mon Sep 17 00:00:00 2001 From: Dmitry Pershin Date: Tue, 26 Sep 2023 20:47:10 +0500 Subject: [PATCH 1/2] pydantic 2 support added. --- pjrpc/server/specs/extractors/pydantic.py | 12 +++---- pjrpc/server/validators/pydantic.py | 6 ++-- pyproject.toml | 2 +- tests/server/resources/openapi-1.json | 43 ++++++++++++++++------- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/pjrpc/server/specs/extractors/pydantic.py b/pjrpc/server/specs/extractors/pydantic.py index aa300f9..4290ed3 100644 --- a/pjrpc/server/specs/extractors/pydantic.py +++ b/pjrpc/server/specs/extractors/pydantic.py @@ -33,7 +33,7 @@ def extract_params_schema(self, method: MethodType, exclude: Iterable[str] = ()) ) params_model = pd.create_model('RequestModel', **field_definitions) - model_schema = params_model.schema(ref_template=self._ref_template) + model_schema = params_model.model_json_schema(ref_template=self._ref_template) parameters_schema = {} for param_name, param_schema in model_schema['properties'].items(): @@ -45,7 +45,7 @@ def extract_params_schema(self, method: MethodType, exclude: Iterable[str] = ()) description=param_schema.get('description', UNSET), deprecated=param_schema.get('deprecated', UNSET), required=required, - definitions=model_schema.get('definitions'), + definitions=model_schema.get('$defs'), ) return parameters_schema @@ -60,8 +60,8 @@ def extract_result_schema(self, method: MethodType) -> Schema: else: return_annotation = result.return_annotation - result_model = pd.create_model('ResultModel', result=(return_annotation, pd.fields.Undefined)) - model_schema = result_model.schema(ref_template=self._ref_template) + result_model = pd.create_model('ResultModel', result=(return_annotation, ...)) + model_schema = result_model.model_json_schema(ref_template=self._ref_template) result_schema = model_schema['properties']['result'] required = 'result' in model_schema.get('required', []) @@ -95,7 +95,7 @@ def extract_errors_schema( field_definitions[field_name] = (annotation, getattr(error, field_name, ...)) result_model = pd.create_model(error.message, **field_definitions) - model_schema = result_model.schema(ref_template=self._ref_template) + model_schema = result_model.model_json_schema(ref_template=self._ref_template) data_schema = model_schema['properties'].get('data', UNSET) required = 'data' in model_schema.get('required', []) @@ -109,7 +109,7 @@ def extract_errors_schema( title=error.message, description=inspect.cleandoc(error.__doc__) if error.__doc__ is not None else UNSET, deprecated=model_schema.get('deprecated', UNSET), - definitions=model_schema.get('definitions'), + definitions=model_schema.get('$defs'), ), ) return errors_schema diff --git a/pjrpc/server/validators/pydantic.py b/pjrpc/server/validators/pydantic.py index 6d107d5..87d9dde 100644 --- a/pjrpc/server/validators/pydantic.py +++ b/pjrpc/server/validators/pydantic.py @@ -24,7 +24,7 @@ def __init__(self, coerce: bool = True, **config_args: Any): config_args.setdefault('extra', 'forbid') # https://pydantic-docs.helpmanual.io/usage/model_config/ - self._model_config = type('ModelConfig', (pydantic.BaseConfig,), config_args) + self._model_config = pydantic.ConfigDict(**config_args) def validate_method( self, method: Callable[..., Any], params: Optional['JsonRpcParams'], exclude: Iterable[str] = (), **kwargs: Any, @@ -43,7 +43,7 @@ def validate_method( signature = self.signature(method, tuple(exclude)) schema = self.build_validation_schema(signature) - params_model = pydantic.create_model(method.__name__, **schema, __config__=self._model_config) + params_model = pydantic.create_model(method.__name__, **schema, model_config=self._model_config) bound_params = self.bind(signature, params) try: @@ -51,7 +51,7 @@ def validate_method( except pydantic.ValidationError as e: raise base.ValidationError(*e.errors()) from e - return {attr: getattr(obj, attr) for attr in obj.__fields_set__} if self._coerce else bound_params.arguments + return {attr: getattr(obj, attr) for attr in obj.model_fields} if self._coerce else bound_params.arguments @ft.lru_cache(maxsize=None) def build_validation_schema(self, signature: inspect.Signature) -> Dict[str, Any]: diff --git a/pyproject.toml b/pyproject.toml index 13a6ae5..f5039c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ jsonschema = {version = ">=3.0,<4.0", optional = true} kombu = { version = ">=5.1", optional = true } markupsafe = { version = "==2.0.1", optional = true } openapi-ui-bundles = { version = ">=0.1", optional = true } -pydantic = {version = ">=1.7.0,<2.0", optional = true} +pydantic = {version = ">=2.0", optional = true} requests = { version = ">=2.0", optional = true } starlette = { version = ">=0.25.0", optional = true } werkzeug = { version = ">=2.0", optional = true} diff --git a/tests/server/resources/openapi-1.json b/tests/server/resources/openapi-1.json index 3e1be77..22986c9 100644 --- a/tests/server/resources/openapi-1.json +++ b/tests/server/resources/openapi-1.json @@ -434,9 +434,15 @@ ] }, "result": { - "title": "Result", - "type": "string", - "nullable": "true" + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Result" } }, "required": [ @@ -532,12 +538,20 @@ "type": "object", "properties": { "param1": { - "title": "Param1", - "type": "number" + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Param1" }, "param2": { - "title": "Param2", "default": 1, + "title": "Param2", "type": "integer" } } @@ -876,8 +890,7 @@ ] }, "result": { - "title": "Result", - "nullable": "true" + "title": "Result" } }, "required": [ @@ -1017,8 +1030,7 @@ ] }, "result": { - "title": "Result", - "nullable": "true" + "title": "Result" } }, "required": [ @@ -1190,9 +1202,16 @@ "type": "string" }, "field2": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], "title": "Field2", - "default": 1, - "type": "integer" + "default": 1 }, "field3": { "$ref": "#/components/schemas/SubModel" From 5b23343523e935839eb483820d5b995268cb077f Mon Sep 17 00:00:00 2001 From: Dmitry Pershin Date: Tue, 26 Sep 2023 20:56:21 +0500 Subject: [PATCH 2/2] bump version 1.8.0. --- CHANGELOG.rst | 6 ++++++ pjrpc/__about__.py | 2 +- pyproject.toml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 68c6f6b..0d91c00 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,12 @@ Changelog ========= +1.8.0 (2023-09-26) +------------------ + +- pydantic 2 support added + + 1.7.0 (2023-08-10) ------------------ diff --git a/pjrpc/__about__.py b/pjrpc/__about__.py index 3e20f1d..4615488 100644 --- a/pjrpc/__about__.py +++ b/pjrpc/__about__.py @@ -2,7 +2,7 @@ __description__ = 'Extensible JSON-RPC library' __url__ = 'https://github.com/dapper91/pjrpc' -__version__ = '1.7.0' +__version__ = '1.8.0' __author__ = 'Dmitry Pershin' __email__ = 'dapper1291@gmail.com' diff --git a/pyproject.toml b/pyproject.toml index f5039c4..655fb24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pjrpc" -version = "1.7.0" +version = "1.8.0" description = "Extensible JSON-RPC library" authors = ["Dmitry Pershin "] license = "Unlicense"