Skip to content

Commit 41ca014

Browse files
committed
Remove extra fields from Header that "MUST NOT be specified" according to the spec.
Solved by creating common base class for Parameter and Header. Also made allowReserved and allowEmptyValue optional.
1 parent ac6acc9 commit 41ca014

File tree

5 files changed

+100
-148
lines changed

5 files changed

+100
-148
lines changed

openapi_pydantic/v3/v3_0_3/header.py

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
from typing import TYPE_CHECKING
2-
3-
from pydantic import Field
4-
51
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
62

7-
from .parameter import Parameter, ParameterLocation
3+
from .parameter import ParameterBase
84

95
_examples = [
106
{
@@ -14,49 +10,26 @@
1410
]
1511

1612

17-
if TYPE_CHECKING:
18-
19-
class Header(Parameter):
20-
"""
21-
The Header Object follows the structure of the
22-
[Parameter Object](#parameterObject) with the following changes:
23-
24-
1. `name` MUST NOT be specified, it is given in the corresponding
25-
`headers` map.
26-
2. `in` MUST NOT be specified, it is implicitly in `header`.
27-
3. All traits that are affected by the location MUST be applicable
28-
to a location of `header` (for example, [`style`](#parameterStyle)).
29-
"""
30-
31-
name: str = Field(default="")
32-
param_in: ParameterLocation = Field(
33-
default=ParameterLocation.HEADER, alias="in"
34-
)
13+
class Header(ParameterBase):
14+
"""
15+
The Header Object follows the structure of the
16+
[Parameter Object](#parameterObject) with the following changes:
3517
36-
elif PYDANTIC_V2:
37-
from typing import Literal
38-
39-
LiteralEmptyString = Literal[""]
40-
41-
class Header(Parameter):
42-
name: LiteralEmptyString = Field(default="")
43-
param_in: Literal[ParameterLocation.HEADER] = Field(
44-
default=ParameterLocation.HEADER, alias="in"
45-
)
18+
1. `name` MUST NOT be specified, it is given in the corresponding
19+
`headers` map.
20+
2. `in` MUST NOT be specified, it is implicitly in `header`.
21+
3. All traits that are affected by the location MUST be applicable
22+
to a location of `header` (for example, [`style`](#parameterStyle)).
23+
"""
4624

25+
if PYDANTIC_V2:
4726
model_config = ConfigDict(
4827
extra="allow",
4928
populate_by_name=True,
5029
json_schema_extra={"examples": _examples},
5130
)
5231

53-
else:
54-
55-
class Header(Parameter):
56-
name: str = Field(default="", const=True)
57-
param_in: ParameterLocation = Field(
58-
default=ParameterLocation.HEADER, const=True, alias="in"
59-
)
32+
else:
6033

6134
class Config:
6235
extra = Extra.allow

openapi_pydantic/v3/v3_0_3/parameter.py

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -75,35 +75,11 @@ class ParameterLocation(str, enum.Enum):
7575
COOKIE = "cookie"
7676

7777

78-
class Parameter(BaseModel):
79-
"""
80-
Describes a single operation parameter.
81-
82-
A unique parameter is defined by a combination of a [name](#parameterName) and
83-
[location](#parameterIn).
84-
"""
85-
86-
"""Fixed Fields"""
87-
88-
name: str
89-
"""
90-
**REQUIRED**. The name of the parameter.
91-
Parameter names are *case sensitive*.
92-
93-
- If [`in`](#parameterIn) is `"path"`, the `name` field MUST correspond to a
94-
template expression occurring within the [path](#pathsPath) field in the
95-
[Paths Object](#pathsObject). See [Path Templating](#pathTemplating) for further
96-
information.
97-
- If [`in`](#parameterIn) is `"header"` and the `name` field is `"Accept"`,
98-
`"Content-Type"` or `"Authorization"`, the parameter definition SHALL be ignored.
99-
- For all other cases, the `name` corresponds to the parameter name used by the
100-
[`in`](#parameterIn) property.
78+
class ParameterBase(BaseModel):
10179
"""
80+
Base class for Parameter and Header.
10281
103-
param_in: ParameterLocation = Field(alias="in")
104-
"""
105-
**REQUIRED**. The location of the parameter. Possible values are `"query"`,
106-
`"header"`, `"path"` or `"cookie"`.
82+
(Header is like Parameter, but has no `name` or `in` fields.)
10783
"""
10884

10985
description: Optional[str] = None
@@ -128,7 +104,7 @@ class Parameter(BaseModel):
128104
Default value is `false`.
129105
"""
130106

131-
allowEmptyValue: bool = False
107+
allowEmptyValue: Optional[bool] = None
132108
"""
133109
Sets the ability to pass empty-valued parameters.
134110
This is valid only for `query` parameters and allows sending a parameter with an
@@ -165,7 +141,7 @@ class Parameter(BaseModel):
165141
For all other styles, the default value is `false`.
166142
"""
167143

168-
allowReserved: bool = False
144+
allowReserved: Optional[bool] = None
169145
"""
170146
Determines whether the parameter value SHOULD allow reserved characters,
171147
as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-2.2)
@@ -230,3 +206,35 @@ class Config:
230206
extra = Extra.allow
231207
allow_population_by_field_name = True
232208
schema_extra = {"examples": _examples}
209+
210+
211+
class Parameter(ParameterBase):
212+
"""
213+
Describes a single operation parameter.
214+
215+
A unique parameter is defined by a combination of a [name](#parameterName) and
216+
[location](#parameterIn).
217+
"""
218+
219+
"""Fixed Fields"""
220+
221+
name: str
222+
"""
223+
**REQUIRED**. The name of the parameter.
224+
Parameter names are *case sensitive*.
225+
226+
- If [`in`](#parameterIn) is `"path"`, the `name` field MUST correspond to a
227+
template expression occurring within the [path](#pathsPath) field in the
228+
[Paths Object](#pathsObject). See [Path Templating](#pathTemplating) for further
229+
information.
230+
- If [`in`](#parameterIn) is `"header"` and the `name` field is `"Accept"`,
231+
`"Content-Type"` or `"Authorization"`, the parameter definition SHALL be ignored.
232+
- For all other cases, the `name` corresponds to the parameter name used by the
233+
[`in`](#parameterIn) property.
234+
"""
235+
236+
param_in: ParameterLocation = Field(alias="in")
237+
"""
238+
**REQUIRED**. The location of the parameter. Possible values are `"query"`,
239+
`"header"`, `"path"` or `"cookie"`.
240+
"""

