From 24ac5bcec398d16a4a1e60ca9c8816db0a815bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Mon, 31 Aug 2026 18:44:06 +0200 Subject: [PATCH 01/58] pre-commit/ruff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ruff 0.16 … disable autofix and disable all new rules for now pre-commit run -a | ansi2txt | grep -E "[A-Z]+[0-9]+" | cut -d ' ' -f 1 | sort | uniq | sort --- .pre-commit-config.yaml | 6 ++--- pyproject.toml | 60 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a463f5bd..c35f861a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,11 +5,11 @@ repos: - id: pycln - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.15.13 + rev: v0.16.5 hooks: # Run the linter. - id: ruff-check - args: [--fix] +# args: [--fix] # Run the formatter. - id: ruff-format - repo: 'https://github.com/pre-commit/pre-commit-hooks' @@ -62,7 +62,7 @@ repos: - repo: https://github.com/astral-sh/uv-pre-commit # uv version. - rev: 0.11.15 + rev: 0.12.7 hooks: # Update the uv lockfile - id: uv-lock diff --git a/pyproject.toml b/pyproject.toml index 69ecf2cf..b90ed816 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,7 +81,65 @@ indent-width = 4 [tool.ruff.lint] fixable = ["ALL"] -ignore = ["E741","F841","E402"] +ignore = [ + "E741", + "F841", + "E402", + "B008", + "B009", + "B015", + "B018", + "B020", + "BLE001", + "C400", + "C408", + "C417", + "C419", + "DTZ005", + "FURB167", + "I001", + "PERF102", + "PIE790", + "PIE804", + "PIE808", + "PLC0208", + "PLR1708", + "PLR1711", + "PLR2044", + "PLW0120", + "PLW0602", + "PT031", + "PYI055", + "RET501", + "RUF010", + "RUF012", + "RUF013", + "RUF017", + "RUF022", + "RUF046", + "RUF059", + "RUF100", + "S110", + "SIM101", + "SIM102", + "SIM103", + "SIM118", + "SIM201", + "SIM210", + "SIM211", + "TC004", + "TC005", + "TRY004", + "TRY201", + "TRY203", + "TRY401", + "UP007", + "UP018", + "UP031", + "UP037", + "UP045" +] + [tool.ruff.format] quote-style = "double" From bbb079259d0bfca455d441387518be8707d24a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Mon, 31 Aug 2026 18:44:56 +0200 Subject: [PATCH 02/58] ruff - B008 --- .pre-commit-config.yaml | 2 +- pyproject.toml | 2 +- tests/api/v1/main.py | 3 ++- tests/api/v2/main.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c35f861a..3bc5cc34 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: hooks: # Run the linter. - id: ruff-check -# args: [--fix] + args: [--fix] # Run the formatter. - id: ruff-format - repo: 'https://github.com/pre-commit/pre-commit-hooks' diff --git a/pyproject.toml b/pyproject.toml index b90ed816..90f75e1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,7 +85,7 @@ ignore = [ "E741", "F841", "E402", - "B008", +# "B008", "B009", "B015", "B018", diff --git a/tests/api/v1/main.py b/tests/api/v1/main.py index 1a744a90..5fd640c3 100644 --- a/tests/api/v1/main.py +++ b/tests/api/v1/main.py @@ -1,4 +1,5 @@ import errno +from typing import Annotated import starlette.status from fastapi import APIRouter, Body, Response, Path @@ -40,7 +41,7 @@ def _idx(l): ) def createPet( response: Response, - pet: PetCreate = Body(..., embed=True), + pet: Annotated[PetCreate, Body(..., embed=True)], ) -> None: if pet.name in ZOO: return JSONResponse( diff --git a/tests/api/v2/main.py b/tests/api/v2/main.py index 360abb01..d97040a8 100644 --- a/tests/api/v2/main.py +++ b/tests/api/v2/main.py @@ -28,7 +28,7 @@ def _idx(l): ) def createPet( response: Response, - pet: schema.Pet = Body(..., embed=True), + pet: Annotated[schema.Pet, Body(..., embed=True)], ) -> schema.Pet: # if isinstance(pet, Cat): # pet = pet.__root__ From a5df05b88d66e3c5acdd6bfaf5d20db1e0e258ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Mon, 31 Aug 2026 18:52:35 +0200 Subject: [PATCH 03/58] ruff - B009 --- pyproject.toml | 2 +- src/aiopenapi3/openapi.py | 2 +- tests/conftest.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 90f75e1c..b3ace83b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,7 +86,7 @@ ignore = [ "F841", "E402", # "B008", - "B009", +# "B009", "B015", "B018", "B020", diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index e43e67f5..6623866a 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -453,7 +453,7 @@ def _get_combined_attributes(schema): return ( getattr(schema, "oneOf", []) # Swagger compat + ( - list(getattr(schema, "discriminator").mapping.values()) + list(schema.discriminator.mapping.values()) if isinstance(getattr(schema, "discriminator", {}), DiscriminatorBase) else [] ) diff --git a/tests/conftest.py b/tests/conftest.py index 26bfea31..c38163a2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -40,7 +40,7 @@ def __repr__(self): @property def schema(self): - return getattr(getattr(aiopenapi3, f"v{self.major}{self.minor}"), "Schema") + return getattr(aiopenapi3, f"v{self.major}{self.minor}").Schema @pytest.fixture(scope="session", params=[_Version(3, 0, 3), _Version(3, 1, 0)], ids=("v30", "v31")) From 3022bb6cc23f92f227446f3666dae227008428fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Mon, 31 Aug 2026 18:54:04 +0200 Subject: [PATCH 04/58] ruff - B015 --- pyproject.toml | 2 +- tests/schema_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b3ace83b..36b4c1f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,7 +87,7 @@ ignore = [ "E402", # "B008", # "B009", - "B015", +# "B015", "B018", "B020", "BLE001", diff --git a/tests/schema_test.py b/tests/schema_test.py index 4f1534d8..f8e7a1b1 100644 --- a/tests/schema_test.py +++ b/tests/schema_test.py @@ -359,7 +359,7 @@ def test_schema_with_patternProperties(with_schema_patternProperties): O = api.components.schemas["O"].get_type() a = A.model_validate({"I_5": 100}) assert list(a.aio3_patternProperty("^I_")) == [("I_5", 100)] - sorted(typing.get_args(a.aio3_patternProperty.__annotations__["item"])) == ["^I_", "^S_"] + assert sorted(typing.get_args(a.aio3_patternProperty.__annotations__["item"])) == ["^I_", "^S_"] assert a.aio3_patternProperties == {"^S_": [], "^I_": [("I_5", 100)]} From c84b2e64dbcf3075d7fc61e981edbd26bf40e1ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Mon, 31 Aug 2026 18:55:41 +0200 Subject: [PATCH 05/58] ruff - B018 --- pyproject.toml | 2 +- tests/apiv2_test.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 36b4c1f4..b1e10707 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,7 +88,7 @@ ignore = [ # "B008", # "B009", # "B015", - "B018", +# "B018", "B020", "BLE001", "C400", diff --git a/tests/apiv2_test.py b/tests/apiv2_test.py index 18dd67fe..42e6115c 100644 --- a/tests/apiv2_test.py +++ b/tests/apiv2_test.py @@ -149,8 +149,8 @@ def randomPet(client, name=None, cat=False): @pytest.mark.asyncio(loop_scope="session") async def test_Request(server, client): - client._.createPet.data - client._.createPet.parameters + assert client._.createPet.data is not None + assert client._.createPet.parameters == [] client._.createPet.args() client._.createPet.return_value() From 9a2a27fe22c2d750c30e3fbaeaa4b7b96094963a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Mon, 31 Aug 2026 18:57:30 +0200 Subject: [PATCH 06/58] ruff - B020 --- pyproject.toml | 2 +- src/aiopenapi3/model.py | 44 ++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b1e10707..954f9de4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,7 +89,7 @@ ignore = [ # "B009", # "B015", # "B018", - "B020", +# "B020", "BLE001", "C400", "C408", diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index 15a9779e..e4e5d964 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -547,37 +547,37 @@ def createAnnotation( _names = tuple(filter(lambda x: x, _names)) r = [Literal[_names]] # type: ignore[assignment,list-item] else: - for _type in Model.types(schema) if not _type else [_type]: - if _type in ("boolean", "integer", "number", "string"): - oneOf = [i for i in getattr(schema, "oneOf", []) if _type in Model.types(i)] - anyOf = [i for i in getattr(schema, "anyOf", []) if _type in Model.types(i)] - allOf = [i for i in getattr(schema, "allOf", []) if _type in Model.types(i)] + for _t in Model.types(schema) if not _type else [_type]: + if _t in ("boolean", "integer", "number", "string"): + oneOf = [i for i in getattr(schema, "oneOf", []) if _t in Model.types(i)] + anyOf = [i for i in getattr(schema, "anyOf", []) if _t in Model.types(i)] + allOf = [i for i in getattr(schema, "allOf", []) if _t in Model.types(i)] if not (anyOf or oneOf or allOf): - v = class_from_schema(schema, _type) + v = class_from_schema(schema, _t) r.append(v) else: - v = [Model.createAnnotation(i, _type=_type) for i in oneOf] + v = [Model.createAnnotation(i, _type=_t) for i in oneOf] r.extend(v) - v = [Model.createAnnotation(i, _type=_type) for i in anyOf] + v = [Model.createAnnotation(i, _type=_t) for i in anyOf] r.extend(v) - v = [Model.createAnnotation(i, _type=_type) for i in allOf] + v = [Model.createAnnotation(i, _type=_t) for i in allOf] r.extend(v) - elif _type == "array": + elif _t == "array": r.extend( - list( - Model.createAnnotation(i, _type=_type) + [ + Model.createAnnotation(i, _type=_t) for i in getattr(schema, "oneOf", []) - if Model.is_type(i, _type) - ) + if Model.is_type(i, _t) + ] ) r.extend( - list( - Model.createAnnotation(i, _type=_type) + [ + Model.createAnnotation(i, _type=_t) for i in getattr(schema, "anyOf", []) - if Model.is_type(i, _type) - ) + if Model.is_type(i, _t) + ] ) if isinstance(schema.items, list): @@ -595,12 +595,12 @@ def createAnnotation( else: raise TypeError(schema.items) r.append(v) # type: ignore[arg-type] - elif _type == "object": + elif _t == "object": r.append(schema.get_type(fwdref=fwdref)) - elif _type == "null": + elif _t == "null": nullable = True else: - raise ValueError(_type) + raise ValueError(_t) if len(r) == 1: rr = r[0] @@ -609,7 +609,7 @@ def createAnnotation( else: rr = None # type: ignore[assignment] if nullable is True: - rr = Optional[rr] # type: ignore[assignment] + rr = rr | None # type: ignore[assignment] elif isinstance(schema, ReferenceBase): rr = Model.createAnnotation(schema._target, fwdref=True) else: From d8df3f70861773a6d041c50311216cd2cd8cd091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Mon, 31 Aug 2026 18:59:55 +0200 Subject: [PATCH 07/58] ruff - C408 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 954f9de4..8d4d3dc9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,7 +92,7 @@ ignore = [ # "B020", "BLE001", "C400", - "C408", +# "C408", "C417", "C419", "DTZ005", From 2e04d3e4ff04ac685adcdba5e426e9efda955aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Mon, 31 Aug 2026 19:03:38 +0200 Subject: [PATCH 08/58] ruff - C408 --- .pre-commit-config.yaml | 2 +- src/aiopenapi3/base.py | 8 ++++---- src/aiopenapi3/extra/cookies.py | 2 +- src/aiopenapi3/model.py | 16 ++++++++-------- src/aiopenapi3/openapi.py | 16 ++++++++-------- src/aiopenapi3/request.py | 20 +++++++++----------- src/aiopenapi3/v20/__init__.py | 2 +- src/aiopenapi3/v20/glue.py | 4 ++-- src/aiopenapi3/v30/__init__.py | 2 +- src/aiopenapi3/v30/formdata.py | 4 ++-- src/aiopenapi3/v30/glue.py | 14 +++++++------- src/aiopenapi3/v30/parameter.py | 8 ++++---- src/aiopenapi3/v31/__init__.py | 2 +- src/aiopenapi3/v32/__init__.py | 2 +- tests/api/v1/main.py | 2 +- tests/api/v2/main.py | 2 +- tests/content_length_test.py | 12 ++++++------ tests/extra_test.py | 6 +++--- tests/formdata_test.py | 10 +++++----- tests/path_test.py | 2 +- tests/pathv20_test.py | 4 ++-- tests/petstore_test.py | 6 +++--- tests/petstorev3_test.py | 6 +++--- tests/ref_test.py | 2 +- tests/schema_test.py | 10 +++++----- tests/stream_test.py | 6 +++--- 26 files changed, 84 insertions(+), 86 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3bc5cc34..bffc0692 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: hooks: # Run the linter. - id: ruff-check - args: [--fix] + args: [--fix, --unsafe-fixes] # Run the formatter. - id: ruff-format - repo: 'https://github.com/pre-commit/pre-commit-hooks' diff --git a/src/aiopenapi3/base.py b/src/aiopenapi3/base.py index 2beded1f..492dc10e 100644 --- a/src/aiopenapi3/base.py +++ b/src/aiopenapi3/base.py @@ -47,7 +47,7 @@ def validate_ObjectExtended_extensions(cls, values): return None if not isinstance(values, dict): return values - e = dict() + e = {} rm = set() for k, v in values.items(): if k.startswith("x-"): @@ -106,7 +106,7 @@ def replace(ivalue): data[idx] = n elif isinstance(data, dict): - new = dict() + new = {} for _k, _v in data.items(): n = replace(_v) # Swagger 2.0 Schema.ref resolver … if _v != n: @@ -144,7 +144,7 @@ def replace(ivalue): """ if not value.mapping: - value.mapping = dict() + value.mapping = {} for v in (obj.oneOf or []) + (obj.anyOf or []): k = Path(JSONReference.split(v.ref)[1]).parts[-1] @@ -355,7 +355,7 @@ def __getstate__(self): """ r = BaseModel.__getstate__(self) try: - for k, v in {"_model_type": None, "_model_types": list()}.items(): + for k, v in {"_model_type": None, "_model_types": []}.items(): if k in r["__pydantic_private__"]: r["__pydantic_private__"] = r["__pydantic_private__"].copy() r["__pydantic_private__"][k] = v diff --git a/src/aiopenapi3/extra/cookies.py b/src/aiopenapi3/extra/cookies.py index c77f228b..92bc0710 100644 --- a/src/aiopenapi3/extra/cookies.py +++ b/src/aiopenapi3/extra/cookies.py @@ -55,7 +55,7 @@ def __init__( """ self.cookiejar: http.cookiejar.CookieJar = cookiejar or http.cookiejar.CookieJar() self.policy: Literal["jar", "securitySchemes"] = policy - self.schemes: dict[str, str] = dict() + self.schemes: dict[str, str] = {} if policy not in ["jar", "securitySchemes"]: raise ValueError(f"policy {self.policy} is not a valid policy") diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index e4e5d964..a9b4f40c 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -123,7 +123,7 @@ def validate(self): @property def fields(self): - r = list() + r = [] for k, v in self.properties.items(): r.append((k, (v.annotation, v.default))) return dict(r) @@ -142,7 +142,7 @@ def createFields(self, schema: "SchemaType", overwrite=False): ): continue - args: dict[str, Any] = dict() + args: dict[str, Any] = {} assert schema.required is not None if (v := getattr(f, "default", None)) is not None: args["default"] = v @@ -279,7 +279,7 @@ def _follow(r: "ReferenceType", t: type[_T]) -> TypeGuard[_T]: class Model: # (BaseModel): - ALIASES: dict[str, str] = dict() + ALIASES: dict[str, str] = {} @classmethod def from_schema( @@ -295,12 +295,12 @@ def from_schema( if discriminators is None: discriminators = [] - r: list[_ClassInfo] = list() + r: list[_ClassInfo] = [] types: list[str] = list(Model.types(schema)) multi: bool = len(types) > 1 for _type in types: - args = dict() if multi else None + args = {} if multi else None """ for schema with multiple types, the default value needs to be attached to the RootModel providing empty args creates a FieldInfo without a default value for the subtypes @@ -411,7 +411,7 @@ def get_patternProperty(self_, item): def mkx(): def get_patternProperties(self_): patterns = typing.get_args(self_.aio3_patternProperty.__annotations__["item"]) - r = {k: list() for k in patterns} + r = {k: [] for k in patterns} for name, value in self_.model_extra.items(): for pattern in patterns: if re.match(pattern, name): @@ -533,7 +533,7 @@ def createAnnotation( Required, can be None: Optional[str] Not required, can be None, is … by default: f4: Optional[str] = … """ - r: list[type] = list() + r: list[type] = [] rr: type if (v := getattr(schema, "const", None)) is not None: """ @@ -728,7 +728,7 @@ def booleanFalse(schema: Optional["SchemaType"]) -> bool: @staticmethod def createField(schema: "SchemaType", _type=None, args=None) -> Field: if args is None: - args = dict(default=getattr(schema, "default", None)) + args = {"default": getattr(schema, "default", None)} # """ # readOnly & writeOnly are Optional default None diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index 6623866a..83f8c461 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -280,18 +280,18 @@ def __init__( Raise for http status code """ - self._security: dict[str, tuple[str]] = dict() + self._security: dict[str, tuple[str]] = {} """ authorization informations e.g. {"BasicAuth": ("user","secret")} """ - self._documents: dict[yarl.URL, "RootType"] = dict() + self._documents: dict[yarl.URL, "RootType"] = {} """ the related documents """ - self._server_variables: dict[str, str] = dict() + self._server_variables: dict[str, str] = {} """ server variable mapping """ @@ -492,7 +492,7 @@ def _iterate_schemas(cls, schemas: dict[int, "SchemaType"], next_set: set[int], return processed def _init_schema_types_collect(self, only_required: bool) -> dict[str, "SchemaType"]: - byname: dict[str, "SchemaType"] = dict() + byname: dict[str, "SchemaType"] = {} def is_schema(v: tuple[str, "SchemaType"]) -> bool: return isinstance(v[1], (v20.Schema, v30.Schema, v31.Schema)) @@ -510,7 +510,7 @@ def is_schema(v: tuple[str, "SchemaType"]) -> bool: byname[n] = schema # PathItems - for path, obj in (self.paths or dict()).items(): + for path, obj in (self.paths or {}).items(): for m in obj.model_fields_set & HTTP_METHODS: op = getattr(obj, m) @@ -548,7 +548,7 @@ def is_schema(v: tuple[str, "SchemaType"]) -> bool: byname[n] = schema # PathItems - for path, obj in (self.paths or dict()).items(): + for path, obj in (self.paths or {}).items(): for m in obj.model_fields_set & HTTP_METHODS: op = getattr(obj, m) @@ -617,7 +617,7 @@ def _init_schema_types(self, only_required: bool) -> None: byid: dict[int, "SchemaType"] = {id(i): i for i in byname.values()} data: set[int] = set(byid.keys()) todo: set[int] = self._iterate_schemas(byid, data, set()) - types: dict[str, ForwardRef | type[BaseModel] | type[int] | type[str] | type[float] | type[bool]] = dict() + types: dict[str, ForwardRef | type[BaseModel] | type[int] | type[str] | type[float] | type[bool]] = {} """ Due to Plugins (e.g. Cull/Reduce) byname may be incomplete @@ -700,7 +700,7 @@ def authenticate(self, *args, **kwargs): :param kwargs: scheme=value """ if len(args) == 1 and args[0] is None: - self._security = dict() + self._security = {} schemes = frozenset(kwargs.keys()) diff --git a/src/aiopenapi3/request.py b/src/aiopenapi3/request.py index 7c2ee7dd..f998e48d 100644 --- a/src/aiopenapi3/request.py +++ b/src/aiopenapi3/request.py @@ -352,7 +352,7 @@ def iter_json(response: httpx2.Response) -> Iterator["JSON"]: if not data_.endswith(("\r\r", "\n\n", "\r\n\r\n")): continue - v = dict() + v = {} for l in data_.splitlines(keepends=False): if l == "": continue @@ -452,7 +452,7 @@ async def _send( try: result = await session.send(req, stream=True) except Exception as e: - raise RequestError(self.operation, self, data, parameters or dict()) from e + raise RequestError(self.operation, self, data, parameters or {}) from e return result async def request( # type: ignore[override] @@ -543,7 +543,7 @@ async def aiter_json(response: httpx2.Response) -> AsyncIterator["JSON"]: if not data_.endswith(("\r\r", "\n\n", "\r\n\r\n")): continue - v = dict() + v = {} for l in data_.splitlines(keepends=False): if l == "": continue @@ -591,10 +591,8 @@ class OperationIndex: class OperationTag: def __init__(self, oi: "OperationIndex") -> None: self._oi = oi - self._operations: dict[str, tuple["HTTPMethodType", str, "OperationType", list["ServerType"] | None]] = ( - dict() - ) - self._tags: dict[str, "OperationIndex.OperationTag"] = dict() + self._operations: dict[str, tuple["HTTPMethodType", str, "OperationType", list["ServerType"] | None]] = {} + self._tags: dict[str, "OperationIndex.OperationTag"] = {} def __getattr__(self, item) -> RequestBase: if item in self._operations: @@ -620,7 +618,7 @@ def __init__(self, api: "OpenAPI", use_operation_tags: bool): continue if use_operation_tags and op.tags: for tag in op.tags: - tags = list() + tags = [] while tag: tags.append(tag) tag = api._operationindex.tag(tag) @@ -634,7 +632,7 @@ def __init__(self, api: "OpenAPI", use_operation_tags: bool): for method, op in pi.additionalOperations.items(): if use_operation_tags and op.tags: for tag in op.tags: - tags = list() + tags = [] while tag: tags.append(tag) tag = api._operationindex.tag(tag) @@ -656,7 +654,7 @@ def __init__(self, api: "OpenAPI", use_operation_tags: bool): self._api: "OpenAPI" = api self._root: "RootType" = api._root - self._operations: dict[str, tuple["HTTPMethodType", str, "OperationType", list["ServerType"] | None]] = dict() + self._operations: dict[str, tuple["HTTPMethodType", str, "OperationType", list["ServerType"] | None]] = {} self._tags: dict[str, "OperationIndex.OperationTag"] = collections.defaultdict( lambda: OperationIndex.OperationTag(self) ) @@ -680,7 +678,7 @@ def __init__(self, api: "OpenAPI", use_operation_tags: bool): if use_operation_tags and op.tags: for tag in op.tags: - tree: list[str] = list() + tree: list[str] = [] t: str | None = tag v: TagType | None while t: diff --git a/src/aiopenapi3/v20/__init__.py b/src/aiopenapi3/v20/__init__.py index 1168cb14..88c38de9 100644 --- a/src/aiopenapi3/v20/__init__.py +++ b/src/aiopenapi3/v20/__init__.py @@ -12,7 +12,7 @@ def __init(): - r = dict() + r = {} CLASSES = [ ExternalDocumentation, Reference, diff --git a/src/aiopenapi3/v20/glue.py b/src/aiopenapi3/v20/glue.py index 217872d6..5a1c33f6 100644 --- a/src/aiopenapi3/v20/glue.py +++ b/src/aiopenapi3/v20/glue.py @@ -158,7 +158,7 @@ def _prepare_secschemes_extra(self, scheme: str, value: str | Sequence[str]) -> self.req.auth = httpx2_auth.HeaderApiKey(value, ss.name) def _prepare_parameters(self, provided: Optional["RequestParameters"]): - provided = provided or dict() + provided = provided or {} possible = {_.name: _ for _ in self.operation.parameters + self.root.paths[self.path].parameters} parameters = {i.name: i.default for i in filter(lambda x: x.default is not None, possible.values())} @@ -265,7 +265,7 @@ def _process__status_code(self, result: httpx2.Response, status_code: str) -> "v def _process__headers( self, result: httpx2.Response, headers: dict[str, str], expected_response: "v20ResponseType" ) -> "ResponseHeadersType": - rheaders = dict() + rheaders = {} if expected_response.headers: required = dict(map(lambda x: (x[0].lower(), x[1]), expected_response.headers.items())) """ diff --git a/src/aiopenapi3/v30/__init__.py b/src/aiopenapi3/v30/__init__.py index 8983630a..46fbe895 100644 --- a/src/aiopenapi3/v30/__init__.py +++ b/src/aiopenapi3/v30/__init__.py @@ -16,7 +16,7 @@ def __init(): - r = dict() + r = {} CLASSES = [ Components, Example, diff --git a/src/aiopenapi3/v30/formdata.py b/src/aiopenapi3/v30/formdata.py index 2698457d..da769eac 100644 --- a/src/aiopenapi3/v30/formdata.py +++ b/src/aiopenapi3/v30/formdata.py @@ -40,7 +40,7 @@ class MultipartParameter(NamedTuple): def parameters_from_multipart( data: "BaseModel", media: "MediaTypeType", mph: dict[str, str] ) -> list[MultipartParameter]: - params: list[MultipartParameter] = list() + params: list[MultipartParameter] = [] for k in data.model_fields_set: v = getattr(data, k) ct = "text/plain" @@ -66,7 +66,7 @@ def parameters_from_multipart( allowReserved = False style = "form" explode = True - headers = dict() + headers = {} m = media.schema_.properties[k] """ diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index 461c3d49..6826cfb8 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -271,7 +271,7 @@ def _prepare_parameters(self, provided: Optional["RequestParameters"]) -> dict[s A unique parameter is defined by a combination of a name and location. """ - provided = provided or dict() + provided = provided or {} possible = {_.name: _ for _ in self.operation.parameters + self.root.paths[self.path].parameters} from .. import v30, v31, v32 @@ -279,7 +279,7 @@ def _prepare_parameters(self, provided: Optional["RequestParameters"]) -> dict[s assert isinstance(self.operation, (v30.Operation, v31.Operation, v32.Operation)) if self.operation.requestBody: - rbq: dict[str, str] = dict() # requestBody Parameters + rbq: dict[str, str] = {} # requestBody Parameters ct = "multipart/form-data" if ct in self.operation.requestBody.content: assert self.operation.requestBody.content[ct].encoding is not None @@ -314,7 +314,7 @@ def _prepare_parameters(self, provided: Optional["RequestParameters"]) -> dict[s ) path_parameters = {} - mph = dict() + mph = {} for name, value in parameters.items(): spec = possible[name] values = spec._encode(name, value) @@ -394,8 +394,8 @@ def _prepare_body(self, data_: Optional["RequestData"], mph: dict[str, str]) -> self.req.content = msg.as_string() self.req.headers["Content-Type"] = f'{msg.get_content_type()}; boundary="{msg.get_boundary()}"' elif isinstance(data_, list): - rfiles = list() - rdata: dict[str, str] = dict() + rfiles = [] + rdata: dict[str, str] = {} name: str value: tuple[str, Any] for name, value in cast(Sequence[tuple[str, Any]], data_): @@ -510,7 +510,7 @@ def _process__status_code(self, result: httpx2.Response, status_code: str) -> "v def _process__headers( self, result: httpx2.Response, headers: dict[str, str], expected_response: "v3xResponseType" ) -> "ResponseHeadersType": - rheaders = dict() + rheaders = {} if expected_response.headers: required = dict( map( @@ -583,7 +583,7 @@ def _process_sequence(self, result: httpx2.Response) -> tuple["ResponseHeadersTy return headers, expected_media.itemSchema, content_type def _process_request(self, result: httpx2.Response) -> tuple["ResponseHeadersType", "ResponseDataType"]: - rheaders = dict() + rheaders = {} # spec enforces these are strings status_code = str(result.status_code) content_type = result.headers.get("Content-Type", None) diff --git a/src/aiopenapi3/v30/parameter.py b/src/aiopenapi3/v30/parameter.py index 9456af09..d90e2408 100644 --- a/src/aiopenapi3/v30/parameter.py +++ b/src/aiopenapi3/v30/parameter.py @@ -197,7 +197,7 @@ def _encode__simple(self, name: str, type_: str, value, schema: "v3xSchemaType", elif type_ == "boolean": return {name: json.dumps(value)} elif type_ == "null": - return dict() + return {} elif type_ == "array": assert isinstance(value, (list, tuple)) # blue,black,brown @@ -229,7 +229,7 @@ def _encode__Delimited(self, sep: str, name: str, type_: str, value, schema: "v3 assert explode is False if value is None: - return dict() + return {} if type_ == "array": value = sep.join(value) @@ -245,7 +245,7 @@ def _encode__deepObject(self, name: str, type_: str, value, schema: "v3xSchemaTy assert type_ == "object" and explode is True if not value: - return dict() + return {} values = value if isinstance(value, dict) else value.model_dump() # color[R]=100&color[G]=200&color[B]=150 @@ -266,7 +266,7 @@ def flatten_dict(d: MutableMapping, key: str = ""): def _encode__querystring(self, name: str, type_: str, value, schema: "v3xSchemaType", explode: bool): print(name, type_, value, schema, explode) - values = dict() + values = {} ct = next(iter(self.content.keys())) media = self.content[ct] if ct == "application/x-www-form-urlencoded": diff --git a/src/aiopenapi3/v31/__init__.py b/src/aiopenapi3/v31/__init__.py index 8a8a4a4a..19f84d7d 100644 --- a/src/aiopenapi3/v31/__init__.py +++ b/src/aiopenapi3/v31/__init__.py @@ -14,7 +14,7 @@ def __init(): - r = dict() + r = {} CLASSES = [ Components, Example, diff --git a/src/aiopenapi3/v32/__init__.py b/src/aiopenapi3/v32/__init__.py index 8a8a4a4a..19f84d7d 100644 --- a/src/aiopenapi3/v32/__init__.py +++ b/src/aiopenapi3/v32/__init__.py @@ -14,7 +14,7 @@ def __init(): - r = dict() + r = {} CLASSES = [ Components, Example, diff --git a/tests/api/v1/main.py b/tests/api/v1/main.py index 5fd640c3..4ada3fc3 100644 --- a/tests/api/v1/main.py +++ b/tests/api/v1/main.py @@ -10,7 +10,7 @@ router = APIRouter(prefix="/v1") -ZOO = dict() +ZOO = {} def _idx(l): diff --git a/tests/api/v2/main.py b/tests/api/v2/main.py index d97040a8..8e285e76 100644 --- a/tests/api/v2/main.py +++ b/tests/api/v2/main.py @@ -10,7 +10,7 @@ router = APIRouter(prefix="/v2") -ZOO = dict() +ZOO = {} def _idx(l): diff --git a/tests/content_length_test.py b/tests/content_length_test.py index 4e4e5d35..cf2b34cc 100644 --- a/tests/content_length_test.py +++ b/tests/content_length_test.py @@ -50,16 +50,16 @@ def content_length(request: Request, response: Response, content_length: int = Q @pytest.mark.asyncio(loop_scope="session") async def test_content_length_exceeded(server, client): cl = random.randint(1, client._max_response_content_length) - r = await client._.content_length(parameters=dict(content_length=cl)) + r = await client._.content_length(parameters={"content_length": cl}) assert len(r) == cl cl = client._max_response_content_length - r = await client._.content_length(parameters=dict(content_length=cl)) + r = await client._.content_length(parameters={"content_length": cl}) assert len(r) == cl with pytest.raises(aiopenapi3.errors.ContentLengthExceededError): cl = client._max_response_content_length + 1 - await client._.content_length(parameters=dict(content_length=cl)) + await client._.content_length(parameters={"content_length": cl}) @pytest.mark.asyncio(loop_scope="session") @@ -70,13 +70,13 @@ async def test_sync_content_length_exceeded(server): ) cl = random.randint(1, client._max_response_content_length) - r = await asyncio.to_thread(client._.content_length, parameters=dict(content_length=cl)) + r = await asyncio.to_thread(client._.content_length, parameters={"content_length": cl}) assert len(r) == cl cl = client._max_response_content_length - r = await asyncio.to_thread(client._.content_length, parameters=dict(content_length=cl)) + r = await asyncio.to_thread(client._.content_length, parameters={"content_length": cl}) assert len(r) == cl with pytest.raises(aiopenapi3.errors.ContentLengthExceededError): cl = client._max_response_content_length + 1 - await asyncio.to_thread(client._.content_length, parameters=dict(content_length=cl)) + await asyncio.to_thread(client._.content_length, parameters={"content_length": cl}) diff --git a/tests/extra_test.py b/tests/extra_test.py index b8b535af..9c046bdb 100644 --- a/tests/extra_test.py +++ b/tests/extra_test.py @@ -140,13 +140,13 @@ def test_reduced(with_extra_reduced, httpx2_mock, compressor): assert "A" in api.components.responses assert "A" in api.components.requestBodies - httpx2_mock.add_response(headers={"Content-Type": "application/json", "X-A": "A"}, json=dict(a=1)) + httpx2_mock.add_response(headers={"Content-Type": "application/json", "X-A": "A"}, json={"a": 1}) from aiopenapi3.request import RequestBase req: RequestBase = api._.A data = req.data.get_type().model_construct(a="a") - headers, payload = req(data=data, parameters=dict(Path="a"), return_headers=True) + headers, payload = req(data=data, parameters={"Path": "a"}, return_headers=True) assert payload.a == 1 assert headers["X-A"] == "A" @@ -204,7 +204,7 @@ def test_reduced(with_extra_reduced, httpx2_mock, compressor): from aiopenapi3.extra import Cookies -@pytest.mark.parametrize("cookie", [dict(policy="jar"), dict(policy="securitySchemes")], ids=["jar", "securityScheme"]) +@pytest.mark.parametrize("cookie", [{"policy": "jar"}, {"policy": "securitySchemes"}], ids=["jar", "securityScheme"]) def test_cookies(httpx2_mock, with_extra_cookie, cookie): api = OpenAPI( diff --git a/tests/formdata_test.py b/tests/formdata_test.py index de7cf6cc..d164da21 100644 --- a/tests/formdata_test.py +++ b/tests/formdata_test.py @@ -13,11 +13,11 @@ def test_encode_formdata(): schema = Schema() ITEMS = [ - MultipartParameter("text", "text/plain", "bar", dict(), schema), + MultipartParameter("text", "text/plain", "bar", {}, schema), MultipartParameter("text", "text/plain", "bar", {"X-HEAD": "text"}, schema), - MultipartParameter("audio", "audio/wav", b"jd", dict(), schema), - MultipartParameter("image", "image/png", b"jd", dict(), schema), - MultipartParameter("data", "application/octet-stream", data, dict(), schema), + MultipartParameter("audio", "audio/wav", b"jd", {}, schema), + MultipartParameter("image", "image/png", b"jd", {}, schema), + MultipartParameter("data", "application/octet-stream", data, {}, schema), MultipartParameter("mph", "application/octet-stream", data, {"X-HEAD": "mph"}, schema), ] @@ -65,7 +65,7 @@ def test_formdata_encoding(httpx2_mock, with_paths_requestbody_formdata_encoding ) assert msg.defects == [] and msg.is_multipart() - r = dict() + r = {} for p in msg.get_payload(): name = p.get_param("name", header="content-disposition") payload = p.get_payload(decode=True) diff --git a/tests/path_test.py b/tests/path_test.py index 03d754b2..f1754b4f 100644 --- a/tests/path_test.py +++ b/tests/path_test.py @@ -621,7 +621,7 @@ def test_paths_server_variables(httpx2_mock, with_paths_server_variables): with pytest.raises(ValueError, match="Server Variable host value defoned not allowed"): api._.servers() - api._server_variables = dict() + api._server_variables = {} httpx2_mock.add_response(headers={"Content-Type": "application/json"}, status_code=204) r = api._.path() diff --git a/tests/pathv20_test.py b/tests/pathv20_test.py index dd72d23f..6391a83e 100644 --- a/tests/pathv20_test.py +++ b/tests/pathv20_test.py @@ -179,7 +179,7 @@ def test_paths_parameter_format_v20(httpx2_mock, with_paths_parameter_format_v20 result = api._.formdata(parameters=params) request = httpx2_mock.get_requests()[-1] - files = dict() + files = {} def on_file(file): file.file_object.seek(0) @@ -203,7 +203,7 @@ def on_file(file): ) assert result == "ok" - params = dict(A="a", B=5) + params = {"A": "a", "B": 5} result = api._.urlencoded(parameters=params) request = httpx2_mock.get_requests()[-1] assert (v := urllib.parse.parse_qs(request.content.decode())) is not None and v["A"] == ["a"] and v["B"] == ["5"] diff --git a/tests/petstore_test.py b/tests/petstore_test.py index 39d82cea..e0a43320 100644 --- a/tests/petstore_test.py +++ b/tests/petstore_test.py @@ -46,7 +46,7 @@ class OnMessage(Message): def parsed(self, ctx): def goodPet(i): if not isinstance(i.get("photoUrls", None), list): - i["photoUrls"] = list() + i["photoUrls"] = [] for idx, j in enumerate(i["photoUrls"]): if not isinstance(j, str): i["photoUrls"][idx] = "" @@ -55,7 +55,7 @@ def goodPet(i): i["status"] = "pending" if (c := i.get("category", None)) is None or not isinstance(c, dict): - i["category"] = dict(id=0, name="default") + i["category"] = {"id": 0, "name": "default"} if (c := i.get("name", None)) is None or not isinstance(c, str): i["name"] = "" @@ -65,7 +65,7 @@ def goodPet(i): i["id"] = 0 if False: - for t in i.get("tags", list()): + for t in i.get("tags", []): for k, v in {"name": "default", "id": 0}.items(): if k not in t: t[k] = v diff --git a/tests/petstorev3_test.py b/tests/petstorev3_test.py index d937854b..4793d058 100644 --- a/tests/petstorev3_test.py +++ b/tests/petstorev3_test.py @@ -61,7 +61,7 @@ def received(self, ctx: "Message.Context") -> "Message.Context": if ctx.operationId == "getPetById" and ctx.status_code == "404": import json - ctx.received = json.dumps(dict(code=1, type="error", message=ctx.received.decode())).encode() + ctx.received = json.dumps({"code": 1, "type": "error", "message": ctx.received.decode()}).encode() return ctx def parsed(self, ctx): @@ -69,7 +69,7 @@ def goodPet(i): if i.get("name", None) is None: i["name"] = "default" if not isinstance(i.get("photoUrls", None), list): - i["photoUrls"] = list() + i["photoUrls"] = [] for idx, j in enumerate(i["photoUrls"]): if not isinstance(j, str): i["photoUrls"][idx] = "" @@ -78,7 +78,7 @@ def goodPet(i): i["status"] = "pending" if (c := i.get("category", None)) is None or not isinstance(c, dict): - i["category"] = dict(id=0, name="default") + i["category"] = {"id": 0, "name": "default"} if (c := i.get("tags", None)) is None or not isinstance(c, list): i["tags"] = [] diff --git a/tests/ref_test.py b/tests/ref_test.py index 0b51510f..db4347fb 100644 --- a/tests/ref_test.py +++ b/tests/ref_test.py @@ -85,7 +85,7 @@ def is_required(x): assert items["name"].annotation is str assert items["tag"].annotation == typing.Optional[str] - r = ref.model_validate([dict(id=1, name="dog"), dict(id=2, name="cat", tag="x")]) + r = ref.model_validate([{"id": 1, "name": "dog"}, {"id": 2, "name": "cat", "tag": "x"}]) assert len(r.root) == 2 assert r.root[1].tag == "x" diff --git a/tests/schema_test.py b/tests/schema_test.py index f8e7a1b1..81d8a500 100644 --- a/tests/schema_test.py +++ b/tests/schema_test.py @@ -149,7 +149,7 @@ def test_schema_type_missing(with_schema_type_missing): """ api = OpenAPI("/", with_schema_type_missing) t = api.components.schemas["Any"].get_type() - v = t.model_validate(dict(id=1)) + v = t.model_validate({"id": 1}) assert v.root.id == 1 v = t.model_validate("1") @@ -441,12 +441,12 @@ def test_schema_create_update_read(with_schema_create_update_read): api = OpenAPI("/", with_schema_create_update_read) A = api.components.schemas["A"].get_type() AB = api.components.schemas["AB"].get_type() - A.model_validate(dict(a="a")) + A.model_validate({"a": "a"}) with pytest.raises(ValidationError): - AB.model_validate(dict(a="a")) + AB.model_validate({"a": "a"}) with pytest.raises(ValidationError): - AB.model_validate(dict(b="b")) - AB.model_validate(dict(b="b", a="a")) + AB.model_validate({"b": "b"}) + AB.model_validate({"b": "b", "a": "a"}) def test_schema_constraints(with_schema_constraints): diff --git a/tests/stream_test.py b/tests/stream_test.py index bc0283dd..cf3dceca 100644 --- a/tests/stream_test.py +++ b/tests/stream_test.py @@ -83,7 +83,7 @@ def request_streaming( async def test_stream_data(server, client): cl = client._max_response_content_length req = client.createRequest("file") - headers, schema_, session, result = await req.stream(parameters=dict(content_length=cl)) + headers, schema_, session, result = await req.stream(parameters={"content_length": cl}) chunk = l = 0 async for i in result.aiter_bytes(): @@ -113,7 +113,7 @@ async def test_stream_array(server, client): req = client.createRequest("files") - headers, schema_, session, result = await req.stream(parameters=dict(number=10, size=512 * 1024)) + headers, schema_, session, result = await req.stream(parameters={"number": 10, "size": 512 * 1024}) assert schema_ == req.operation.responses["200"].content["application/json"].schema_ @@ -153,7 +153,7 @@ async def test_sync_stream(server): cl = client._max_response_content_length req = client.createRequest("file") - headers, schema_, session, result = await asyncio.to_thread(req.stream, parameters=dict(content_length=cl)) + headers, schema_, session, result = await asyncio.to_thread(req.stream, parameters={"content_length": cl}) def blocking_recv(rs): r = 0 From a3534d72505cd205a2cfe452fcd17c9b5aceb193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 07:56:43 +0200 Subject: [PATCH 09/58] ruff - C417 --- pyproject.toml | 2 +- src/aiopenapi3/base.py | 4 ++-- src/aiopenapi3/cli.py | 6 ++---- src/aiopenapi3/loader.py | 4 +--- src/aiopenapi3/log.py | 2 +- src/aiopenapi3/openapi.py | 14 +++++++------- src/aiopenapi3/v20/glue.py | 12 ++++-------- src/aiopenapi3/v30/glue.py | 17 ++++++----------- src/aiopenapi3/v30/parameter.py | 2 +- src/aiopenapi3/v30/servers.py | 2 +- src/aiopenapi3/v31/servers.py | 2 +- src/aiopenapi3/v32/servers.py | 2 +- tests/apiv2_test.py | 4 ++-- tests/path_test.py | 4 +--- tests/ref_test.py | 8 ++++---- tests/tls_test.py | 2 +- 16 files changed, 36 insertions(+), 51 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8d4d3dc9..2278183b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -93,7 +93,7 @@ ignore = [ "BLE001", "C400", # "C408", - "C417", +# "C417", "C419", "DTZ005", "FURB167", diff --git a/src/aiopenapi3/base.py b/src/aiopenapi3/base.py index 492dc10e..f21fe897 100644 --- a/src/aiopenapi3/base.py +++ b/src/aiopenapi3/base.py @@ -468,8 +468,8 @@ def parameter_in_path(c: Union["ParameterType", "ReferenceType"]) -> TypeGuard[" assert self.parameters is not None assert pi_.parameters is not None - op: frozenset[str] = frozenset(map(lambda x: x.name, filter(parameter_in_path, self.parameters))) - pi: frozenset[str] = frozenset(map(lambda x: x.name, filter(parameter_in_path, pi_.parameters))) + op: frozenset[str] = frozenset(x.name for x in filter(parameter_in_path, self.parameters)) + pi: frozenset[str] = frozenset(x.name for x in filter(parameter_in_path, pi_.parameters)) invalid = sorted(filter(lambda x: re.match(r"^([a-zA-Z0-9\-\._~]+)$", x) is None or len(x) == 0, op | pi)) if invalid: diff --git a/src/aiopenapi3/cli.py b/src/aiopenapi3/cli.py index 3a01e8e6..6702240b 100644 --- a/src/aiopenapi3/cli.py +++ b/src/aiopenapi3/cli.py @@ -119,10 +119,8 @@ def pr_display_top(pr): def schema_display_stats(api, duration): operations = list( itertools.chain.from_iterable( - map( - lambda x: list(filter(lambda x: x, [x.delete, x.get, x.head, x.options, x.patch, x.post, x.put])), - api.paths._paths.values(), - ) + list(filter(lambda x: x, [x.delete, x.get, x.head, x.options, x.patch, x.post, x.put])) + for x in api.paths._paths.values() ) ) print(f"… {duration} (processing time)") diff --git a/src/aiopenapi3/loader.py b/src/aiopenapi3/loader.py index 1fe8ed7f..b0eb6baf 100644 --- a/src/aiopenapi3/loader.py +++ b/src/aiopenapi3/loader.py @@ -74,9 +74,7 @@ class YAML12Loader(yaml.SafeLoader): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - tags = set( - sum(list(map(lambda x: list(map(lambda y: y[0], x)), YAML12Loader.yaml_implicit_resolvers.values())), []) - ) + tags = set(sum([[y[0] for y in x] for x in YAML12Loader.yaml_implicit_resolvers.values()], [])) for tag in tags: YAML12Loader.remove_implicit_resolver(tag) for tag, regex, initial in YAML12Loader._core_resolvers: diff --git a/src/aiopenapi3/log.py b/src/aiopenapi3/log.py index 78b5cd11..41655896 100644 --- a/src/aiopenapi3/log.py +++ b/src/aiopenapi3/log.py @@ -78,7 +78,7 @@ def init(force: bool = False) -> None: for i in frozenset(config["handlers"].keys()) - frozenset(handlers): del config["handlers"][i] - for i in frozenset(config["formatters"]) - frozenset(map(lambda x: x["formatter"], config["handlers"].values())): + for i in frozenset(config["formatters"]) - frozenset(x["formatter"] for x in config["handlers"].values()): del config["formatters"][i] logging.config.dictConfig(config) diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index 83f8c461..c0bf11c3 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -502,7 +502,7 @@ def is_schema(v: tuple[str, "SchemaType"]) -> bool: documents = cast(list[v20.Root], self._documents.values()) # Schema if only_required is False: - for byid in map(lambda x: x.definitions, documents): + for byid in (x.definitions for x in documents): assert byid is not None and isinstance(byid, dict) for name, schema in filter(is_schema, byid.items()): n = schema._get_identity(name=name) @@ -526,7 +526,7 @@ def is_schema(v: tuple[str, "SchemaType"]) -> bool: raise TypeError(f"{type(response)} at {path}") # Response - for byid in map(lambda x: x.responses, documents): + for byid in (x.responses for x in documents): assert byid is not None and isinstance(byid, dict) for name, response in filter(is_schema, byid.items()): assert response.schema_ @@ -540,7 +540,7 @@ def is_schema(v: tuple[str, "SchemaType"]) -> bool: components = [x.components for x in filter(has_components, documents) if x.components is not None] assert components is not None if only_required is False: - for byid in map(lambda x: x.schemas, components): + for byid in (x.schemas for x in components): assert byid is not None and isinstance(byid, dict) for name, schema in filter(is_schema, byid.items()): n = schema._get_identity(name=name) @@ -599,7 +599,7 @@ def is_schema(v: tuple[str, "SchemaType"]) -> bool: # Response if only_required is False: - for responses in map(lambda x: x.responses, components): + for responses in (x.responses for x in components): assert responses is not None for rname, response in responses.items(): for mt, mto in response.content.items(): @@ -622,9 +622,9 @@ def _init_schema_types(self, only_required: bool) -> None: """ Due to Plugins (e.g. Cull/Reduce) byname may be incomplete """ - resolved: list["SchemaType"] = list( - map(lambda x: byid[x]._target if isinstance(byid[x], ReferenceBase) else byid[x], todo | data) - ) + resolved: list["SchemaType"] = [ + byid[x]._target if isinstance(byid[x], ReferenceBase) else byid[x] for x in todo | data + ] self.plugins.init.resolved(initialized=self._root, resolved=resolved) # print(f"{len(todo | data)} {only_required=}") diff --git a/src/aiopenapi3/v20/glue.py b/src/aiopenapi3/v20/glue.py index 5a1c33f6..5db229fa 100644 --- a/src/aiopenapi3/v20/glue.py +++ b/src/aiopenapi3/v20/glue.py @@ -96,7 +96,7 @@ def _prepare_security(self): return else: options = " or ".join( - sorted(map(lambda x: f"{{{x}}}", [" and ".join(sorted(i.root.keys())) for i in security])) + sorted(f"{{{x}}}" for x in [" and ".join(sorted(i.root.keys())) for i in security]) ) raise ValueError(f"No security requirement provided (accepts {options})") @@ -108,9 +108,7 @@ def _prepare_security(self): self._prepare_secschemes(scheme, value) break else: - options = " or ".join( - sorted(map(lambda x: f"{{{x}}}", [" and ".join(sorted(i.root.keys())) for i in security])) - ) + options = " or ".join(sorted(f"{{{x}}}" for x in [" and ".join(sorted(i.root.keys())) for i in security])) raise ValueError( f"No security requirement satisfied (accepts {options} given {{{' and '.join(sorted(self.security.keys()))}}})" ) @@ -166,9 +164,7 @@ def _prepare_parameters(self, provided: Optional["RequestParameters"]): available = frozenset(parameters.keys()) accepted = frozenset(possible.keys()) - required = frozenset( - map(lambda x: x[0], filter(lambda y: y[1].required and y[1].in_ != "body", possible.items())) - ) + required = frozenset(x[0] for x in filter(lambda y: y[1].required and y[1].in_ != "body", possible.items())) if available - accepted: raise ValueError(f"Parameter {sorted(available - accepted)} unknown (accepted {sorted(accepted)})") if required - available: @@ -267,7 +263,7 @@ def _process__headers( ) -> "ResponseHeadersType": rheaders = {} if expected_response.headers: - required = dict(map(lambda x: (x[0].lower(), x[1]), expected_response.headers.items())) + required = {x[0].lower(): x[1] for x in expected_response.headers.items()} """ Swagger 2.0 does not have optional header - all defined headers are required https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#header-object diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index 6826cfb8..052380c5 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -107,7 +107,7 @@ def _prepare_security(self) -> None: return else: options = " or ".join( - sorted(map(lambda x: f"{{{x}}}", [" and ".join(sorted(i.root.keys())) for i in security])) + sorted(f"{{{x}}}" for x in [" and ".join(sorted(i.root.keys())) for i in security]) ) raise ValueError(f"No security requirement satisfied (accepts {options})") @@ -119,9 +119,7 @@ def _prepare_security(self) -> None: self._prepare_secschemes(scheme, value) break else: - options = " or ".join( - sorted(map(lambda x: f"{{{x}}}", [" and ".join(sorted(i.root.keys())) for i in security])) - ) + options = " or ".join(sorted(f"{{{x}}}" for x in [" and ".join(sorted(i.root.keys())) for i in security])) raise ValueError( f"No security requirement satisfied (accepts {options} given {{{' and '.join(sorted(self.security.keys()))}}}" ) @@ -305,7 +303,7 @@ def _prepare_parameters(self, provided: Optional["RequestParameters"]) -> dict[s available = frozenset(parameters.keys()) accepted = frozenset(possible.keys()) - required = frozenset(map(lambda x: x[0], filter(lambda y: y[1].required, possible.items()))) + required = frozenset(x[0] for x in filter(lambda y: y[1].required, possible.items())) if available - accepted: raise ValueError(f"Parameter {sorted(available - accepted)} unknown (accepted {sorted(accepted)})") if required - available: @@ -512,12 +510,9 @@ def _process__headers( ) -> "ResponseHeadersType": rheaders = {} if expected_response.headers: - required = dict( - map( - lambda x: (x[0].lower(), x[1]), - filter(lambda x: x[1].required is True, expected_response.headers.items()), - ) - ) + required = { + x[0].lower(): x[1] for x in filter(lambda x: x[1].required is True, expected_response.headers.items()) + } available = frozenset(headers.keys()) if missing := (required.keys() - available): missed = {k: required[k] for k in missing} diff --git a/src/aiopenapi3/v30/parameter.py b/src/aiopenapi3/v30/parameter.py index d90e2408..6a17c512 100644 --- a/src/aiopenapi3/v30/parameter.py +++ b/src/aiopenapi3/v30/parameter.py @@ -298,7 +298,7 @@ def _decode_simple(self, value, schema: "v3xSchemaType", explode: bool): return dict(more_itertools.chunked(value.split(","), 2)) else: # R=100,G=200,B=150 - return dict(map(lambda y: (y[0], y[2]), map(lambda x: x.partition("="), value.split(",")))) + return {y[0]: y[2] for y in (x.partition("=") for x in value.split(","))} else: # convert basic type return value diff --git a/src/aiopenapi3/v30/servers.py b/src/aiopenapi3/v30/servers.py index 635e37a0..921f7fbd 100644 --- a/src/aiopenapi3/v30/servers.py +++ b/src/aiopenapi3/v30/servers.py @@ -49,7 +49,7 @@ def validate_parameter_enum(self, parameters: dict[str, str]): def createUrl(self, variables: dict[str, str]) -> str: self.validate_parameter_enum(variables) - vars: dict[str, str | None] = dict(map(lambda x: (x[0], x[1].default), self.variables.items())) + vars: dict[str, str | None] = {x[0]: x[1].default for x in self.variables.items()} vars.update(variables) url: str = self.url.format(**vars) return url diff --git a/src/aiopenapi3/v31/servers.py b/src/aiopenapi3/v31/servers.py index de74372f..9ebf1c9f 100644 --- a/src/aiopenapi3/v31/servers.py +++ b/src/aiopenapi3/v31/servers.py @@ -49,7 +49,7 @@ def validate_parameter_enum(self, parameters: dict[str, str]): def createUrl(self, variables: dict[str, str]) -> str: self.validate_parameter_enum(variables) - vars: dict[str, str | None] = dict(map(lambda x: (x[0], x[1].default), self.variables.items())) + vars: dict[str, str | None] = {x[0]: x[1].default for x in self.variables.items()} vars.update(variables) url: str = self.url.format(**vars) return url diff --git a/src/aiopenapi3/v32/servers.py b/src/aiopenapi3/v32/servers.py index 3737b53f..1e8ac510 100644 --- a/src/aiopenapi3/v32/servers.py +++ b/src/aiopenapi3/v32/servers.py @@ -54,7 +54,7 @@ def validate_parameter_enum(self, parameters: dict[str, str]): def createUrl(self, variables: dict[str, str]) -> str: self.validate_parameter_enum(variables) - vars: dict[str, str | None] = dict(map(lambda x: (x[0], x[1].default), self.variables.items())) + vars: dict[str, str | None] = {x[0]: x[1].default for x in self.variables.items()} vars.update(variables) url: str = self.url.format(**vars) return url diff --git a/tests/apiv2_test.py b/tests/apiv2_test.py index 42e6115c..141a6c04 100644 --- a/tests/apiv2_test.py +++ b/tests/apiv2_test.py @@ -276,13 +276,13 @@ def test_allOf_resolution(openapi_version, petstore_expanded): items = typing.get_args(ref.__fields__["__root__"].outer_type_)[0].__fields__ try: - assert sorted(map(lambda x: x.name, filter(lambda y: y.required, items.values()))) == sorted(["id", "name"]), ( + assert sorted(x.name for x in filter(lambda y: y.required, items.values())) == sorted(["id", "name"]), ( ref.model_json_schema() ) except Exception as e: print(e) - assert sorted(map(lambda x: x.name, items.values())) == ["id", "name", "tag"] + assert sorted(x.name for x in items.values()) == ["id", "name", "tag"] assert items["id"].outer_type_ is int assert items["name"].outer_type_ is str diff --git a/tests/path_test.py b/tests/path_test.py index f1754b4f..c01d9f3b 100644 --- a/tests/path_test.py +++ b/tests/path_test.py @@ -348,9 +348,7 @@ def test_paths_parameter_format(httpx2_mock, with_paths_parameter_format): r = api._.deepObjectNestedExplodeQuery(parameters={"object": data}) request = httpx2_mock.get_requests()[-1] u = yarl.URL(str(request.url)) - expected = dict( - list(map(lambda x: (f"object{''.join('[inner]' for _ in range(x))}[size]", depth - x), range(depth))) - ) + expected = {f"object{''.join('[inner]' for _ in range(x))}[size]": depth - x for x in range(depth)} # 'object[size]=3&object[inner][size]=2&object[inner][inner][size]=1' assert all(u.query[k] == str(v) for k, v in expected.items()) diff --git a/tests/ref_test.py b/tests/ref_test.py index db4347fb..ebfd87e2 100644 --- a/tests/ref_test.py +++ b/tests/ref_test.py @@ -69,15 +69,15 @@ def is_nullable(x): # Optional[…] or | None return typing.get_origin(x.annotation) == typing.Union and type(None) in typing.get_args(x.annotation) - assert sorted(map(lambda x: x[0], filter(lambda y: is_nullable(y[1]), items.items()))) == sorted( - ["created", "tag"] - ), ref.model_json_schema() + assert sorted(x[0] for x in filter(lambda y: is_nullable(y[1]), items.items())) == sorted(["created", "tag"]), ( + ref.model_json_schema() + ) def is_required(x): # not assign a default '= Field(default=…)' or '= …' return x.default == pydantic_core.PydanticUndefined - assert sorted(map(lambda x: x[0], filter(lambda y: is_required(y[1]), items.items()))) == sorted(["id", "name"]), ( + assert sorted(x[0] for x in filter(lambda y: is_required(y[1]), items.items())) == sorted(["id", "name"]), ( ref.model_json_schema() ) diff --git a/tests/tls_test.py b/tests/tls_test.py index 29b87419..2a3d3989 100644 --- a/tests/tls_test.py +++ b/tests/tls_test.py @@ -246,5 +246,5 @@ async def test_certificate_invalid(client): with pytest.raises(ValueError, match=r"Invalid parameter for SecurityScheme tls mutualTLS") as e: client.authenticate(tls=(p := ("/does/not/exist", "/tmp"))) assert isinstance(e.value.__context__, FileNotFoundError) and e.value.__context__.args[0] == sorted( - map(lambda x: Path(x), p) + Path(x) for x in p ) From 935382e193977ea8b1761d76f19fc9aa4f8707e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 07:57:52 +0200 Subject: [PATCH 10/58] ruff - C419 --- pyproject.toml | 2 +- src/aiopenapi3/v20/glue.py | 2 +- src/aiopenapi3/v30/glue.py | 2 +- src/aiopenapi3/v31/info.py | 2 +- src/aiopenapi3/v32/info.py | 2 +- tests/parsing_test.py | 10 ++++------ tests/petstore_test.py | 2 +- tests/petstorev3_test.py | 2 +- 8 files changed, 11 insertions(+), 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2278183b..29f7ee4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,7 +94,7 @@ ignore = [ "C400", # "C408", # "C417", - "C419", +# "C419", "DTZ005", "FURB167", "I001", diff --git a/src/aiopenapi3/v20/glue.py b/src/aiopenapi3/v20/glue.py index 5db229fa..9e88a2af 100644 --- a/src/aiopenapi3/v20/glue.py +++ b/src/aiopenapi3/v20/glue.py @@ -92,7 +92,7 @@ def _prepare_security(self): return if not self.security: - if any([{} == i.root for i in security]): + if any({} == i.root for i in security): return else: options = " or ".join( diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index 052380c5..00612074 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -103,7 +103,7 @@ def _prepare_security(self) -> None: return if not self.security: - if any([{} == i.root for i in security]): + if any({} == i.root for i in security): return else: options = " or ".join( diff --git a/src/aiopenapi3/v31/info.py b/src/aiopenapi3/v31/info.py index ee7c5c17..c3269fa5 100644 --- a/src/aiopenapi3/v31/info.py +++ b/src/aiopenapi3/v31/info.py @@ -31,7 +31,7 @@ def validate_License(self): """ A URL to the license used for the API. This MUST be in the form of a URL. The url field is mutually exclusive of the identifier field. """ - assert not all([getattr(self, i, None) is not None for i in ["identifier", "url"]]) + assert not all(getattr(self, i, None) is not None for i in ["identifier", "url"]) return self diff --git a/src/aiopenapi3/v32/info.py b/src/aiopenapi3/v32/info.py index 33dd3090..c66850e2 100644 --- a/src/aiopenapi3/v32/info.py +++ b/src/aiopenapi3/v32/info.py @@ -39,7 +39,7 @@ def validate_License(self): """ A URL to the license used for the API. This MUST be in the form of a URL. The url field is mutually exclusive of the identifier field. """ - assert not all([getattr(self, i, None) is not None for i in ["identifier", "url"]]) + assert not all(getattr(self, i, None) is not None for i in ["identifier", "url"]) return self diff --git a/tests/parsing_test.py b/tests/parsing_test.py index 6126b531..88fa13e5 100644 --- a/tests/parsing_test.py +++ b/tests/parsing_test.py @@ -113,12 +113,10 @@ def test_parsing_paths_links_invalid(with_parsing_paths_links_invalid): spec = OpenAPI(URLBASE, with_parsing_paths_links_invalid) assert all( - [ - i in str(e.value) - for i in [ - "operationId and operationRef are mutually exclusive, only one of them is allowed", - "operationId and operationRef are mutually exclusive, one of them must be specified", - ] + i in str(e.value) + for i in [ + "operationId and operationRef are mutually exclusive, only one of them is allowed", + "operationId and operationRef are mutually exclusive, one of them must be specified", ] ) diff --git a/tests/petstore_test.py b/tests/petstore_test.py index e0a43320..292d5d7f 100644 --- a/tests/petstore_test.py +++ b/tests/petstore_test.py @@ -234,7 +234,7 @@ def test_pets(api, login): # findPetsByStatus is patched r = api._.findPetsByStatus(parameters={"status": ["invalid"]}) - assert all([i.status == "pending" for i in r]) + assert all(i.status == "pending" for i in r) def test_store(api): diff --git a/tests/petstorev3_test.py b/tests/petstorev3_test.py index 4793d058..5b35a1e3 100644 --- a/tests/petstorev3_test.py +++ b/tests/petstorev3_test.py @@ -259,7 +259,7 @@ def test_pets(api, login): # findPetsByStatus is patched r = api._.findPetsByStatus(parameters={"status": ["available"]}) - assert all([i.status == "available" for i in r]) + assert all(i.status == "available" for i in r) @pytest.mark.xfail From 2533c4dbdaa713f1a3d36325818324612f26c671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:01:32 +0200 Subject: [PATCH 11/58] ruff - FURB167 --- pyproject.toml | 2 +- src/aiopenapi3/loader.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 29f7ee4c..a0843110 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,7 +96,7 @@ ignore = [ # "C417", # "C419", "DTZ005", - "FURB167", +# "FURB167", "I001", "PERF102", "PIE790", diff --git a/src/aiopenapi3/loader.py b/src/aiopenapi3/loader.py index b0eb6baf..4dc1eb6f 100644 --- a/src/aiopenapi3/loader.py +++ b/src/aiopenapi3/loader.py @@ -42,7 +42,7 @@ class YAML12Loader(yaml.SafeLoader): """ _core_resolvers = [ - ["bool", re.compile(r"""^(?:|true|True|TRUE|false|False|FALSE)$""", re.X), list("tTfF")], + ["bool", re.compile(r"""^(?:|true|True|TRUE|false|False|FALSE)$""", re.VERBOSE), list("tTfF")], [ "int", re.compile( @@ -51,7 +51,7 @@ class YAML12Loader(yaml.SafeLoader): |[-+]?(?:[0-9]+) |0x[0-9a-fA-F]+ )$""", - re.X, + re.VERBOSE, ), list("-+0123456789"), ], @@ -61,11 +61,11 @@ class YAML12Loader(yaml.SafeLoader): r"""^(?:[-+]?(?:\.[0-9]+|[0-9]+(\.[0-9]*)?)(?:[eE][-+]?[0-9]+)? |[-+]?\.(?:inf|Inf|INF) |\.(?:nan|NaN|NAN))$""", - re.X, + re.VERBOSE, ), list("-+0123456789."), ], - ["null", re.compile(r"""^(?:~||null|Null|NULL)$""", re.X), ["~", "n", "N", ""]], + ["null", re.compile(r"""^(?:~||null|Null|NULL)$""", re.VERBOSE), ["~", "n", "N", ""]], ] """ core tags from From 063b92ce9b1bce2b63392135a0b0f836e5bd71c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:03:51 +0200 Subject: [PATCH 12/58] ruff - I001 --- docs/source/_ext/aioai3.py | 3 +-- docs/source/conf.py | 7 +++--- pyproject.toml | 2 +- src/aiopenapi3/__init__.py | 17 ++++++------- src/aiopenapi3/__main__.py | 1 + src/aiopenapi3/_types.py | 6 ++--- src/aiopenapi3/base.py | 23 ++++++++--------- src/aiopenapi3/cli.py | 26 +++++++++---------- src/aiopenapi3/debug.py | 8 +++--- src/aiopenapi3/errors.py | 12 ++++----- src/aiopenapi3/extra/__init__.py | 2 +- src/aiopenapi3/extra/cookies.py | 2 +- src/aiopenapi3/extra/reduce.py | 4 +-- src/aiopenapi3/loader.py | 10 ++++---- src/aiopenapi3/log.py | 5 ++-- src/aiopenapi3/model.py | 13 ++++------ src/aiopenapi3/openapi.py | 43 ++++++++++++-------------------- src/aiopenapi3/plugin.py | 13 ++++------ src/aiopenapi3/pydanticv2.py | 15 +++++------ src/aiopenapi3/request.py | 36 +++++++++++++------------- src/aiopenapi3/v20/__init__.py | 11 ++++---- src/aiopenapi3/v20/general.py | 7 +++--- src/aiopenapi3/v20/glue.py | 21 ++++++---------- src/aiopenapi3/v20/parameter.py | 8 +++--- src/aiopenapi3/v20/paths.py | 5 ++-- src/aiopenapi3/v20/root.py | 8 +++--- src/aiopenapi3/v20/schemas.py | 6 ++--- src/aiopenapi3/v20/security.py | 2 +- src/aiopenapi3/v30/__init__.py | 13 +++++----- src/aiopenapi3/v30/components.py | 3 +-- src/aiopenapi3/v30/formdata.py | 8 +++--- src/aiopenapi3/v30/general.py | 10 +++----- src/aiopenapi3/v30/glue.py | 35 +++++++++++++------------- src/aiopenapi3/v30/media.py | 3 +-- src/aiopenapi3/v30/parameter.py | 16 ++++++------ src/aiopenapi3/v30/paths.py | 11 ++++---- src/aiopenapi3/v30/root.py | 3 --- src/aiopenapi3/v30/schemas.py | 6 ++--- src/aiopenapi3/v30/security.py | 3 ++- src/aiopenapi3/v31/__init__.py | 10 ++++---- src/aiopenapi3/v31/components.py | 3 +-- src/aiopenapi3/v31/general.py | 9 +++---- src/aiopenapi3/v31/info.py | 2 +- src/aiopenapi3/v31/media.py | 3 +-- src/aiopenapi3/v31/parameter.py | 9 +++---- src/aiopenapi3/v31/paths.py | 11 ++++---- src/aiopenapi3/v31/root.py | 8 +++--- src/aiopenapi3/v31/schemas.py | 6 ++--- src/aiopenapi3/v31/security.py | 2 +- src/aiopenapi3/v32/__init__.py | 10 ++++---- src/aiopenapi3/v32/components.py | 5 ++-- src/aiopenapi3/v32/general.py | 9 +++---- src/aiopenapi3/v32/info.py | 2 +- src/aiopenapi3/v32/media.py | 4 +-- src/aiopenapi3/v32/parameter.py | 9 +++---- src/aiopenapi3/v32/paths.py | 11 ++++---- src/aiopenapi3/v32/root.py | 8 +++--- src/aiopenapi3/v32/schemas.py | 6 ++--- src/aiopenapi3/v32/security.py | 2 +- tests/api/v1/main.py | 4 +-- tests/api/v1/schema.py | 2 +- tests/api/v2/main.py | 2 +- tests/api/v2/schema.py | 8 +++--- tests/apiv1_test.py | 9 +++---- tests/apiv2_test.py | 21 ++++++---------- tests/cli_test.py | 5 ++-- tests/clone_test.py | 1 - tests/conftest.py | 4 +-- tests/content_length_test.py | 11 +++----- tests/debug_test.py | 2 +- tests/error_test.py | 14 +++++++---- tests/extra_test.py | 4 +-- tests/formdata_test.py | 3 ++- tests/forms_test.py | 31 ++++++++++------------- tests/loader_test.py | 11 ++++---- tests/parse_data_test.py | 3 +-- tests/parsing_test.py | 7 +++--- tests/path_test.py | 10 ++++---- tests/pathv20_test.py | 4 +-- tests/petstorev3_test.py | 1 - tests/pickle_test.py | 5 ++-- tests/plugin_test.py | 2 +- tests/ref_test.py | 8 +++--- tests/schema_test.py | 9 +++---- tests/sequential_test.py | 12 +++------ tests/stream_test.py | 15 ++++------- tests/tls_test.py | 9 +++---- tests/v32_test.py | 4 +-- 88 files changed, 342 insertions(+), 435 deletions(-) diff --git a/docs/source/_ext/aioai3.py b/docs/source/_ext/aioai3.py index 7708b8b2..4cc5d6d1 100644 --- a/docs/source/_ext/aioai3.py +++ b/docs/source/_ext/aioai3.py @@ -1,8 +1,7 @@ +from docutils import nodes from sphinx.domains import Domain from sphinx.roles import XRefRole -from docutils import nodes - def resolve_url(env, name): resolve_target = getattr(env.config, "linkcode_resolve", None) diff --git a/docs/source/conf.py b/docs/source/conf.py index 118180f1..b5aee812 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,13 +6,12 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -import os +import datetime import importlib import inspect -from pathlib import Path +import os import sys -import datetime - +from pathlib import Path sys.path.append(str(p := (Path(".").absolute() / "_ext"))) assert p.exists(), f"{p} {os.getcwd()}" diff --git a/pyproject.toml b/pyproject.toml index a0843110..cfbaca74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,7 +97,7 @@ ignore = [ # "C419", "DTZ005", # "FURB167", - "I001", +# "I001", "PERF102", "PIE790", "PIE804", diff --git a/src/aiopenapi3/__init__.py b/src/aiopenapi3/__init__.py index 7cb9c190..c341345b 100644 --- a/src/aiopenapi3/__init__.py +++ b/src/aiopenapi3/__init__.py @@ -1,18 +1,17 @@ -from .version import __version__ -from .openapi import OpenAPI -from .loader import FileSystemLoader from .errors import ( - SpecError, - ReferenceResolutionError, + ContentTypeError, HTTPError, - ResponseError, HTTPStatusError, - ContentTypeError, + ReferenceResolutionError, + RequestError, ResponseDecodingError, + ResponseError, ResponseSchemaError, - RequestError, + SpecError, ) - +from .loader import FileSystemLoader +from .openapi import OpenAPI +from .version import __version__ __all__ = [ "__version__", diff --git a/src/aiopenapi3/__main__.py b/src/aiopenapi3/__main__.py index 7d876942..7d73de00 100644 --- a/src/aiopenapi3/__main__.py +++ b/src/aiopenapi3/__main__.py @@ -1,4 +1,5 @@ import sys + from .cli import main if __name__ == "__main__": diff --git a/src/aiopenapi3/_types.py b/src/aiopenapi3/_types.py index 18cfffa3..8730f263 100644 --- a/src/aiopenapi3/_types.py +++ b/src/aiopenapi3/_types.py @@ -1,13 +1,11 @@ import re -from typing import TYPE_CHECKING, Union, TypeAlias, Optional, Literal from collections.abc import Sequence +from typing import TYPE_CHECKING, Literal, Optional, TypeAlias, Union import yaml - -from httpx._types import RequestContent, FileTypes, RequestFiles, AuthTypes # noqa +from httpx._types import AuthTypes, FileTypes, RequestContent, RequestFiles # noqa from pydantic import BaseModel - from . import v20, v30, v31, v32 if TYPE_CHECKING: diff --git a/src/aiopenapi3/base.py b/src/aiopenapi3/base.py index f21fe897..1fa2678a 100644 --- a/src/aiopenapi3/base.py +++ b/src/aiopenapi3/base.py @@ -1,25 +1,22 @@ -import typing -import warnings -from typing import Any, ForwardRef, Union, cast -from collections.abc import Sequence - -import re import builtins import keyword +import re +import typing import uuid - +import warnings +from collections.abc import Sequence from pathlib import Path +from typing import Any, ForwardRef, TypeGuard, Union, cast -from typing import TypeGuard - -from pydantic import RootModel, BaseModel, TypeAdapter, Field, AnyUrl, model_validator, PrivateAttr, ConfigDict +from pydantic import AnyUrl, BaseModel, ConfigDict, Field, PrivateAttr, RootModel, TypeAdapter, model_validator +from .errors import OperationParameterValidationError, ReferenceResolutionError from .json import JSONPointer, JSONReference -from .errors import ReferenceResolutionError, OperationParameterValidationError if typing.TYPE_CHECKING: from aiopenapi3 import OpenAPI - from ._types import SchemaType, JSON, PathItemType, ParameterType, ReferenceType, DiscriminatorType + + from ._types import JSON, DiscriminatorType, ParameterType, PathItemType, ReferenceType, SchemaType HTTP_METHODS = frozenset(["get", "delete", "head", "post", "put", "patch", "trace", "query"]) @@ -156,8 +153,8 @@ def replace(ivalue): else: if v._target is None: continue - from .model import Model from . import errors + from .model import Model if "object" not in (t := sorted(Model.types(v._target))): raise errors.SpecError(f"Discriminated Union on a schema with types {t}") diff --git a/src/aiopenapi3/cli.py b/src/aiopenapi3/cli.py index 6702240b..b00b2459 100644 --- a/src/aiopenapi3/cli.py +++ b/src/aiopenapi3/cli.py @@ -1,22 +1,22 @@ import argparse +import cProfile import datetime -import sys -import json -import itertools -import typing -from pstats import SortKey -import pstats -import io import importlib.util -import cProfile -import tracemalloc +import io +import itertools +import json import linecache import logging +import pstats +import sys +import tracemalloc +import typing +from pstats import SortKey +import httpx2 import jmespath import yaml import yarl -import httpx2 import aiopenapi3.plugin @@ -25,12 +25,12 @@ from pathlib import Path -from .openapi import OpenAPI - -from .loader import ChainLoader, RedirectLoader, WebLoader import aiopenapi3.loader from aiopenapi3.v30.formdata import decode_content_type + +from .loader import ChainLoader, RedirectLoader, WebLoader from .log import init +from .openapi import OpenAPI if typing.TYPE_CHECKING: import aiopenapi3.request diff --git a/src/aiopenapi3/debug.py b/src/aiopenapi3/debug.py index 8346fc12..69a455c4 100644 --- a/src/aiopenapi3/debug.py +++ b/src/aiopenapi3/debug.py @@ -1,7 +1,9 @@ -from aiopenapi3.plugin import Document -import yaml -from pathlib import Path import json +from pathlib import Path + +import yaml + +from aiopenapi3.plugin import Document class DescriptionDocumentDumper(Document): diff --git a/src/aiopenapi3/errors.py b/src/aiopenapi3/errors.py index bd42ae52..7de7606d 100644 --- a/src/aiopenapi3/errors.py +++ b/src/aiopenapi3/errors.py @@ -1,20 +1,20 @@ +import dataclasses import typing from typing import Optional -import dataclasses import httpx2 import pydantic if typing.TYPE_CHECKING: from ._types import ( - SchemaType, - RequestType, + ExpectedType, + HeaderType, + OperationType, RequestData, RequestParameters, + RequestType, + SchemaType, ServerType, - HeaderType, - ExpectedType, - OperationType, ) diff --git a/src/aiopenapi3/extra/__init__.py b/src/aiopenapi3/extra/__init__.py index 5f3c500f..7d576874 100644 --- a/src/aiopenapi3/extra/__init__.py +++ b/src/aiopenapi3/extra/__init__.py @@ -1,4 +1,4 @@ -from .reduce import Cull, Reduce from .cookies import Cookies +from .reduce import Cull, Reduce __all__ = ["Cull", "Reduce", "Cookies"] diff --git a/src/aiopenapi3/extra/cookies.py b/src/aiopenapi3/extra/cookies.py index 92bc0710..7de978fd 100644 --- a/src/aiopenapi3/extra/cookies.py +++ b/src/aiopenapi3/extra/cookies.py @@ -1,7 +1,7 @@ -from typing import Literal import email.message import http.cookiejar import urllib.request +from typing import Literal import aiopenapi3.plugin diff --git a/src/aiopenapi3/extra/reduce.py b/src/aiopenapi3/extra/reduce.py index d52e790a..54d1afde 100644 --- a/src/aiopenapi3/extra/reduce.py +++ b/src/aiopenapi3/extra/reduce.py @@ -1,7 +1,7 @@ -import typing -from typing import Union import logging import re +import typing +from typing import Union from ..plugin import Document, Init diff --git a/src/aiopenapi3/loader.py b/src/aiopenapi3/loader.py index 4dc1eb6f..e2c4de93 100644 --- a/src/aiopenapi3/loader.py +++ b/src/aiopenapi3/loader.py @@ -1,12 +1,12 @@ import abc +import importlib import logging +import re import typing -import yaml + import httpx2 +import yaml import yarl -import re - -import importlib # prefer a fast json library here as we may parse large documents for i in ["orjson", "simdjson", "ujson", "json"]: @@ -24,7 +24,7 @@ from .plugin import Plugins if typing.TYPE_CHECKING: - from ._types import YAMLLoaderType, JSON + from ._types import JSON, YAMLLoaderType log = logging.getLogger("aiopenapi3.loader") diff --git a/src/aiopenapi3/log.py b/src/aiopenapi3/log.py index 41655896..3428512d 100644 --- a/src/aiopenapi3/log.py +++ b/src/aiopenapi3/log.py @@ -1,9 +1,8 @@ -import sys import logging.config import os -from typing import Any - +import sys from pathlib import Path +from typing import Any handlers: list[str] | None = None diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index a9b4f40c..b1ede310 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -4,21 +4,18 @@ import logging import re import sys -from typing import Any, cast, TypeVar import typing +from typing import Annotated, Any, Literal, Optional, TypeGuard, TypeVar, Union, cast -from typing import TypeGuard - -from typing import Optional, Union, Annotated, Literal -from pydantic import BaseModel, Field, RootModel, ConfigDict import pydantic +from pydantic import BaseModel, ConfigDict, Field, RootModel -from .base import ReferenceBase, SchemaBase from . import me -from .pydanticv2 import field_class_to_schema, create_model +from .base import ReferenceBase, SchemaBase +from .pydanticv2 import create_model, field_class_to_schema if typing.TYPE_CHECKING: - from ._types import SchemaType, ReferenceType, DiscriminatorType + from ._types import DiscriminatorType, ReferenceType, SchemaType type_format_to_class: dict[str, dict[str | None, type]] = collections.defaultdict(dict) diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index c0bf11c3..89d684b0 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -1,49 +1,38 @@ -import typing - -from typing import Any, Union, cast, Optional, ForwardRef -from collections.abc import Callable -import logging import copy +import logging +import pathlib import pickle import random - -import pathlib - - -from typing import TypeGuard - +import typing +from collections.abc import Callable +from typing import Any, ForwardRef, Optional, TypeGuard, Union, cast import httpx2 import yarl from pydantic import BaseModel -from aiopenapi3.v30.general import Reference import aiopenapi3.request +from aiopenapi3.v30.general import Reference + +from . import log, v20, v30, v31, v32 +from .base import DiscriminatorBase, ReferenceBase, RootBase, SchemaBase +from .errors import HTTPClientError, HTTPServerError, ReferenceResolutionError from .json import JSONReference -from . import v20 -from . import v30 -from . import v31 -from . import v32 -from . import log -from .request import OperationIndex, HTTP_METHODS -from .errors import ReferenceResolutionError, HTTPClientError, HTTPServerError from .loader import Loader, NullLoader +from .model import Model, is_basemodel from .plugin import Plugin, Plugins -from .base import RootBase, ReferenceBase, SchemaBase, DiscriminatorBase -from .request import RequestBase +from .request import HTTP_METHODS, OperationIndex, RequestBase from .v30.paths import Operation -from .model import is_basemodel, Model - if typing.TYPE_CHECKING: from ._types import ( - RootType, JSON, - PathItemType, - SchemaType, + HTTPMethodType, OperationType, + PathItemType, RequestType, - HTTPMethodType, + RootType, + SchemaType, ServerType, ) diff --git a/src/aiopenapi3/plugin.py b/src/aiopenapi3/plugin.py index b5f2acee..9c30f062 100644 --- a/src/aiopenapi3/plugin.py +++ b/src/aiopenapi3/plugin.py @@ -1,18 +1,15 @@ -import dataclasses -from typing import TYPE_CHECKING, Any, Optional import abc - -from typing import TypeGuard - - -from pydantic import BaseModel +import dataclasses +from typing import TYPE_CHECKING, Any, Optional, TypeGuard import yarl +from pydantic import BaseModel if TYPE_CHECKING: + import httpx2 + from aiopenapi3 import OpenAPI - import httpx2 from .base import PathItemBase, SchemaBase from .request import RequestBase diff --git a/src/aiopenapi3/pydanticv2.py b/src/aiopenapi3/pydanticv2.py index 2a2af8cf..cbe50653 100644 --- a/src/aiopenapi3/pydanticv2.py +++ b/src/aiopenapi3/pydanticv2.py @@ -1,9 +1,9 @@ +from datetime import date, datetime, time, timedelta from decimal import Decimal -from datetime import datetime, date, time, timedelta -from ipaddress import IPv4Network, IPv6Network, IPv4Interface, IPv6Interface, IPv4Address, IPv6Address +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network from pathlib import Path -from typing import Any from re import Pattern +from typing import Any from uuid import UUID from pydantic import TypeAdapter @@ -39,12 +39,13 @@ (field_class, TypeAdapter(field_class).json_schema()) for field_class in field_classes_to_support ) -from pydantic import ConfigDict, BaseModel, PydanticUserError -from pydantic.main import ModelT -from typing import cast -from collections.abc import Callable import sys import types +from collections.abc import Callable +from typing import cast + +from pydantic import BaseModel, ConfigDict, PydanticUserError +from pydantic.main import ModelT def create_model( # noqa: C901 diff --git a/src/aiopenapi3/request.py b/src/aiopenapi3/request.py index f998e48d..251234e1 100644 --- a/src/aiopenapi3/request.py +++ b/src/aiopenapi3/request.py @@ -1,14 +1,12 @@ import abc import collections import contextlib -import typing import json import logging -from contextlib import closing +import typing +from collections.abc import AsyncGenerator, AsyncIterator, Generator, Iterator +from contextlib import aclosing, closing from typing import Any, NamedTuple, Optional, Union, cast -from collections.abc import AsyncIterator, AsyncGenerator, Generator -from collections.abc import Iterator -from contextlib import aclosing import httpx2 import pydantic @@ -16,32 +14,32 @@ from aiopenapi3.errors import ContentLengthExceededError - from .base import HTTP_METHODS, ReferenceBase +from .errors import OperationIdDuplicationError, RequestError from .version import __version__ -from .errors import RequestError, OperationIdDuplicationError if typing.TYPE_CHECKING: + from aiopenapi3 import OpenAPI + from ._types import ( - RequestParameters, - RequestData, - RequestFiles, - RequestContent, - RequestType, + JSON, AuthTypes, - SchemaType, + HTTPMethodType, + OperationType, ParameterType, PathItemType, - OperationType, - JSON, - RootType, - ServerType, + RequestContent, + RequestData, + RequestFiles, + RequestParameters, + RequestType, ResponseDataType, ResponseHeadersType, - HTTPMethodType, + RootType, + SchemaType, + ServerType, TagType, ) - from aiopenapi3 import OpenAPI log = logging.getLogger("aiopenapi3.request") diff --git a/src/aiopenapi3/v20/__init__.py b/src/aiopenapi3/v20/__init__.py index 88c38de9..93359a0d 100644 --- a/src/aiopenapi3/v20/__init__.py +++ b/src/aiopenapi3/v20/__init__.py @@ -1,12 +1,11 @@ -from .glue import Request, AsyncRequest - from .general import ExternalDocumentation, Reference -from .info import Contact, License, Info -from .parameter import Parameter, Header -from .paths import Response, Operation, PathItem, Paths +from .glue import AsyncRequest, Request +from .info import Contact, Info, License +from .parameter import Header, Parameter +from .paths import Operation, PathItem, Paths, Response from .root import Root from .schemas import Schema -from .security import SecurityScheme, SecurityRequirement +from .security import SecurityRequirement, SecurityScheme from .tag import Tag from .xml import XML diff --git a/src/aiopenapi3/v20/general.py b/src/aiopenapi3/v20/general.py index 75551136..77f1f572 100644 --- a/src/aiopenapi3/v20/general.py +++ b/src/aiopenapi3/v20/general.py @@ -1,14 +1,13 @@ import typing from typing import Any, Union -from pydantic import Field, ConfigDict, PrivateAttr +from pydantic import ConfigDict, Field, PrivateAttr - -from ..base import ObjectExtended, ObjectBase, ReferenceBase +from ..base import ObjectBase, ObjectExtended, ReferenceBase if typing.TYPE_CHECKING: - from .schemas import Schema from .parameter import Parameter + from .schemas import Schema class ExternalDocumentation(ObjectExtended): diff --git a/src/aiopenapi3/v20/glue.py b/src/aiopenapi3/v20/glue.py index 9e88a2af..e76e993f 100644 --- a/src/aiopenapi3/v20/glue.py +++ b/src/aiopenapi3/v20/glue.py @@ -1,18 +1,13 @@ +import json import typing -from typing import Union, cast, Optional from collections.abc import Sequence -import json - -from typing import TypeGuard - +from typing import Optional, TypeGuard, Union, cast import httpx2 import pydantic -from ..request import RequestBase, AsyncRequestBase -from ..errors import HTTPStatusError, ContentTypeError, ResponseSchemaError, ResponseDecodingError, HeadersMissingError - - +from ..errors import ContentTypeError, HeadersMissingError, HTTPStatusError, ResponseDecodingError, ResponseSchemaError +from ..request import AsyncRequestBase, RequestBase from .parameter import Parameter from .root import Root @@ -23,15 +18,15 @@ if typing.TYPE_CHECKING: from .._types import ( - RequestParameters, + HeaderType, RequestData, - ResponseHeadersType, + RequestParameters, ResponseDataType, - HeaderType, + ResponseHeadersType, ) - from .schemas import Schema from .general import Reference from .paths import Response as v20ResponseType + from .schemas import Schema def in_body(x: Union["Parameter", "Reference"]) -> TypeGuard["Parameter"]: diff --git a/src/aiopenapi3/v20/parameter.py b/src/aiopenapi3/v20/parameter.py index 6b01214c..5c3f21da 100644 --- a/src/aiopenapi3/v20/parameter.py +++ b/src/aiopenapi3/v20/parameter.py @@ -1,13 +1,13 @@ -import io import enum -from typing import Optional, Any +import io +from typing import Any, Optional from pydantic import Field -from .general import Reference -from .schemas import Schema from ..base import ObjectExtended, ParameterBase from ..errors import ParameterFormatError +from .general import Reference +from .schemas import Schema class _ParameterCodec: diff --git a/src/aiopenapi3/v20/paths.py b/src/aiopenapi3/v20/paths.py index 2927544d..40e23935 100644 --- a/src/aiopenapi3/v20/paths.py +++ b/src/aiopenapi3/v20/paths.py @@ -2,12 +2,11 @@ from pydantic import Field, model_validator -from .general import ExternalDocumentation -from .general import Reference +from ..base import ObjectExtended, OperationBase, PathItemBase, PathsBase +from .general import ExternalDocumentation, Reference from .parameter import Header, Parameter from .schemas import Schema from .security import SecurityRequirement -from ..base import ObjectExtended, PathsBase, OperationBase, PathItemBase class Response(ObjectExtended): diff --git a/src/aiopenapi3/v20/root.py b/src/aiopenapi3/v20/root.py index b585c712..0308d4bd 100644 --- a/src/aiopenapi3/v20/root.py +++ b/src/aiopenapi3/v20/root.py @@ -1,13 +1,13 @@ from pydantic import Field -from .general import Reference, ExternalDocumentation +from ..base import ObjectExtended, RootBase +from .general import ExternalDocumentation, Reference from .info import Info from .parameter import Parameter -from .paths import Response, Paths, PathItem +from .paths import PathItem, Paths, Response from .schemas import Schema -from .security import SecurityScheme, SecurityRequirement +from .security import SecurityRequirement, SecurityScheme from .tag import Tag -from ..base import ObjectExtended, RootBase class Root(ObjectExtended, RootBase): diff --git a/src/aiopenapi3/v20/schemas.py b/src/aiopenapi3/v20/schemas.py index dde8c23f..097ba370 100644 --- a/src/aiopenapi3/v20/schemas.py +++ b/src/aiopenapi3/v20/schemas.py @@ -1,10 +1,10 @@ -from typing import Union, Any, Optional +from typing import Any, Optional, Union -from pydantic import Field, model_validator, ValidatorFunctionWrapHandler, ValidationInfo +from pydantic import Field, ValidationInfo, ValidatorFunctionWrapHandler, model_validator +from ..base import ObjectExtended, SchemaBase from .general import Reference from .xml import XML -from ..base import ObjectExtended, SchemaBase class Schema(ObjectExtended, SchemaBase): diff --git a/src/aiopenapi3/v20/security.py b/src/aiopenapi3/v20/security.py index 8d227eb2..37cc8602 100644 --- a/src/aiopenapi3/v20/security.py +++ b/src/aiopenapi3/v20/security.py @@ -1,4 +1,4 @@ -from typing import Union, Annotated, Literal +from typing import Annotated, Literal, Union from pydantic import Field, RootModel diff --git a/src/aiopenapi3/v30/__init__.py b/src/aiopenapi3/v30/__init__.py index 46fbe895..87fde3a6 100644 --- a/src/aiopenapi3/v30/__init__.py +++ b/src/aiopenapi3/v30/__init__.py @@ -1,16 +1,15 @@ -from .glue import Request, AsyncRequest - from .components import Components from .example import Example from .general import ExternalDocumentation, Reference -from .info import Contact, License, Info +from .glue import AsyncRequest, Request +from .info import Contact, Info, License from .media import Encoding, MediaType -from .parameter import Parameter, Header -from .paths import RequestBody, Link, Response, Operation, PathItem, Paths, Callback, RuntimeExpression +from .parameter import Header, Parameter +from .paths import Callback, Link, Operation, PathItem, Paths, RequestBody, Response, RuntimeExpression from .root import Root from .schemas import Discriminator, Schema -from .security import OAuthFlow, OAuthFlows, SecurityScheme, SecurityRequirement -from .servers import ServerVariable, Server +from .security import OAuthFlow, OAuthFlows, SecurityRequirement, SecurityScheme +from .servers import Server, ServerVariable from .tag import Tag from .xml import XML diff --git a/src/aiopenapi3/v30/components.py b/src/aiopenapi3/v30/components.py index 5ec859bf..071a8e61 100644 --- a/src/aiopenapi3/v30/components.py +++ b/src/aiopenapi3/v30/components.py @@ -1,11 +1,10 @@ from pydantic import Field from ..base import ObjectExtended - from .example import Example -from .paths import RequestBody, Link, Response, Callback from .general import Reference from .parameter import Header, Parameter +from .paths import Callback, Link, RequestBody, Response from .schemas import Schema from .security import SecurityScheme diff --git a/src/aiopenapi3/v30/formdata.py b/src/aiopenapi3/v30/formdata.py index da769eac..b35f57f9 100644 --- a/src/aiopenapi3/v30/formdata.py +++ b/src/aiopenapi3/v30/formdata.py @@ -1,15 +1,15 @@ import base64 +import collections import quopri -from typing import TYPE_CHECKING, NamedTuple -from email.mime import multipart, nonmultipart from email.message import Message -import collections +from email.mime import multipart, nonmultipart +from typing import TYPE_CHECKING, NamedTuple from .parameter import encode_parameter - if TYPE_CHECKING: from pydantic import BaseModel + from .._types import MediaTypeType, SchemaType diff --git a/src/aiopenapi3/v30/general.py b/src/aiopenapi3/v30/general.py index b4ce57a9..fa519208 100644 --- a/src/aiopenapi3/v30/general.py +++ b/src/aiopenapi3/v30/general.py @@ -1,15 +1,13 @@ import typing -from typing import Union, Any +from typing import Any, Union -from pydantic import Field, PrivateAttr, ConfigDict - - -from ..base import ObjectExtended, ObjectBase, ReferenceBase +from pydantic import ConfigDict, Field, PrivateAttr +from ..base import ObjectBase, ObjectExtended, ReferenceBase if typing.TYPE_CHECKING: - from .schemas import Schema from .parameter import Parameter + from .schemas import Schema class ExternalDocumentation(ObjectExtended): diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index 00612074..1b4547cf 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -1,15 +1,16 @@ import io -from typing import Union, TYPE_CHECKING, Optional, cast, Any -from collections.abc import Sequence import json import urllib.parse +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any, Optional, Union, cast import httpx2 try: + import inspect + import httpx2_auth from httpx2_auth import SupportMultiAuth - import inspect except ImportError: httpx2_auth = None else: @@ -23,33 +24,33 @@ import pydantic # import pydantic.json - import aiopenapi3.v30.media -from ..request import RequestBase, AsyncRequestBase -from ..errors import HTTPStatusError, ContentTypeError, ResponseDecodingError, ResponseSchemaError, HeadersMissingError + +from ..errors import ContentTypeError, HeadersMissingError, HTTPStatusError, ResponseDecodingError, ResponseSchemaError +from ..request import AsyncRequestBase, RequestBase +from ..v31.root import Root as v31Root from .formdata import ( + MultipartParameter, + encode_multipart_parameters, parameters_from_multipart, parameters_from_urlencoded, - encode_multipart_parameters, - MultipartParameter, ) - from .root import Root as v30Root -from ..v31.root import Root as v31Root if TYPE_CHECKING: from .._types import ( - SchemaType, - RequestParameters, - RequestData, ParameterType, + RequestData, RequestFileParameter, - ResponseHeadersType, + RequestParameters, ResponseDataType, + ResponseHeadersType, + SchemaType, ) - - from .paths import Response as v30Response, MediaType as v30MediaType - from ..v31.paths import Response as v31Response, MediaType as v31MediaType + from ..v31.paths import MediaType as v31MediaType + from ..v31.paths import Response as v31Response + from .paths import MediaType as v30MediaType + from .paths import Response as v30Response v3xResponseType = Union[v30Response, v31Response] v3xMediaTypeType = Union[v30MediaType, v31MediaType] diff --git a/src/aiopenapi3/v30/media.py b/src/aiopenapi3/v30/media.py index 9ef6f58b..351744e3 100644 --- a/src/aiopenapi3/v30/media.py +++ b/src/aiopenapi3/v30/media.py @@ -1,10 +1,9 @@ import typing -from typing import Union, Any +from typing import Any, Union from pydantic import Field from ..base import ObjectExtended - from .example import Example from .general import Reference from .schemas import Schema diff --git a/src/aiopenapi3/v30/parameter.py b/src/aiopenapi3/v30/parameter.py index 6a17c512..1716187d 100644 --- a/src/aiopenapi3/v30/parameter.py +++ b/src/aiopenapi3/v30/parameter.py @@ -1,26 +1,26 @@ -import enum import datetime import decimal +import enum +import json import typing import uuid -import json -from typing import Union, Any from collections.abc import MutableMapping +from typing import Any, Union -from pydantic import BaseModel, Field, model_validator import more_itertools +from pydantic import BaseModel, Field, model_validator -from ..base import ObjectExtended, ParameterBase as ParameterBase_, ReferenceBase +from ..base import ObjectExtended, ReferenceBase +from ..base import ParameterBase as ParameterBase_ from ..errors import ParameterFormatError - +from ..model import TYPES_SCHEMA_MAP from .example import Example from .general import Reference from .schemas import Schema -from ..model import TYPES_SCHEMA_MAP if typing.TYPE_CHECKING: - from .paths import MediaType from .._types import v3xSchemaType + from .paths import MediaType class _ParameterCodec: diff --git a/src/aiopenapi3/v30/paths.py b/src/aiopenapi3/v30/paths.py index d2b48008..d166646c 100644 --- a/src/aiopenapi3/v30/paths.py +++ b/src/aiopenapi3/v30/paths.py @@ -1,14 +1,13 @@ -from typing import Union, Any +from typing import Any, Union -from pydantic import Field, model_validator, RootModel +from pydantic import Field, RootModel, model_validator -from ..base import ObjectExtended, PathsBase, OperationBase, PathItemBase -from .general import ExternalDocumentation -from .general import Reference +from ..base import ObjectExtended, OperationBase, PathItemBase, PathsBase +from .general import ExternalDocumentation, Reference from .media import MediaType from .parameter import Header, Parameter -from .servers import Server from .security import SecurityRequirement +from .servers import Server class RequestBody(ObjectExtended): diff --git a/src/aiopenapi3/v30/root.py b/src/aiopenapi3/v30/root.py index 8e25e964..7bb6d598 100644 --- a/src/aiopenapi3/v30/root.py +++ b/src/aiopenapi3/v30/root.py @@ -1,11 +1,8 @@ from typing import Any - from pydantic import Field - from ..base import ObjectExtended, RootBase - from .components import Components from .general import Reference from .info import Info diff --git a/src/aiopenapi3/v30/schemas.py b/src/aiopenapi3/v30/schemas.py index c901ab76..ea4b0829 100644 --- a/src/aiopenapi3/v30/schemas.py +++ b/src/aiopenapi3/v30/schemas.py @@ -1,8 +1,8 @@ -from typing import Union, Any +from typing import Any, Union -from pydantic import Field, model_validator, ConfigDict +from pydantic import ConfigDict, Field, model_validator -from ..base import ObjectExtended, SchemaBase, DiscriminatorBase +from ..base import DiscriminatorBase, ObjectExtended, SchemaBase from .general import Reference from .xml import XML diff --git a/src/aiopenapi3/v30/security.py b/src/aiopenapi3/v30/security.py index fd0cca5e..8137b70c 100644 --- a/src/aiopenapi3/v30/security.py +++ b/src/aiopenapi3/v30/security.py @@ -1,4 +1,5 @@ -from typing import Union, Annotated, Literal +from typing import Annotated, Literal, Union + from pydantic import Field, RootModel, constr from ..base import ObjectExtended diff --git a/src/aiopenapi3/v31/__init__.py b/src/aiopenapi3/v31/__init__.py index 19f84d7d..ce0877f5 100644 --- a/src/aiopenapi3/v31/__init__.py +++ b/src/aiopenapi3/v31/__init__.py @@ -1,14 +1,14 @@ from .components import Components from .example import Example from .general import ExternalDocumentation, Reference -from .info import Contact, License, Info +from .info import Contact, Info, License from .media import Encoding, MediaType -from .parameter import Parameter, Header -from .paths import RequestBody, Link, Response, Operation, PathItem, Paths, Callback, RuntimeExpression +from .parameter import Header, Parameter +from .paths import Callback, Link, Operation, PathItem, Paths, RequestBody, Response, RuntimeExpression from .root import Root from .schemas import Discriminator, Schema -from .security import OAuthFlow, OAuthFlows, SecurityScheme, SecurityRequirement -from .servers import ServerVariable, Server +from .security import OAuthFlow, OAuthFlows, SecurityRequirement, SecurityScheme +from .servers import Server, ServerVariable from .tag import Tag from .xml import XML diff --git a/src/aiopenapi3/v31/components.py b/src/aiopenapi3/v31/components.py index 03ce1e2d..40be317a 100644 --- a/src/aiopenapi3/v31/components.py +++ b/src/aiopenapi3/v31/components.py @@ -1,11 +1,10 @@ from pydantic import Field from ..base import ObjectExtended - from .example import Example -from .paths import RequestBody, Link, Response, Callback, PathItem from .general import Reference from .parameter import Header, Parameter +from .paths import Callback, Link, PathItem, RequestBody, Response from .schemas import Schema from .security import SecurityScheme diff --git a/src/aiopenapi3/v31/general.py b/src/aiopenapi3/v31/general.py index cab70d30..bb789bb3 100644 --- a/src/aiopenapi3/v31/general.py +++ b/src/aiopenapi3/v31/general.py @@ -1,14 +1,13 @@ import typing -from typing import Union, Any +from typing import Any, Union -from pydantic import Field, AnyUrl, PrivateAttr, ConfigDict +from pydantic import AnyUrl, ConfigDict, Field, PrivateAttr - -from ..base import ObjectExtended, ObjectBase, ReferenceBase +from ..base import ObjectBase, ObjectExtended, ReferenceBase if typing.TYPE_CHECKING: - from .schemas import Schema from .paths import Parameter, PathItem + from .schemas import Schema class ExternalDocumentation(ObjectExtended): diff --git a/src/aiopenapi3/v31/info.py b/src/aiopenapi3/v31/info.py index c3269fa5..95fb0abe 100644 --- a/src/aiopenapi3/v31/info.py +++ b/src/aiopenapi3/v31/info.py @@ -1,4 +1,4 @@ -from pydantic import Field, EmailStr, model_validator +from pydantic import EmailStr, Field, model_validator from aiopenapi3.base import ObjectExtended diff --git a/src/aiopenapi3/v31/media.py b/src/aiopenapi3/v31/media.py index 22b396fa..9957ab9b 100644 --- a/src/aiopenapi3/v31/media.py +++ b/src/aiopenapi3/v31/media.py @@ -3,11 +3,10 @@ from pydantic import Field from ..base import ObjectExtended - from .example import Example from .general import Reference -from .schemas import Schema from .parameter import Header +from .schemas import Schema class Encoding(ObjectExtended): diff --git a/src/aiopenapi3/v31/parameter.py b/src/aiopenapi3/v31/parameter.py index 484680bd..bcd2be58 100644 --- a/src/aiopenapi3/v31/parameter.py +++ b/src/aiopenapi3/v31/parameter.py @@ -1,17 +1,16 @@ import enum import typing -from typing import Union, Any +from typing import Any, Union from pydantic import Field -from ..base import ObjectExtended, ParameterBase as _ParameterBase - +from ..base import ObjectExtended +from ..base import ParameterBase as _ParameterBase +from ..v30.parameter import _ParameterCodec from .example import Example from .general import Reference from .schemas import Schema -from ..v30.parameter import _ParameterCodec - if typing.TYPE_CHECKING: from .paths import MediaType diff --git a/src/aiopenapi3/v31/paths.py b/src/aiopenapi3/v31/paths.py index c65628aa..1c384f8d 100644 --- a/src/aiopenapi3/v31/paths.py +++ b/src/aiopenapi3/v31/paths.py @@ -1,14 +1,13 @@ -from typing import Union, Any +from typing import Any, Union -from pydantic import Field, model_validator, RootModel +from pydantic import Field, RootModel, model_validator -from ..base import ObjectExtended, PathsBase, OperationBase, PathItemBase -from .general import ExternalDocumentation -from .general import Reference +from ..base import ObjectExtended, OperationBase, PathItemBase, PathsBase +from .general import ExternalDocumentation, Reference from .media import MediaType from .parameter import Header, Parameter -from .servers import Server from .security import SecurityRequirement +from .servers import Server class RequestBody(ObjectExtended): diff --git a/src/aiopenapi3/v31/root.py b/src/aiopenapi3/v31/root.py index e596a4a5..1294f2bb 100644 --- a/src/aiopenapi3/v31/root.py +++ b/src/aiopenapi3/v31/root.py @@ -3,14 +3,12 @@ from pydantic import Field, model_validator from ..base import ObjectExtended, RootBase - +from .components import Components +from .general import Reference from .info import Info -from .paths import Paths, PathItem +from .paths import PathItem, Paths from .security import SecurityRequirement from .servers import Server - -from .components import Components -from .general import Reference from .tag import Tag diff --git a/src/aiopenapi3/v31/schemas.py b/src/aiopenapi3/v31/schemas.py index 91c8ebdb..81061fcb 100644 --- a/src/aiopenapi3/v31/schemas.py +++ b/src/aiopenapi3/v31/schemas.py @@ -1,8 +1,8 @@ -from typing import Union, Any, Optional +from typing import Any, Optional, Union -from pydantic import Field, model_validator, ConfigDict +from pydantic import ConfigDict, Field, model_validator -from ..base import ObjectExtended, SchemaBase, DiscriminatorBase +from ..base import DiscriminatorBase, ObjectExtended, SchemaBase from .xml import XML diff --git a/src/aiopenapi3/v31/security.py b/src/aiopenapi3/v31/security.py index 75c87837..5301d2b4 100644 --- a/src/aiopenapi3/v31/security.py +++ b/src/aiopenapi3/v31/security.py @@ -1,6 +1,6 @@ from pathlib import Path +from typing import Annotated, Literal, Union -from typing import Union, Annotated, Literal from pydantic import Field, RootModel, constr from ..base import ObjectExtended diff --git a/src/aiopenapi3/v32/__init__.py b/src/aiopenapi3/v32/__init__.py index 19f84d7d..ce0877f5 100644 --- a/src/aiopenapi3/v32/__init__.py +++ b/src/aiopenapi3/v32/__init__.py @@ -1,14 +1,14 @@ from .components import Components from .example import Example from .general import ExternalDocumentation, Reference -from .info import Contact, License, Info +from .info import Contact, Info, License from .media import Encoding, MediaType -from .parameter import Parameter, Header -from .paths import RequestBody, Link, Response, Operation, PathItem, Paths, Callback, RuntimeExpression +from .parameter import Header, Parameter +from .paths import Callback, Link, Operation, PathItem, Paths, RequestBody, Response, RuntimeExpression from .root import Root from .schemas import Discriminator, Schema -from .security import OAuthFlow, OAuthFlows, SecurityScheme, SecurityRequirement -from .servers import ServerVariable, Server +from .security import OAuthFlow, OAuthFlows, SecurityRequirement, SecurityScheme +from .servers import Server, ServerVariable from .tag import Tag from .xml import XML diff --git a/src/aiopenapi3/v32/components.py b/src/aiopenapi3/v32/components.py index cceedc38..4bbf0642 100644 --- a/src/aiopenapi3/v32/components.py +++ b/src/aiopenapi3/v32/components.py @@ -1,14 +1,13 @@ from pydantic import Field from ..base import ObjectExtended - from .example import Example -from .paths import RequestBody, Link, Response, Callback, PathItem from .general import Reference +from .media import MediaType from .parameter import Header, Parameter +from .paths import Callback, Link, PathItem, RequestBody, Response from .schemas import Schema from .security import SecurityScheme -from .media import MediaType class Components(ObjectExtended): diff --git a/src/aiopenapi3/v32/general.py b/src/aiopenapi3/v32/general.py index 9db600a7..ee46e9d6 100644 --- a/src/aiopenapi3/v32/general.py +++ b/src/aiopenapi3/v32/general.py @@ -1,14 +1,13 @@ import typing -from typing import Union, Any +from typing import Any, Union -from pydantic import Field, AnyUrl, PrivateAttr, ConfigDict +from pydantic import AnyUrl, ConfigDict, Field, PrivateAttr - -from ..base import ObjectExtended, ObjectBase, ReferenceBase +from ..base import ObjectBase, ObjectExtended, ReferenceBase if typing.TYPE_CHECKING: - from .schemas import Schema from .paths import Parameter, PathItem + from .schemas import Schema class ExternalDocumentation(ObjectExtended): diff --git a/src/aiopenapi3/v32/info.py b/src/aiopenapi3/v32/info.py index c66850e2..4d9b3b7e 100644 --- a/src/aiopenapi3/v32/info.py +++ b/src/aiopenapi3/v32/info.py @@ -1,4 +1,4 @@ -from pydantic import Field, EmailStr, model_validator +from pydantic import EmailStr, Field, model_validator from aiopenapi3.base import ObjectExtended diff --git a/src/aiopenapi3/v32/media.py b/src/aiopenapi3/v32/media.py index 825a9f1e..875c08ab 100644 --- a/src/aiopenapi3/v32/media.py +++ b/src/aiopenapi3/v32/media.py @@ -2,6 +2,7 @@ if sys.version_info < (3, 12): from typing import Any + from typing_extensions import Self else: from typing import Any, Self @@ -9,11 +10,10 @@ from pydantic import Field from ..base import ObjectExtended - from .example import Example from .general import Reference -from .schemas import Schema from .parameter import Header +from .schemas import Schema class Encoding(ObjectExtended): diff --git a/src/aiopenapi3/v32/parameter.py b/src/aiopenapi3/v32/parameter.py index 3072ff0d..fedc14e3 100644 --- a/src/aiopenapi3/v32/parameter.py +++ b/src/aiopenapi3/v32/parameter.py @@ -1,17 +1,16 @@ import enum import typing -from typing import Union, Any +from typing import Any, Union from pydantic import Field -from ..base import ObjectExtended, ParameterBase as _ParameterBase - +from ..base import ObjectExtended +from ..base import ParameterBase as _ParameterBase +from ..v30.parameter import _ParameterCodec from .example import Example from .general import Reference from .schemas import Schema -from ..v30.parameter import _ParameterCodec - if typing.TYPE_CHECKING: from .paths import MediaType diff --git a/src/aiopenapi3/v32/paths.py b/src/aiopenapi3/v32/paths.py index 578f2ea4..c71f862c 100644 --- a/src/aiopenapi3/v32/paths.py +++ b/src/aiopenapi3/v32/paths.py @@ -1,14 +1,13 @@ -from typing import Union, Any +from typing import Any, Union -from pydantic import Field, model_validator, RootModel +from pydantic import Field, RootModel, model_validator -from ..base import ObjectExtended, PathsBase, OperationBase, PathItemBase -from .general import ExternalDocumentation -from .general import Reference +from ..base import ObjectExtended, OperationBase, PathItemBase, PathsBase +from .general import ExternalDocumentation, Reference from .media import MediaType from .parameter import Header, Parameter -from .servers import Server from .security import SecurityRequirement +from .servers import Server class RequestBody(ObjectExtended): diff --git a/src/aiopenapi3/v32/root.py b/src/aiopenapi3/v32/root.py index bd3cf559..a51fc35d 100644 --- a/src/aiopenapi3/v32/root.py +++ b/src/aiopenapi3/v32/root.py @@ -4,14 +4,12 @@ from pydantic import Field, model_validator from ..base import ObjectExtended, RootBase - +from .components import Components +from .general import Reference from .info import Info -from .paths import Paths, PathItem +from .paths import PathItem, Paths from .security import SecurityRequirement from .servers import Server - -from .components import Components -from .general import Reference from .tag import Tag diff --git a/src/aiopenapi3/v32/schemas.py b/src/aiopenapi3/v32/schemas.py index 6a7b6384..690b8637 100644 --- a/src/aiopenapi3/v32/schemas.py +++ b/src/aiopenapi3/v32/schemas.py @@ -1,9 +1,9 @@ import typing -from typing import Union, Any, Optional +from typing import Any, Optional, Union -from pydantic import Field, model_validator, ConfigDict +from pydantic import ConfigDict, Field, model_validator -from ..base import ObjectExtended, SchemaBase, DiscriminatorBase +from ..base import DiscriminatorBase, ObjectExtended, SchemaBase from .xml import XML if typing.TYPE_CHECKING: diff --git a/src/aiopenapi3/v32/security.py b/src/aiopenapi3/v32/security.py index bb5c9cd4..a46d23b3 100644 --- a/src/aiopenapi3/v32/security.py +++ b/src/aiopenapi3/v32/security.py @@ -1,6 +1,6 @@ from pathlib import Path +from typing import Annotated, Literal, Union -from typing import Union, Annotated, Literal from pydantic import Field, RootModel, constr from ..base import ObjectExtended diff --git a/tests/api/v1/main.py b/tests/api/v1/main.py index 4ada3fc3..32051c7d 100644 --- a/tests/api/v1/main.py +++ b/tests/api/v1/main.py @@ -2,10 +2,10 @@ from typing import Annotated import starlette.status -from fastapi import APIRouter, Body, Response, Path +from fastapi import APIRouter, Body, Path, Response from fastapi.responses import JSONResponse -from .schema import Pets, Pet, PetCreate, Error +from .schema import Error, Pet, PetCreate, Pets router = APIRouter(prefix="/v1") diff --git a/tests/api/v1/schema.py b/tests/api/v1/schema.py index 759c8768..e06b15b7 100644 --- a/tests/api/v1/schema.py +++ b/tests/api/v1/schema.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, RootModel, Field +from pydantic import BaseModel, Field, RootModel class PetBase(BaseModel): diff --git a/tests/api/v2/main.py b/tests/api/v2/main.py index 8e285e76..bf00dcab 100644 --- a/tests/api/v2/main.py +++ b/tests/api/v2/main.py @@ -3,7 +3,7 @@ from typing import Annotated import starlette.status -from fastapi import Body, Response, Header, APIRouter, Path +from fastapi import APIRouter, Body, Header, Path, Response from fastapi.responses import JSONResponse from . import schema diff --git a/tests/api/v2/schema.py b/tests/api/v2/schema.py index ee7468f1..3e149188 100644 --- a/tests/api/v2/schema.py +++ b/tests/api/v2/schema.py @@ -1,10 +1,8 @@ -from datetime import timedelta import uuid +from datetime import timedelta +from typing import Annotated, Literal, Union - -from typing import Literal, Union, Annotated - -from pydantic import BaseModel, RootModel, Field, ConfigDict +from pydantic import BaseModel, ConfigDict, Field, RootModel # from pydantic.fields import Undefined diff --git a/tests/apiv1_test.py b/tests/apiv1_test.py index 40deba3f..8c5f9b7f 100644 --- a/tests/apiv1_test.py +++ b/tests/apiv1_test.py @@ -4,17 +4,14 @@ import pytest import pytest_asyncio +# pytest.skip(allow_module_level=True) +from api.v1.main import router +from fastapi import FastAPI from hypercorn.asyncio import serve from hypercorn.config import Config -from fastapi import FastAPI - import aiopenapi3 -# pytest.skip(allow_module_level=True) - -from api.v1.main import router - app = FastAPI( version="1.0.0", title="Dorthu's Petstore", servers=[{"url": "/", "description": "Default, relative server"}] ) diff --git a/tests/apiv2_test.py b/tests/apiv2_test.py index 141a6c04..45cd2433 100644 --- a/tests/apiv2_test.py +++ b/tests/apiv2_test.py @@ -1,16 +1,19 @@ +import asyncio import datetime import random -import asyncio +import typing import uuid from typing import ForwardRef -import typing - import pydantic - import pytest import pytest_asyncio +from api.v2.main import router +from api.v2.schema import Dog as _Dog +# pytest.skip(allow_module_level=True) +from fastapi import FastAPI +from fastapi.responses import PlainTextResponse from hypercorn.asyncio import serve from hypercorn.config import Config @@ -18,16 +21,6 @@ from aiopenapi3 import OpenAPI from aiopenapi3.v31.schemas import Schema -from api.v2.schema import Dog as _Dog - -# pytest.skip(allow_module_level=True) - -from fastapi import FastAPI -from fastapi.responses import PlainTextResponse - - -from api.v2.main import router - app = FastAPI( version="1.0.0", title="Dorthu's Petstore", servers=[{"url": "/", "description": "Default, relative server"}] ) diff --git a/tests/cli_test.py b/tests/cli_test.py index 56e09809..5f9adee0 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -1,11 +1,10 @@ +import json import os import shlex from pathlib import Path -import json - -from aiopenapi3.cli import main import aiopenapi3.log +from aiopenapi3.cli import main def test_validate_cli(): diff --git a/tests/clone_test.py b/tests/clone_test.py index 56dc3eef..194ee175 100644 --- a/tests/clone_test.py +++ b/tests/clone_test.py @@ -2,7 +2,6 @@ from aiopenapi3 import OpenAPI - """ https://github.com/pydantic/pydantic/issues/6010 """ diff --git a/tests/conftest.py b/tests/conftest.py index c38163a2..868e43f7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,5 @@ -import os import dataclasses - +import os from pathlib import Path import pytest @@ -81,6 +80,7 @@ def _get_parsed_yaml(filename, version=None): with open("tests/fixtures/" + filename) as f: raw = f.read() import yaml + from aiopenapi3.loader import YAML12Loader parsed = yaml.load(raw, Loader=YAML12Loader) diff --git a/tests/content_length_test.py b/tests/content_length_test.py index cf2b34cc..69cea32d 100644 --- a/tests/content_length_test.py +++ b/tests/content_length_test.py @@ -1,18 +1,15 @@ import asyncio import random -from hypercorn.asyncio import serve -from hypercorn.config import Config -from fastapi import FastAPI, Request, Response, Query -from fastapi.responses import PlainTextResponse - import pytest import pytest_asyncio - +from fastapi import FastAPI, Query, Request, Response +from fastapi.responses import PlainTextResponse +from hypercorn.asyncio import serve +from hypercorn.config import Config import aiopenapi3 - app = FastAPI(version="1.0.0", title="TLS tests", servers=[{"url": "/", "description": "Default, relative server"}]) diff --git a/tests/debug_test.py b/tests/debug_test.py index bc2c1406..a286c260 100644 --- a/tests/debug_test.py +++ b/tests/debug_test.py @@ -1,5 +1,5 @@ -import pytest import httpx2 +import pytest import aiopenapi3.debug from aiopenapi3 import OpenAPI, ResponseSchemaError diff --git a/tests/error_test.py b/tests/error_test.py index ce11958a..8f0823d0 100644 --- a/tests/error_test.py +++ b/tests/error_test.py @@ -1,11 +1,15 @@ -from aiopenapi3 import OpenAPI -from aiopenapi3 import ResponseSchemaError, ContentTypeError, HTTPStatusError, ResponseDecodingError, RequestError - import httpx2 - - import pytest +from aiopenapi3 import ( + ContentTypeError, + HTTPStatusError, + OpenAPI, + RequestError, + ResponseDecodingError, + ResponseSchemaError, +) + def test_response_error(httpx_mock, with_paths_response_error_vXX): api = OpenAPI("/", with_paths_response_error_vXX, session_factory=httpx2.Client) diff --git a/tests/extra_test.py b/tests/extra_test.py index 9c046bdb..753f45ef 100644 --- a/tests/extra_test.py +++ b/tests/extra_test.py @@ -1,15 +1,13 @@ import re import typing - from pathlib import Path import httpx2 import pytest from aiopenapi3 import OpenAPI -from aiopenapi3.loader import FileSystemLoader - from aiopenapi3.extra import Cull, Reduce +from aiopenapi3.loader import FileSystemLoader if typing.TYPE_CHECKING: from aiopenapi3.plugin import Document diff --git a/tests/formdata_test.py b/tests/formdata_test.py index d164da21..b40bfacb 100644 --- a/tests/formdata_test.py +++ b/tests/formdata_test.py @@ -1,8 +1,9 @@ from pathlib import Path + import httpx2 from aiopenapi3 import OpenAPI -from aiopenapi3.v30.formdata import encode_multipart_parameters, MultipartParameter +from aiopenapi3.v30.formdata import MultipartParameter, encode_multipart_parameters def test_encode_formdata(): diff --git a/tests/forms_test.py b/tests/forms_test.py index c1d444b9..76a9f5af 100644 --- a/tests/forms_test.py +++ b/tests/forms_test.py @@ -3,11 +3,14 @@ import datetime import decimal -import aiopenapi3 - -from flask import Flask, render_template_string, Blueprint, abort +import pytest +import pytest_asyncio +from asgiref.wsgi import WsgiToAsgi +from flask import Blueprint, Flask, Response, abort, render_template_string, request from flask_bootstrap import Bootstrap4 as Bootstrap from flask_wtf import FlaskForm +from hypercorn.asyncio import serve +from hypercorn.config import Config from wtforms.fields import ( BooleanField, DateField, @@ -17,34 +20,26 @@ DecimalRangeField, EmailField, FileField, - MultipleFileField, FloatField, + HiddenField, IntegerField, IntegerRangeField, + MultipleFileField, + PasswordField, RadioField, - SelectField, SearchField, + SelectField, SelectMultipleField, - SubmitField, StringField, + SubmitField, TelField, + TextAreaField, TimeField, URLField, - HiddenField, - PasswordField, - TextAreaField, ) - from wtforms.validators import DataRequired, NumberRange -from flask import request, Response - -from asgiref.wsgi import WsgiToAsgi - -import pytest -import pytest_asyncio -from hypercorn.asyncio import serve -from hypercorn.config import Config +import aiopenapi3 # csrf.exempt(serve_test) diff --git a/tests/loader_test.py b/tests/loader_test.py index 5c5817a4..62140534 100644 --- a/tests/loader_test.py +++ b/tests/loader_test.py @@ -1,11 +1,11 @@ import json - from pathlib import Path -import yarl import pytest -from aiopenapi3 import OpenAPI, FileSystemLoader, ReferenceResolutionError -from aiopenapi3.loader import Loader, Plugins, NullLoader +import yarl + +from aiopenapi3 import FileSystemLoader, OpenAPI, ReferenceResolutionError +from aiopenapi3.loader import Loader, NullLoader, Plugins SPECTPL = """ openapi: "3.0.0" @@ -77,8 +77,9 @@ def test_webload(): # FIXME https://github.com/pydantic/pydantic/issues/5730 pytest.skip() name = "https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/network/resource-manager/Microsoft.Network/stable/2018-10-01/serviceEndpointPolicy.json" - from aiopenapi3.loader import WebLoader import yarl + from aiopenapi3.loader import WebLoader + loader = WebLoader(yarl.URL(name)) api = OpenAPI.load_sync(name, loader=loader) diff --git a/tests/parse_data_test.py b/tests/parse_data_test.py index 6eda87a8..a8afc8b2 100644 --- a/tests/parse_data_test.py +++ b/tests/parse_data_test.py @@ -1,7 +1,6 @@ -import pytest - import pathlib +import pytest import yarl from aiopenapi3 import FileSystemLoader, OpenAPI diff --git a/tests/parsing_test.py b/tests/parsing_test.py index 88fa13e5..acd8afa1 100644 --- a/tests/parsing_test.py +++ b/tests/parsing_test.py @@ -4,12 +4,11 @@ import uuid - import pytest - from pydantic import ValidationError + from aiopenapi3 import OpenAPI, ReferenceResolutionError -from aiopenapi3.errors import OperationParameterValidationError, OperationIdDuplicationError +from aiopenapi3.errors import OperationIdDuplicationError, OperationParameterValidationError URLBASE = "/" @@ -180,8 +179,8 @@ def test_parsing_schema_names(with_parsing_schema_names): def test_pydantic_classes(): - from typing import Union, ForwardRef, Annotated, Literal import types + from typing import Annotated, ForwardRef, Literal, Union from pydantic import BaseModel, Field diff --git a/tests/path_test.py b/tests/path_test.py index c01d9f3b..48c17bcc 100644 --- a/tests/path_test.py +++ b/tests/path_test.py @@ -4,20 +4,20 @@ import base64 import copy -import uuid import pathlib +import uuid -import pytest import httpx2 +import pytest import yarl from aiopenapi3 import OpenAPI from aiopenapi3.errors import ( - OperationParameterValidationError, - OperationIdDuplicationError, HeadersMissingError, HTTPClientError, HTTPServerError, + OperationIdDuplicationError, + OperationParameterValidationError, ) URLBASE = "/" @@ -509,7 +509,7 @@ def test_paths_response_status_pattern_default(httpx2_mock, with_paths_response_ def test_paths_response_error(mocker, httpx2_mock, with_paths_response_error_vXX): - from aiopenapi3 import ResponseSchemaError, ContentTypeError, HTTPStatusError, ResponseDecodingError + from aiopenapi3 import ContentTypeError, HTTPStatusError, ResponseDecodingError, ResponseSchemaError api = OpenAPI("/", with_paths_response_error_vXX, session_factory=httpx2.Client) diff --git a/tests/pathv20_test.py b/tests/pathv20_test.py index 6391a83e..15c7cdc5 100644 --- a/tests/pathv20_test.py +++ b/tests/pathv20_test.py @@ -1,11 +1,11 @@ import io -import uuid import urllib +import uuid -import yarl import httpx2 import pytest import python_multipart +import yarl from httpx2._multipart import MultipartStream from aiopenapi3 import OpenAPI diff --git a/tests/petstorev3_test.py b/tests/petstorev3_test.py index 5b35a1e3..34c8dea7 100644 --- a/tests/petstorev3_test.py +++ b/tests/petstorev3_test.py @@ -7,7 +7,6 @@ from aiopenapi3.plugin import Document, Message from aiopenapi3.v20 import Reference - try: import httpx2_auth except ImportError: diff --git a/tests/pickle_test.py b/tests/pickle_test.py index 164ee115..deda4def 100644 --- a/tests/pickle_test.py +++ b/tests/pickle_test.py @@ -2,10 +2,9 @@ Tests parsing specs """ -from pathlib import Path -import pickle import copy - +import pickle +from pathlib import Path from aiopenapi3 import OpenAPI diff --git a/tests/plugin_test.py b/tests/plugin_test.py index df70f38c..8bfb03bd 100644 --- a/tests/plugin_test.py +++ b/tests/plugin_test.py @@ -5,7 +5,7 @@ import yarl from aiopenapi3 import FileSystemLoader, OpenAPI -from aiopenapi3.plugin import Init, Message, Document +from aiopenapi3.plugin import Document, Init, Message class OnInit(Init): diff --git a/tests/ref_test.py b/tests/ref_test.py index ebfd87e2..84c3e8c1 100644 --- a/tests/ref_test.py +++ b/tests/ref_test.py @@ -7,15 +7,13 @@ allOfs are populated as expected as well. """ -from typing import ForwardRef - import typing +from typing import ForwardRef -from aiopenapi3 import OpenAPI - +from pydantic import RootModel from pydantic._internal._model_construction import ModelMetaclass -from pydantic import RootModel +from aiopenapi3 import OpenAPI def test_ref_resolution(openapi_version, petstore_expanded): diff --git a/tests/schema_test.py b/tests/schema_test.py index 81d8a500..f5b117b5 100644 --- a/tests/schema_test.py +++ b/tests/schema_test.py @@ -2,15 +2,13 @@ import typing import uuid from datetime import datetime - - from pathlib import Path -import yarl import httpx2 +import pydantic import pytest +import yarl from pydantic import ValidationError -import pydantic import aiopenapi3 from aiopenapi3 import OpenAPI @@ -304,8 +302,7 @@ class OnDocument(Document): def parsed(self, ctx): ctx.document["openapi"] = str(openapi_version) - from aiopenapi3.loader import YAML12Loader - from aiopenapi3.loader import FileSystemLoader + from aiopenapi3.loader import FileSystemLoader, YAML12Loader OpenAPI.load_file( "/test.yaml", diff --git a/tests/sequential_test.py b/tests/sequential_test.py index d396e8fb..6f7cb38f 100644 --- a/tests/sequential_test.py +++ b/tests/sequential_test.py @@ -1,17 +1,13 @@ import asyncio - from collections.abc import AsyncIterable - -from hypercorn.asyncio import serve -from hypercorn.config import Config import pydantic -from fastapi import FastAPI -from fastapi.sse import EventSourceResponse, ServerSentEvent - import pytest import pytest_asyncio - +from fastapi import FastAPI +from fastapi.sse import EventSourceResponse, ServerSentEvent +from hypercorn.asyncio import serve +from hypercorn.config import Config import aiopenapi3 diff --git a/tests/stream_test.py b/tests/stream_test.py index cf3dceca..b03f2e2b 100644 --- a/tests/stream_test.py +++ b/tests/stream_test.py @@ -1,24 +1,19 @@ import asyncio import random import string - -from typing import Annotated - from pathlib import Path +from typing import Annotated -from hypercorn.asyncio import serve -from hypercorn.config import Config import pydantic -from fastapi import FastAPI, Request, Response, Query, UploadFile, Body -from fastapi.responses import PlainTextResponse - import pytest import pytest_asyncio - +from fastapi import Body, FastAPI, Query, Request, Response, UploadFile +from fastapi.responses import PlainTextResponse +from hypercorn.asyncio import serve +from hypercorn.config import Config import aiopenapi3 - app = FastAPI(version="1.0.0", title="TLS tests", servers=[{"url": "/", "description": "Default, relative server"}]) diff --git a/tests/tls_test.py b/tests/tls_test.py index 2a3d3989..18077811 100644 --- a/tests/tls_test.py +++ b/tests/tls_test.py @@ -3,21 +3,18 @@ import ssl from pathlib import Path +import cryptography import httpx2 import pytest import pytest_asyncio - +import trustme +from fastapi import FastAPI, Request, Response from hypercorn.asyncio import serve from hypercorn.config import Config -import trustme -import cryptography - import aiopenapi3 from aiopenapi3.plugin import Document -from fastapi import FastAPI, Request, Response - app = FastAPI(version="1.0.0", title="TLS tests", servers=[{"url": "/", "description": "Default, relative server"}]) diff --git a/tests/v32_test.py b/tests/v32_test.py index ac233e79..2c8d030d 100644 --- a/tests/v32_test.py +++ b/tests/v32_test.py @@ -1,10 +1,8 @@ import httpx2 - import pytest from pytest_httpx2 import IteratorStream -from aiopenapi3 import OpenAPI -from aiopenapi3 import v32 +from aiopenapi3 import OpenAPI, v32 def test_Components(): From f34f8eb191339fba881424f9f226e07fa11121ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:04:50 +0200 Subject: [PATCH 13/58] ruff - PERF102 --- pyproject.toml | 2 +- src/aiopenapi3/extra/reduce.py | 4 ++-- src/aiopenapi3/model.py | 2 +- src/aiopenapi3/openapi.py | 2 +- src/aiopenapi3/request.py | 2 +- src/aiopenapi3/v20/glue.py | 2 +- src/aiopenapi3/v30/glue.py | 4 ++-- tests/api/v1/main.py | 2 +- tests/api/v2/main.py | 2 +- tests/forms_test.py | 2 +- tests/petstore_test.py | 4 ++-- tests/petstorev3_test.py | 4 ++-- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cfbaca74..3615ee8f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,7 +98,7 @@ ignore = [ "DTZ005", # "FURB167", # "I001", - "PERF102", +# "PERF102", "PIE790", "PIE804", "PIE808", diff --git a/src/aiopenapi3/extra/reduce.py b/src/aiopenapi3/extra/reduce.py index 54d1afde..eceabfb0 100644 --- a/src/aiopenapi3/extra/reduce.py +++ b/src/aiopenapi3/extra/reduce.py @@ -97,13 +97,13 @@ def initialized(self, ctx: "Init.Context") -> "Init.Context": del ctx.initialized.components.schemas[name] for name, response in list(ctx.initialized.components.responses.items()): - for k, v in response.content.items(): + for v in response.content.values(): if v.schema_._model_type is None: del ctx.initialized.components.responses[name] break for name, requestBody in list(ctx.initialized.components.requestBodies.items()): - for k, v in requestBody.content.items(): + for v in requestBody.content.values(): if v.schema_._model_type is None: del ctx.initialized.components.requestBodies[name] break diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index b1ede310..bed06cfb 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -428,7 +428,7 @@ def get_patternProperties(self_): def mkx(): def validate_patternProperties(self_): patterns = typing.get_args(self_.aio3_patternProperty.__annotations__["item"]) - for name, value in self_.model_extra.items(): + for name in self_.model_extra.keys(): for pattern in patterns: if re.match(pattern, name): break diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index 89d684b0..9c6d7325 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -590,7 +590,7 @@ def is_schema(v: tuple[str, "SchemaType"]) -> bool: if only_required is False: for responses in (x.responses for x in components): assert responses is not None - for rname, response in responses.items(): + for response in responses.values(): for mt, mto in response.content.items(): if mto.schema_ is None: continue diff --git a/src/aiopenapi3/request.py b/src/aiopenapi3/request.py index 251234e1..7a36448e 100644 --- a/src/aiopenapi3/request.py +++ b/src/aiopenapi3/request.py @@ -604,7 +604,7 @@ def __init__(self, api: "OpenAPI", use_operation_tags: bool): self.operations = [] self.r: Iterator[int] pi: "PathItemType" - for path, pi in api.paths.items(): + for pi in api.paths.values(): op: "OperationType" if pi.ref: # pi = pi.ref._target diff --git a/src/aiopenapi3/v20/glue.py b/src/aiopenapi3/v20/glue.py index e76e993f..faf54c0c 100644 --- a/src/aiopenapi3/v20/glue.py +++ b/src/aiopenapi3/v20/glue.py @@ -98,7 +98,7 @@ def _prepare_security(self): for s in security: if frozenset(s.root.keys()) - frozenset(self.security.keys()): continue - for scheme, _ in s.root.items(): + for scheme in s.root.keys(): value = self.security[scheme] self._prepare_secschemes(scheme, value) break diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index 1b4547cf..ab983f57 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -115,7 +115,7 @@ def _prepare_security(self) -> None: for s in security: if frozenset(s.root.keys()) - frozenset(self.security.keys()): continue - for scheme, _ in s.root.items(): + for scheme in s.root.keys(): value = self.security[scheme] self._prepare_secschemes(scheme, value) break @@ -282,7 +282,7 @@ def _prepare_parameters(self, provided: Optional["RequestParameters"]) -> dict[s ct = "multipart/form-data" if ct in self.operation.requestBody.content: assert self.operation.requestBody.content[ct].encoding is not None - for k, v in self.operation.requestBody.content[ct].encoding.items(): + for v in self.operation.requestBody.content[ct].encoding.values(): assert v.headers is not None and isinstance(v.headers, dict) rbq.update(v.headers) possible.update(rbq) diff --git a/tests/api/v1/main.py b/tests/api/v1/main.py index 32051c7d..31013e6c 100644 --- a/tests/api/v1/main.py +++ b/tests/api/v1/main.py @@ -62,7 +62,7 @@ def listPet(limit: int | None = None) -> Pets: @router.get("/pets/{petId}", operation_id="getPet", response_model=Pet, responses={404: {"model": Error}}) def getPet(pet_id: int = Path(..., alias="petId")) -> Pets: - for k, v in ZOO.items(): + for v in ZOO.values(): if pet_id == v.id: return v else: diff --git a/tests/api/v2/main.py b/tests/api/v2/main.py index bf00dcab..dc9b46df 100644 --- a/tests/api/v2/main.py +++ b/tests/api/v2/main.py @@ -53,7 +53,7 @@ def listPet(limit: int | None = None) -> schema.Pets: @router.get("/pets/{petId}", operation_id="getPet", response_model=schema.Pet, responses={404: {"model": schema.Error}}) def getPet(pet_id: str = Path(..., alias="petId")) -> schema.Pets: - for k, pet in ZOO.items(): + for pet in ZOO.values(): if pet_id == pet.identifier: return pet else: diff --git a/tests/forms_test.py b/tests/forms_test.py index 76a9f5af..5178404f 100644 --- a/tests/forms_test.py +++ b/tests/forms_test.py @@ -285,7 +285,7 @@ def form_type(request): async def client(server, form_type, with_paths_requestbody_formdata_wtforms): data = copy.deepcopy(with_paths_requestbody_formdata_wtforms) if form_type != "multipart/form-data": - for op, v in data["paths"].items(): + for v in data["paths"].values(): v["post"]["requestBody"]["content"][form_type] = v["post"]["requestBody"]["content"]["multipart/form-data"] del v["post"]["requestBody"]["content"]["multipart/form-data"] diff --git a/tests/petstore_test.py b/tests/petstore_test.py index 292d5d7f..20d9fabd 100644 --- a/tests/petstore_test.py +++ b/tests/petstore_test.py @@ -26,8 +26,8 @@ class OnDocument(Document): PetResponse = {"description": "successful operation", "schema": {"$ref": "#/definitions/Pet"}} def parsed(self, ctx): - for name, path in ctx.document["paths"].items(): - for method, action in path.items(): + for path in ctx.document["paths"].values(): + for action in path.values(): if "default" not in action["responses"]: action["responses"]["default"] = OnDocument.ApiResponse diff --git a/tests/petstorev3_test.py b/tests/petstorev3_test.py index 34c8dea7..1f21fb89 100644 --- a/tests/petstorev3_test.py +++ b/tests/petstorev3_test.py @@ -36,8 +36,8 @@ class OnDocument(Document): PetResponse = {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Pet"}}}} def parsed(self, ctx): - for name, path in ctx.document["paths"].items(): - for method, action in path.items(): + for path in ctx.document["paths"].values(): + for action in path.values(): if "default" not in action["responses"]: action["responses"]["default"] = OnDocument.ApiResponse From 463db20e33249795fea77c51b65dcd631033536c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:06:29 +0200 Subject: [PATCH 14/58] ruff - PIE790 --- pyproject.toml | 2 +- src/aiopenapi3/errors.py | 6 ------ src/aiopenapi3/v20/security.py | 2 -- src/aiopenapi3/v30/formdata.py | 1 - src/aiopenapi3/v30/security.py | 4 ---- src/aiopenapi3/v31/security.py | 4 ---- src/aiopenapi3/v32/security.py | 4 ---- tests/v32_test.py | 1 - 8 files changed, 1 insertion(+), 23 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3615ee8f..8d93d788 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,7 +99,7 @@ ignore = [ # "FURB167", # "I001", # "PERF102", - "PIE790", +# "PIE790", "PIE804", "PIE808", "PLC0208", diff --git a/src/aiopenapi3/errors.py b/src/aiopenapi3/errors.py index 7de7606d..07919ed5 100644 --- a/src/aiopenapi3/errors.py +++ b/src/aiopenapi3/errors.py @@ -79,8 +79,6 @@ class ParameterFormatError(SpecError): The specified parameter encoding is invalid for the parameter family """ - pass - class HTTPError(ErrorBase): pass @@ -204,11 +202,7 @@ def __str__(self): class HTTPClientError(HTTPStatusIndicatedError): """response code 4xx""" - pass - @dataclasses.dataclass(repr=False) class HTTPServerError(HTTPStatusIndicatedError): """response code 5xx""" - - pass diff --git a/src/aiopenapi3/v20/security.py b/src/aiopenapi3/v20/security.py index 37cc8602..f9810e37 100644 --- a/src/aiopenapi3/v20/security.py +++ b/src/aiopenapi3/v20/security.py @@ -47,8 +47,6 @@ class SecurityScheme( https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#security-scheme-object """ - pass - class SecurityRequirement(RootModel): """ diff --git a/src/aiopenapi3/v30/formdata.py b/src/aiopenapi3/v30/formdata.py index b35f57f9..dbd8d131 100644 --- a/src/aiopenapi3/v30/formdata.py +++ b/src/aiopenapi3/v30/formdata.py @@ -165,7 +165,6 @@ def encode_multipart_parameters( f.headers["Content-Encoding"] = codec else: """OpenAPI 3.0""" - pass data = encode_content(v, codec) diff --git a/src/aiopenapi3/v30/security.py b/src/aiopenapi3/v30/security.py index 8137b70c..30f338b1 100644 --- a/src/aiopenapi3/v30/security.py +++ b/src/aiopenapi3/v30/security.py @@ -74,8 +74,6 @@ class SecurityScheme( .. _Security Scheme: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#security-scheme-object """ - pass - class SecurityRequirement(RootModel[dict[str, list[str]]]): """ @@ -83,5 +81,3 @@ class SecurityRequirement(RootModel[dict[str, list[str]]]): .. _SecurityRequirement: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#security-requirement-object """ - - pass diff --git a/src/aiopenapi3/v31/security.py b/src/aiopenapi3/v31/security.py index 5301d2b4..b82e0a1e 100644 --- a/src/aiopenapi3/v31/security.py +++ b/src/aiopenapi3/v31/security.py @@ -91,8 +91,6 @@ class SecurityScheme( .. _Security Scheme: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#security-scheme-object """ - pass - class SecurityRequirement(RootModel[dict[str, list[str]]]): """ @@ -100,5 +98,3 @@ class SecurityRequirement(RootModel[dict[str, list[str]]]): .. _SecurityRequirement: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#securityRequirementObject """ - - pass diff --git a/src/aiopenapi3/v32/security.py b/src/aiopenapi3/v32/security.py index a46d23b3..02b2cd5c 100644 --- a/src/aiopenapi3/v32/security.py +++ b/src/aiopenapi3/v32/security.py @@ -97,8 +97,6 @@ class SecurityScheme( .. _here: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#security-scheme-object """ - pass - class SecurityRequirement(RootModel[dict[str, list[str]]]): """ @@ -108,5 +106,3 @@ class SecurityRequirement(RootModel[dict[str, list[str]]]): .. _here: https://spec.openapis.org/oas/v3.2.0.html#security-requirement-object """ - - pass diff --git a/tests/v32_test.py b/tests/v32_test.py index 2c8d030d..b53ce198 100644 --- a/tests/v32_test.py +++ b/tests/v32_test.py @@ -81,7 +81,6 @@ async def test_MediaType(httpx2_mock, with_schema_itemSchema): # prefixEncoding # itemEncoding - pass @pytest.mark.httpx2_mock(can_send_already_matched_responses=True) From ce887ab411efec21572c546167a21e64a310cc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:06:54 +0200 Subject: [PATCH 15/58] ruff - PIE804 --- pyproject.toml | 2 +- tests/path_test.py | 2 +- tests/pathv20_test.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8d93d788..2c98b16e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -100,7 +100,7 @@ ignore = [ # "I001", # "PERF102", # "PIE790", - "PIE804", +# "PIE804", "PIE808", "PLC0208", "PLR1708", diff --git a/tests/path_test.py b/tests/path_test.py index 48c17bcc..78758565 100644 --- a/tests/path_test.py +++ b/tests/path_test.py @@ -194,7 +194,7 @@ def test_paths_security_combined(httpx2_mock, with_paths_security): with pytest.raises(ValueError, match="No security requirement satisfied"): r = api._.api_v1_auth_login_combined(data={}, parameters={}) - api.authenticate(**{"user": "theuser", "token": "thetoken"}) + api.authenticate(user="theuser", token="thetoken") r = api._.api_v1_auth_login_combined(data={}, parameters={}) api.authenticate(None) diff --git a/tests/pathv20_test.py b/tests/pathv20_test.py index 15c7cdc5..4306f1b1 100644 --- a/tests/pathv20_test.py +++ b/tests/pathv20_test.py @@ -66,7 +66,7 @@ def test_paths_security_v20_combined_securityparameters(httpx2_mock, with_paths_ with pytest.raises(ValueError, match="No security requirement satisfied"): api._.combinedSecurity(data={}, parameters={}) - api.authenticate(**{"user": "u", "token": "t"}) + api.authenticate(user="u", token="t") api._.combinedSecurity(data={}, parameters={}) api.authenticate(None) @@ -85,7 +85,7 @@ def test_paths_security_v20_alternate_securityparameters(httpx2_mock, with_paths ): api._.alternateSecurity(data={}, parameters={}) - api.authenticate(**{"user": "u", "token": "t"}) + api.authenticate(user="u", token="t") api._.alternateSecurity(data={}, parameters={}) api.authenticate(None) From 287356ad8b58d87022af6015b9eeb660961993a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:07:31 +0200 Subject: [PATCH 16/58] ruff - PIE808 --- pyproject.toml | 2 +- tests/schema_test.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2c98b16e..e37cb25e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,7 +101,7 @@ ignore = [ # "PERF102", # "PIE790", # "PIE804", - "PIE808", +# "PIE808", "PLC0208", "PLR1708", "PLR1711", diff --git a/tests/schema_test.py b/tests/schema_test.py index f5b117b5..d73c4cd4 100644 --- a/tests/schema_test.py +++ b/tests/schema_test.py @@ -458,7 +458,7 @@ def test_schema_constraints(with_schema_constraints): A("i" * i) B = (_B := api.components.schemas["B"]).get_type() - for i in range(0, 12): + for i in range(12): if _B.exclusiveMaximum > i > _B.exclusiveMinimum: B(i) else: @@ -466,7 +466,7 @@ def test_schema_constraints(with_schema_constraints): B(i) C = (_C := api.components.schemas["C"]).get_type() - for i in range(0, 12): + for i in range(12): if i % _C.multipleOf != 0: with pytest.raises(ValidationError): C(i) From aadc1eb030d263ed5d1d7e733169349922a2fb8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:08:02 +0200 Subject: [PATCH 17/58] ruff - PLC0208 --- pyproject.toml | 2 +- tests/path_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e37cb25e..bc6b0502 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,7 +102,7 @@ ignore = [ # "PIE790", # "PIE804", # "PIE808", - "PLC0208", +# "PLC0208", "PLR1708", "PLR1711", "PLR2044", diff --git a/tests/path_test.py b/tests/path_test.py index 78758565..0e6eb52c 100644 --- a/tests/path_test.py +++ b/tests/path_test.py @@ -472,7 +472,7 @@ def test_paths_tags(httpx2_mock, with_paths_tags): OpenAPI(URLBASE, with_paths_tags, session_factory=httpx2.Client, use_operation_tags=False) spec = copy.deepcopy(with_paths_tags) - for k in {"/user/", "/item/"}: + for k in ("/user/", "/item/"): spec["paths"][k]["get"]["operationId"] = f"list{k[1:-1]}" api = OpenAPI(URLBASE, spec, session_factory=httpx2.Client, use_operation_tags=False) From a258b40e26b9e2123f64dbb37a7bb504455eb678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:09:29 +0200 Subject: [PATCH 18/58] ruff - PLR1708 --- pyproject.toml | 2 +- src/aiopenapi3/model.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bc6b0502..1b12dd5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,7 +103,7 @@ ignore = [ # "PIE804", # "PIE808", # "PLC0208", - "PLR1708", +# "PLR1708", "PLR1711", "PLR2044", "PLW0120", diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index bed06cfb..79b717c6 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -668,7 +668,7 @@ def types(schema: "SchemaType") -> typing.Generator[str, None, None]: tmp = oneOf & allOf & anyOf typesfilter |= tmp else: - raise StopIteration + return if typesfilter: values = values & typesfilter From ba3c0aac6f08b87542a521660816ab0110691742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:10:10 +0200 Subject: [PATCH 19/58] ruff - PLR1711 --- pyproject.toml | 2 +- src/aiopenapi3/model.py | 1 - src/aiopenapi3/openapi.py | 1 - tests/extra_test.py | 1 - tests/path_test.py | 5 ----- tests/pathv20_test.py | 4 ---- tests/schema_test.py | 3 --- 7 files changed, 1 insertion(+), 16 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1b12dd5a..cf06ea03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -104,7 +104,7 @@ ignore = [ # "PIE808", # "PLC0208", # "PLR1708", - "PLR1711", +# "PLR1711", "PLR2044", "PLW0120", "PLW0602", diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index 79b717c6..26c5eb23 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -229,7 +229,6 @@ def _createAnnotations( pass else: raise ValueError() - return def model(self) -> type[BaseModel] | type[None]: if self.root: diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index 9c6d7325..9bc08a55 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -358,7 +358,6 @@ def _init_references(self): e.document = names[i] raise processed = set(values.keys()) - return # for i in self._documents.values(): # i._resolve_references(self) diff --git a/tests/extra_test.py b/tests/extra_test.py index 753f45ef..9ad24998 100644 --- a/tests/extra_test.py +++ b/tests/extra_test.py @@ -100,7 +100,6 @@ def test_reduced_small(): loader=FileSystemLoader(Path("tests/").absolute()), plugins=[PetStoreReduced()], ) - return @pytest.mark.parametrize("compressor", [Reduce, Cull]) diff --git a/tests/path_test.py b/tests/path_test.py index 0e6eb52c..02d963c3 100644 --- a/tests/path_test.py +++ b/tests/path_test.py @@ -393,8 +393,6 @@ def test_paths_parameter_format(httpx2_mock, with_paths_parameter_format): assert u.parts[9] == "100" assert u.parts[10] == "3.3245460039402305e+23" - return - @pytest.mark.httpx2_mock(can_send_already_matched_responses=True) def test_paths_parameter_format_complex(httpx2_mock, with_paths_parameter_format_complex): @@ -428,7 +426,6 @@ def test_paths_response_header(httpx2_mock, with_paths_response_header): h, b = api._.types(return_headers=True) assert h["X-object"].A == 1 assert h["X-object"].B == "2" - return @pytest.mark.httpx2_mock(can_send_already_matched_responses=True) @@ -597,8 +594,6 @@ def test_paths_servers(httpx2_mock, with_paths_servers): request = httpx2_mock.get_requests()[-1] assert request.url.host == "operation" - return - @pytest.mark.httpx2_mock(can_send_already_matched_responses=True) def test_paths_server_variables(httpx2_mock, with_paths_server_variables): diff --git a/tests/pathv20_test.py b/tests/pathv20_test.py index 4306f1b1..d2cb9048 100644 --- a/tests/pathv20_test.py +++ b/tests/pathv20_test.py @@ -148,8 +148,6 @@ def test_paths_response_header_v20(httpx2_mock, with_paths_response_header_v20): # h, b = api._.get(return_headers=True) # request = httpx2_mock.get_requests()[-1] - return - @pytest.mark.httpx2_mock(can_send_already_matched_responses=True) def test_paths_parameter_format_v20(httpx2_mock, with_paths_parameter_format_v20): @@ -209,8 +207,6 @@ def on_file(file): assert (v := urllib.parse.parse_qs(request.content.decode())) is not None and v["A"] == ["a"] and v["B"] == ["5"] assert result == "ok" - return - def test_paths_response_file(httpx2_mock, with_paths_parameter_format_v20): httpx2_mock.add_response(headers={"Content-Type": "application/octet-stream"}, content=b"\x00") diff --git a/tests/schema_test.py b/tests/schema_test.py index d73c4cd4..719e931d 100644 --- a/tests/schema_test.py +++ b/tests/schema_test.py @@ -431,7 +431,6 @@ def test_schema_discriminated_union_deep(with_schema_discriminated_union_deep): pet = Pet(dog) d = Dog.model_construct() - return None def test_schema_create_update_read(with_schema_create_update_read): @@ -527,8 +526,6 @@ def test_schema_pathitems(httpx2_mock, with_schema_pathitems): r = req() r = api._.b() - return - def test_schema_baseurl_v20(with_schema_baseurl_v20): api = OpenAPI("/", with_schema_baseurl_v20, session_factory=httpx2.Client) From 09988e382f57ae1a2840dd53cd1070f0932d6ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:14:48 +0200 Subject: [PATCH 20/58] ruff - PLR2044 --- pyproject.toml | 2 +- src/aiopenapi3/_types.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cf06ea03..98a44ba0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -105,7 +105,7 @@ ignore = [ # "PLC0208", # "PLR1708", # "PLR1711", - "PLR2044", +# "PLR2044", "PLW0120", "PLW0602", "PT031", diff --git a/src/aiopenapi3/_types.py b/src/aiopenapi3/_types.py index 8730f263..e7cc6fd9 100644 --- a/src/aiopenapi3/_types.py +++ b/src/aiopenapi3/_types.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Literal, Optional, TypeAlias, Union import yaml -from httpx._types import AuthTypes, FileTypes, RequestContent, RequestFiles # noqa +from httpx2._types import AuthTypes, FileTypes, RequestContent, RequestFiles # noqa from pydantic import BaseModel from . import v20, v30, v31, v32 @@ -70,13 +70,12 @@ "RequestParameters", "ReferenceType", "PrimitiveTypes", - # "YAMLLoaderType", # httpx forwards "RequestContent", "RequestFiles", "AuthTypes", - # + # end httpx "JSON", "RequestFilesParameter", "RequestFileParameter", From 4f32cc47d742682d86ce98ce7a05476991f2ae4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:15:27 +0200 Subject: [PATCH 21/58] ruff - PLW0120 --- pyproject.toml | 2 +- tests/api/v1/main.py | 20 +++++++++----------- tests/api/v2/main.py | 18 ++++++++---------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 98a44ba0..711aaffb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,7 +106,7 @@ ignore = [ # "PLR1708", # "PLR1711", # "PLR2044", - "PLW0120", +# "PLW0120", "PLW0602", "PT031", "PYI055", diff --git a/tests/api/v1/main.py b/tests/api/v1/main.py index 31013e6c..7ae03dea 100644 --- a/tests/api/v1/main.py +++ b/tests/api/v1/main.py @@ -65,11 +65,10 @@ def getPet(pet_id: int = Path(..., alias="petId")) -> Pets: for v in ZOO.values(): if pet_id == v.id: return v - else: - return JSONResponse( - status_code=starlette.status.HTTP_404_NOT_FOUND, - content=Error(code=errno.ENOENT, message=f"{pet_id} not found").model_dump(), - ) + return JSONResponse( + status_code=starlette.status.HTTP_404_NOT_FOUND, + content=Error(code=errno.ENOENT, message=f"{pet_id} not found").model_dump(), + ) @router.delete("/pets/{petId}", operation_id="deletePet", responses={204: {"model": None}, 404: {"model": Error}}) @@ -79,9 +78,8 @@ def deletePet(response: Response, pet_id: int = Path(..., alias="petId")) -> Pet del ZOO[k] response.status_code = starlette.status.HTTP_204_NO_CONTENT return response - else: - return JSONResponse( - status_code=starlette.status.HTTP_404_NOT_FOUND, - content=Error(code=errno.ENOENT, message=f"{pet_id} not found").model_dump(), - media_type="application/json; utf-8", - ) + return JSONResponse( + status_code=starlette.status.HTTP_404_NOT_FOUND, + content=Error(code=errno.ENOENT, message=f"{pet_id} not found").model_dump(), + media_type="application/json; utf-8", + ) diff --git a/tests/api/v2/main.py b/tests/api/v2/main.py index dc9b46df..d3d6f03f 100644 --- a/tests/api/v2/main.py +++ b/tests/api/v2/main.py @@ -56,11 +56,10 @@ def getPet(pet_id: str = Path(..., alias="petId")) -> schema.Pets: for pet in ZOO.values(): if pet_id == pet.identifier: return pet - else: - return JSONResponse( - status_code=starlette.status.HTTP_404_NOT_FOUND, - content=schema.Error(code=errno.ENOENT, message=f"{pet_id} not found").model_dump(), - ) + return JSONResponse( + status_code=starlette.status.HTTP_404_NOT_FOUND, + content=schema.Error(code=errno.ENOENT, message=f"{pet_id} not found").model_dump(), + ) @router.delete( @@ -76,11 +75,10 @@ def deletePet( del ZOO[k] response.status_code = starlette.status.HTTP_204_NO_CONTENT return response - else: - return JSONResponse( - status_code=starlette.status.HTTP_404_NOT_FOUND, - content=schema.Error(code=errno.ENOENT, message=f"{pet_id} not found").model_dump(), - ) + return JSONResponse( + status_code=starlette.status.HTTP_404_NOT_FOUND, + content=schema.Error(code=errno.ENOENT, message=f"{pet_id} not found").model_dump(), + ) @router.patch("/pets", operation_id="patchPets", responses={200: {"model": schema.Pets}}) From 863769052fd0e3ef5707984bf596db5ccc8ba49a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:17:07 +0200 Subject: [PATCH 22/58] ruff - PLW0602 --- pyproject.toml | 2 +- src/aiopenapi3/model.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 711aaffb..6549f0f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,7 +107,7 @@ ignore = [ # "PLR1711", # "PLR2044", # "PLW0120", - "PLW0602", +# "PLW0602", "PT031", "PYI055", "RET501", diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index 26c5eb23..2dcc1000 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -39,7 +39,6 @@ def generate_type_format_to_class(): initialize type_format_to_class :return: None """ - global type_format_to_class for cls, spec in field_class_to_schema: if "type" not in spec: # FIXME Decimal is anyOf now From 9868c007346e097184641ab6dbb4b912523ca893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:18:28 +0200 Subject: [PATCH 23/58] ruff - PT031 --- pyproject.toml | 2 +- tests/schema_test.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6549f0f3..73bd10cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -108,7 +108,7 @@ ignore = [ # "PLR2044", # "PLW0120", # "PLW0602", - "PT031", +# "PT031", "PYI055", "RET501", "RUF010", diff --git a/tests/schema_test.py b/tests/schema_test.py index 719e931d..200970db 100644 --- a/tests/schema_test.py +++ b/tests/schema_test.py @@ -389,19 +389,19 @@ def test_schema_discriminated_union_warnings(with_schema_discriminated_union_war s = copy.deepcopy(with_schema_discriminated_union_warning) api = OpenAPI("/", s) + s = copy.deepcopy(with_schema_discriminated_union_warning) + s["components"]["schemas"]["B"]["properties"]["object_type"]["enum"] = ["f"] with pytest.warns( DiscriminatorWarning, match=r"Discriminated Union member key property enum mismatches property mapping \S+ \!= \S+", ): - s = copy.deepcopy(with_schema_discriminated_union_warning) - s["components"]["schemas"]["B"]["properties"]["object_type"]["enum"] = ["f"] api = OpenAPI("/", s) + s = copy.deepcopy(with_schema_discriminated_union_warning) + del s["components"]["schemas"]["B"]["properties"]["object_type"]["enum"] with pytest.warns( DiscriminatorWarning, match=r"Discriminated Union member \S+ without const/enum key property \S+" ): - s = copy.deepcopy(with_schema_discriminated_union_warning) - del s["components"]["schemas"]["B"]["properties"]["object_type"]["enum"] api = OpenAPI("/", s) if (openapi_version.major, openapi_version.minor, openapi_version.patch) >= (3, 1, 0): From 7e9291e7db1b106aa6ecee2251c786ec7bd29356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:21:16 +0200 Subject: [PATCH 24/58] ruff - PYI055 --- pyproject.toml | 2 +- src/aiopenapi3/model.py | 2 +- src/aiopenapi3/openapi.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 73bd10cd..f9886ae4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -109,7 +109,7 @@ ignore = [ # "PLW0120", # "PLW0602", # "PT031", - "PYI055", +# "PYI055", "RET501", "RUF010", "RUF012", diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index 2dcc1000..47938863 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -246,7 +246,7 @@ def model(self) -> type[BaseModel] | type[None]: @classmethod def collapse(cls, schema: "SchemaType", items: list["_ClassInfo"]) -> type[BaseModel]: - r: list[type[BaseModel] | type[None]] + r: list[type[BaseModel | None]] r = [i.model() for i in items] type_name = schema._get_identity("L8") diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index 9bc08a55..e03caf53 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -605,7 +605,7 @@ def _init_schema_types(self, only_required: bool) -> None: byid: dict[int, "SchemaType"] = {id(i): i for i in byname.values()} data: set[int] = set(byid.keys()) todo: set[int] = self._iterate_schemas(byid, data, set()) - types: dict[str, ForwardRef | type[BaseModel] | type[int] | type[str] | type[float] | type[bool]] = {} + types: dict[str, type[BaseModel | int | str | float | bool] | ForwardRef] = {} """ Due to Plugins (e.g. Cull/Reduce) byname may be incomplete From ff26ecbe70c05720ec81e015bbc49d5ee61992ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:22:53 +0200 Subject: [PATCH 25/58] ruff - RUF010 --- pyproject.toml | 2 +- src/aiopenapi3/v30/parameter.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f9886ae4..1061ee47 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -111,7 +111,7 @@ ignore = [ # "PT031", # "PYI055", "RET501", - "RUF010", +# "RUF010", "RUF012", "RUF013", "RUF017", diff --git a/src/aiopenapi3/v30/parameter.py b/src/aiopenapi3/v30/parameter.py index 1716187d..19c5095f 100644 --- a/src/aiopenapi3/v30/parameter.py +++ b/src/aiopenapi3/v30/parameter.py @@ -121,7 +121,7 @@ def _encode__label(self, name: str, type_: str, value, schema: "v3xSchemaType", # .blue value = f".{value}" elif type_ in frozenset(["number", "integer"]): - value = f".{str(value)}" + value = f".{value!s}" elif type_ == "boolean": value = f".{json.dumps(value)}" elif type_ == "null": From 7824abf2d26a66be5e37345c19a63dd3293f42bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:40:30 +0200 Subject: [PATCH 26/58] ruff - RUF012 --- docs/source/_ext/aioai3.py | 4 +++- pyproject.toml | 2 +- src/aiopenapi3/loader.py | 3 ++- src/aiopenapi3/model.py | 4 ++-- src/aiopenapi3/plugin.py | 4 ++-- src/aiopenapi3/v20/parameter.py | 4 ++-- tests/forms_test.py | 3 ++- tests/parse_data_test.py | 3 ++- tests/petstore_test.py | 6 ++++-- tests/petstorev3_test.py | 8 ++++++-- 10 files changed, 26 insertions(+), 15 deletions(-) diff --git a/docs/source/_ext/aioai3.py b/docs/source/_ext/aioai3.py index 4cc5d6d1..7855a284 100644 --- a/docs/source/_ext/aioai3.py +++ b/docs/source/_ext/aioai3.py @@ -1,3 +1,5 @@ +from typing import ClassVar + from docutils import nodes from sphinx.domains import Domain from sphinx.roles import XRefRole @@ -14,7 +16,7 @@ class aiopenapi3Domain(Domain): name = "aioai3" label = "aiopenapi3 code linker" - roles = { + roles: ClassVar = { "ref": XRefRole(), } diff --git a/pyproject.toml b/pyproject.toml index 1061ee47..6d233b74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -112,7 +112,7 @@ ignore = [ # "PYI055", "RET501", # "RUF010", - "RUF012", +# "RUF012", "RUF013", "RUF017", "RUF022", diff --git a/src/aiopenapi3/loader.py b/src/aiopenapi3/loader.py index e2c4de93..e1044e11 100644 --- a/src/aiopenapi3/loader.py +++ b/src/aiopenapi3/loader.py @@ -3,6 +3,7 @@ import logging import re import typing +from typing import ClassVar import httpx2 import yaml @@ -41,7 +42,7 @@ class YAML12Loader(yaml.SafeLoader): add the YAML 1.2 core tags """ - _core_resolvers = [ + _core_resolvers: ClassVar = [ ["bool", re.compile(r"""^(?:|true|True|TRUE|false|False|FALSE)$""", re.VERBOSE), list("tTfF")], [ "int", diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index 47938863..bbff1474 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -5,7 +5,7 @@ import re import sys import typing -from typing import Annotated, Any, Literal, Optional, TypeGuard, TypeVar, Union, cast +from typing import Annotated, Any, ClassVar, Literal, Optional, TypeGuard, TypeVar, Union, cast import pydantic from pydantic import BaseModel, ConfigDict, Field, RootModel @@ -274,7 +274,7 @@ def _follow(r: "ReferenceType", t: type[_T]) -> TypeGuard[_T]: class Model: # (BaseModel): - ALIASES: dict[str, str] = {} + ALIASES: ClassVar[dict[str, str]] = {} @classmethod def from_schema( diff --git a/src/aiopenapi3/plugin.py b/src/aiopenapi3/plugin.py index 9c30f062..4ffa7ca1 100644 --- a/src/aiopenapi3/plugin.py +++ b/src/aiopenapi3/plugin.py @@ -1,6 +1,6 @@ import abc import dataclasses -from typing import TYPE_CHECKING, Any, Optional, TypeGuard +from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypeGuard import yarl from pydantic import BaseModel @@ -190,7 +190,7 @@ def __call__(self, **kwargs): class Plugins: - _domains: dict[str, type[Plugin]] = {"init": Init, "document": Document, "message": Message} + _domains: ClassVar[dict[str, type[Plugin]]] = {"init": Init, "document": Document, "message": Message} def __init__(self, plugins: list[Plugin]): for p in plugins: diff --git a/src/aiopenapi3/v20/parameter.py b/src/aiopenapi3/v20/parameter.py index 5c3f21da..de697caf 100644 --- a/src/aiopenapi3/v20/parameter.py +++ b/src/aiopenapi3/v20/parameter.py @@ -1,6 +1,6 @@ import enum import io -from typing import Any, Optional +from typing import Any, ClassVar, Optional from pydantic import Field @@ -11,7 +11,7 @@ class _ParameterCodec: - SEPERATOR_VALUES = {"csv": ",", "ssv": " ", "tsv": "\t", "pipes": "|"} + SEPERATOR_VALUES: ClassVar = {"csv": ",", "ssv": " ", "tsv": "\t", "pipes": "|"} """ Describing Parameters diff --git a/tests/forms_test.py b/tests/forms_test.py index 5178404f..c4b07f7c 100644 --- a/tests/forms_test.py +++ b/tests/forms_test.py @@ -2,6 +2,7 @@ import copy import datetime import decimal +from typing import ClassVar import pytest import pytest_asyncio @@ -78,7 +79,7 @@ class Meta: class SelectForm(FlaskForm): - CHOICES = [("cpp", "C++"), ("py", "Python"), ("txt", "Plain Text"), ("rb", "Ruby"), ("c", "C")] + CHOICES: ClassVar = [("cpp", "C++"), ("py", "Python"), ("txt", "Plain Text"), ("rb", "Ruby"), ("c", "C")] class Meta: csrf = False diff --git a/tests/parse_data_test.py b/tests/parse_data_test.py index a8afc8b2..a7945012 100644 --- a/tests/parse_data_test.py +++ b/tests/parse_data_test.py @@ -1,4 +1,5 @@ import pathlib +from typing import ClassVar import pytest import yarl @@ -20,7 +21,7 @@ def pytest_generate_tests(metafunc): @pytest.mark.skip class TestParseData: # a map specifying multiple argument sets for a test method - params = { + params: ClassVar = { "test_data": [("dir", "file"), "tests/data", lambda x: x.is_file() and x.suffix in (".json", ".yaml")], "test_data_open5gs": [ ("dir", "file"), diff --git a/tests/petstore_test.py b/tests/petstore_test.py index 20d9fabd..81e5f12d 100644 --- a/tests/petstore_test.py +++ b/tests/petstore_test.py @@ -1,3 +1,5 @@ +from typing import ClassVar + import httpx2 import pytest @@ -22,8 +24,8 @@ def session_factory(*args, **kwargs) -> httpx2.Client: class OnDocument(Document): - ApiResponse = {"description": "successful operation", "schema": {"$ref": "#/definitions/ApiResponse"}} - PetResponse = {"description": "successful operation", "schema": {"$ref": "#/definitions/Pet"}} + ApiResponse: ClassVar = {"description": "successful operation", "schema": {"$ref": "#/definitions/ApiResponse"}} + PetResponse: ClassVar = {"description": "successful operation", "schema": {"$ref": "#/definitions/Pet"}} def parsed(self, ctx): for path in ctx.document["paths"].values(): diff --git a/tests/petstorev3_test.py b/tests/petstorev3_test.py index 1f21fb89..548c4a62 100644 --- a/tests/petstorev3_test.py +++ b/tests/petstorev3_test.py @@ -1,4 +1,5 @@ import random +from typing import ClassVar import httpx2 import pytest @@ -29,11 +30,14 @@ def session_factory(*args, **kwargs) -> httpx2.Client: class OnDocument(Document): - ApiResponse = { + ApiResponse: ClassVar = { "description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}, } - PetResponse = {"description": "", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Pet"}}}} + PetResponse: ClassVar = { + "description": "", + "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Pet"}}}, + } def parsed(self, ctx): for path in ctx.document["paths"].values(): From f2d6bcbce7abdbe2b5e6d91e8d1a677a5b1082c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:41:50 +0200 Subject: [PATCH 27/58] ruff - RUF013 --- pyproject.toml | 2 +- src/aiopenapi3/extra/cookies.py | 2 +- src/aiopenapi3/model.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6d233b74..44aa7438 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,7 +113,7 @@ ignore = [ "RET501", # "RUF010", # "RUF012", - "RUF013", +# "RUF013", "RUF017", "RUF022", "RUF046", diff --git a/src/aiopenapi3/extra/cookies.py b/src/aiopenapi3/extra/cookies.py index 7de978fd..6818a520 100644 --- a/src/aiopenapi3/extra/cookies.py +++ b/src/aiopenapi3/extra/cookies.py @@ -41,7 +41,7 @@ def info(self) -> email.message.Message: return info def __init__( - self, cookiejar: http.cookiejar.CookieJar = None, policy: Literal["jar", "securitySchemes"] = "jar" + self, cookiejar: http.cookiejar.CookieJar | None = None, policy: Literal["jar", "securitySchemes"] = "jar" ) -> None: """ diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index bbff1474..f9d3a290 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -314,7 +314,7 @@ def createClassInfo( schemanames: list[str], discriminators: list["DiscriminatorType"], extra: list["SchemaType"] | None, - args: dict[str, Any] = None, + args: dict[str, Any] | None = None, ) -> _ClassInfo: from . import v20, v30, v31 From 7ead1998f9cfbf9fef894ee8d9cc8bdec83bec79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:42:49 +0200 Subject: [PATCH 28/58] ruff - RUF017 --- pyproject.toml | 2 +- src/aiopenapi3/loader.py | 8 +++++++- src/aiopenapi3/model.py | 9 ++++++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 44aa7438..d413fa6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,7 +114,7 @@ ignore = [ # "RUF010", # "RUF012", # "RUF013", - "RUF017", +# "RUF017", "RUF022", "RUF046", "RUF059", diff --git a/src/aiopenapi3/loader.py b/src/aiopenapi3/loader.py index e1044e11..4f848328 100644 --- a/src/aiopenapi3/loader.py +++ b/src/aiopenapi3/loader.py @@ -20,6 +20,8 @@ assert json is not None +import functools +import operator from pathlib import Path # noqa:E402 from .plugin import Plugins @@ -75,7 +77,11 @@ class YAML12Loader(yaml.SafeLoader): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - tags = set(sum([[y[0] for y in x] for x in YAML12Loader.yaml_implicit_resolvers.values()], [])) + tags = set( + functools.reduce( + operator.iadd, [[y[0] for y in x] for x in YAML12Loader.yaml_implicit_resolvers.values()], [] + ) + ) for tag in tags: YAML12Loader.remove_implicit_resolver(tag) for tag, regex, initial in YAML12Loader._core_resolvers: diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index f9d3a290..87865088 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -72,6 +72,9 @@ def class_from_schema(s, _type): return b +import functools +import operator + import pydantic_core @@ -648,18 +651,18 @@ def types(schema: "SchemaType") -> typing.Generator[str, None, None]: # allOf - intersection of types allOfs: list["SchemaType"] - if allOfs := sum([getattr(schema, "allOf", [])], []): + if allOfs := functools.reduce(operator.iadd, [getattr(schema, "allOf", [])], []): for x in allOfs: allOf &= set(Model.types(x)) # anyOf - union of types anyOfs: list["SchemaType"] - if anyOfs := sum([getattr(schema, "anyOf", [])], []): + if anyOfs := functools.reduce(operator.iadd, [getattr(schema, "anyOf", [])], []): anyOf = set.union(*[set(Model.types(x)) for x in anyOfs]) if anyOfs else set() # oneOf - union of types oneOfs: list["SchemaType"] - if oneOfs := sum([getattr(schema, "oneOf", [])], []): + if oneOfs := functools.reduce(operator.iadd, [getattr(schema, "oneOf", [])], []): oneOf = set.union(*[set(Model.types(x)) for x in oneOfs]) if oneOfs else set() if allOfs or anyOfs or oneOfs: From fec50006cae3221918c16d330fa4c481960e6fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:45:57 +0200 Subject: [PATCH 29/58] ruff - RUF022 --- pyproject.toml | 2 +- src/aiopenapi3/__init__.py | 14 +++++----- src/aiopenapi3/_types.py | 44 ++++++++++++++++---------------- src/aiopenapi3/extra/__init__.py | 2 +- src/aiopenapi3/v20/__init__.py | 18 ++++++------- src/aiopenapi3/v30/__init__.py | 36 +++++++++++++------------- 6 files changed, 58 insertions(+), 58 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d413fa6d..473b0eb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,7 +115,7 @@ ignore = [ # "RUF012", # "RUF013", # "RUF017", - "RUF022", +# "RUF022", "RUF046", "RUF059", "RUF100", diff --git a/src/aiopenapi3/__init__.py b/src/aiopenapi3/__init__.py index c341345b..88dba863 100644 --- a/src/aiopenapi3/__init__.py +++ b/src/aiopenapi3/__init__.py @@ -14,16 +14,16 @@ from .version import __version__ __all__ = [ - "__version__", - "OpenAPI", + "ContentTypeError", "FileSystemLoader", - "SpecError", - "ReferenceResolutionError", "HTTPError", - "ResponseError", "HTTPStatusError", - "ContentTypeError", + "OpenAPI", + "ReferenceResolutionError", + "RequestError", "ResponseDecodingError", + "ResponseError", "ResponseSchemaError", - "RequestError", + "SpecError", + "__version__", ] diff --git a/src/aiopenapi3/_types.py b/src/aiopenapi3/_types.py index e7cc6fd9..3b561847 100644 --- a/src/aiopenapi3/_types.py +++ b/src/aiopenapi3/_types.py @@ -51,34 +51,34 @@ HTTPMethodMatchType = Union[re.Pattern, HTTPMethodType] __all__: list[str] = [ - "RootType", - "ServerType", - "SchemaType", - "v3xSchemaType", + # end httpx + "JSON", + "AuthTypes", "DiscriminatorType", - "PathItemType", - "OperationType", - "ParameterType", - "HeaderType", - "RequestType", "ExpectedType", + "HTTPMethodMatchType", + "HTTPMethodType", + "HeaderType", "MediaTypeType", - "ResponseHeadersType", - "ResponseDataType", - "TagType", - "RequestData", - "RequestParameters", - "ReferenceType", + "OperationType", + "ParameterType", + "PathItemType", "PrimitiveTypes", - "YAMLLoaderType", + "ReferenceType", # httpx forwards "RequestContent", + "RequestData", + "RequestFileParameter", "RequestFiles", - "AuthTypes", - # end httpx - "JSON", "RequestFilesParameter", - "RequestFileParameter", - "HTTPMethodType", - "HTTPMethodMatchType", + "RequestParameters", + "RequestType", + "ResponseDataType", + "ResponseHeadersType", + "RootType", + "SchemaType", + "ServerType", + "TagType", + "YAMLLoaderType", + "v3xSchemaType", ] diff --git a/src/aiopenapi3/extra/__init__.py b/src/aiopenapi3/extra/__init__.py index 7d576874..9dec5359 100644 --- a/src/aiopenapi3/extra/__init__.py +++ b/src/aiopenapi3/extra/__init__.py @@ -1,4 +1,4 @@ from .cookies import Cookies from .reduce import Cull, Reduce -__all__ = ["Cull", "Reduce", "Cookies"] +__all__ = ["Cookies", "Cull", "Reduce"] diff --git a/src/aiopenapi3/v20/__init__.py b/src/aiopenapi3/v20/__init__.py index 93359a0d..09699ee1 100644 --- a/src/aiopenapi3/v20/__init__.py +++ b/src/aiopenapi3/v20/__init__.py @@ -40,23 +40,23 @@ def __init(): __init() __all__ = [ - "Request", + "XML", "AsyncRequest", - "ExternalDocumentation", - "Reference", "Contact", - "License", - "Info", - "Parameter", + "ExternalDocumentation", "Header", - "Response", + "Info", + "License", "Operation", + "Parameter", "PathItem", "Paths", + "Reference", + "Request", + "Response", "Root", "Schema", - "SecurityScheme", "SecurityRequirement", + "SecurityScheme", "Tag", - "XML", ] diff --git a/src/aiopenapi3/v30/__init__.py b/src/aiopenapi3/v30/__init__.py index 87fde3a6..2666ca63 100644 --- a/src/aiopenapi3/v30/__init__.py +++ b/src/aiopenapi3/v30/__init__.py @@ -57,36 +57,36 @@ def __init(): __init() __all__ = [ - "Request", + "XML", "AsyncRequest", + "Callback", "Components", - "Example", - "ExternalDocumentation", - "Reference", "Contact", - "License", - "Info", + "Discriminator", "Encoding", - "MediaType", - "Parameter", + "Example", + "ExternalDocumentation", "Header", - "RequestBody", + "Info", + "License", "Link", - "Response", + "MediaType", + "OAuthFlow", + "OAuthFlows", "Operation", + "Parameter", "PathItem", "Paths", - "Callback", - "RuntimeExpression", + "Reference", + "Request", + "RequestBody", + "Response", "Root", - "Discriminator", + "RuntimeExpression", "Schema", - "OAuthFlow", - "OAuthFlows", - "SecurityScheme", "SecurityRequirement", - "ServerVariable", + "SecurityScheme", "Server", + "ServerVariable", "Tag", - "XML", ] From 7bda35c0f3edb1c558933ca7d6eb7b07dae64d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:47:05 +0200 Subject: [PATCH 30/58] ruff - RUF046 --- pyproject.toml | 2 +- tests/forms_test.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 473b0eb6..76d93bcb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -116,7 +116,7 @@ ignore = [ # "RUF013", # "RUF017", # "RUF022", - "RUF046", +# "RUF046", "RUF059", "RUF100", "S110", diff --git a/tests/forms_test.py b/tests/forms_test.py index c4b07f7c..0cef21e8 100644 --- a/tests/forms_test.py +++ b/tests/forms_test.py @@ -349,8 +349,8 @@ async def test_Numbers(server, client, form_type): decimal=decimal.Decimal(1 / 3), decimalrange=decimal.Decimal(2 / 3), float=float(1 / 3), - integer=int(9), - integerrange=int(9), + integer=(9), + integerrange=(9), ) r = await client._.numbers(data=data) From 9c04f5872f733eed72716c03542ad544621e9fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:51:54 +0200 Subject: [PATCH 31/58] ruff - RUF059 --- pyproject.toml | 2 +- src/aiopenapi3/cli.py | 2 +- src/aiopenapi3/request.py | 4 ++-- src/aiopenapi3/v30/glue.py | 2 +- tests/apiv1_test.py | 4 ++-- tests/path_test.py | 6 +++--- tests/pathv20_test.py | 2 +- tests/stream_test.py | 6 +++--- tests/tls_test.py | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 76d93bcb..f002dd58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,7 +117,7 @@ ignore = [ # "RUF017", # "RUF022", # "RUF046", - "RUF059", +# "RUF059", "RUF100", "S110", "SIM101", diff --git a/src/aiopenapi3/cli.py b/src/aiopenapi3/cli.py index b00b2459..cb0fcb53 100644 --- a/src/aiopenapi3/cli.py +++ b/src/aiopenapi3/cli.py @@ -245,7 +245,7 @@ def prepare_arg(value): req.data.get_type().model_validate(data) try: - headers, ret, response = req.request(parameters=parameters, data=data) + _headers, ret, response = req.request(parameters=parameters, data=data) except aiopenapi3.errors.ResponseSchemaError as e: print(e.response.json()) print(e.response.headers) diff --git a/src/aiopenapi3/request.py b/src/aiopenapi3/request.py index 7a36448e..3e47a6db 100644 --- a/src/aiopenapi3/request.py +++ b/src/aiopenapi3/request.py @@ -162,7 +162,7 @@ def __call__( :param kwargs: :return: body or (header, body) """ - headers, data, result = self.request(*args, context=context, **kwargs) # type: ignore[misc] + headers, data, _result = self.request(*args, context=context, **kwargs) # type: ignore[misc] if return_headers: return headers, data return data @@ -438,7 +438,7 @@ async def __anext__(self) -> pydantic.BaseModel: async def __call__( # type: ignore[override] self, *args, return_headers: bool = False, context: Any = None, **kwargs ) -> Union["JSON", tuple[dict[str, str], "JSON"]]: - headers, data, result = await self.request(*args, context=context, **kwargs) # type: ignore [misc] + headers, data, _result = await self.request(*args, context=context, **kwargs) # type: ignore [misc] if return_headers: return headers, data return data diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index ab983f57..ca055148 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -535,7 +535,7 @@ def _process__content_type( https://datatracker.ietf.org/doc/html/rfc7231#appendix-D media-range = ( "*/*" / ( type "/*" ) / ( type "/" subtype ) ) *( OWS ";" OWS parameter ) """ - content_type, _, encoding = content_type.partition(";") + content_type, _, _encoding = content_type.partition(";") expected_media: Optional["v3xMediaTypeType"] = ( expected_response.content.get(content_type, None) or expected_response.content.get(content_type.partition("/")[0] + "/*", None) diff --git a/tests/apiv1_test.py b/tests/apiv1_test.py index 8c5f9b7f..5e8d64f9 100644 --- a/tests/apiv1_test.py +++ b/tests/apiv1_test.py @@ -60,14 +60,14 @@ async def test_createPet(server, client): @pytest.mark.asyncio(loop_scope="session") async def test_listPet(server, client): - h, r = await asyncio.to_thread(client._.createPet, **randomPet(uuid.uuid4())) + _h, _r = await asyncio.to_thread(client._.createPet, **randomPet(uuid.uuid4())) l = await asyncio.to_thread(client._.listPet) assert len(l) > 0 @pytest.mark.asyncio(loop_scope="session") async def test_getPet(server, client): - h, pet = await asyncio.to_thread(client._.createPet, **randomPet(uuid.uuid4())) + _h, pet = await asyncio.to_thread(client._.createPet, **randomPet(uuid.uuid4())) r = await asyncio.to_thread(client._.getPet, parameters={"petId": pet.id}) # FastAPI 0.101 Serialization changes # assert type(r).model_json_schema() == type(pet).model_json_schema() diff --git a/tests/path_test.py b/tests/path_test.py index 02d963c3..46c766dd 100644 --- a/tests/path_test.py +++ b/tests/path_test.py @@ -410,7 +410,7 @@ def test_paths_response_header(httpx2_mock, with_paths_response_header): ) api = OpenAPI(URLBASE, with_paths_response_header, session_factory=httpx2.Client) - h, b = api._.get(return_headers=True) + h, _b = api._.get(return_headers=True) request = httpx2_mock.get_requests()[-1] assert isinstance(h["X-required"], str) @@ -419,11 +419,11 @@ def test_paths_response_header(httpx2_mock, with_paths_response_header): with pytest.raises(HeadersMissingError) as e: httpx2_mock.add_response(headers={"Content-Type": "application/json", "X-optional": "1,2,3"}, json="get") - h, b = api._.get(return_headers=True) + _h, _b = api._.get(return_headers=True) assert list(e.value.missing.keys()) == ["x-required"] httpx2_mock.add_response(headers={"Content-Type": "application/json", "X-object": "A,1,B,2,C,3"}, json="types") - h, b = api._.types(return_headers=True) + h, _b = api._.types(return_headers=True) assert h["X-object"].A == 1 assert h["X-object"].B == "2" diff --git a/tests/pathv20_test.py b/tests/pathv20_test.py index d2cb9048..78dda3ae 100644 --- a/tests/pathv20_test.py +++ b/tests/pathv20_test.py @@ -133,7 +133,7 @@ def test_paths_response_header_v20(httpx2_mock, with_paths_response_header_v20): headers={"Content-Type": "application/json", "X-required": "1", "X-optional": "1,2,3"}, json="get" ) api = OpenAPI(URLBASE, with_paths_response_header_v20, session_factory=httpx2.Client) - h, b = api._.get(return_headers=True) + h, _b = api._.get(return_headers=True) request = httpx2_mock.get_requests()[-1] assert isinstance(h["X-required"], str) diff --git a/tests/stream_test.py b/tests/stream_test.py index b03f2e2b..d876168c 100644 --- a/tests/stream_test.py +++ b/tests/stream_test.py @@ -78,7 +78,7 @@ def request_streaming( async def test_stream_data(server, client): cl = client._max_response_content_length req = client.createRequest("file") - headers, schema_, session, result = await req.stream(parameters={"content_length": cl}) + _headers, _schema, session, result = await req.stream(parameters={"content_length": cl}) chunk = l = 0 async for i in result.aiter_bytes(): @@ -108,7 +108,7 @@ async def test_stream_array(server, client): req = client.createRequest("files") - headers, schema_, session, result = await req.stream(parameters={"number": 10, "size": 512 * 1024}) + _headers, schema_, session, result = await req.stream(parameters={"number": 10, "size": 512 * 1024}) assert schema_ == req.operation.responses["200"].content["application/json"].schema_ @@ -148,7 +148,7 @@ async def test_sync_stream(server): cl = client._max_response_content_length req = client.createRequest("file") - headers, schema_, session, result = await asyncio.to_thread(req.stream, parameters={"content_length": cl}) + _headers, _schema, session, result = await asyncio.to_thread(req.stream, parameters={"content_length": cl}) def blocking_recv(rs): r = 0 diff --git a/tests/tls_test.py b/tests/tls_test.py index 18077811..61abb189 100644 --- a/tests/tls_test.py +++ b/tests/tls_test.py @@ -89,7 +89,7 @@ async def wait_for_server(server): for i in range(10): try: host, _, port = server.bind[0].rpartition(":") - r, w = await asyncio.open_connection(host=host, port=port) + _r, w = await asyncio.open_connection(host=host, port=port) except Exception as e: await asyncio.sleep(0.1) else: From e18804f7428821214ec1f27d943ee74cfbeb9ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:53:23 +0200 Subject: [PATCH 32/58] ruff - RUF100 --- pyproject.toml | 2 +- src/aiopenapi3/_types.py | 2 +- src/aiopenapi3/loader.py | 2 +- src/aiopenapi3/openapi.py | 2 +- src/aiopenapi3/plugin.py | 24 ++++++++++++------------ src/aiopenapi3/pydanticv2.py | 2 +- src/aiopenapi3/v30/glue.py | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f002dd58..488c1cef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -118,7 +118,7 @@ ignore = [ # "RUF022", # "RUF046", # "RUF059", - "RUF100", +# "RUF100", "S110", "SIM101", "SIM102", diff --git a/src/aiopenapi3/_types.py b/src/aiopenapi3/_types.py index 3b561847..fb05647a 100644 --- a/src/aiopenapi3/_types.py +++ b/src/aiopenapi3/_types.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Literal, Optional, TypeAlias, Union import yaml -from httpx2._types import AuthTypes, FileTypes, RequestContent, RequestFiles # noqa +from httpx2._types import AuthTypes, FileTypes, RequestContent, RequestFiles from pydantic import BaseModel from . import v20, v30, v31, v32 diff --git a/src/aiopenapi3/loader.py b/src/aiopenapi3/loader.py index 4f848328..a58288c0 100644 --- a/src/aiopenapi3/loader.py +++ b/src/aiopenapi3/loader.py @@ -22,7 +22,7 @@ import functools import operator -from pathlib import Path # noqa:E402 +from pathlib import Path from .plugin import Plugins diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index e03caf53..bc1ecc15 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -697,7 +697,7 @@ def authenticate(self, *args, **kwargs): elif isinstance(self._root, (v30.Root, v31.Root)): v = schemes - frozenset(SecuritySchemes := self._root.components.securitySchemes) else: - raise TypeError(self._root) # noqa + raise TypeError(self._root) if v: raise ValueError(f"{self.info.title} does not accept security schemes {sorted(v)}") diff --git a/src/aiopenapi3/plugin.py b/src/aiopenapi3/plugin.py index 4ffa7ca1..63756a0b 100644 --- a/src/aiopenapi3/plugin.py +++ b/src/aiopenapi3/plugin.py @@ -50,19 +50,19 @@ class Context: def schemas(self, ctx: "Init.Context") -> "Init.Context": # pragma: no cover """modify the Schema before creating Models""" - return ctx # noqa + return ctx def resolved(self, ctx: "Init.Context") -> "Init.Context": # pragma: no cover """modify the resolved paths/PathItems before initializing the Operations""" - return ctx # noqa + return ctx def paths(self, ctx: "Init.Context") -> "Init.Context": # pragma: no cover """modify the paths/PathItems before initializing the Operations""" - return ctx # noqa + return ctx def initialized(self, ctx: "Init.Context") -> "Init.Context": # pragma: no cover """it is initialized""" - return ctx # noqa + return ctx class Document(Plugin): @@ -79,11 +79,11 @@ class Context: def loaded(self, ctx: "Document.Context") -> "Document.Context": # pragma: no cover """modify the text before parsing""" - return ctx # noqa + return ctx def parsed(self, ctx: "Document.Context") -> "Document.Context": # pragma: no cover """modify the parsed dict before …""" - return ctx # noqa + return ctx class Message(Plugin): @@ -128,31 +128,31 @@ def marshalled(self, ctx: "Message.Context") -> "Message.Context": # pragma: no """ modify the dict before sending """ - return ctx # noqa + return ctx def sending(self, ctx: "Message.Context") -> "Message.Context": # pragma: no cover """ modify the text before sending """ - return ctx # noqa + return ctx def received(self, ctx: "Message.Context") -> "Message.Context": # pragma: no cover """ modify the received text """ - return ctx # noqa + return ctx def parsed(self, ctx: "Message.Context") -> "Message.Context": # pragma: no cover """ modify the parsed dict structure """ - return ctx # noqa + return ctx def unmarshalled(self, ctx: "Message.Context") -> "Message.Context": # pragma: no cover """ modify the object """ - return ctx # noqa + return ctx class Domain: @@ -203,7 +203,7 @@ def __init__(self, plugins: list[Plugin]): def _get_domain(self, name: str, plugins: list[Plugin]) -> "Domain": domain: type[Plugin] | None if (domain := self._domains.get(name)) is None: - raise ValueError(name) # noqa + raise ValueError(name) def domain_type_f(p: Plugin) -> TypeGuard[Plugin]: return isinstance(p, domain) diff --git a/src/aiopenapi3/pydanticv2.py b/src/aiopenapi3/pydanticv2.py index cbe50653..22df0395 100644 --- a/src/aiopenapi3/pydanticv2.py +++ b/src/aiopenapi3/pydanticv2.py @@ -48,7 +48,7 @@ from pydantic.main import ModelT -def create_model( # noqa: C901 +def create_model( model_name: str, /, *, diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index ca055148..02d05215 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -419,7 +419,7 @@ def _prepare_body(self, data_: Optional["RequestData"], mph: dict[str, str]) -> elif isinstance(value, str): rdata[name] = value else: - raise TypeError(type(value)) # noqa + raise TypeError(type(value)) self.req.files = rfiles self.req.data = rdata else: From 9338e1d3d2297624c62154345fdfe1bd3ff90fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 08:54:29 +0200 Subject: [PATCH 33/58] ruff - SIM101 --- pyproject.toml | 2 +- tests/petstore_test.py | 2 +- tests/petstorev3_test.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 488c1cef..673bcd2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -120,7 +120,7 @@ ignore = [ # "RUF059", # "RUF100", "S110", - "SIM101", +# "SIM101", "SIM102", "SIM103", "SIM118", diff --git a/tests/petstore_test.py b/tests/petstore_test.py index 81e5f12d..505342ef 100644 --- a/tests/petstore_test.py +++ b/tests/petstore_test.py @@ -199,7 +199,7 @@ def test_pets(api, login): assert (isinstance(r, list) and len(r) >= 0) or isinstance(r, ApiResponse) r = api._.findPetsByTags(parameters={"tags": ["unknown"]}) - assert isinstance(r, list) or isinstance(r, ApiResponse) + assert isinstance(r, (list, ApiResponse)) # deletePet r = api._.findPetsByStatus(parameters={"status": ["available", "pending", "sold"]}) diff --git a/tests/petstorev3_test.py b/tests/petstorev3_test.py index 548c4a62..0ef30d21 100644 --- a/tests/petstorev3_test.py +++ b/tests/petstorev3_test.py @@ -224,7 +224,7 @@ def test_pets(api, login): assert (isinstance(r, list) and len(r) >= 0) or isinstance(r, ApiResponse) r = api._.findPetsByTags(parameters={"tags": ["unknown"]}) - assert isinstance(r, list) or isinstance(r, ApiResponse) + assert isinstance(r, (list, ApiResponse)) # deletePet r = api._.findPetsByStatus(parameters={"status": ["available", "pending", "sold"]}) From a771d77959ac57208da0405ae8e16f9031c7a21a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:08:52 +0200 Subject: [PATCH 34/58] ruff - SIM102 --- pyproject.toml | 2 +- src/aiopenapi3/base.py | 8 ++++---- src/aiopenapi3/model.py | 16 +++++++--------- src/aiopenapi3/request.py | 4 ++-- src/aiopenapi3/v20/parameter.py | 9 ++++----- src/aiopenapi3/v30/glue.py | 2 +- src/aiopenapi3/v30/servers.py | 5 ++--- src/aiopenapi3/v31/servers.py | 5 ++--- src/aiopenapi3/v32/servers.py | 5 ++--- tests/petstore_test.py | 16 ++++++++-------- tests/petstorev3_test.py | 16 ++++++++-------- tests/plugin_test.py | 5 ++--- 12 files changed, 43 insertions(+), 50 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 673bcd2e..190b3a10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -121,7 +121,7 @@ ignore = [ # "RUF100", "S110", # "SIM101", - "SIM102", +# "SIM102", "SIM103", "SIM118", "SIM201", diff --git a/src/aiopenapi3/base.py b/src/aiopenapi3/base.py index 1fa2678a..f22065de 100644 --- a/src/aiopenapi3/base.py +++ b/src/aiopenapi3/base.py @@ -121,13 +121,13 @@ def replace(ivalue): continue # v3.1 - Schema $ref - if isinstance(root, (v20.root.Root, v30.root.Root, v31.root.Root)): - if isinstance(value, SchemaBase): + if isinstance(root, (v20.root.Root, v30.root.Root, v31.root.Root)): # noqa: SIM102 + if isinstance(value, SchemaBase): # noqa: SIM102 if (r := getattr(value, "ref", None)) and not isinstance(r, ReferenceBase): value = _Reference.model_construct(ref=r) setattr(obj, slot, value) - if isinstance(root, (v30.root.Root, v31.root.Root)): + if isinstance(root, (v30.root.Root, v31.root.Root)): # noqa: SIM102 if isinstance(value, (v30.Discriminator, v31.Discriminator)): """ Discriminated Unions - implementing undefined behavior @@ -192,7 +192,7 @@ def replace(ivalue): PathItem Ref is ambiguous https://github.com/OAI/OpenAPI-Specification/issues/2635 """ - if isinstance(root, (v20.root.Root, v30.root.Root, v31.root.Root)): + if isinstance(root, (v20.root.Root, v30.root.Root, v31.root.Root)): # noqa: SIM102 if isinstance(obj, _PathItem) and slot == "ref": ref = _Reference.model_construct(ref=value) ref._target = api.resolve_jr(root, obj, ref) diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index 87865088..b762ee4c 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -218,12 +218,11 @@ def _createAnnotations( if typing.get_origin(r) == Literal: canbenull = False - if canbenull: - if getattr(f, "const", None) is None: - """not const""" - if name not in schema.required or Model.is_nullable(f): - """not required - or nullable""" - r = Optional[r] # type: ignore[assignment] + if canbenull and getattr(f, "const", None) is None: + """not const""" + if name not in schema.required or Model.is_nullable(f): + """not required - or nullable""" + r = Optional[r] # type: ignore[assignment] self.properties[Model.nameof(name)].annotation = r @@ -462,9 +461,8 @@ def validate_patternProperties(self_): else: raise ValueError(_type) - if _type in ("array", "object"): - if schema.enum or getattr(schema, "const", None): - raise NotImplementedError("complex enums/const are not supported") + if _type in ("array", "object") and (schema.enum or getattr(schema, "const", None)): + raise NotImplementedError("complex enums/const are not supported") classinfo.config = Model.createConfigDict(schema) diff --git a/src/aiopenapi3/request.py b/src/aiopenapi3/request.py index 3e47a6db..279187e4 100644 --- a/src/aiopenapi3/request.py +++ b/src/aiopenapi3/request.py @@ -625,7 +625,7 @@ def __init__(self, api: "OpenAPI", use_operation_tags: bool): else: self.operations.append(op.operationId) - if hasattr(pi, "additionalOperations"): # v32 + if hasattr(pi, "additionalOperations"): # v32 # noqa: SIM102 if pi.additionalOperations: for method, op in pi.additionalOperations.items(): if use_operation_tags and op.tags: @@ -702,7 +702,7 @@ def __init__(self, api: "OpenAPI", use_operation_tags: bool): raise OperationIdDuplicationError(operationId, [item, other]) self._operations[operationId] = item - if hasattr(pi, "additionalOperations"): # v32 + if hasattr(pi, "additionalOperations"): # v32 # noqa: SIM102 if pi.additionalOperations: for method, op in pi.additionalOperations.items(): if op.operationId is None: diff --git a/src/aiopenapi3/v20/parameter.py b/src/aiopenapi3/v20/parameter.py index de697caf..e087561a 100644 --- a/src/aiopenapi3/v20/parameter.py +++ b/src/aiopenapi3/v20/parameter.py @@ -33,11 +33,10 @@ def _encode__collection(self, values): def _encode(self, name, value): if self.type == "array": value = self._encode__collection(value) - elif self.in_ == "formData": - if self.type == "file": - # https://www.python-httpx.org/quickstart/#sending-multipart-file-uploads - # we expect (filename, data, content-type) - assert isinstance(value, tuple) and len(value) == 3 and isinstance(value[1], io.IOBase) + elif self.in_ == "formData" and self.type == "file": + # https://www.python-httpx.org/quickstart/#sending-multipart-file-uploads + # we expect (filename, data, content-type) + assert isinstance(value, tuple) and len(value) == 3 and isinstance(value[1], io.IOBase) return {name: value} diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index 02d05215..fc73f301 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -92,7 +92,7 @@ def args(self, content_type: str = "application/json") -> dict[str, Any]: def return_value(self, http_status: int = 200, content_type: str = "application/json") -> Optional["SchemaType"]: status_key = str(http_status) - if a := self.operation.responses.get(status_key) or self.operation.responses.get(status_key[0] + "XX"): + if a := self.operation.responses.get(status_key) or self.operation.responses.get(status_key[0] + "XX"): # noqa: SIM102 if b := a.content.get(content_type): return b.schema_ return None diff --git a/src/aiopenapi3/v30/servers.py b/src/aiopenapi3/v30/servers.py index 921f7fbd..a23f659e 100644 --- a/src/aiopenapi3/v30/servers.py +++ b/src/aiopenapi3/v30/servers.py @@ -43,9 +43,8 @@ def validate_server_url_parameters(self) -> "Server": def validate_parameter_enum(self, parameters: dict[str, str]): for name, value in parameters.items(): - if v := self.variables.get(name): - if v.enum and value not in v.enum: - raise ValueError(f"Server Variable {name} value {value} not allowed ({v.enum})") + if (v := self.variables.get(name)) and v.enum and value not in v.enum: + raise ValueError(f"Server Variable {name} value {value} not allowed ({v.enum})") def createUrl(self, variables: dict[str, str]) -> str: self.validate_parameter_enum(variables) diff --git a/src/aiopenapi3/v31/servers.py b/src/aiopenapi3/v31/servers.py index 9ebf1c9f..43df0fcd 100644 --- a/src/aiopenapi3/v31/servers.py +++ b/src/aiopenapi3/v31/servers.py @@ -43,9 +43,8 @@ def validate_server_url_parameters(self) -> "Server": def validate_parameter_enum(self, parameters: dict[str, str]): for name, value in parameters.items(): - if v := self.variables.get(name): - if v.enum and value not in v.enum: - raise ValueError(f"Server Variable {name} value {value} not allowed ({v.enum})") + if (v := self.variables.get(name)) and v.enum and value not in v.enum: + raise ValueError(f"Server Variable {name} value {value} not allowed ({v.enum})") def createUrl(self, variables: dict[str, str]) -> str: self.validate_parameter_enum(variables) diff --git a/src/aiopenapi3/v32/servers.py b/src/aiopenapi3/v32/servers.py index 1e8ac510..f542571f 100644 --- a/src/aiopenapi3/v32/servers.py +++ b/src/aiopenapi3/v32/servers.py @@ -48,9 +48,8 @@ def validate_server_url_parameters(self) -> "Server": def validate_parameter_enum(self, parameters: dict[str, str]): for name, value in parameters.items(): - if v := self.variables.get(name): - if v.enum and value not in v.enum: - raise ValueError(f"Server Variable {name} value {value} not allowed ({v.enum})") + if (v := self.variables.get(name)) and v.enum and value not in v.enum: + raise ValueError(f"Server Variable {name} value {value} not allowed ({v.enum})") def createUrl(self, variables: dict[str, str]) -> str: self.validate_parameter_enum(variables) diff --git a/tests/petstore_test.py b/tests/petstore_test.py index 505342ef..bb99a18c 100644 --- a/tests/petstore_test.py +++ b/tests/petstore_test.py @@ -74,14 +74,14 @@ def goodPet(i): Pet = self.api.resolve_jr(self.api._root, None, Reference(**{"$ref": "#/definitions/Pet"})) - if ctx.operationId == "getPetById": - if Pet == ctx.expected_type: - goodPet(ctx.parsed) - - if ctx.operationId in frozenset(["findPetsByStatus", "findPetsByTags"]): - if Pet == getattr(ctx.expected_type.items, "_target", None): - for i in ctx.parsed: - goodPet(i) + if ctx.operationId == "getPetById" and Pet == ctx.expected_type: + goodPet(ctx.parsed) + + if ctx.operationId in frozenset(["findPetsByStatus", "findPetsByTags"]) and Pet == getattr( + ctx.expected_type.items, "_target", None + ): + for i in ctx.parsed: + goodPet(i) return ctx diff --git a/tests/petstorev3_test.py b/tests/petstorev3_test.py index 0ef30d21..9d8a6adb 100644 --- a/tests/petstorev3_test.py +++ b/tests/petstorev3_test.py @@ -93,14 +93,14 @@ def goodPet(i): Pet = self.api.resolve_jr(self.api._root, None, Reference(**{"$ref": "#/components/schemas/Pet"})) - if ctx.operationId == "getPetById": - if Pet == ctx.expected_type: - goodPet(ctx.parsed) - - if ctx.operationId in frozenset(["findPetsByStatus", "findPetsByTags"]): - if Pet == getattr(ctx.expected_type.items, "_target", None): - for i in ctx.parsed: - goodPet(i) + if ctx.operationId == "getPetById" and Pet == ctx.expected_type: + goodPet(ctx.parsed) + + if ctx.operationId in frozenset(["findPetsByStatus", "findPetsByTags"]) and Pet == getattr( + ctx.expected_type.items, "_target", None + ): + for i in ctx.parsed: + goodPet(i) return ctx diff --git a/tests/plugin_test.py b/tests/plugin_test.py index 8bfb03bd..425af487 100644 --- a/tests/plugin_test.py +++ b/tests/plugin_test.py @@ -71,9 +71,8 @@ def parsed(self, ctx): return ctx def unmarshalled(self, ctx): - if ctx.operationId == "listPets": - if ctx.unmarshalled[0].id == 2: - ctx.unmarshalled[0].id = 3 + if ctx.operationId == "listPets" and ctx.unmarshalled[0].id == 2: + ctx.unmarshalled[0].id = 3 return ctx From 50c108257e745a2021d442ee510983b44f73d04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:09:35 +0200 Subject: [PATCH 35/58] ruff - SIM103 --- pyproject.toml | 2 +- src/aiopenapi3/model.py | 8 ++------ src/aiopenapi3/openapi.py | 4 +--- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 190b3a10..cd7eecf8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -122,7 +122,7 @@ ignore = [ "S110", # "SIM101", # "SIM102", - "SIM103", +# "SIM103", "SIM118", "SIM201", "SIM210", diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index b762ee4c..332bd56a 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -83,9 +83,7 @@ class ConfiguredRootModel(RootModel): def is_basemodel(m) -> bool: - if inspect.isclass(m) and issubclass(m, pydantic.BaseModel): - return True - return False + return bool(inspect.isclass(m) and issubclass(m, pydantic.BaseModel)) if sys.version_info < (3, 11): @@ -95,9 +93,7 @@ def is_basemodel(m) -> bool: if isinstance(m, typing.GenericAlias): return False - if inspect.isclass(m) and issubclass(m, pydantic.BaseModel): - return True - return False + return bool(inspect.isclass(m) and issubclass(m, pydantic.BaseModel)) @dataclasses.dataclass diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index bc1ecc15..bd568942 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -43,9 +43,7 @@ def has_components(y: Optional["RootType"]) -> TypeGuard[v30.Root | v31.Root]: # return all([y, y.components]) if y is None: return False - if y.components is None: - return False - return True + return y.components is not None def is_schema(v: tuple[str, "SchemaType"]) -> TypeGuard["SchemaType"]: From 6121fac411ee1890603c8a7455fe0faa545d013d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:10:06 +0200 Subject: [PATCH 36/58] ruff - SIM118 --- pyproject.toml | 2 +- src/aiopenapi3/base.py | 2 +- src/aiopenapi3/extra/reduce.py | 2 +- src/aiopenapi3/model.py | 2 +- src/aiopenapi3/v20/glue.py | 2 +- src/aiopenapi3/v30/formdata.py | 2 +- src/aiopenapi3/v30/glue.py | 4 ++-- tests/extra_test.py | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cd7eecf8..9718d18f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -123,7 +123,7 @@ ignore = [ # "SIM101", # "SIM102", # "SIM103", - "SIM118", +# "SIM118", "SIM201", "SIM210", "SIM211", diff --git a/src/aiopenapi3/base.py b/src/aiopenapi3/base.py index f22065de..c117aa7c 100644 --- a/src/aiopenapi3/base.py +++ b/src/aiopenapi3/base.py @@ -53,7 +53,7 @@ def validate_ObjectExtended_extensions(cls, values): if len(e): for i in rm: del values[i] - if "extensions" in values.keys(): + if "extensions" in values: raise ValueError("extensions") values["extensions"] = e diff --git a/src/aiopenapi3/extra/reduce.py b/src/aiopenapi3/extra/reduce.py index eceabfb0..c2ec29e1 100644 --- a/src/aiopenapi3/extra/reduce.py +++ b/src/aiopenapi3/extra/reduce.py @@ -55,7 +55,7 @@ def _reduced_paths(self, ctx: "Document.Context") -> dict: reduced[path_key][operation_key] = operation_value elif isinstance(operation, tuple) and len(operation) == 2: pattern, operation_patterns = operation - for path_key in ctx.document["paths"].keys(): + for path_key in ctx.document["paths"]: if (isinstance(pattern, str) and pattern == path_key) or ( isinstance(pattern, re.Pattern) and re.match(pattern, path_key) ): diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index 332bd56a..f800f59c 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -424,7 +424,7 @@ def get_patternProperties(self_): def mkx(): def validate_patternProperties(self_): patterns = typing.get_args(self_.aio3_patternProperty.__annotations__["item"]) - for name in self_.model_extra.keys(): + for name in self_.model_extra: for pattern in patterns: if re.match(pattern, name): break diff --git a/src/aiopenapi3/v20/glue.py b/src/aiopenapi3/v20/glue.py index faf54c0c..4ca36fc3 100644 --- a/src/aiopenapi3/v20/glue.py +++ b/src/aiopenapi3/v20/glue.py @@ -98,7 +98,7 @@ def _prepare_security(self): for s in security: if frozenset(s.root.keys()) - frozenset(self.security.keys()): continue - for scheme in s.root.keys(): + for scheme in s.root: value = self.security[scheme] self._prepare_secschemes(scheme, value) break diff --git a/src/aiopenapi3/v30/formdata.py b/src/aiopenapi3/v30/formdata.py index dbd8d131..60aec53c 100644 --- a/src/aiopenapi3/v30/formdata.py +++ b/src/aiopenapi3/v30/formdata.py @@ -61,7 +61,7 @@ def parameters_from_multipart( style = e.style or "form" explode = e.explode if e.explode is not None else (True if style == "form" else False) allowReserved = e.allowReserved or False - headers = {name: mph[name] for name in e.headers.keys() if name in mph} + headers = {name: mph[name] for name in e.headers if name in mph} else: allowReserved = False style = "form" diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index fc73f301..d6c6c09f 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -115,7 +115,7 @@ def _prepare_security(self) -> None: for s in security: if frozenset(s.root.keys()) - frozenset(self.security.keys()): continue - for scheme in s.root.keys(): + for scheme in s.root: value = self.security[scheme] self._prepare_secschemes(scheme, value) break @@ -413,7 +413,7 @@ def _prepare_body(self, data_: Optional["RequestData"], mph: dict[str, str]) -> assert media.encoding is not None if (e := media.encoding.get(name)) is not None: assert e.headers - headers.update({name: mph[name] for name in e.headers.keys() if name in mph}) + headers.update({name: mph[name] for name in e.headers if name in mph}) _value = (alias, fh, content_type, headers) rfiles.append((name, _value)) elif isinstance(value, str): diff --git a/tests/extra_test.py b/tests/extra_test.py index 9ad24998..42060dd5 100644 --- a/tests/extra_test.py +++ b/tests/extra_test.py @@ -60,7 +60,7 @@ def parsed(self, ctx: "Document.Context") -> "Document.Context": # Check if description matches the desired format if description.strip() == "Usage: on='{on}'": parameter["name"] = "on" - if "content" in parameter.keys(): + if "content" in parameter: parameter["schema"] = parameter["content"].get("application/json", {}).get("schema", {}) del parameter["content"] # Drop requirement for @odata.type since it's not actually enforced From 01043aa42c954139239772ba8722690342548726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:10:29 +0200 Subject: [PATCH 37/58] ruff - SIM201 --- pyproject.toml | 2 +- tests/petstorev3_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9718d18f..916716a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -124,7 +124,7 @@ ignore = [ # "SIM102", # "SIM103", # "SIM118", - "SIM201", +# "SIM201", "SIM210", "SIM211", "TC004", diff --git a/tests/petstorev3_test.py b/tests/petstorev3_test.py index 9d8a6adb..f8ceab49 100644 --- a/tests/petstorev3_test.py +++ b/tests/petstorev3_test.py @@ -87,7 +87,7 @@ def goodPet(i): i["tags"] = [] else: for t in c: - if not isinstance(t, dict) or not set(t.keys()) == frozenset(["id", "name"]): + if not isinstance(t, dict) or set(t.keys()) != frozenset(["id", "name"]): i["tags"] = [] break From d885064e88cac75d7d35f8f6ea863ec8f9dec2f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:10:56 +0200 Subject: [PATCH 38/58] ruff - SIM210 --- pyproject.toml | 2 +- src/aiopenapi3/v30/formdata.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 916716a9..f2b46d1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -125,7 +125,7 @@ ignore = [ # "SIM103", # "SIM118", # "SIM201", - "SIM210", +# "SIM210", "SIM211", "TC004", "TC005", diff --git a/src/aiopenapi3/v30/formdata.py b/src/aiopenapi3/v30/formdata.py index 60aec53c..4e9651da 100644 --- a/src/aiopenapi3/v30/formdata.py +++ b/src/aiopenapi3/v30/formdata.py @@ -59,7 +59,7 @@ def parameters_from_multipart( if (e := media.encoding.get(k, None)) is not None: ct = e.contentType or ct style = e.style or "form" - explode = e.explode if e.explode is not None else (True if style == "form" else False) + explode = e.explode if e.explode is not None else (style == "form") allowReserved = e.allowReserved or False headers = {name: mph[name] for name in e.headers if name in mph} else: From 6347ee6235219a13c49ee337959c628ce59c0f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:11:51 +0200 Subject: [PATCH 39/58] ruff - SIM211 --- pyproject.toml | 2 +- src/aiopenapi3/v30/parameter.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f2b46d1c..aa635ae3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -126,7 +126,7 @@ ignore = [ # "SIM118", # "SIM201", # "SIM210", - "SIM211", +# "SIM211", "TC004", "TC005", "TRY004", diff --git a/src/aiopenapi3/v30/parameter.py b/src/aiopenapi3/v30/parameter.py index 19c5095f..8208b71e 100644 --- a/src/aiopenapi3/v30/parameter.py +++ b/src/aiopenapi3/v30/parameter.py @@ -36,11 +36,11 @@ def _codec(self): elif self.in_ == "query": style = self.style or "form" assert style in frozenset(["form", "spaceDelimited", "pipeDelimited", "deepObject"]) - explode = self.explode if self.explode is not None else (False if style != "form" else True) + explode = self.explode if self.explode is not None else (style == "form") elif self.in_ == "cookie": style = self.style or "form" assert style in frozenset(["form"]) - explode = self.explode if self.explode is not None else (False if style != "form" else True) + explode = self.explode if self.explode is not None else (style == "form") elif self.in_ == "querystring": style = "querystring" explode = None From fb58406f6c0bda7bc8806bb318cee999da50a22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:12:56 +0200 Subject: [PATCH 40/58] ruff - TC004 --- pyproject.toml | 2 +- src/aiopenapi3/cli.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index aa635ae3..32083b3a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -127,7 +127,7 @@ ignore = [ # "SIM201", # "SIM210", # "SIM211", - "TC004", +# "TC004", "TC005", "TRY004", "TRY201", diff --git a/src/aiopenapi3/cli.py b/src/aiopenapi3/cli.py index cb0fcb53..6373a9e3 100644 --- a/src/aiopenapi3/cli.py +++ b/src/aiopenapi3/cli.py @@ -26,6 +26,7 @@ from pathlib import Path import aiopenapi3.loader +import aiopenapi3.request from aiopenapi3.v30.formdata import decode_content_type from .loader import ChainLoader, RedirectLoader, WebLoader @@ -33,7 +34,7 @@ from .openapi import OpenAPI if typing.TYPE_CHECKING: - import aiopenapi3.request + pass init() From 45f8726cf6d800668cca5cfc0d3b70fa69a9ff8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:13:33 +0200 Subject: [PATCH 41/58] ruff - TC005 --- pyproject.toml | 2 +- src/aiopenapi3/_types.py | 6 +----- src/aiopenapi3/cli.py | 4 ---- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 32083b3a..e6b8aa04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -128,7 +128,7 @@ ignore = [ # "SIM210", # "SIM211", # "TC004", - "TC005", +# "TC005", "TRY004", "TRY201", "TRY203", diff --git a/src/aiopenapi3/_types.py b/src/aiopenapi3/_types.py index fb05647a..5353ef22 100644 --- a/src/aiopenapi3/_types.py +++ b/src/aiopenapi3/_types.py @@ -1,6 +1,6 @@ import re from collections.abc import Sequence -from typing import TYPE_CHECKING, Literal, Optional, TypeAlias, Union +from typing import Literal, Optional, TypeAlias, Union import yaml from httpx2._types import AuthTypes, FileTypes, RequestContent, RequestFiles @@ -8,10 +8,6 @@ from . import v20, v30, v31, v32 -if TYPE_CHECKING: - pass - - RequestFileParameter = tuple[str, FileTypes] RequestFilesParameter = Sequence[RequestFileParameter] diff --git a/src/aiopenapi3/cli.py b/src/aiopenapi3/cli.py index 6373a9e3..3ca2580d 100644 --- a/src/aiopenapi3/cli.py +++ b/src/aiopenapi3/cli.py @@ -10,7 +10,6 @@ import pstats import sys import tracemalloc -import typing from pstats import SortKey import httpx2 @@ -33,9 +32,6 @@ from .log import init from .openapi import OpenAPI -if typing.TYPE_CHECKING: - pass - init() # log: Callable[..., None] | None = None From 8d8cc26b81e33c013ee1fd26d3a2b46911196766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:15:55 +0200 Subject: [PATCH 42/58] ruff - TRY004 --- pyproject.toml | 2 +- src/aiopenapi3/openapi.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e6b8aa04..33099d1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,7 +129,7 @@ ignore = [ # "SIM211", # "TC004", # "TC005", - "TRY004", +# "TRY004", "TRY201", "TRY203", "TRY401", diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index bd568942..d17ce55e 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -424,9 +424,9 @@ def _init_operationindex(self, use_operation_tags: bool) -> bool: elif isinstance(self._root, v32.Root): self._root.paths = v32.Paths(paths={}, extensions={}) else: - raise ValueError(self._root) + raise TypeError(self._root) else: - raise ValueError(self._root) + raise TypeError(self._root) self._operationindex = OperationIndex(self, use_operation_tags) return p is None From 70270984cd9915c2ad330c5f94e92f457a6fa83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:18:23 +0200 Subject: [PATCH 43/58] ruff - TRY201 --- pyproject.toml | 2 +- src/aiopenapi3/openapi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 33099d1e..cd74559a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,7 +130,7 @@ ignore = [ # "TC004", # "TC005", # "TRY004", - "TRY201", +# "TRY201", "TRY203", "TRY401", "UP007", diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index d17ce55e..f7e69e18 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -646,7 +646,7 @@ def _init_schema_types(self, only_required: bool) -> None: assert v.__name__ in types, v.__name__ v.model_rebuild(_types_namespace={"__types": types}) except Exception as e: - raise e + raise @property def url(self) -> yarl.URL: From 5757e4e7fb1b2facad4e01742ee4e38f9e8af0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:20:44 +0200 Subject: [PATCH 44/58] ruff - TRY203 --- pyproject.toml | 2 +- src/aiopenapi3/openapi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cd74559a..6c2f2162 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -131,7 +131,7 @@ ignore = [ # "TC005", # "TRY004", # "TRY201", - "TRY203", +# "TRY203", "TRY401", "UP007", "UP018", diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index f7e69e18..fbbfa059 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -645,7 +645,7 @@ def _init_schema_types(self, only_required: bool) -> None: for v in byid[id(thes)]._model_types: assert v.__name__ in types, v.__name__ v.model_rebuild(_types_namespace={"__types": types}) - except Exception as e: + except Exception: # noqa: TRY203 raise @property From a451a3540a8887485ed5ce6b4c0db97d0f257d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:22:17 +0200 Subject: [PATCH 45/58] ruff - TRY401 --- pyproject.toml | 2 +- src/aiopenapi3/cli.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6c2f2162..9c12df2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -132,7 +132,7 @@ ignore = [ # "TRY004", # "TRY201", # "TRY203", - "TRY401", +# "TRY401", "UP007", "UP018", "UP031", diff --git a/src/aiopenapi3/cli.py b/src/aiopenapi3/cli.py index 3ca2580d..228db35b 100644 --- a/src/aiopenapi3/cli.py +++ b/src/aiopenapi3/cli.py @@ -277,8 +277,8 @@ def cmd_validate(args: argparse.Namespace) -> None: return end = datetime.datetime.now() duration = end - begin - except ValueError as e: - logg.exception(e) + except ValueError: + logg.exception("error") else: if args.verbose: schema_display_stats(api, duration) From 323fb0fad7bb180e684a1ddf71adccbae9966b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:26:25 +0200 Subject: [PATCH 46/58] ruff - UP031 --- pyproject.toml | 2 +- src/aiopenapi3/cli.py | 4 ++-- src/aiopenapi3/v30/parameter.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9c12df2b..574e9f61 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -135,7 +135,7 @@ ignore = [ # "TRY401", "UP007", "UP018", - "UP031", +# "UP031", "UP037", "UP045" ] diff --git a/src/aiopenapi3/cli.py b/src/aiopenapi3/cli.py index 228db35b..17def8dc 100644 --- a/src/aiopenapi3/cli.py +++ b/src/aiopenapi3/cli.py @@ -89,13 +89,13 @@ def tm_display_top(snapshot, key_type="lineno", limit=10): ) top_stats = snapshot.statistics(key_type) - print("Top %s lines" % limit) + print(f"Top {limit} lines") for index, stat in enumerate(top_stats[:limit], 1): frame = stat.traceback[0] print(f"#{index}: {frame.filename}:{frame.lineno}: {stat.size / 1024:.1f} KiB") line = linecache.getline(frame.filename, frame.lineno).strip() if line: - print(" %s" % line) + print(f" {line}") other = top_stats[limit:] if other: diff --git a/src/aiopenapi3/v30/parameter.py b/src/aiopenapi3/v30/parameter.py index 8208b71e..dca44709 100644 --- a/src/aiopenapi3/v30/parameter.py +++ b/src/aiopenapi3/v30/parameter.py @@ -340,7 +340,7 @@ class Parameter(ParameterBase, _ParameterCodec): @model_validator(mode="after") def validate_Parameter(self): assert self.in_ != "path" or self.required is True, ( - "Parameter '%s' must be required since it is in the path" % self.name + f"Parameter '{self.name}' must be required since it is in the path" ) return self From c2395f037814a40ddc73b458bd307eb8e4a49e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 09:27:04 +0200 Subject: [PATCH 47/58] ruff - UP037 --- pyproject.toml | 2 +- src/aiopenapi3/cli.py | 2 +- src/aiopenapi3/extra/reduce.py | 2 +- src/aiopenapi3/model.py | 6 ++--- src/aiopenapi3/openapi.py | 26 +++++++++---------- src/aiopenapi3/plugin.py | 2 +- src/aiopenapi3/request.py | 46 +++++++++++++++++----------------- src/aiopenapi3/v20/glue.py | 4 +-- src/aiopenapi3/v30/glue.py | 4 +-- 9 files changed, 47 insertions(+), 47 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 574e9f61..618bf0d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -136,7 +136,7 @@ ignore = [ "UP007", "UP018", # "UP031", - "UP037", +# "UP037", "UP045" ] diff --git a/src/aiopenapi3/cli.py b/src/aiopenapi3/cli.py index 17def8dc..1eeae805 100644 --- a/src/aiopenapi3/cli.py +++ b/src/aiopenapi3/cli.py @@ -231,7 +231,7 @@ def prepare_arg(value): if auth: api.authenticate(**auth) - req: "aiopenapi3.request.RequestBase" + req: aiopenapi3.request.RequestBase if args.method: req = api.createRequest((args.operationId, args.method)) else: diff --git a/src/aiopenapi3/extra/reduce.py b/src/aiopenapi3/extra/reduce.py index c2ec29e1..cffbd39f 100644 --- a/src/aiopenapi3/extra/reduce.py +++ b/src/aiopenapi3/extra/reduce.py @@ -35,7 +35,7 @@ def __init__( super().__init__() def _reduced_paths(self, ctx: "Document.Context") -> dict: - reduced: dict[str, dict[str, "PathItemType"]] = {} + reduced: dict[str, dict[str, PathItemType]] = {} if "paths" not in ctx.document: return reduced keep_keys = {"summary", "description", "servers", "parameters"} diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index f800f59c..32455163 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -644,18 +644,18 @@ def types(schema: "SchemaType") -> typing.Generator[str, None, None]: ) # allOf - intersection of types - allOfs: list["SchemaType"] + allOfs: list[SchemaType] if allOfs := functools.reduce(operator.iadd, [getattr(schema, "allOf", [])], []): for x in allOfs: allOf &= set(Model.types(x)) # anyOf - union of types - anyOfs: list["SchemaType"] + anyOfs: list[SchemaType] if anyOfs := functools.reduce(operator.iadd, [getattr(schema, "anyOf", [])], []): anyOf = set.union(*[set(Model.types(x)) for x in anyOfs]) if anyOfs else set() # oneOf - union of types - oneOfs: list["SchemaType"] + oneOfs: list[SchemaType] if oneOfs := functools.reduce(operator.iadd, [getattr(schema, "oneOf", [])], []): oneOf = set.union(*[set(Model.types(x)) for x in oneOfs]) if oneOfs else set() diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index fbbfa059..85612ba7 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -249,7 +249,7 @@ def __init__( Loader - loading referenced documents """ - self._createRequest: Callable[["OpenAPI", str, str, "OperationType", list["ServerType"] | None], "RequestBase"] + self._createRequest: Callable[[OpenAPI, str, str, OperationType, list[ServerType] | None], RequestBase] """ creates the Async/Request for the protocol required """ @@ -273,7 +273,7 @@ def __init__( e.g. {"BasicAuth": ("user","secret")} """ - self._documents: dict[yarl.URL, "RootType"] = {} + self._documents: dict[yarl.URL, RootType] = {} """ the related documents """ @@ -283,7 +283,7 @@ def __init__( server variable mapping """ - self._server_select: Callable[[list["ServerType"]], "ServerType"] = random.choice + self._server_select: Callable[[list[ServerType]], ServerType] = random.choice self._init_plugins(plugins) """ @@ -366,10 +366,10 @@ def _init_operationindex(self, use_operation_tags: bool) -> bool: if isinstance(self._root, v20.Root): if self.paths: - obj: "PathItemType" + obj: PathItemType for path, obj in self.paths.items(): for m in obj.model_fields_set & HTTP_METHODS: - op: "Operation" = getattr(obj, m) + op: Operation = getattr(obj, m) op._validate_path_parameters(obj, path, (m, cast(str, op.operationId))) if op.operationId is None: continue @@ -394,7 +394,7 @@ def _init_operationindex(self, use_operation_tags: bool) -> bool: for schemas in allschemas: name: str - schema: "SchemaType" + schema: SchemaType for name, schema in filter(is_schema, schemas.items()): schema._get_identity(name=name, prefix="OP") @@ -478,7 +478,7 @@ def _iterate_schemas(cls, schemas: dict[int, "SchemaType"], next_set: set[int], return processed def _init_schema_types_collect(self, only_required: bool) -> dict[str, "SchemaType"]: - byname: dict[str, "SchemaType"] = {} + byname: dict[str, SchemaType] = {} def is_schema(v: tuple[str, "SchemaType"]) -> bool: return isinstance(v[1], (v20.Schema, v30.Schema, v31.Schema)) @@ -599,8 +599,8 @@ def is_schema(v: tuple[str, "SchemaType"]) -> bool: return byname def _init_schema_types(self, only_required: bool) -> None: - byname: dict[str, "SchemaType"] = self._init_schema_types_collect(only_required) - byid: dict[int, "SchemaType"] = {id(i): i for i in byname.values()} + byname: dict[str, SchemaType] = self._init_schema_types_collect(only_required) + byid: dict[int, SchemaType] = {id(i): i for i in byname.values()} data: set[int] = set(byid.keys()) todo: set[int] = self._iterate_schemas(byid, data, set()) types: dict[str, type[BaseModel | int | str | float | bool] | ForwardRef] = {} @@ -608,7 +608,7 @@ def _init_schema_types(self, only_required: bool) -> None: """ Due to Plugins (e.g. Cull/Reduce) byname may be incomplete """ - resolved: list["SchemaType"] = [ + resolved: list[SchemaType] = [ byid[x]._target if isinstance(byid[x], ReferenceBase) else byid[x] for x in todo | data ] self.plugins.init.resolved(initialized=self._root, resolved=resolved) @@ -674,7 +674,7 @@ def url(self) -> yarl.URL: return r elif isinstance(self._root, (v30.Root, v31.Root, v32.Root)): assert self._root.servers - server: "ServerType" = self._server_select(self._root.servers) + server: ServerType = self._server_select(self._root.servers) return self._base_url.join(yarl.URL(server.createUrl(self._server_variables))) def authenticate(self, *args, **kwargs): @@ -741,8 +741,8 @@ def createRequest(self, operationId: str | tuple[str, "HTTPMethodType"]) -> "Req :return: the returned Request is either :class:`aiopenapi3.request.RequestBase` or - in case of a httpx2.AsyncClient session_factory - :class:`aiopenapi3.request.AsyncRequestBase` """ - operation: Optional["OperationType"] = None - request: Optional["RequestType"] = None + operation: Optional[OperationType] = None + request: Optional[RequestType] = None try: if isinstance(operationId, str): *tags, opn = operationId.split(".") diff --git a/src/aiopenapi3/plugin.py b/src/aiopenapi3/plugin.py index 63756a0b..9c0255f1 100644 --- a/src/aiopenapi3/plugin.py +++ b/src/aiopenapi3/plugin.py @@ -23,7 +23,7 @@ class Plugin(abc.ABC): class Context: ... def __init__(self) -> None: - self._api: Optional["OpenAPI"] = None + self._api: Optional[OpenAPI] = None @property def api(self): diff --git a/src/aiopenapi3/request.py b/src/aiopenapi3/request.py index 279187e4..69f35ecf 100644 --- a/src/aiopenapi3/request.py +++ b/src/aiopenapi3/request.py @@ -47,14 +47,14 @@ class RequestParameter: def __init__(self, url: yarl.URL | str): self.url: str = str(url) - self.auth: Optional["AuthTypes"] = None + self.auth: Optional[AuthTypes] = None self.cookies: dict[str, str] = {} # self.path = {} self.params: dict[str, str] = {} - self.content: Optional["RequestContent"] = None + self.content: Optional[RequestContent] = None self.headers: dict[str, str] = {} self.data: dict[str, str] = {} # form-data - self.files: Optional["RequestFiles"] = {} # form-data files + self.files: Optional[RequestFiles] = {} # form-data files self.cert: Any = None @@ -68,7 +68,7 @@ class StreamResponse(NamedTuple): class Sequencer: def __init__(self, headers: "ResponseHeadersType", stream: Iterator["JSON"], model: pydantic.BaseModel) -> None: self.headers: ResponseHeadersType = headers - self.stream: Iterator["JSON"] = stream + self.stream: Iterator[JSON] = stream self.model = model def __iter__(self) -> Iterator: @@ -113,7 +113,7 @@ def __init__( operation: "OperationType", servers: list["ServerType"] | None, ): - self.api: "OpenAPI" = api + self.api: OpenAPI = api """ OpenAPI object """ @@ -123,7 +123,7 @@ def __init__( API document root """ - self.method: "HTTPMethodType" = method + self.method: HTTPMethodType = method """ HTTP method """ @@ -133,12 +133,12 @@ def __init__( HTTP path """ - self.vars: Optional["RequestBase.Vars"] = None + self.vars: Optional[RequestBase.Vars] = None """ Parameter & Data """ - self.operation: "OperationType" = operation + self.operation: OperationType = operation """ associated OpenAPI Operation """ @@ -148,7 +148,7 @@ def __init__( RequestParameter """ - self.servers: list["ServerType"] | None = servers + self.servers: list[ServerType] | None = servers """ Servers to use for this request """ @@ -219,7 +219,7 @@ def _build_req(self, session: httpx2.Client | httpx2.AsyncClient) -> httpx2.Requ url: yarl.URL = self.api.url if self.servers: - server: "ServerType" = self.api._server_select(self.servers) + server: ServerType = self.api._server_select(self.servers) url = self.api._base_url.join(yarl.URL(server.createUrl(self.api._server_variables))) req = session.build_request( @@ -421,8 +421,8 @@ class Sequencer: def __init__( self, headers: "ResponseHeadersType", stream: AsyncIterator["JSON"], model: pydantic.BaseModel ) -> None: - self.headers: "ResponseHeadersType" = headers - self.stream: AsyncIterator["JSON"] = stream + self.headers: ResponseHeadersType = headers + self.stream: AsyncIterator[JSON] = stream self.model = model def __aiter__(self) -> AsyncIterator: @@ -589,8 +589,8 @@ class OperationIndex: class OperationTag: def __init__(self, oi: "OperationIndex") -> None: self._oi = oi - self._operations: dict[str, tuple["HTTPMethodType", str, "OperationType", list["ServerType"] | None]] = {} - self._tags: dict[str, "OperationIndex.OperationTag"] = {} + self._operations: dict[str, tuple[HTTPMethodType, str, OperationType, list[ServerType] | None]] = {} + self._tags: dict[str, OperationIndex.OperationTag] = {} def __getattr__(self, item) -> RequestBase: if item in self._operations: @@ -603,9 +603,9 @@ class Iter: def __init__(self, api: "OpenAPI", use_operation_tags: bool): self.operations = [] self.r: Iterator[int] - pi: "PathItemType" + pi: PathItemType for pi in api.paths.values(): - op: "OperationType" + op: OperationType if pi.ref: # pi = pi.ref._target pi = cast("PathItemType", cast(ReferenceBase, pi.ref)._target) @@ -649,17 +649,17 @@ def __next__(self): return self.operations[next(self.r)] def __init__(self, api: "OpenAPI", use_operation_tags: bool): - self._api: "OpenAPI" = api - self._root: "RootType" = api._root + self._api: OpenAPI = api + self._root: RootType = api._root - self._operations: dict[str, tuple["HTTPMethodType", str, "OperationType", list["ServerType"] | None]] = {} - self._tags: dict[str, "OperationIndex.OperationTag"] = collections.defaultdict( + self._operations: dict[str, tuple[HTTPMethodType, str, OperationType, list[ServerType] | None]] = {} + self._tags: dict[str, OperationIndex.OperationTag] = collections.defaultdict( lambda: OperationIndex.OperationTag(self) ) - pi: "PathItemType" + pi: PathItemType for path, pi in self._root.paths.items(): - op: "OperationType" - servers: list["ServerType"] | None + op: OperationType + servers: list[ServerType] | None if pi.ref: pi = pi.ref._target for method in pi.model_fields_set & HTTP_METHODS: diff --git a/src/aiopenapi3/v20/glue.py b/src/aiopenapi3/v20/glue.py index 4ca36fc3..768b7f53 100644 --- a/src/aiopenapi3/v20/glue.py +++ b/src/aiopenapi3/v20/glue.py @@ -265,7 +265,7 @@ def _process__headers( """ available = frozenset(result.headers.keys()) if missing := (required.keys() - available): - report: dict[str, "HeaderType"] = {k: required[k] for k in missing} + report: dict[str, HeaderType] = {k: required[k] for k in missing} raise HeadersMissingError(self.operation, report, result) for name, header in expected_response.headers.items(): data = headers.get(name, None) @@ -280,7 +280,7 @@ def _process_stream(self, result: httpx2.Response) -> tuple["ResponseHeadersType return headers, expected_response.schema_ def _process_request(self, result: httpx2.Response) -> tuple["ResponseHeadersType", Optional["ResponseDataType"]]: - rheaders: "ResponseHeadersType" + rheaders: ResponseHeadersType # spec enforces these are strings status_code = str(result.status_code) content_type = result.headers.get("Content-Type", None) diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index d6c6c09f..8239c82f 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -462,7 +462,7 @@ def _prepare_body(self, data_: Optional["RequestData"], mph: dict[str, str]) -> elif (ct := "application/octet-stream") in self.operation.requestBody.content: self.req.headers["Content-Type"] = ct - value: "RequestFileParameter" + value: RequestFileParameter if isinstance(data_, tuple) and len(data_) >= 2: # (name, file-like-object, …) self.req.content = data_[1] @@ -536,7 +536,7 @@ def _process__content_type( media-range = ( "*/*" / ( type "/*" ) / ( type "/" subtype ) ) *( OWS ";" OWS parameter ) """ content_type, _, _encoding = content_type.partition(";") - expected_media: Optional["v3xMediaTypeType"] = ( + expected_media: Optional[v3xMediaTypeType] = ( expected_response.content.get(content_type, None) or expected_response.content.get(content_type.partition("/")[0] + "/*", None) or expected_response.content.get("*/*", None) From d3801aaf33dbaeff798c149f835e88cdd7e39708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 12:49:08 +0200 Subject: [PATCH 48/58] ruff - UP045 --- pyproject.toml | 2 +- src/aiopenapi3/_types.py | 4 ++-- src/aiopenapi3/model.py | 6 +++--- src/aiopenapi3/openapi.py | 4 ++-- src/aiopenapi3/plugin.py | 2 +- src/aiopenapi3/request.py | 8 ++++---- src/aiopenapi3/v30/glue.py | 2 +- tests/ref_test.py | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 618bf0d2..a77af74f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -137,7 +137,7 @@ ignore = [ "UP018", # "UP031", # "UP037", - "UP045" +# "UP045" ] diff --git a/src/aiopenapi3/_types.py b/src/aiopenapi3/_types.py index 5353ef22..9ae1489c 100644 --- a/src/aiopenapi3/_types.py +++ b/src/aiopenapi3/_types.py @@ -1,6 +1,6 @@ import re from collections.abc import Sequence -from typing import Literal, Optional, TypeAlias, Union +from typing import Literal, TypeAlias, Union import yaml from httpx2._types import AuthTypes, FileTypes, RequestContent, RequestFiles @@ -11,7 +11,7 @@ RequestFileParameter = tuple[str, FileTypes] RequestFilesParameter = Sequence[RequestFileParameter] -JSON: TypeAlias = Optional[Union[dict[str, "JSON"], list["JSON"], str, int, float, bool]] +JSON: TypeAlias = Union[dict[str, "JSON"], list["JSON"], str, int, float, bool] | None """ Define a JSON type https://github.com/python/typing/issues/182#issuecomment-1320974824 diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index 32455163..30ed8c17 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -175,7 +175,7 @@ def _createAnnotations( if _type == "array": v = Model.createAnnotation(schema) if Model.is_nullable(schema): - v = Optional[v] # type: ignore[assignment] + v = v | None # type: ignore[assignment] self.root = v elif _type == "object": if ( @@ -201,7 +201,7 @@ def _createAnnotations( """ v = dict[str, Model.createAnnotation(schema.additionalProperties)] # type: ignore[misc,index] if Model.is_nullable(schema): - v = Optional[v] # type: ignore[assignment] + v = v | None # type: ignore[assignment] self.root = v else: assert schema.properties is not None @@ -218,7 +218,7 @@ def _createAnnotations( """not const""" if name not in schema.required or Model.is_nullable(f): """not required - or nullable""" - r = Optional[r] # type: ignore[assignment] + r = r | None # type: ignore[assignment] self.properties[Model.nameof(name)].annotation = r diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index 85612ba7..dc49a862 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -741,8 +741,8 @@ def createRequest(self, operationId: str | tuple[str, "HTTPMethodType"]) -> "Req :return: the returned Request is either :class:`aiopenapi3.request.RequestBase` or - in case of a httpx2.AsyncClient session_factory - :class:`aiopenapi3.request.AsyncRequestBase` """ - operation: Optional[OperationType] = None - request: Optional[RequestType] = None + operation: OperationType | None = None + request: RequestType | None = None try: if isinstance(operationId, str): *tags, opn = operationId.split(".") diff --git a/src/aiopenapi3/plugin.py b/src/aiopenapi3/plugin.py index 9c0255f1..5032e291 100644 --- a/src/aiopenapi3/plugin.py +++ b/src/aiopenapi3/plugin.py @@ -23,7 +23,7 @@ class Plugin(abc.ABC): class Context: ... def __init__(self) -> None: - self._api: Optional[OpenAPI] = None + self._api: OpenAPI | None = None @property def api(self): diff --git a/src/aiopenapi3/request.py b/src/aiopenapi3/request.py index 69f35ecf..448f1e10 100644 --- a/src/aiopenapi3/request.py +++ b/src/aiopenapi3/request.py @@ -47,14 +47,14 @@ class RequestParameter: def __init__(self, url: yarl.URL | str): self.url: str = str(url) - self.auth: Optional[AuthTypes] = None + self.auth: AuthTypes | None = None self.cookies: dict[str, str] = {} # self.path = {} self.params: dict[str, str] = {} - self.content: Optional[RequestContent] = None + self.content: RequestContent | None = None self.headers: dict[str, str] = {} self.data: dict[str, str] = {} # form-data - self.files: Optional[RequestFiles] = {} # form-data files + self.files: RequestFiles | None = {} # form-data files self.cert: Any = None @@ -133,7 +133,7 @@ def __init__( HTTP path """ - self.vars: Optional[RequestBase.Vars] = None + self.vars: RequestBase.Vars | None = None """ Parameter & Data """ diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index 8239c82f..9f47760a 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -536,7 +536,7 @@ def _process__content_type( media-range = ( "*/*" / ( type "/*" ) / ( type "/" subtype ) ) *( OWS ";" OWS parameter ) """ content_type, _, _encoding = content_type.partition(";") - expected_media: Optional[v3xMediaTypeType] = ( + expected_media: v3xMediaTypeType | None = ( expected_response.content.get(content_type, None) or expected_response.content.get(content_type.partition("/")[0] + "/*", None) or expected_response.content.get("*/*", None) diff --git a/tests/ref_test.py b/tests/ref_test.py index 84c3e8c1..7353b086 100644 --- a/tests/ref_test.py +++ b/tests/ref_test.py @@ -81,7 +81,7 @@ def is_required(x): assert items["id"].annotation is int assert items["name"].annotation is str - assert items["tag"].annotation == typing.Optional[str] + assert items["tag"].annotation == str | None r = ref.model_validate([{"id": 1, "name": "dog"}, {"id": 2, "name": "cat", "tag": "x"}]) assert len(r.root) == 2 From aaf0ea4464a8c9ea169e460662f61224a5b54d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 13:05:15 +0200 Subject: [PATCH 49/58] ruff - DTZ005 --- .pre-commit-config.yaml | 2 +- docs/source/conf.py | 2 +- pyproject.toml | 2 +- src/aiopenapi3/cli.py | 4 ++-- tests/forms_test.py | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bffc0692..6bf637b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -62,7 +62,7 @@ repos: - repo: https://github.com/astral-sh/uv-pre-commit # uv version. - rev: 0.12.7 + rev: 0.12.8 hooks: # Update the uv lockfile - id: uv-lock diff --git a/docs/source/conf.py b/docs/source/conf.py index b5aee812..a90aba48 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -25,7 +25,7 @@ """ project = "aiopenapi3" -copyright = f"{datetime.datetime.now().date().year}, Markus Kötter" +copyright = f"{datetime.datetime.now().date().year}, Markus Kötter" # noqa: DTZ005 author = "Markus Kötter" # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index a77af74f..b6a53de3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,7 +95,7 @@ ignore = [ # "C408", # "C417", # "C419", - "DTZ005", +# "DTZ005", # "FURB167", # "I001", # "PERF102", diff --git a/src/aiopenapi3/cli.py b/src/aiopenapi3/cli.py index 1eeae805..9dfabb43 100644 --- a/src/aiopenapi3/cli.py +++ b/src/aiopenapi3/cli.py @@ -269,13 +269,13 @@ def cmd_validate(args: argparse.Namespace) -> None: loader = loader_prepare(args, session_factory) try: - begin = datetime.datetime.now() + begin = datetime.datetime.now(tz=datetime.timezone.utc) try: api = OpenAPI.load_file(args.input, yarl.URL(args.input), plugins=plugins, loader=loader) except aiopenapi3.errors.ReferenceResolutionError as e0: print(f"{e0} {e0.document} {e0.element}") return - end = datetime.datetime.now() + end = datetime.datetime.now(tz=datetime.timezone.utc) duration = end - begin except ValueError: logg.exception("error") diff --git a/tests/forms_test.py b/tests/forms_test.py index 0cef21e8..b86f6ab3 100644 --- a/tests/forms_test.py +++ b/tests/forms_test.py @@ -330,11 +330,11 @@ async def test_String(server, client, form_type): @pytest.mark.asyncio(loop_scope="session") async def test_DateTime(server, client, form_type): cls = client._.datetime.operation.requestBody.content[form_type].schema_.get_type() - now = datetime.datetime.now() + t = (2026, 8, 29, 15, 4, 22, 32582) + dtl = datetime.datetime(*t) # noqa: DTZ001 + dt = dtl.astimezone(datetime.timezone.utc) - data = cls( - time=now.time(), date=now.date(), datetime=datetime.datetime.now(tz=datetime.timezone.utc), datetimelocal=now - ) + data = cls(time=dt.time(), date=dt.date(), datetime=dt, datetimelocal=dtl) r = await client._.datetime(data=data) assert r == "ok" From ff2c59b27dcff45452a4cbf1c17e11372007785c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 14:28:19 +0200 Subject: [PATCH 50/58] ruff - BLE001 --- pyproject.toml | 2 +- src/aiopenapi3/base.py | 12 ++++-------- src/aiopenapi3/debug.py | 2 +- src/aiopenapi3/loader.py | 6 +++--- src/aiopenapi3/model.py | 5 +---- tests/apiv2_test.py | 2 +- tests/path_test.py | 29 +++++++++++++++++++++-------- tests/petstore_test.py | 2 +- tests/petstorev3_test.py | 2 +- tests/stream_test.py | 2 +- tests/tls_test.py | 2 +- 11 files changed, 36 insertions(+), 30 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b6a53de3..7862b763 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,7 +90,7 @@ ignore = [ # "B015", # "B018", # "B020", - "BLE001", +# "BLE001", "C400", # "C408", # "C417", diff --git a/src/aiopenapi3/base.py b/src/aiopenapi3/base.py index c117aa7c..60efd4b9 100644 --- a/src/aiopenapi3/base.py +++ b/src/aiopenapi3/base.py @@ -351,14 +351,10 @@ def __getstate__(self): :return: """ r = BaseModel.__getstate__(self) - try: - for k, v in {"_model_type": None, "_model_types": []}.items(): - if k in r["__pydantic_private__"]: - r["__pydantic_private__"] = r["__pydantic_private__"].copy() - r["__pydantic_private__"][k] = v - - except Exception: - pass + for k, v in {"_model_type": None, "_model_types": []}.items(): + if k in r["__pydantic_private__"]: + r["__pydantic_private__"] = r["__pydantic_private__"].copy() + r["__pydantic_private__"][k] = v return r def _get_identity(self, prefix="XLS", name=None): diff --git a/src/aiopenapi3/debug.py b/src/aiopenapi3/debug.py index 69a455c4..7b9dfe09 100644 --- a/src/aiopenapi3/debug.py +++ b/src/aiopenapi3/debug.py @@ -38,7 +38,7 @@ def log_response(response): try: if data := request.read(): print(json.dumps(json.loads(data.decode()), indent=4)) - except Exception as e: + except Exception as e: # noqa: BLE001 print(e) diff --git a/src/aiopenapi3/loader.py b/src/aiopenapi3/loader.py index a58288c0..488e026b 100644 --- a/src/aiopenapi3/loader.py +++ b/src/aiopenapi3/loader.py @@ -171,11 +171,11 @@ def parse(self, plugins: Plugins, url: yarl.URL, data: str): if file.suffix not in (".yaml", ".json"): try: return self.parse(plugins, url.with_path("/test.yaml"), data) - except Exception as e: + except Exception as e: # noqa: BLE001 pass try: return self.parse(plugins, url.with_path("/test.json"), data) - except Exception as e: + except Exception as e: # noqa: BLE001 pass if file.suffix == ".yaml": @@ -299,7 +299,7 @@ def load(self, plugins: "Plugins", url: yarl.URL, codec: str | None = None): r = i.load(plugins, url, codec) log.debug(f"using {i}") return r - except Exception as exc: + except Exception as exc: # noqa: BLE001 errors.append((i, str(exc))) for l, e in errors: log.debug(f"{l} {e}") diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index 30ed8c17..3130e60f 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -806,10 +806,7 @@ def nameof(name: str, args=None): if name.startswith("model_"): rename = f"x{name}" - try: - rename = re.sub(r"[#@\.-]", "_", rename) - except Exception as e: - print(e) + rename = re.sub(r"[#@\.-]", "_", rename) if rename[0] == "_": rename = rename.lstrip("_") + "_" diff --git a/tests/apiv2_test.py b/tests/apiv2_test.py index 45cd2433..6778fe52 100644 --- a/tests/apiv2_test.py +++ b/tests/apiv2_test.py @@ -272,7 +272,7 @@ def test_allOf_resolution(openapi_version, petstore_expanded): assert sorted(x.name for x in filter(lambda y: y.required, items.values())) == sorted(["id", "name"]), ( ref.model_json_schema() ) - except Exception as e: + except Exception as e: # noqa: BLE001 print(e) assert sorted(x.name for x in items.values()) == ["id", "name", "tag"] diff --git a/tests/path_test.py b/tests/path_test.py index 46c766dd..312b1e9d 100644 --- a/tests/path_test.py +++ b/tests/path_test.py @@ -5,6 +5,7 @@ import base64 import copy import pathlib +import re import uuid import httpx2 @@ -164,14 +165,6 @@ def test_paths_security(httpx2_mock, with_paths_security): request = httpx2_mock.get_requests()[-1] assert request.headers["Authorization"].split(" ")[1] == base64.b64encode((auth + ":" + auth).encode()).decode() - try: - pass - except Exception: - api.authenticate(None, digestAuth=(auth, auth)) - api._.api_v1_auth_login_create(data={}, parameters={}) - request = httpx2_mock.get_requests()[-1] - # can't test? - api.authenticate(None, bearerAuth=auth) api._.api_v1_auth_login_create(data={}, parameters={}) request = httpx2_mock.get_requests()[-1] @@ -183,6 +176,26 @@ def test_paths_security(httpx2_mock, with_paths_security): request = httpx2_mock.get_requests()[-1] +def test_paths_security_digest(httpx2_mock, with_paths_security): + api = OpenAPI(URLBASE, with_paths_security, session_factory=httpx2.Client, use_operation_tags=False) + auth = str(uuid.uuid4()) + httpx2_mock.add_response( + status_code=401, + headers={ + "WWW-Authenticate": """Digest realm="testrealm@host.com",qop="auth,auth-int",nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",opaque="5ccc069c403ebaf9f0171e9517f40e41" """ + }, + ) + httpx2_mock.add_response(headers={"Content-Type": "application/json"}, json="user") + + api.authenticate(None, digestAuth=(auth, auth)) + api._.api_v1_auth_login_create(data={}, parameters={}) + request = httpx2_mock.get_requests()[-1] + + reg = re.compile(r'([^\s,]+) ?[=] ?"?([^\s,"]+)"?') + v = dict(reg.findall(request.headers["Authorization"])) + assert v["username"] == auth + + def test_paths_security_combined(httpx2_mock, with_paths_security): api = OpenAPI(URLBASE, with_paths_security, session_factory=httpx2.Client, use_operation_tags=False) httpx2_mock.add_response(headers={"Content-Type": "application/json"}, json="user") diff --git a/tests/petstore_test.py b/tests/petstore_test.py index bb99a18c..8247f620 100644 --- a/tests/petstore_test.py +++ b/tests/petstore_test.py @@ -206,7 +206,7 @@ def test_pets(api, login): for i, pet in enumerate(r): try: api._.deletePet(parameters={"petId": pet.id}) - except Exception: + except Exception: # noqa: BLE001 pass if i > 3: break diff --git a/tests/petstorev3_test.py b/tests/petstorev3_test.py index f8ceab49..d040415f 100644 --- a/tests/petstorev3_test.py +++ b/tests/petstorev3_test.py @@ -231,7 +231,7 @@ def test_pets(api, login): for i, pet in enumerate(r): try: api._.deletePet(parameters={"petId": pet.id}) - except Exception: + except Exception: # noqa: BLE001 pass if i > 3: break diff --git a/tests/stream_test.py b/tests/stream_test.py index d876168c..e6ef1086 100644 --- a/tests/stream_test.py +++ b/tests/stream_test.py @@ -123,7 +123,7 @@ def cb(): data = yield try: file = t.model_validate(data) - except Exception as e: + except Exception as e: # noqa: BLE001 print(e) assert len(file.name) == 16 # process received File model here diff --git a/tests/tls_test.py b/tests/tls_test.py index 61abb189..c477a09d 100644 --- a/tests/tls_test.py +++ b/tests/tls_test.py @@ -90,7 +90,7 @@ async def wait_for_server(server): try: host, _, port = server.bind[0].rpartition(":") _r, w = await asyncio.open_connection(host=host, port=port) - except Exception as e: + except Exception as e: # noqa: BLE001 await asyncio.sleep(0.1) else: await w.drain() From 6dccdaa0ecf2f11ca00d8dd309b34280ce6d8b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 14:33:29 +0200 Subject: [PATCH 51/58] ruff - S110 --- pyproject.toml | 2 +- src/aiopenapi3/loader.py | 4 ++-- tests/petstore_test.py | 2 +- tests/petstorev3_test.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7862b763..145bb0ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -119,7 +119,7 @@ ignore = [ # "RUF046", # "RUF059", # "RUF100", - "S110", +# "S110", # "SIM101", # "SIM102", # "SIM103", diff --git a/src/aiopenapi3/loader.py b/src/aiopenapi3/loader.py index 488e026b..e66553b9 100644 --- a/src/aiopenapi3/loader.py +++ b/src/aiopenapi3/loader.py @@ -171,11 +171,11 @@ def parse(self, plugins: Plugins, url: yarl.URL, data: str): if file.suffix not in (".yaml", ".json"): try: return self.parse(plugins, url.with_path("/test.yaml"), data) - except Exception as e: # noqa: BLE001 + except Exception as e: # noqa: BLE001, S110 pass try: return self.parse(plugins, url.with_path("/test.json"), data) - except Exception as e: # noqa: BLE001 + except Exception as e: # noqa: BLE001, S110 pass if file.suffix == ".yaml": diff --git a/tests/petstore_test.py b/tests/petstore_test.py index 8247f620..627db699 100644 --- a/tests/petstore_test.py +++ b/tests/petstore_test.py @@ -206,7 +206,7 @@ def test_pets(api, login): for i, pet in enumerate(r): try: api._.deletePet(parameters={"petId": pet.id}) - except Exception: # noqa: BLE001 + except Exception: # noqa: BLE001, S110 pass if i > 3: break diff --git a/tests/petstorev3_test.py b/tests/petstorev3_test.py index d040415f..24ad3797 100644 --- a/tests/petstorev3_test.py +++ b/tests/petstorev3_test.py @@ -231,7 +231,7 @@ def test_pets(api, login): for i, pet in enumerate(r): try: api._.deletePet(parameters={"petId": pet.id}) - except Exception: # noqa: BLE001 + except Exception: # noqa: BLE001, S110 pass if i > 3: break From b4e4c3f0a1fadf7a2430c45ad6ee870c2c2d31c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 14:34:18 +0200 Subject: [PATCH 52/58] ruff - RET501 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 145bb0ea..a21c58ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,7 +110,7 @@ ignore = [ # "PLW0602", # "PT031", # "PYI055", - "RET501", +# "RET501", # "RUF010", # "RUF012", # "RUF013", From 9854acedf59b2c402ffd4a41bbbf4c6b422a9fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 14:35:39 +0200 Subject: [PATCH 53/58] ruff - UP018 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a21c58ae..d1007866 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -134,7 +134,7 @@ ignore = [ # "TRY203", # "TRY401", "UP007", - "UP018", +# "UP018", # "UP031", # "UP037", # "UP045" From 982bab9e92a355ecde801b467d1bd7d0f53bebe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 14:36:37 +0200 Subject: [PATCH 54/58] ruff - C400 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d1007866..b66f7e46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,7 +91,7 @@ ignore = [ # "B018", # "B020", # "BLE001", - "C400", +# "C400", # "C408", # "C417", # "C419", From b1c668023d02076b3ba4ba69d4922338da93f37b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 14:38:58 +0200 Subject: [PATCH 55/58] ruff - E402 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b66f7e46..3395b253 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ fixable = ["ALL"] ignore = [ "E741", "F841", - "E402", +# "E402", # "B008", # "B009", # "B015", From 1ec43b0ef2207f3446912e83b81166ab62afd2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 14:40:02 +0200 Subject: [PATCH 56/58] ruff - finish --- .pre-commit-config.yaml | 2 +- pyproject.toml | 59 +++-------------------------------------- 2 files changed, 4 insertions(+), 57 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6bf637b9..5fb1eb37 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: hooks: # Run the linter. - id: ruff-check - args: [--fix, --unsafe-fixes] + args: [--fix] # Run the formatter. - id: ruff-format - repo: 'https://github.com/pre-commit/pre-commit-hooks' diff --git a/pyproject.toml b/pyproject.toml index 3395b253..9249e92b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,62 +82,9 @@ indent-width = 4 [tool.ruff.lint] fixable = ["ALL"] ignore = [ - "E741", - "F841", -# "E402", -# "B008", -# "B009", -# "B015", -# "B018", -# "B020", -# "BLE001", -# "C400", -# "C408", -# "C417", -# "C419", -# "DTZ005", -# "FURB167", -# "I001", -# "PERF102", -# "PIE790", -# "PIE804", -# "PIE808", -# "PLC0208", -# "PLR1708", -# "PLR1711", -# "PLR2044", -# "PLW0120", -# "PLW0602", -# "PT031", -# "PYI055", -# "RET501", -# "RUF010", -# "RUF012", -# "RUF013", -# "RUF017", -# "RUF022", -# "RUF046", -# "RUF059", -# "RUF100", -# "S110", -# "SIM101", -# "SIM102", -# "SIM103", -# "SIM118", -# "SIM201", -# "SIM210", -# "SIM211", -# "TC004", -# "TC005", -# "TRY004", -# "TRY201", -# "TRY203", -# "TRY401", - "UP007", -# "UP018", -# "UP031", -# "UP037", -# "UP045" + "E741", # ambiguous-variable-name + "F841", # unused-variable + "UP007", # https://github.com/astral-sh/ruff/issues/21347 ] From 224e3a3bc604aa711c9988e07dd8f4513a4468d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 14:47:07 +0200 Subject: [PATCH 57/58] ruff - UP045 --- src/aiopenapi3/model.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aiopenapi3/model.py b/src/aiopenapi3/model.py index 3130e60f..1c9be864 100644 --- a/src/aiopenapi3/model.py +++ b/src/aiopenapi3/model.py @@ -175,7 +175,7 @@ def _createAnnotations( if _type == "array": v = Model.createAnnotation(schema) if Model.is_nullable(schema): - v = v | None # type: ignore[assignment] + v = Optional[v] # noqa: UP045 # type: ignore[assignment] self.root = v elif _type == "object": if ( @@ -201,7 +201,7 @@ def _createAnnotations( """ v = dict[str, Model.createAnnotation(schema.additionalProperties)] # type: ignore[misc,index] if Model.is_nullable(schema): - v = v | None # type: ignore[assignment] + v = Optional[v] # noqa: UP045 # type: ignore[assignment] self.root = v else: assert schema.properties is not None @@ -218,7 +218,7 @@ def _createAnnotations( """not const""" if name not in schema.required or Model.is_nullable(f): """not required - or nullable""" - r = r | None # type: ignore[assignment] + r = Optional[r] # noqa: UP045 # type: ignore[assignment] self.properties[Model.nameof(name)].annotation = r From c1ab2cd1de42d1c225a56c7909516416c763d171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20K=C3=B6tter?= Date: Tue, 1 Sep 2026 15:08:59 +0200 Subject: [PATCH 58/58] ruff - UP007 partially applied, c.f. https://github.com/astral-sh/ruff/issues/21347 --- pyproject.toml | 4 ++- src/aiopenapi3/_types.py | 49 +++++++++++++++++----------------- src/aiopenapi3/extra/reduce.py | 4 +-- src/aiopenapi3/openapi.py | 4 +-- src/aiopenapi3/v20/security.py | 8 ++---- src/aiopenapi3/v30/glue.py | 6 ++--- src/aiopenapi3/v30/security.py | 6 ++--- src/aiopenapi3/v31/security.py | 14 +++++----- src/aiopenapi3/v32/security.py | 14 +++++----- tests/api/v2/schema.py | 6 ++--- 10 files changed, 53 insertions(+), 62 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9249e92b..f828d920 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,9 +84,11 @@ fixable = ["ALL"] ignore = [ "E741", # ambiguous-variable-name "F841", # unused-variable - "UP007", # https://github.com/astral-sh/ruff/issues/21347 ] +[tool.ruff.lint.per-file-ignores] +"src/aiopenapi3/model.py" = ["UP007"] # https://github.com/astral-sh/ruff/issues/21347 +"tests/parsing_test.py" = ["UP007"] [tool.ruff.format] quote-style = "double" diff --git a/src/aiopenapi3/_types.py b/src/aiopenapi3/_types.py index 9ae1489c..eeab9a72 100644 --- a/src/aiopenapi3/_types.py +++ b/src/aiopenapi3/_types.py @@ -1,6 +1,6 @@ import re from collections.abc import Sequence -from typing import Literal, TypeAlias, Union +from typing import Literal, TypeAlias import yaml from httpx2._types import AuthTypes, FileTypes, RequestContent, RequestFiles @@ -11,43 +11,42 @@ RequestFileParameter = tuple[str, FileTypes] RequestFilesParameter = Sequence[RequestFileParameter] -JSON: TypeAlias = Union[dict[str, "JSON"], list["JSON"], str, int, float, bool] | None +JSON: TypeAlias = dict[str, "JSON"] | list["JSON"] | str | int | float | bool | None """ Define a JSON type https://github.com/python/typing/issues/182#issuecomment-1320974824 """ -RequestData = Union[JSON, BaseModel, RequestFilesParameter] -RequestParameter = Union[str, BaseModel] +RequestData = JSON | BaseModel | RequestFilesParameter +RequestParameter = str | BaseModel RequestParameters = dict[str, RequestParameter] -RootType = Union[v20.Root, v30.Root, v31.Root] -ServerType = Union[v30.Server, v31.Server] -ReferenceType = Union[v20.Reference, v30.Reference, v31.Reference] -SchemaType = Union[v20.Schema, v30.Schema, v31.Schema] -v3xSchemaType = Union[v30.Schema, v31.Schema] -DiscriminatorType = Union[v30.Discriminator, v31.Discriminator] -PathItemType = Union[v20.PathItem, v30.PathItem, v31.PathItem] -OperationType = Union[v20.Operation, v30.Operation, v31.Operation] -ParameterType = Union[v20.Parameter, v30.Parameter, v31.Parameter] -HeaderType = Union[v20.Header, v30.Header, v31.Header] -RequestType = Union[v20.Request, v30.Request] -AsyncRequestType = Union[v20.AsyncRequest, v30.AsyncRequest] -MediaTypeType = Union[v30.MediaType, v31.MediaType] -ExpectedType = Union[v20.Response, MediaTypeType] -ResponseHeadersType = dict[str, Union[str, BaseModel, list[BaseModel]]] -ResponseDataType = Union[BaseModel, bytes, str] -TagType = Union[v20.Tag, v30.Tag, v32.Tag] +RootType = v20.Root | v30.Root | v31.Root +ServerType = v30.Server | v31.Server +ReferenceType = v20.Reference | v30.Reference | v31.Reference +SchemaType = v20.Schema | v30.Schema | v31.Schema +v3xSchemaType = v30.Schema | v31.Schema +DiscriminatorType = v30.Discriminator | v31.Discriminator +PathItemType = v20.PathItem, v30.PathItem | v31.PathItem +OperationType = v20.Operation | v30.Operation | v31.Operation +ParameterType = v20.Parameter | v30.Parameter | v31.Parameter +HeaderType = v20.Header | v30.Header | v31.Header +RequestType = v20.Request | v30.Request +AsyncRequestType = v20.AsyncRequest | v30.AsyncRequest +MediaTypeType = v30.MediaType | v31.MediaType +ExpectedType = v20.Response | MediaTypeType +ResponseHeadersType = dict[str, str | BaseModel | list[BaseModel]] +ResponseDataType = BaseModel, bytes | str +TagType = v20.Tag | v30.Tag | v32.Tag -YAMLLoaderType = Union[type[yaml.Loader], type[yaml.CLoader], type[yaml.SafeLoader], type[yaml.CSafeLoader]] +YAMLLoaderType = type[yaml.Loader] | type[yaml.CLoader] | type[yaml.SafeLoader] | type[yaml.CSafeLoader] -PrimitiveTypes = Union[str, float, int, bool] +PrimitiveTypes = str | float | int | bool HTTPMethodType = Literal["get", "put", "post", "delete", "options", "head", "patch", "trace"] -HTTPMethodMatchType = Union[re.Pattern, HTTPMethodType] +HTTPMethodMatchType = re.Pattern | HTTPMethodType __all__: list[str] = [ - # end httpx "JSON", "AuthTypes", "DiscriminatorType", diff --git a/src/aiopenapi3/extra/reduce.py b/src/aiopenapi3/extra/reduce.py index cffbd39f..4e179b67 100644 --- a/src/aiopenapi3/extra/reduce.py +++ b/src/aiopenapi3/extra/reduce.py @@ -8,8 +8,8 @@ if typing.TYPE_CHECKING: from .._types import HTTPMethodMatchType, PathItemType - PathMatchType = Union[re.Pattern, str] - OperationIdMatchType = Union[re.Pattern, str] + PathMatchType = re.Pattern | str + OperationIdMatchType = re.Pattern | str class Reduce(Document, Init): diff --git a/src/aiopenapi3/openapi.py b/src/aiopenapi3/openapi.py index dc49a862..8ea5629a 100644 --- a/src/aiopenapi3/openapi.py +++ b/src/aiopenapi3/openapi.py @@ -5,7 +5,7 @@ import random import typing from collections.abc import Callable -from typing import Any, ForwardRef, Optional, TypeGuard, Union, cast +from typing import Any, ForwardRef, Optional, TypeGuard, cast import httpx2 import yarl @@ -522,7 +522,7 @@ def is_schema(v: tuple[str, "SchemaType"]) -> bool: elif isinstance(self._root, (v30.Root, v31.Root)): # Schema - documents = cast(Union[list[v30.Root], list[v31.Root]], self._documents.values()) + documents = cast(list[v30.Root] | list[v31.Root], self._documents.values()) components = [x.components for x in filter(has_components, documents) if x.components is not None] assert components is not None if only_required is False: diff --git a/src/aiopenapi3/v20/security.py b/src/aiopenapi3/v20/security.py index f9810e37..10d62997 100644 --- a/src/aiopenapi3/v20/security.py +++ b/src/aiopenapi3/v20/security.py @@ -1,4 +1,4 @@ -from typing import Annotated, Literal, Union +from typing import Annotated, Literal from pydantic import Field, RootModel @@ -32,11 +32,7 @@ class oauth2(_SecurityScheme): class SecurityScheme( RootModel[ Annotated[ - Union[ - _SecuritySchemes.basic, - _SecuritySchemes.apiKey, - _SecuritySchemes.oauth2, - ], + _SecuritySchemes.basic | _SecuritySchemes.apiKey | _SecuritySchemes.oauth2, Field(discriminator="type"), ] ] diff --git a/src/aiopenapi3/v30/glue.py b/src/aiopenapi3/v30/glue.py index 9f47760a..cb4d6218 100644 --- a/src/aiopenapi3/v30/glue.py +++ b/src/aiopenapi3/v30/glue.py @@ -2,7 +2,7 @@ import json import urllib.parse from collections.abc import Sequence -from typing import TYPE_CHECKING, Any, Optional, Union, cast +from typing import TYPE_CHECKING, Any, Optional, cast import httpx2 @@ -52,8 +52,8 @@ from .paths import MediaType as v30MediaType from .paths import Response as v30Response - v3xResponseType = Union[v30Response, v31Response] - v3xMediaTypeType = Union[v30MediaType, v31MediaType] + v3xResponseType = v30Response | v31Response + v3xMediaTypeType = v30MediaType | v31MediaType class Request(RequestBase): diff --git a/src/aiopenapi3/v30/security.py b/src/aiopenapi3/v30/security.py index 30f338b1..edc3df27 100644 --- a/src/aiopenapi3/v30/security.py +++ b/src/aiopenapi3/v30/security.py @@ -1,4 +1,4 @@ -from typing import Annotated, Literal, Union +from typing import Annotated, Literal from pydantic import Field, RootModel, constr @@ -61,9 +61,7 @@ class openIdConnect(_SecurityScheme): class SecurityScheme( RootModel[ Annotated[ - Union[ - _SecuritySchemes.apiKey, _SecuritySchemes.http, _SecuritySchemes.oauth2, _SecuritySchemes.openIdConnect - ], + _SecuritySchemes.apiKey | _SecuritySchemes.http | _SecuritySchemes.oauth2 | _SecuritySchemes.openIdConnect, Field(discriminator="type"), ] ] diff --git a/src/aiopenapi3/v31/security.py b/src/aiopenapi3/v31/security.py index b82e0a1e..76777a54 100644 --- a/src/aiopenapi3/v31/security.py +++ b/src/aiopenapi3/v31/security.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Annotated, Literal, Union +from typing import Annotated, Literal from pydantic import Field, RootModel, constr @@ -74,13 +74,11 @@ class openIdConnect(_SecurityScheme): class SecurityScheme( RootModel[ Annotated[ - Union[ - _SecuritySchemes.apiKey, - _SecuritySchemes.http, - _SecuritySchemes.mutualTLS, - _SecuritySchemes.oauth2, - _SecuritySchemes.openIdConnect, - ], + _SecuritySchemes.apiKey + | _SecuritySchemes.http + | _SecuritySchemes.mutualTLS + | _SecuritySchemes.oauth2 + | _SecuritySchemes.openIdConnect, Field(discriminator="type"), ] ] diff --git a/src/aiopenapi3/v32/security.py b/src/aiopenapi3/v32/security.py index 02b2cd5c..1a007783 100644 --- a/src/aiopenapi3/v32/security.py +++ b/src/aiopenapi3/v32/security.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Annotated, Literal, Union +from typing import Annotated, Literal from pydantic import Field, RootModel, constr @@ -79,13 +79,11 @@ class openIdConnect(_SecurityScheme): class SecurityScheme( RootModel[ Annotated[ - Union[ - _SecuritySchemes.apiKey, - _SecuritySchemes.http, - _SecuritySchemes.mutualTLS, - _SecuritySchemes.oauth2, - _SecuritySchemes.openIdConnect, - ], + _SecuritySchemes.apiKey + | _SecuritySchemes.http + | _SecuritySchemes.mutualTLS + | _SecuritySchemes.oauth2 + | _SecuritySchemes.openIdConnect, Field(discriminator="type"), ] ] diff --git a/tests/api/v2/schema.py b/tests/api/v2/schema.py index 3e149188..2440979b 100644 --- a/tests/api/v2/schema.py +++ b/tests/api/v2/schema.py @@ -1,6 +1,6 @@ import uuid from datetime import timedelta -from typing import Annotated, Literal, Union +from typing import Annotated, Literal from pydantic import BaseModel, ConfigDict, Field, RootModel @@ -26,7 +26,7 @@ class WhiteCat(PetBase): white_name: str -class Cat(RootModel[Annotated[Union[BlackCat, WhiteCat], Field(discriminator="color")]]): +class Cat(RootModel[Annotated[BlackCat | WhiteCat, Field(discriminator="color")]]): def __getattr__(self, item): return getattr(self.root, item) @@ -40,7 +40,7 @@ class Dog(PetBase): age: timedelta -class Pet(RootModel[Annotated[Union[Cat, Dog], Field(discriminator="pet_type")]]): +class Pet(RootModel[Annotated[Cat | Dog, Field(discriminator="pet_type")]]): def __getattr__(self, item): return getattr(self.root, item)