def _visited_union(self, results: Sequence[JsonSchema]) -> JsonSchema: if len(results) == 1: return results[0] elif any(alt == {} for alt in results): return JsonSchema() elif all(alt.keys() == {"type"} for alt in results): types: Any = chain.from_iterable( [res["type"]] if isinstance(res["type"], ( str, JsonType)) else res["type"] for res in results) return json_schema(type=list(types)) elif (len(results) == 2 and all("type" in res for res in results) and { "type": "null" } in results): for result in results: if result != {"type": "null"}: types = result["type"] if isinstance(types, (str, JsonType)): types = [types] if "null" not in types: result = JsonSchema({ **result, "type": [*types, "null"] }) return result else: raise NotImplementedError else: return json_schema(anyOf=results)
def wrapper(self: "SchemaBuilder", *args, **kwargs): if self._schema is None: return method(self, *args, **kwargs) elif self._schema.override: return JsonSchema(**self._schema.as_dict()) else: return JsonSchema(method(self, *args, **kwargs), **self._schema.as_dict())
def _set_missing_properties(schema: JsonSchema, properties: Optional[Mapping[str, JsonSchema]], key: str) -> JsonSchema: if properties is None: return schema missing = { name: prop for name, prop in properties.items() if prop.get(key, False) } schema.setdefault("properties", {}).update(missing) return schema
def _union_result(self, results: Iterable[JsonSchema]) -> JsonSchema: results = list(results) if len(results) == 1: return results[0] elif all(alt.keys() == {"type"} for alt in results): types = chain.from_iterable( [res["type"]] if isinstance(res["type"], JsonType ) else res["type"] for res in results) return json_schema(type=list(types)) elif (len(results) == 2 and all("type" in res for res in results) and { "type": "null" } in results): for result in results: if result != {"type": "null"}: types = result["type"] if isinstance(types, str): types = [types] # type: ignore if "null" not in types: result = JsonSchema({ **result, "type": [*types, "null"] }) return result else: raise NotImplementedError() else: return json_schema(anyOf=results)
def ref_schema(self, ref: Optional[str]) -> Optional[JsonSchema]: if ref not in self.refs: return None elif self._ignore_first_ref: self._ignore_first_ref = False return None else: assert isinstance(ref, str) return JsonSchema({"$ref": self.ref_factory(ref)})
def to_json_schema_7(schema: JsonSchema) -> JsonSchema7: result = schema.copy() isolate_ref(result) if "$defs" in result: result["definitions"] = { **result.pop("$defs"), **result.get("definitions", {}) } if "dependentRequired" in result: result["dependencies"] = { **result.pop("dependentRequired"), **result.get("dependencies", {}), } return JsonSchema7(result)
def visit_field(self, field: ObjectField) -> JsonSchema: result = full_schema( self.visit_with_conv(field.type, self._field_conversion(field)), field.schema, ) if (not field.flattened and not field.pattern_properties and not field.additional_properties and not field.required and "default" not in result): result = JsonSchema(result) with suppress(Exception): result["default"] = serialize( field.type, field.get_default(), fall_back_on_any=False, check_type=True, conversion=field.serialization, ) return result
def to_open_api_3_0(schema: JsonSchema) -> OpenAPI30: result = schema.copy() for key in ("dependentRequired", "unevaluatedProperties", "$defs"): result.pop(key, ...) isolate_ref(result) if "null" in result.get("type", ()): result.setdefault("nullable", True) if result["type"] == "null": result.pop("type") else: types = [t for t in result["type"] if t != "null"] result["type"] = types if len(types) > 1 else types[0] if {"type": "null"} in result.get("anyOf", ()): result.setdefault("nullable", True) result["anyOf"] = [a for a in result["anyOf"] if a != {"type": "null"}] if "examples" in result: result.setdefault("example", result.pop("examples")[0]) return OpenAPI30(result)
def _properties_schema(self, field: Field, field_type: AnyType) -> JsonSchema: with self._without_ref(): props_schema = self.visit_field(field, field_type) if not props_schema.get("type") == JsonType.OBJECT: raise TypeError("properties field must have an 'object' type") if "patternProperties" in props_schema: if (len(props_schema["patternProperties"]) != 1 or "additionalProperties" in props_schema): # don't try to merge the schemas pass else: return next(iter(props_schema["patternProperties"].values())) elif "additionalProperties" in props_schema: if isinstance(props_schema["additionalProperties"], JsonSchema): return props_schema["additionalProperties"] else: # there is maybe only properties pass return JsonSchema()
def _properties_schema(self, field: ObjectField) -> JsonSchema: assert field.pattern_properties is not None or field.additional_properties with context_setter(self): self._ignore_first_ref = True props_schema = self.visit_field(field) if not props_schema.get("type") == JsonType.OBJECT: raise TypeError("properties field must have an 'object' type") if "patternProperties" in props_schema: if (len(props_schema["patternProperties"]) != 1 or "additionalProperties" in props_schema): # don't try to merge the schemas pass else: return next(iter(props_schema["patternProperties"].values())) elif "additionalProperties" in props_schema: if isinstance(props_schema["additionalProperties"], JsonSchema): return props_schema["additionalProperties"] else: # there is maybe only properties pass return JsonSchema()
def full_schema(base_schema: JsonSchema, schema: Optional[Schema]) -> JsonSchema: if schema is not None: base_schema = JsonSchema(base_schema) schema.merge_into(base_schema) return base_schema
def primitive(self, cls: Type) -> JsonSchema: return JsonSchema(type=JsonType.from_type(cls))
def any(self) -> JsonSchema: return JsonSchema()
def primitive(self, cls: Type) -> JsonSchema: if cls in constraint_by_type: self._check_constraints(constraint_by_type[cls]) return JsonSchema(type=JsonType.from_type(cls))
def _ref_schema(self, ref: str) -> JsonSchema: return JsonSchema({"$ref": self.ref_factory(ref)})