openapi_pydantic/v3/v3_1_0/header.py

Lines changed: 13 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
from typing import TYPE_CHECKING
2-
3-
from pydantic import Field
4-
51
from openapi_pydantic.compat import PYDANTIC_V2, ConfigDict, Extra
62

7-
from .parameter import Parameter, ParameterLocation
3+
from .parameter import ParameterBase
84

95
_examples = [
106
{
@@ -13,49 +9,27 @@
139
}
1410
]
1511

16-
if TYPE_CHECKING:
17-
18-
class Header(Parameter):
19-
"""
20-
The Header Object follows the structure of the
21-
[Parameter Object](#parameterObject) with the following changes:
22-
23-
1. `name` MUST NOT be specified, it is given in the corresponding
24-
`headers` map.
25-
2. `in` MUST NOT be specified, it is implicitly in `header`.
26-
3. All traits that are affected by the location MUST be applicable
27-
to a location of `header` (for example, [`style`](#parameterStyle)).
28-
"""
29-
30-
name: str = Field(default="")
31-
param_in: ParameterLocation = Field(
32-
default=ParameterLocation.HEADER, alias="in"
33-
)
34-
35-
elif PYDANTIC_V2:
36-
from typing import Literal
3712

38-
LiteralEmptyString = Literal[""]
13+
class Header(ParameterBase):
14+
"""
15+
The Header Object follows the structure of the
16+
[Parameter Object](#parameterObject) with the following changes:
3917
40-
class Header(Parameter):
41-
name: LiteralEmptyString = Field(default="")
42-
param_in: Literal[ParameterLocation.HEADER] = Field(
43-
default=ParameterLocation.HEADER, alias="in"
44-
)
18+
1. `name` MUST NOT be specified, it is given in the corresponding
19+
`headers` map.
20+
2. `in` MUST NOT be specified, it is implicitly in `header`.
21+
3. All traits that are affected by the location MUST be applicable
22+
to a location of `header` (for example, [`style`](#parameterStyle)).
23+
"""
4524

25+
if PYDANTIC_V2:
4626
model_config = ConfigDict(
4727
extra="allow",
4828
populate_by_name=True,
4929
json_schema_extra={"examples": _examples},
5030
)
5131

52-
else:
53-
54-
class Header(Parameter):
55-
name: str = Field(default="", const=True)
56-
param_in: ParameterLocation = Field(
57-
default=ParameterLocation.HEADER, const=True, alias="in"
58-
)
32+
else:
5933

6034
class Config:
6135
extra = Extra.allow

openapi_pydantic/v3/v3_1_0/parameter.py

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -75,35 +75,11 @@ class ParameterLocation(str, enum.Enum):
7575
COOKIE = "cookie"
7676

