Replace openapi-core deprecated interface

openapi-core==0.23.0 dropped deprecated interface breaking us. The
change itself is small, but not backwards compatible and uncovers quite
some issues.

relates-to: https://review.opendev.org/c/openstack/manila/+/982081
relates-to: https://review.opendev.org/c/openstack/cinder/+/982079
Closes-Bug: #2145963
Change-Id: Ifcf90af5bfbeebca3a8728a71b30b09d6cd4ede5
Signed-off-by: Artem Goncharov <artem.goncharov@gmail.com>
This commit is contained in:
Artem Goncharov
2026-03-30 16:44:14 +02:00
parent e61ddd03e7
commit 107eff989d
7 changed files with 83 additions and 28 deletions
+3 -2
View File
@@ -17,7 +17,7 @@ from typing import Any
import jsonref
import yaml
from openapi_core import Spec
from openapi_core import OpenAPI
from pydantic import BaseModel
VERSION_RE = re.compile(r"^[Vv]([0-9]+)(\.([0-9]+))?$")
@@ -124,7 +124,8 @@ def get_openapi_spec(path: str | Path):
"""Load OpenAPI spec from a file"""
with open(path) as fp:
spec_data = jsonref.replace_refs(yaml.safe_load(fp), proxies=False)
return Spec.from_dict(spec_data)
return spec_data
# return OpenAPI.from_dict(spec_data)
def find_openapi_operation(spec, operationId: str):
-2
View File
@@ -14,8 +14,6 @@ from typing import Any
from pydantic import BaseModel, ConfigDict, Field
# from openapi_core import Spec
class TypeSchema(BaseModel):
# TODO(gtema): enums are re-shuffled on every serialization
+58 -21
View File
@@ -16,27 +16,28 @@ import datetime
import enum
import importlib
import inspect
import jsonref
import logging
import re
from collections.abc import Callable
from pathlib import Path
from typing import Any, Literal
from collections.abc import Callable
import re
from codegenerator import common
from codegenerator.common.schema import ParameterSchema
from codegenerator.common.schema import PathSchema
from codegenerator.common.schema import SpecSchema
from codegenerator.common.schema import TypeSchema
from codegenerator.metadata import MetadataGenerator
from codegenerator import model
from codegenerator.openapi.utils import rst_to_md
from openapi_core import Spec
import jsonref
from openapi_core import OpenAPI
from openapi_spec_validator import validate
from ruamel.yaml.scalarstring import LiteralScalarString
from ruamel.yaml import YAML
from ruamel.yaml.scalarstring import LiteralScalarString
from wsme import types as wtypes
from codegenerator import common, model
from codegenerator.common.schema import (
ParameterSchema,
PathSchema,
SpecSchema,
TypeSchema,
)
from codegenerator.metadata import MetadataGenerator
from codegenerator.openapi.utils import rst_to_md
VERSION_RE = re.compile(r"[Vv][0-9\.]*")
@@ -223,12 +224,10 @@ class OpenStackServerSourceBase:
model_data = openapi_spec.model_dump(
exclude_none=True, exclude_defaults=True, by_alias=True
)
Spec.from_dict(model_data)
OpenAPI.from_dict(model_data)
validate(model_data)
openapi_spec = Spec.from_dict(
jsonref.replace_refs(model_data, proxies=False)
)
openapi_spec = jsonref.replace_refs(model_data, proxies=False)
# Build the metadata as if we would do this normally
metadata = MetadataGenerator.build_metadata(
@@ -1202,6 +1201,8 @@ class OpenStackServerSourceBase:
if isinstance(schema, dict):
# Forcibly convert to TypeSchema
if isinstance(schema.get("type"), tuple):
schema["type"] = list(schema["type"])
schema = TypeSchema(**schema)
properties = getattr(schema, "properties", None)
if properties:
@@ -1216,6 +1217,17 @@ class OpenStackServerSourceBase:
for k, v in properties.items():
typ = v.get("type")
if isinstance(typ, tuple):
# A very dirty hack. In cinder a type is defined as tuple.
# Before
# https://review.opendev.org/c/openstack/cinder/+/982079 is
# merged we need to tweak the schema.
typ = list(typ)
v["type"] = typ
if v.get("enum") and isinstance(v["enum"], tuple):
# Hack for the manila bug
# https://review.opendev.org/c/openstack/manila/+/982081
v["enum"] = list(v["enum"])
if typ == "object":
schema.properties[k] = self._sanitize_schema(v)
if typ == "array":
@@ -1635,16 +1647,41 @@ def _convert_wsme_to_jsonschema(body_spec):
res["enum"] = list(values)
# elif hasattr(body_spec, "__name__") and body_spec.__name__ == "bool":
elif wtypes.isdict(body_spec):
# The value may be a custom wrapper. In this case (when no
# conversion is possible just fallback to the {}
try:
value_type = _convert_wsme_to_jsonschema(body_spec.value_type)
except RuntimeError:
value_type = {}
res = {
"type": "object",
"additionalProperties": _convert_wsme_to_jsonschema(
body_spec.value_type
),
"additionalProperties": value_type,
# _convert_wsme_to_jsonschema(
# body_spec.value_type
# ),
}
elif wtypes.isusertype(body_spec):
if hasattr(body_spec, "types"):
# Multitype like magnum defines
type_defs = getattr(body_spec, "types")
res_types: list[str] = []
for typ in type_defs:
if typ is str:
res_types.append("string")
elif typ is float:
res_types.append("number")
elif typ is int:
res_types.append("integer")
elif typ is bool:
res_types.append("boolean")
else:
raise RuntimeError(f"Unsupported multitype entity {typ}")
res = {"type": res_types}
return res
basetype = body_spec.basetype
name = body_spec.name
if basetype is str:
if basetype is str and name:
res = {"type": "string", "format": name}
else:
raise RuntimeError(f"Unsupported basetype {basetype}")
+13 -1
View File
@@ -23,6 +23,7 @@ from codegenerator.common import (
BasePrimitiveType,
)
from codegenerator.common import rust as common_rust
from codegenerator.model import OneOfType, PrimitiveType
BASIC_FIELDS = [
"id",
@@ -581,7 +582,18 @@ class RequestTypeManager(common_rust.TypeManager):
)
):
dict_type_model = self._get_adt_by_reference(field.data_type)
simplified_data_type = JsonValue()
simplified_data_type = None
if isinstance(dict_type_model.value_type, OneOfType):
if all(
isinstance(item, PrimitiveType)
for item in dict_type_model.value_type.kinds
):
# When all of the enum types are primitives just use the
# plain string and deser it into the enum later in the
# cli-sdk setter.
simplified_data_type = String()
if not simplified_data_type:
simplified_data_type = JsonValue()
simplified_data_type.original_data_type = (
field_data_type.value_type
)
+7
View File
@@ -335,6 +335,13 @@ Some({{ val }})
.into_iter(),
);
{%- elif param.data_type.value_type.__class__.__name__ == "String" and original_type.__class__.__name__ == "Enum" %}
{# in magnum labels_XXX is a dict with the multitype value. In SDK this maps to Enum which we need to deser into #}
let mut data: Vec<(String, {{ sdk_mod_path[-1] }}::{{ original_type.name }})> = Vec::new();
for (k, v) in {{ val_var }}.iter() {
data.push((k.clone(), serde_json::from_str(&v)?));
}
{{ dst_var }}.{{ param.remote_name }}(data.into_iter());
{%- elif param.data_type.value_type.__class__.__name__ == "Option" %}
{{ dst_var }}.{{ param.remote_name }}({{ val_var | replace("&", "") }}.iter().cloned().map(|(k, v)| (k, v.map(Into::into))));
{%- elif param.data_type.value_type.__class__.__name__ == "JsonValue" and original_type.__class__.__name__ == "ArrayInput" %}
+1 -1
View File
@@ -11,7 +11,7 @@ dynamic = ["version"]
dependencies = [
"Jinja2>=3.0", # BSD
"jsonref>=1.0", # MIT
"openapi_core>=0.17", # BSD
"openapi_core>=0.19", # BSD
"pydantic>=2.6", # MIT
"ruamel.yaml>=0.18", # MIT
"jsonschema>=4.19", # MIT
+1 -1
View File
@@ -1,6 +1,6 @@
Jinja2>=3.0 # BSD
jsonref>=1.0 # MIT
openapi_core>=0.17 # BSD
openapi_core>=0.19.0 # BSD
pydantic>=2.6 # MIT
ruamel.yaml>=0.18 # MIT
jsonschema>=4.19 # MIT