Skip to content

Commit 3a59faa

Browse files
authored
Merge pull request #801 from marshmallow-code/packaging
Use packaging.Version
2 parents be3dd41 + d9e7ef9 commit 3a59faa

File tree

9 files changed

+35
-95
lines changed

9 files changed

+35
-95
lines changed

setup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import re
22
from setuptools import setup, find_packages
33

4+
INSTALL_REQUIRES = "packaging>=21.3"
5+
46
EXTRAS_REQUIRE = {
57
"marshmallow": ["marshmallow>=3.13.0"],
68
"yaml": ["PyYAML>=3.10"],
@@ -63,6 +65,7 @@ def read(fname):
6365
packages=find_packages("src"),
6466
package_dir={"": "src"},
6567
include_package_data=True,
68+
install_requires=INSTALL_REQUIRES,
6669
extras_require=EXTRAS_REQUIRE,
6770
license="MIT",
6871
zip_safe=False,

src/apispec/core.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
"""Core apispec classes and functions."""
22

33
from __future__ import annotations
4-
from collections.abc import Sequence
5-
64

7-
import typing
5+
from collections.abc import Sequence
86
from copy import deepcopy
97
import warnings
8+
import typing
9+
10+
from packaging.version import Version
1011

1112
from .exceptions import (
1213
APISpecError,
@@ -15,7 +16,7 @@
1516
DuplicateParameterError,
1617
InvalidParameterError,
1718
)
18-
from .utils import OpenAPIVersion, deepupdate, COMPONENT_SUBSECTIONS, build_reference
19+
from .utils import deepupdate, COMPONENT_SUBSECTIONS, build_reference
1920

2021
if typing.TYPE_CHECKING:
2122
from .plugin import BasePlugin
@@ -27,6 +28,9 @@
2728

2829
VALID_METHODS = {2: VALID_METHODS_OPENAPI_V2, 3: VALID_METHODS_OPENAPI_V3}
2930

31+
MIN_INCLUSIVE_OPENAPI_VERSION = Version("2.0")
32+
MAX_EXCLUSIVE_OPENAPI_VERSION = Version("4.0")
33+
3034

3135
class Components:
3236
"""Stores OpenAPI components
@@ -38,7 +42,7 @@ class Components:
3842
def __init__(
3943
self,
4044
plugins: Sequence[BasePlugin],
41-
openapi_version: OpenAPIVersion,
45+
openapi_version: Version,
4246
) -> None:
4347
self._plugins = plugins
4448
self.openapi_version = openapi_version
@@ -405,7 +409,7 @@ class APISpec:
405409
:param str version: API version
406410
:param list|tuple plugins: Plugin instances.
407411
See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#infoObject
408-
:param str|OpenAPIVersion openapi_version: OpenAPI Specification version.
412+
:param str openapi_version: OpenAPI Specification version.
409413
Should be in the form '2.x' or '3.x.x' to comply with the OpenAPI standard.
410414
:param options: Optional top-level keys
411415
See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#openapi-object
@@ -415,15 +419,21 @@ def __init__(
415419
self,
416420
title: str,
417421
version: str,
418-
openapi_version: OpenAPIVersion | str,
422+
openapi_version: str,
419423
plugins: Sequence[BasePlugin] = (),
420424
**options: typing.Any,
421425
) -> None:
422426
self.title = title
423427
self.version = version
424-
self.openapi_version = OpenAPIVersion(openapi_version)
425428
self.options = options
426429
self.plugins = plugins
430+
self.openapi_version = Version(openapi_version)
431+
if not (
432+
MIN_INCLUSIVE_OPENAPI_VERSION
433+
<= self.openapi_version
434+
< MAX_EXCLUSIVE_OPENAPI_VERSION
435+
):
436+
raise APISpecError(f"Not a valid OpenAPI version number: {openapi_version}")
427437

428438
# Metadata
429439
self._tags: list[dict] = []
@@ -444,10 +454,10 @@ def to_dict(self) -> dict[str, typing.Any]:
444454
if self._tags:
445455
ret["tags"] = self._tags
446456
if self.openapi_version.major < 3:
447-
ret["swagger"] = self.openapi_version.vstring
457+
ret["swagger"] = str(self.openapi_version)
448458
ret.update(self.components.to_dict())
449459
else:
450-
ret["openapi"] = self.openapi_version.vstring
460+
ret["openapi"] = str(self.openapi_version)
451461
components_dict = self.components.to_dict()
452462
if components_dict:
453463
ret["components"] = components_dict

src/apispec/ext/marshmallow/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ class UserSchema(Schema):
7474

7575
import warnings
7676
import typing
77+
from packaging.version import Version
7778

7879
from marshmallow import Schema
7980

8081
from apispec import BasePlugin, APISpec
81-
from apispec.utils import OpenAPIVersion
8282
from .common import resolve_schema_instance, make_schema_key, resolve_schema_cls
8383
from .openapi import OpenAPIConverter
8484
from .schema_resolver import SchemaResolver
@@ -120,7 +120,7 @@ def __init__(
120120
super().__init__()
121121
self.schema_name_resolver = schema_name_resolver or resolver
122122
self.spec: APISpec | None = None
123-
self.openapi_version: OpenAPIVersion | None = None
123+
self.openapi_version: Version | None = None
124124
self.converter: OpenAPIConverter | None = None
125125
self.resolver: SchemaResolver | None = None
126126

src/apispec/ext/marshmallow/field_converter.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@
1111
import operator
1212
import typing
1313
import warnings
14+
from packaging.version import Version
1415

1516
import marshmallow
1617
from marshmallow.orderedset import OrderedSet
1718

18-
from apispec.utils import OpenAPIVersion
19-
2019

2120
RegexType = type(re.compile(""))
2221

@@ -90,7 +89,7 @@ class FieldConverterMixin:
9089
"""Adds methods for converting marshmallow fields to an OpenAPI properties."""
9190

9291
field_mapping = DEFAULT_FIELD_MAPPING
93-
openapi_version: OpenAPIVersion
92+
openapi_version: Version
9493

9594
def init_attribute_functions(self):
9695
self.attribute_functions = [

src/apispec/ext/marshmallow/openapi.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99

1010
from __future__ import annotations
1111
import typing
12+
from packaging.version import Version
1213

1314
import marshmallow
1415
from marshmallow.utils import is_collection
1516

1617
from apispec import APISpec
17-
from apispec.utils import OpenAPIVersion
1818
from apispec.exceptions import APISpecError
1919
from .field_converter import FieldConverterMixin
2020
from .common import (
@@ -40,7 +40,7 @@
4040
class OpenAPIConverter(FieldConverterMixin):
4141
"""Adds methods for generating OpenAPI specification from marshmallow schemas and fields.
4242
43-
:param str|OpenAPIVersion openapi_version: The OpenAPI version to use.
43+
:param Version openapi_version: The OpenAPI version to use.
4444
Should be in the form '2.x' or '3.x.x' to comply with the OpenAPI standard.
4545
:param callable schema_name_resolver: Callable to generate the schema definition name.
4646
Receives the `Schema` class and returns the name to be used in refs within
@@ -52,11 +52,11 @@ class OpenAPIConverter(FieldConverterMixin):
5252

5353
def __init__(
5454
self,
55-
openapi_version: OpenAPIVersion | str,
55+
openapi_version: Version,
5656
schema_name_resolver,
5757
spec: APISpec,
5858
) -> None:
59-
self.openapi_version = OpenAPIVersion(openapi_version)
59+
self.openapi_version = openapi_version
6060
self.schema_name_resolver = schema_name_resolver
6161
self.spec = spec
6262
self.init_attribute_functions()

src/apispec/utils.py

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import json
99
import typing
1010

11-
from distutils import version
12-
1311
from apispec import exceptions
1412

1513
if typing.TYPE_CHECKING:
@@ -73,7 +71,7 @@ def validate_spec(spec: APISpec) -> bool:
7371
" pip install 'apispec[validation]'"
7472
) from error
7573
parser_kwargs = {}
76-
if spec.openapi_version.version[0] == 3:
74+
if spec.openapi_version.major == 3:
7775
parser_kwargs["backend"] = "openapi-spec-validator"
7876
try:
7977
prance.BaseParser(spec_string=json.dumps(spec.to_dict()), **parser_kwargs)
@@ -83,53 +81,6 @@ def validate_spec(spec: APISpec) -> bool:
8381
return True
8482