7777

78-
class Parameter(BaseModel):
79-
"""
80-
Describes a single operation parameter.
81-
82-
A unique parameter is defined by a combination of a [name](#parameterName) and
83-
[location](#parameterIn).
84-
"""
85-
86-
"""Fixed Fields"""
87-
88-
name: str
89-
"""
90-
**REQUIRED**. The name of the parameter.
91-
Parameter names are *case sensitive*.
92-
93-
- If [`in`](#parameterIn) is `"path"`, the `name` field MUST correspond to a
94-
template expression occurring within the [path](#pathsPath) field in the
95-
[Paths Object](#pathsObject).
96-
See [Path Templating](#pathTemplating) for further information.
97-
- If [`in`](#parameterIn) is `"header"` and the `name` field is `"Accept"`,
98-
`"Content-Type"` or `"Authorization"`, the parameter definition SHALL be ignored.
99-
- For all other cases, the `name` corresponds to the parameter name used by the
100-
[`in`](#parameterIn) property.
78+
class ParameterBase(BaseModel):
10179
"""
80+
Base class for Parameter and Header.
10281
103-
param_in: ParameterLocation = Field(alias="in")
104-
"""
105-
**REQUIRED**. The location of the parameter. Possible values are `"query"`,
106-
`"header"`, `"path"` or `"cookie"`.
82+
(Header is like Parameter, but has no `name` or `in` fields.)
10783
"""
10884

10985
description: Optional[str] = None
@@ -128,7 +104,7 @@ class Parameter(BaseModel):
128104
Default value is `false`.
129105
"""
130106

131-
allowEmptyValue: bool = False
107+
allowEmptyValue: Optional[bool] = None
132108
"""
133109
Sets the ability to pass empty-valued parameters.
134110
This is valid only for `query` parameters and allows sending a parameter with an
@@ -165,7 +141,7 @@ class Parameter(BaseModel):
165141
For all other styles, the default value is `false`.
166142
"""
167143

168-
allowReserved: bool = False
144+
allowReserved: Optional[bool] = None
169145
"""
170146
Determines whether the parameter value SHOULD allow reserved characters,
171147
as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-2.2)
@@ -231,3 +207,35 @@ class Config:
231207
extra = Extra.allow
232208
allow_population_by_field_name = True
233209
schema_extra = {"examples": _examples}
210+
211+
212+
class Parameter(ParameterBase):
213+
"""
214+
Describes a single operation parameter.
215+
216+
A unique parameter is defined by a combination of a [name](#parameterName) and
217+
[location](#parameterIn).
218+
"""
219+
220+
"""Fixed Fields"""
221+
222+
name: str
223+
"""
224+
**REQUIRED**. The name of the parameter.
225+
Parameter names are *case sensitive*.
226+
227+
- If [`in`](#parameterIn) is `"path"`, the `name` field MUST correspond to a
228+
template expression occurring within the [path](#pathsPath) field in the
229+
[Paths Object](#pathsObject).
230+
See [Path Templating](#pathTemplating) for further information.
231+
- If [`in`](#parameterIn) is `"header"` and the `name` field is `"Accept"`,
232+
`"Content-Type"` or `"Authorization"`, the parameter definition SHALL be ignored.
233+
- For all other cases, the `name` corresponds to the parameter name used by the
234+
[`in`](#parameterIn) property.
235+
"""
236+
237+
param_in: ParameterLocation = Field(alias="in")
238+
"""
239+
**REQUIRED**. The location of the parameter. Possible values are `"query"`,
240+
`"header"`, `"path"` or `"cookie"`.
241+
"""

tests/test_alias.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
from typing import Callable
22

33
from openapi_pydantic import (
4-
Header,
54
MediaType,
65
Parameter,
7-
ParameterLocation,
86
PathItem,
97
Reference,
108
Schema,
@@ -15,15 +13,6 @@
1513
validate_func_name = "model_validate" if PYDANTIC_V2 else "parse_obj"
1614

1715

18-
def test_header_alias() -> None:
19-
header_1 = Header(param_in="header")
20-
model_validate: Callable[[dict], Header] = getattr(Header, validate_func_name)
21-
header_2 = model_validate({"param_in": "header"})
22-
header_3 = model_validate({"in": "header"})
23-
header_4 = model_validate({"in": ParameterLocation.HEADER})
24-
assert header_1 == header_2 == header_3 == header_4
25-
26-
2716
def test_media_type_alias() -> None:
2817
media_type_1 = MediaType(media_type_schema=Schema())
2918
media_type_2 = MediaType(schema=Schema())

0 commit comments

Comments
 (0)