some random stuff. caelestia incoming
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
import datetime
|
||||
import ipaddress
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import MISSING, dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from typing_extensions import TYPE_CHECKING, Self, TypeAlias
|
||||
|
||||
from mashumaro.config import BaseConfig
|
||||
from mashumaro.core.meta.helpers import iter_all_subclasses
|
||||
from mashumaro.helper import pass_through
|
||||
from mashumaro.jsonschema.dialects import DRAFT_2020_12, JSONSchemaDialect
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from mashumaro.jsonschema.plugins import BasePlugin
|
||||
else:
|
||||
BasePlugin = Any
|
||||
|
||||
try:
|
||||
from mashumaro.mixins.orjson import (
|
||||
DataClassORJSONMixin as DataClassJSONMixin,
|
||||
)
|
||||
except ImportError: # pragma: no cover
|
||||
from mashumaro.mixins.json import DataClassJSONMixin # type: ignore
|
||||
|
||||
|
||||
# https://github.com/python/mypy/issues/3186
|
||||
Number: TypeAlias = Union[int, float]
|
||||
|
||||
Null = object()
|
||||
|
||||
|
||||
class JSONSchemaInstanceType(Enum):
|
||||
NULL = "null"
|
||||
BOOLEAN = "boolean"
|
||||
OBJECT = "object"
|
||||
ARRAY = "array"
|
||||
NUMBER = "number"
|
||||
STRING = "string"
|
||||
INTEGER = "integer"
|
||||
|
||||
|
||||
class JSONSchemaInstanceFormat(Enum):
|
||||
pass
|
||||
|
||||
|
||||
class JSONSchemaStringFormat(JSONSchemaInstanceFormat):
|
||||
DATETIME = "date-time"
|
||||
DATE = "date"
|
||||
TIME = "time"
|
||||
DURATION = "duration"
|
||||
EMAIL = "email"
|
||||
IDN_EMAIL = "idn-email"
|
||||
HOSTNAME = "hostname"
|
||||
IDN_HOSTNAME = "idn-hostname"
|
||||
IPV4ADDRESS = "ipv4"
|
||||
IPV6ADDRESS = "ipv6"
|
||||
URI = "uri"
|
||||
URI_REFERENCE = "uri-reference"
|
||||
IRI = "iri"
|
||||
IRI_REFERENCE = "iri-reference"
|
||||
UUID = "uuid"
|
||||
URI_TEMPLATE = "uri-template"
|
||||
JSON_POINTER = "json-pointer"
|
||||
RELATIVE_JSON_POINTER = "relative-json-pointer"
|
||||
REGEX = "regex"
|
||||
|
||||
|
||||
class JSONSchemaInstanceFormatExtension(JSONSchemaInstanceFormat):
|
||||
TIMEDELTA = "time-delta"
|
||||
TIME_ZONE = "time-zone"
|
||||
IPV4NETWORK = "ipv4network"
|
||||
IPV6NETWORK = "ipv6network"
|
||||
IPV4INTERFACE = "ipv4interface"
|
||||
IPV6INTERFACE = "ipv6interface"
|
||||
DECIMAL = "decimal"
|
||||
FRACTION = "fraction"
|
||||
BASE64 = "base64"
|
||||
PATH = "path"
|
||||
|
||||
|
||||
DATETIME_FORMATS = {
|
||||
datetime.datetime: JSONSchemaStringFormat.DATETIME,
|
||||
datetime.date: JSONSchemaStringFormat.DATE,
|
||||
datetime.time: JSONSchemaStringFormat.TIME,
|
||||
}
|
||||
|
||||
|
||||
IPADDRESS_FORMATS = {
|
||||
ipaddress.IPv4Address: JSONSchemaStringFormat.IPV4ADDRESS,
|
||||
ipaddress.IPv6Address: JSONSchemaStringFormat.IPV6ADDRESS,
|
||||
ipaddress.IPv4Network: JSONSchemaInstanceFormatExtension.IPV4NETWORK,
|
||||
ipaddress.IPv6Network: JSONSchemaInstanceFormatExtension.IPV6NETWORK,
|
||||
ipaddress.IPv4Interface: JSONSchemaInstanceFormatExtension.IPV4INTERFACE,
|
||||
ipaddress.IPv6Interface: JSONSchemaInstanceFormatExtension.IPV6INTERFACE,
|
||||
}
|
||||
|
||||
|
||||
def _deserialize_json_schema_instance_format(
|
||||
value: Any,
|
||||
) -> JSONSchemaInstanceFormat:
|
||||
for cls in iter_all_subclasses(JSONSchemaInstanceFormat):
|
||||
try:
|
||||
return cls(value)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
raise ValueError(value)
|
||||
|
||||
|
||||
@dataclass(unsafe_hash=True)
|
||||
class JSONSchema(DataClassJSONMixin):
|
||||
# Common keywords
|
||||
schema: Optional[str] = None
|
||||
type: Optional[JSONSchemaInstanceType] = None
|
||||
enum: Optional[list[Any]] = None
|
||||
const: Optional[Any] = field(default_factory=lambda: MISSING)
|
||||
format: Optional[JSONSchemaInstanceFormat] = None
|
||||
title: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
anyOf: Optional[List["JSONSchema"]] = None
|
||||
reference: Optional[str] = None
|
||||
definitions: Optional[Dict[str, "JSONSchema"]] = None
|
||||
default: Optional[Any] = field(default_factory=lambda: MISSING)
|
||||
deprecated: Optional[bool] = None
|
||||
examples: Optional[list[Any]] = None
|
||||
# Keywords for Objects
|
||||
properties: Optional[Dict[str, "JSONSchema"]] = None
|
||||
patternProperties: Optional[Dict[str, "JSONSchema"]] = None
|
||||
additionalProperties: Union["JSONSchema", bool, None] = None
|
||||
propertyNames: Optional["JSONSchema"] = None
|
||||
# Keywords for Arrays
|
||||
prefixItems: Optional[List["JSONSchema"]] = None
|
||||
items: Optional["JSONSchema"] = None
|
||||
contains: Optional["JSONSchema"] = None
|
||||
# Validation keywords for numeric instances
|
||||
multipleOf: Optional[Number] = None
|
||||
maximum: Optional[Number] = None
|
||||
exclusiveMaximum: Optional[Number] = None
|
||||
minimum: Optional[Number] = None
|
||||
exclusiveMinimum: Optional[Number] = None
|
||||
# Validation keywords for Strings
|
||||
maxLength: Optional[int] = None
|
||||
minLength: Optional[int] = None
|
||||
pattern: Optional[str] = None
|
||||
# Validation keywords for Arrays
|
||||
maxItems: Optional[int] = None
|
||||
minItems: Optional[int] = None
|
||||
uniqueItems: Optional[bool] = None
|
||||
maxContains: Optional[int] = None
|
||||
minContains: Optional[int] = None
|
||||
# Validation keywords for Objects
|
||||
maxProperties: Optional[int] = None
|
||||
minProperties: Optional[int] = None
|
||||
required: Optional[list[str]] = None
|
||||
dependentRequired: Optional[dict[str, set[str]]] = None
|
||||
|
||||
class Config(BaseConfig):
|
||||
omit_none = True
|
||||
serialize_by_alias = True
|
||||
aliases = {
|
||||
"schema": "$schema",
|
||||
"reference": "$ref",
|
||||
"definitions": "$defs",
|
||||
}
|
||||
serialization_strategy = {
|
||||
int: pass_through,
|
||||
float: pass_through,
|
||||
Null: pass_through,
|
||||
JSONSchemaInstanceFormat: {
|
||||
"deserialize": _deserialize_json_schema_instance_format,
|
||||
},
|
||||
}
|
||||
|
||||
def __pre_serialize__(self) -> Self:
|
||||
if self.const is None:
|
||||
self.const = Null
|
||||
if self.default is None:
|
||||
self.default = Null
|
||||
return self
|
||||
|
||||
def __post_serialize__(self, d: dict[Any, Any]) -> dict[Any, Any]:
|
||||
const = d.get("const")
|
||||
if const is MISSING:
|
||||
d.pop("const")
|
||||
elif const is Null:
|
||||
d["const"] = None
|
||||
default = d.get("default")
|
||||
if default is MISSING:
|
||||
d.pop("default")
|
||||
elif default is Null:
|
||||
d["default"] = None
|
||||
return d
|
||||
|
||||
|
||||
@dataclass
|
||||
class JSONObjectSchema(JSONSchema):
|
||||
type: Optional[JSONSchemaInstanceType] = JSONSchemaInstanceType.OBJECT
|
||||
|
||||
|
||||
@dataclass
|
||||
class JSONArraySchema(JSONSchema):
|
||||
type: Optional[JSONSchemaInstanceType] = JSONSchemaInstanceType.ARRAY
|
||||
|
||||
|
||||
@dataclass
|
||||
class Context:
|
||||
dialect: JSONSchemaDialect = DRAFT_2020_12
|
||||
definitions: dict[str, JSONSchema] = field(default_factory=dict)
|
||||
all_refs: Optional[bool] = None
|
||||
ref_prefix: Optional[str] = None
|
||||
plugins: Sequence[BasePlugin] = ()
|
||||
Reference in New Issue
Block a user