8583

86-
class OpenAPIVersion(version.LooseVersion):
87-
"""OpenAPI version
88-
89-
:param str|OpenAPIVersion openapi_version: OpenAPI version
90-
91-
Parses an OpenAPI version expressed as string. Provides shortcut to digits
92-
(major, minor, patch).
93-
94-
Example: ::
95-
96-
ver = OpenAPIVersion('3.0.2')
97-
assert ver.major == 3
98-
assert ver.minor == 0
99-
assert ver.patch == 1
100-
assert ver.vstring == '3.0.2'
101-
assert str(ver) == '3.0.2'
102-
"""
103-
104-
MIN_INCLUSIVE_VERSION = version.LooseVersion("2.0")
105-
MAX_EXCLUSIVE_VERSION = version.LooseVersion("4.0")
106-
107-
def __init__(self, openapi_version: version.LooseVersion | str) -> None:
108-
if isinstance(openapi_version, version.LooseVersion):
109-
openapi_version = openapi_version.vstring
110-
if (
111-
not self.MIN_INCLUSIVE_VERSION
112-
<= openapi_version
113-
< self.MAX_EXCLUSIVE_VERSION
114-
):
115-
raise exceptions.APISpecError(
116-
f"Not a valid OpenAPI version number: {openapi_version}"
117-
)
118-
super().__init__(openapi_version)
119-
120-
@property
121-
def major(self) -> int:
122-
return int(self.version[0])
123-
124-
@property
125-
def minor(self) -> int:
126-
return int(self.version[1])
127-
128-
@property
129-
def patch(self) -> int:
130-
return int(self.version[2])
131-
132-
13384
# from django.contrib.admindocs.utils
13485
def trim_docstring(docstring: str) -> str:
13586
"""Uniformly trims leading/trailing whitespace from docstrings.

tests/test_core.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,10 @@ def test_openapi_metadata(self, spec):
148148
assert metadata["info"]["version"] == "1.0.0"
149149
assert metadata["info"]["description"] == description
150150
if spec.openapi_version.major < 3:
151-
assert metadata["swagger"] == spec.openapi_version.vstring
151+
assert metadata["swagger"] == str(spec.openapi_version)
152152
assert metadata["security"] == [{"apiKey": []}]
153153
else:
154-
assert metadata["openapi"] == spec.openapi_version.vstring
154+
assert metadata["openapi"] == str(spec.openapi_version)
155155
security_schemes = {
156156
"bearerAuth": dict(type="http", scheme="bearer", bearerFormat="JWT")
157157
}

tests/test_ext_marshmallow_field.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ def custom_string2properties(self, field, **kwargs):
398398
CustomStringField()
399399
)
400400
assert properties["x-customString"] == (
401-
spec_fixture.openapi.openapi_version == "2.0"
401+
spec_fixture.openapi.openapi_version.major == 2
402402
)
403403

404404

tests/test_utils.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,4 @@
1-
import pytest
2-
31
from apispec import utils
4-
from apispec.exceptions import APISpecError
5-
6-
7-
class TestOpenAPIVersion:
8-
@pytest.mark.parametrize("version", ("1.0", "4.0"))
9-
def test_openapi_version_invalid_version(self, version):
10-
message = "Not a valid OpenAPI version number:"
11-
with pytest.raises(APISpecError, match=message):
12-
utils.OpenAPIVersion(version)
13-
14-
@pytest.mark.parametrize("version", ("3.0.1", utils.OpenAPIVersion("3.0.1")))
15-
def test_openapi_version_string_or_openapi_version_param(self, version):
16-
assert utils.OpenAPIVersion(version) == utils.OpenAPIVersion("3.0.1")
17-
18-
def test_openapi_version_digits(self):
19-
ver = utils.OpenAPIVersion("3.0.1")
20-
assert ver.major == 3
21-
assert ver.minor == 0
22-
assert ver.patch == 1
23-
assert ver.vstring == "3.0.1"
24-
assert str(ver) == "3.0.1"
252

263

274
def test_build_reference():

0 commit comments

Comments
 (0)