|
43 | 43 | GraphQLScope, |
44 | 44 | InputFileType, |
45 | 45 | InputModelRefStrategy, |
| 46 | + JsonSchemaVersion, |
46 | 47 | ModuleSplitMode, |
47 | 48 | NamingStrategy, |
48 | 49 | OpenAPIScope, |
| 50 | + OpenAPIVersion, |
49 | 51 | ReadOnlyWriteOnlyModelType, |
50 | 52 | ReuseScope, |
51 | 53 | TargetPydanticVersion, |
@@ -288,6 +290,42 @@ def is_schema(data: dict) -> bool: |
288 | 290 | return isinstance(data.get("properties"), dict) |
289 | 291 |
|
290 | 292 |
|
| 293 | +def detect_jsonschema_version(data: dict[str, Any]) -> JsonSchemaVersion: |
| 294 | + """Detect JSON Schema version from $schema field. |
| 295 | +
|
| 296 | + Returns Auto if version cannot be detected, allowing all features. |
| 297 | + """ |
| 298 | + schema = data.get("$schema", "") |
| 299 | + if not isinstance(schema, str): |
| 300 | + return JsonSchemaVersion.Auto |
| 301 | + if "draft-04" in schema: |
| 302 | + return JsonSchemaVersion.Draft04 |
| 303 | + if "draft-07" in schema: |
| 304 | + return JsonSchemaVersion.Draft07 |
| 305 | + if "2019-09" in schema: |
| 306 | + return JsonSchemaVersion.Draft201909 |
| 307 | + if "2020-12" in schema: |
| 308 | + return JsonSchemaVersion.Draft202012 |
| 309 | + return JsonSchemaVersion.Auto |
| 310 | + |
| 311 | + |
| 312 | +def detect_openapi_version(data: dict[str, Any]) -> OpenAPIVersion: |
| 313 | + """Detect OpenAPI version from openapi/swagger field. |
| 314 | +
|
| 315 | + Returns Auto if version cannot be detected, allowing all features. |
| 316 | + """ |
| 317 | + if "swagger" in data: |
| 318 | + return OpenAPIVersion.V20 |
| 319 | + openapi = data.get("openapi", "") |
| 320 | + if not isinstance(openapi, str): |
| 321 | + return OpenAPIVersion.Auto |
| 322 | + if openapi.startswith("3.1"): |
| 323 | + return OpenAPIVersion.V31 |
| 324 | + if openapi.startswith("3.0"): |
| 325 | + return OpenAPIVersion.V30 |
| 326 | + return OpenAPIVersion.Auto |
| 327 | + |
| 328 | + |
291 | 329 | RAW_DATA_TYPES: list[InputFileType] = [ |
292 | 330 | InputFileType.Json, |
293 | 331 | InputFileType.Yaml, |
@@ -338,6 +376,35 @@ def __init__( |
338 | 376 | super().__init__(message=message) |
339 | 377 |
|
340 | 378 |
|
| 379 | +class SchemaVersionError(Error): |
| 380 | + """Base exception for schema version-related errors.""" |
| 381 | + |
| 382 | + |
| 383 | +class UnsupportedVersionError(SchemaVersionError): |
| 384 | + """Raised when an unsupported schema version is encountered.""" |
| 385 | + |
| 386 | + def __init__(self, version: str, supported: list[str]) -> None: |
| 387 | + """Initialize with version and list of supported versions.""" |
| 388 | + self.version = version |
| 389 | + self.supported = supported |
| 390 | + message = f"Unsupported schema version: {version}. Supported versions: {', '.join(supported)}" |
| 391 | + super().__init__(message=message) |
| 392 | + |
| 393 | + |
| 394 | +class SchemaValidationError(SchemaVersionError): |
| 395 | + """Raised when strict validation fails for a schema construct.""" |
| 396 | + |
| 397 | + def __init__(self, message: str, version: str, feature: str) -> None: |
| 398 | + """Initialize with message, version, and feature name.""" |
| 399 | + self.version = version |
| 400 | + self.feature = feature |
| 401 | + super().__init__(message=f"[{version}] {message}") |
| 402 | + |
| 403 | + |
| 404 | +class VersionMismatchWarning(UserWarning): |
| 405 | + """Warning for version-specific feature usage in wrong version.""" |
| 406 | + |
| 407 | + |
341 | 408 | class SchemaParseError(Error): |
342 | 409 | """Raised when an error occurs during schema parsing with path context.""" |
343 | 410 |
|
@@ -966,17 +1033,25 @@ def __getattr__(name: str) -> Any: |
966 | 1033 | "InputModelRefStrategy", |
967 | 1034 | "InvalidClassNameError", |
968 | 1035 | "InvalidFileFormatError", |
| 1036 | + "JsonSchemaVersion", |
969 | 1037 | "LiteralType", |
970 | 1038 | "ModuleSplitMode", |
971 | 1039 | "NamingStrategy", |
972 | 1040 | "OpenAPIScope", |
| 1041 | + "OpenAPIVersion", |
973 | 1042 | "PythonVersion", |
974 | 1043 | "PythonVersionMin", |
975 | 1044 | "ReadOnlyWriteOnlyModelType", |
976 | 1045 | "ReuseScope", |
977 | 1046 | "SchemaParseError", |
| 1047 | + "SchemaValidationError", |
| 1048 | + "SchemaVersionError", |
978 | 1049 | "TargetPydanticVersion", |
| 1050 | + "UnsupportedVersionError", |
| 1051 | + "VersionMismatchWarning", |
979 | 1052 | "clear_dynamic_models_cache", # noqa: F822 |
| 1053 | + "detect_jsonschema_version", |
| 1054 | + "detect_openapi_version", |
980 | 1055 | "generate", |
981 | 1056 | "generate_dynamic_models", # noqa: F822 |
982 | 1057 | ] |
0 commit comments