Skip to content

Commit ebd8ee7

Browse files
authored
Fix x-order bug (#2562)
* Use private _attributes_set property * Pydantic 2.12.0 saves the json_schema_extra in A property called _attributes set * This means changes to the json_schema_extra dict will not take effect during its rendering as json * Ensure that we use the dict from the _attributes_set if we can * Always add x-order to any dictionary we are initialising json_schema_extra with * Ensure nullable properties are not required * Find the schemas present in the openapi schema * Determine if the properties are nullable * Ensure that nullable properties are not in the required list * Fix lint * Make function more readable * Fix infinite recursion * Fix lint
1 parent 1087c98 commit ebd8ee7

File tree

2 files changed

+28
-7
lines changed

2 files changed

+28
-7
lines changed

python/cog/predictor.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -282,20 +282,30 @@ class Config:
282282
parameter.default.default is PydanticUndefined
283283
or parameter.default.default is ...
284284
):
285-
if PYDANTIC_V2:
286-
parameter.default.default = None
287-
else:
285+
parameter.default.default = None
286+
if not PYDANTIC_V2:
288287
parameter.default.default_factory = None
289-
parameter.default.default = None
290288
default = parameter.default
291289

290+
extra: Dict[str, Any] = {}
292291
if PYDANTIC_V2:
293292
# https://github.com/pydantic/pydantic/blob/2.7/pydantic/json_schema.py#L1436-L1446
294293
# json_schema_extra can be a callable, but we don't set that and users shouldn't set that
295294
if not default.json_schema_extra: # type: ignore
296-
default.json_schema_extra = {} # type: ignore
295+
default.json_schema_extra = {"x-order": order} # type: ignore
297296
assert isinstance(default.json_schema_extra, dict) # type: ignore
298-
extra = default.json_schema_extra # type: ignore
297+
# In Pydantic 2.12.0 the json_schema_extra field is copied into a variable called "_attributes_set"
298+
# that gets created in the constructor.
299+
# This means that changes to that dictionary after the construction don't take effect during the render
300+
# to openapi schema JSON.
301+
# To get around this, we will reference the dictionary in the attributes_set variable and make changes to
302+
# json_schema_extra take effect.
303+
if hasattr(default, "_attributes_set"):
304+
if "json_schema_extra" not in default._attributes_set: # type: ignore
305+
default._attributes_set["json_schema_extra"] = {"x-order": order}
306+
extra = default._attributes_set["json_schema_extra"] # type: ignore
307+
else:
308+
extra = default.json_schema_extra # type: ignore
299309
else:
300310
extra = default.extra # type: ignore
301311
extra["x-order"] = order

python/cog/server/helpers.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ def update_openapi_schema_for_pydantic_2(
461461
_extract_enum_properties(openapi_schema)
462462
_set_default_enumeration_description(openapi_schema)
463463
_restore_allof_for_prediction_id_put(openapi_schema)
464+
_ensure_nullable_properties_not_required(openapi_schema)
464465

465466

466467
def _remove_webhook_events_filter_title(
@@ -501,7 +502,7 @@ def _update_nullable_anyof(
501502
if len(non_null_items) < len(value) and not in_header:
502503
openapi_schema["nullable"] = True
503504

504-
elif isinstance(openapi_schema, list):
505+
elif isinstance(openapi_schema, list): # type: ignore
505506
for item in openapi_schema:
506507
_update_nullable_anyof(item, in_header=in_header)
507508

@@ -593,3 +594,13 @@ def _restore_allof_for_prediction_id_put(
593594
ref = value["$ref"]
594595
del value["$ref"]
595596
value["allOf"] = [{"$ref": ref}]
597+
598+
599+
def _ensure_nullable_properties_not_required(openapi_schema: Dict[str, Any]) -> None:
600+
schemas = openapi_schema["components"]["schemas"]
601+
for schema in schemas.values():
602+
properties = schema.get("properties", {})
603+
nullable = {k for k, v in properties.items() if v.get("nullable", False)}
604+
605+
if "required" in schema and nullable:
606+
schema["required"] = [k for k in schema["required"] if k not in nullable]

0 commit comments

Comments
 (0)