Skip to content

Commit e295961

Browse files
authored
Pydantic v1/2 compatibility (#2014)
* Update pydantic v1/2 compatibility * Fix flake8 and code styling * Handle Pydantic v1/v2 compatibility in openapi.py
1 parent 8111d83 commit e295961

File tree

5 files changed

+52
-27
lines changed

5 files changed

+52
-27
lines changed

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ ARG ADD_DEB_PACKAGES="\
6969
python3-netcdf4 \
7070
python3-pandas \
7171
python3-psycopg2 \
72+
python3-pydantic \
7273
python3-pymongo \
7374
python3-pyproj \
7475
python3-rasterio \

pygeoapi/models/config.py

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# ****************************** -*-
2-
# flake8: noqa
31
# =================================================================
42
#
53
# Authors: Sander Schaminee <sander.schaminee@geocat.net>
@@ -32,20 +30,33 @@
3230
# =================================================================
3331

3432
from pydantic import BaseModel, Field
33+
import pydantic
34+
35+
# Handle Pydantic v1/v2 compatibility
36+
if pydantic.VERSION.startswith('1'):
37+
model_validator = 'parse_obj'
38+
model_fields = '__fields__'
39+
regex_param = {'regex': r'^\d+\.\d+\..+$'}
40+
else:
41+
model_validator = 'model_validate'
42+
model_fields = 'model_fields'
43+
regex_param = {'pattern': r'^\d+\.\d+\..+$'}
3544

3645

3746
class APIRules(BaseModel):
38-
""" Pydantic model for API design rules that must be adhered to. """
39-
api_version: str = Field(pattern=r'^\d+\.\d+\..+$',
40-
description="Semantic API version number.")
47+
"""
48+
Pydantic model for API design rules that must be adhered to.
49+
"""
50+
api_version: str = Field(**regex_param,
51+
description='Semantic API version number.')
4152
url_prefix: str = Field(
42-
"",
53+
'',
4354
description="If set, pygeoapi routes will be prepended with the "
4455
"given URL path prefix (e.g. '/v1'). "
4556
"Defaults to an empty string (no prefix)."
4657
)
4758
version_header: str = Field(
48-
"",
59+
'',
4960
description="If set, pygeoapi will set a response header with this "
5061
"name and its value will hold the API version. "
5162
"Defaults to an empty string (i.e. no header). "
@@ -59,47 +70,55 @@ class APIRules(BaseModel):
5970

6071
@staticmethod
6172
def create(**rules_config) -> 'APIRules':
62-
""" Returns a new APIRules instance for the current API version
63-
and configured rules. """
73+
"""
74+
Returns a new APIRules instance for the current API version
75+
and configured rules.
76+
"""
6477
obj = {
65-
k: v for k, v in rules_config.items() if k in APIRules.model_fields
78+
k: v for k, v in rules_config.items()
79+
if k in getattr(APIRules, model_fields)
6680
}
6781
# Validation will fail if required `api_version` is missing
6882
# or if `api_version` is not a semantic version number
69-
return APIRules.model_validate(obj)
83+
model_validator_ = getattr(APIRules, model_validator)
84+
return model_validator_(obj)
7085

7186
@property
7287
def response_headers(self) -> dict:
73-
""" Gets a dictionary of additional response headers for the current
74-
API rules. Returns an empty dict if no rules apply. """
88+
"""
89+
Gets a dictionary of additional response headers for the current
90+
API rules. Returns an empty dict if no rules apply.
91+
"""
7592
headers = {}
7693
if self.version_header:
7794
headers[self.version_header] = self.api_version
7895
return headers
7996

80-
def get_url_prefix(self, style: str = None) -> str:
97+
def get_url_prefix(self, style: str = '') -> str:
8198
"""
8299
Returns an API URL prefix to use in all paths.
83100
May include a (partial) API version. See docs for syntax.
101+
84102
:param style: Set to 'django', 'flask' or 'starlette' to return a
85103
specific prefix formatted for those frameworks.
86104
If not set, only the prefix itself will be returned.
87105
"""
88106
if not self.url_prefix:
89-
return ""
107+
return ''
90108
major, minor, build = self.api_version.split('.')
91109
prefix = self.url_prefix.format(
92110
api_version=self.api_version,
93111
api_major=major,
94112
api_minor=minor,
95113
api_build=build
96114
).strip('/')
97-
style = (style or '').lower()
115+
98116
if style == 'django':
99117
# Django requires the slash at the end
100-
return rf"^{prefix}/"
118+
return rf'^{prefix}/'
101119
elif style in ('flask', 'starlette'):
102120
# Flask and Starlette need the slash in front
103-
return f"/{prefix}"
104-
# If no format is specified, return only the bare prefix
105-
return prefix
121+
return f'/{prefix}'
122+
else:
123+
# If no format is specified, return only the bare prefix
124+
return prefix

pygeoapi/models/openapi.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,17 @@
3232
from enum import Enum
3333

3434
from pydantic import BaseModel
35+
import pydantic
3536

3637

3738
class SupportedFormats(Enum):
38-
JSON = "json"
39-
YAML = "yaml"
39+
JSON = 'json'
40+
YAML = 'yaml'
4041

41-
42-
class OAPIFormat(BaseModel):
43-
root: SupportedFormats = SupportedFormats.YAML
42+
# Handle Pydantic v1/v2 compatibility
43+
if pydantic.VERSION.startswith('1'):
44+
class OAPIFormat(BaseModel):
45+
__root__: SupportedFormats = SupportedFormats.YAML
46+
else:
47+
class OAPIFormat(BaseModel):
48+
root: SupportedFormats = SupportedFormats.YAML

requirements-django.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
Django
2-
pydantic>2.0
2+
pydantic

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ filelock
44
Flask
55
jinja2
66
jsonschema
7-
pydantic>2.0
7+
pydantic
88
pygeofilter
99
pygeoif
1010
pyproj

0 commit comments

Comments
 (0)