def register_pattern(base_type: Type, pattern: str) -> None: """base_type should be a typing.NewType that should always have the given regex pattern. That means that its underlying type ('__supertype__') had better be a str! """ class PatternEncoder(FieldEncoder): @property def json_schema(self): return {"type": "string", "pattern": pattern} JsonSchemaMixin.register_field_encoders({base_type: PatternEncoder()})
def test_inferred_class(): assert isinstance( JsonSchemaMixin.from_dict({ "unique_id": "abc", "stage": "two", "additional_information": "def", }), StageTwoFoo, )
from dataclasses import dataclass, field from typing import Optional, List drop_lock: Lock = dbt.flags.MP_CONTEXT.Lock() IAMDuration = NewType('IAMDuration', int) class IAMDurationEncoder(FieldEncoder): @property def json_schema(self): return {'type': 'integer', 'minimum': 0, 'maximum': 65535} JsonSchemaMixin.register_field_encoders({IAMDuration: IAMDurationEncoder()}) class RedshiftConnectionMethod(StrEnum): DATABASE = 'database' IAM = 'iam' @dataclass class RedshiftCredentials(PostgresCredentials): method: RedshiftConnectionMethod = RedshiftConnectionMethod.DATABASE password: Optional[str] = None cluster_id: Optional[str] = field( default=None, metadata={'description': 'If using IAM auth, the name of the cluster'}, )
'cannot encode {} into timedelta'.format(value) ) from None @property def json_schema(self) -> JsonDict: return {'type': 'string'} class NVEnum(StrEnum): novalue = 'novalue' def __eq__(self, other): return isinstance(other, NVEnum) @dataclass class NoValue(JsonSchemaMixin): """Sometimes, you want a way to say none that isn't None""" novalue: NVEnum = NVEnum.novalue JsonSchemaMixin.register_field_encoders({ Port: PortEncoder(), timedelta: TimeDeltaFieldEncoder(), Path: PathEncoder(), }) FQNPath = Tuple[str, ...] PathSet = AbstractSet[FQNPath]
def to_python(self, value): if isinstance(value, (tuple, list)): return list(value) else: return [value] @property def json_schema(self): return {"type": ["array", "string"], "items": {"type": "string"}} JsonSchemaMixin.register_field_encoders({ ListOrTuple: ListOrTupleEncoder(), Union[ListOrTuple, str]: ListOrTupleOrAloneEncoder(), Union[str, ListOrTuple]: ListOrTupleOrAloneEncoder(), }) @dataclass class Foo(JsonSchemaMixin): thing: Union[ListOrTuple, str] def test_registered(): first = Foo(thing="one") second = Foo(thing=["two", "2"]) third = Foo(thing=("three", "3"))
class NoValueEncoder(FieldEncoder): # the FieldEncoder class specifies a narrow range that only includes value # types (str, float, None) but we want to support something extra def to_wire(self, value: NoValue) -> Dict[str, str]: # type: ignore return {'novalue': 'novalue'} def to_python(self, value) -> NoValue: if (not isinstance(value, dict) or 'novalue' not in value or value['novalue'] != 'novalue'): raise ValidationError('Got invalid NoValue: {}'.format(value)) return NoValue() @property def json_schema(self): return { 'type': 'object', 'properties': { 'novalue': { 'enum': ['novalue'], } } } JsonSchemaMixin.register_field_encoders({ Port: PortEncoder(), timedelta: TimeDeltaFieldEncoder(), Real: RealEncoder(), NoValue: NoValueEncoder(), })
from enum import Enum from datetime import datetime from uuid import UUID from hologram import JsonSchemaMixin, FieldEncoder Postcode = NewType("Postcode", str) class PostcodeField(FieldEncoder): @property def json_schema(self): return {"type": "string", "minLength": 5, "maxLength": 8} JsonSchemaMixin.register_field_encoders({Postcode: PostcodeField()}) class SubSchemas(JsonSchemaMixin): pass class Weekday(Enum): MON = "Monday" TUE = "Tuesday" WED = "Wednesday" THU = "Thursday" FRI = "Friday" @dataclass(eq=True)