def wrapper(cls: Type[T]) -> Table[T]: name = table_name or cls.__name__ if cls.__bases__ == (object,): cls = dataclass(cls) cons: List[Union[Column, Index]] = list(args) if issubclass(cls, tuple): defaults = getattr(cls, "_field_defaults", {}) else: defaults = { k: (NO_DEFAULT if a.default == NOTHING else a.default) for k, a in fields_dict(cls).items() } for key, value in get_type_hints(cls).items(): cons.append( Column( key, ctype=value, table_name=name, default=defaults.get(key, NO_DEFAULT), ) ) return Table(name, cons=cons, source=cls)
def dataclass(_cls: type = None, *, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False) -> type: """ This decorator does the same as attr.dataclass, but also applies :func:`add_schema`. It adds a `.Schema` attribute to the class object >>> @dataclass ... class Artist: ... name: str >>> Artist.Schema <class 'marshmallow.schema.Artist'> >>> from marshmallow import Schema >>> @dataclass(order=True) # preserve field order ... class Point: ... x:float ... y:float ... Schema: ClassVar[Type[Schema]] = Schema # For the type checker ... >>> Point.Schema(strict=True).load({'x':0, 'y':0}).data # This line can be statically type checked Point(x=0.0, y=0.0) """ dc = attr.dataclass(_cls, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen) return add_schema(dc) if _cls else lambda cls: add_schema(dc(cls))
def dataclass( _cls: type = None, *, these=None, repr_ns=None, repr=True, cmp=True, hash=None, init=True, slots=False, frozen=False, weakref_slot=True, str=False, kw_only=False, cache_hash=False, auto_exc=False, ) -> type: """ This decorator does the same as attr.dataclass, but also applies :func:`add_schema`. It adds a `.Schema` attribute to the class object >>> @dataclass ... class Artist: ... name: str >>> Artist.Schema <class 'marshmallow.schema.Artist'> >>> from marshmallow import Schema >>> @dataclass ... class Point: ... x:float ... y:float ... Schema: ClassVar[Type[Schema]] = Schema # For the type checker ... >>> Point.Schema(strict=True).load({'x':0, 'y':0}).data # This line can be statically type checked Point(x=0.0, y=0.0) """ dc = attr.dataclass( _cls, these=these, repr_ns=repr_ns, repr=repr, cmp=cmp, hash=hash, init=init, slots=slots, frozen=frozen, weakref_slot=weakref_slot, str=str, kw_only=kw_only, cache_hash=cache_hash, auto_exc=auto_exc, ) return add_schema(dc) if _cls else lambda cls: add_schema(dc(cls))
def prepare(cls=None, slots=True, frozen=True, cmp=False): """ A common class decorator to decorate AST node classes. We can either use `@prepare` with default parameters, or `@prepare(...)` to override the default values of the parameters. By default, `cmp=False` makes the objects hashable, and the hash is an object's id. Therefore, every AST node is unique, even if they have identical attributes (think of two identical rows or steps at two different places in a document). """ wrapper = dataclass(slots=slots, frozen=frozen, cmp=cmp) if cls is None: return wrapper return wrapper(cls)
def __init_subclass__(cls, kw_only: bool = False, always_dump: str = "", exclude: str = ""): _yaml_loadable(attr.dataclass(cls, kw_only=kw_only)) # Merge always_dump with superclass's __always_dump. super_always_dump = cls.__always_dump super_exclude = cls.__exclude assert type(super_always_dump) == frozenset assert type(super_exclude) == frozenset cls.__always_dump = super_always_dump | frozenset(always_dump.split()) cls.__exclude = super_exclude | frozenset(exclude.split()) del super_always_dump, always_dump del super_exclude, exclude all_fields = {f.name for f in attr.fields(cls)} dump_fields = cls.__always_dump - {"*"} # remove "*" if exists exclude_fields = cls.__exclude if "*" in cls.__always_dump: assert ( not dump_fields ), f"Invalid always_dump, contains * and elements {dump_fields}" for exclude_field in exclude_fields: assert ( exclude_field in all_fields ), f'Invalid exclude, contains "{exclude_field}" missing from class {cls.__name__}' else: assert (not exclude_fields ), f"Invalid exclude, always_dump does not contain *" for dump_field in dump_fields: assert ( dump_field in all_fields ), f'Invalid always_dump, contains "{dump_field}" missing from class {cls.__name__}'
WIDTH = 64 HEIGHT = 64 RENDER_Y_ZEROS = np.full((2, 1), 0.5) RENDER_Y_STEREO = np.full((2, 2), 0.5) OPACITY = 2 / 3 def behead(string: str, header: str) -> str: if not string.startswith(header): raise ValueError(f"{string} does not start with {header}") return string[len(header) :] my_dataclass = attr.dataclass(frozen=True, repr=False) @my_dataclass class NamedDebug: name: str def __init_subclass__(cls): my_dataclass(cls) def __repr__(self): return self.name if TYPE_CHECKING: def __init__(self, *args, **kwargs):
def class_schema(clazz: type) -> Type[marshmallow.Schema]: """ Convert a class to a marshmallow schema :param clazz: A python class (may be a dataclass) :return: A marshmallow Schema corresponding to the dataclass .. note:: All the arguments supported by marshmallow field classes are can be passed in the `metadata` dictionary of a field. If you want to use a custom marshmallow field (one that has no equivalent python type), you can pass it as the ``marshmallow_field`` key in the metadata dictionary. >>> Meters = NewType('Meters', float) >>> @attr.dataclass() ... class Building: ... height: Optional[Meters] ... name: str = attr.ib(default="anonymous") ... class Meta: # marshmallow meta attributes are supported ... ordered = True ... >>> class_schema(Building) # Returns a marshmallow schema class (not an instance) <class 'marshmallow.schema.Building'> >>> @attr.dataclass() ... class City: ... name: str = attr.ib(metadata={'required':True}) ... best_building: Building # Reference to another attr. A schema will be created for it too. ... other_buildings: List[Building] = attr.ib(factory=list) ... >>> citySchema = class_schema(City)(strict=True) >>> city, _ = citySchema.load({"name":"Paris", "best_building": {"name": "Eiffel Tower"}, "other_buildings": []}) >>> city City(name='Paris', best_building=Building(height=None, name='Eiffel Tower'), other_buildings=[]) >>> citySchema.load({"name":"Paris"}) Traceback (most recent call last): ... marshmallow.exceptions.ValidationError: {'best_building': ['Missing data for required field.']} >>> city_json, _ = class_schema(Building)(strict=True).dump(city.best_building) >>> city_json # We get an OrderedDict because we specified order = True in the Meta class OrderedDict([('height', None), ('name', 'Eiffel Tower')]) >>> @attr.dataclass() ... class Person: ... name: str = attr.ib(default="Anonymous") ... friends: List['Person'] = attr.ib(factory=list) # Recursive field ... >>> person, _ = class_schema(Person)(strict=True).load({ ... "friends": [{"name": "Roger Boucher"}] ... }) >>> person Person(name='Anonymous', friends=[Person(name='Roger Boucher', friends=[])]) >>> @attr.dataclass() ... class C: ... important: int = attr.ib(init=True, default=0) ... unimportant: int = attr.ib(init=False, default=0) # Only fields that are in the __init__ method will be added: ... >>> c, _ = class_schema(C)(strict=True).load({ ... "important": 9, # This field will be imported ... "unimportant": 9 # This field will NOT be imported ... }) >>> c C(important=9, unimportant=0) >>> @attr.dataclass ... class Website: ... url:str = attr.ib(metadata = { ... "marshmallow_field": marshmallow.fields.Url() # Custom marshmallow field ... }) ... >>> class_schema(Website)(strict=True).load({"url": "I am not a good URL !"}) Traceback (most recent call last): ... marshmallow.exceptions.ValidationError: {'url': ['Not a valid URL.']} >>> @attr.dataclass ... class NeverValid: ... @marshmallow.validates_schema ... def validate(self, data): ... raise marshmallow.ValidationError('never valid') ... >>> _, err = class_schema(NeverValid)().load({}) >>> err {'_schema': ['never valid']} >>> # noinspection PyTypeChecker >>> class_schema(None) # unsupported type Traceback (most recent call last): ... TypeError: None is not a dataclass and cannot be turned into one. """ try: # noinspection PyDataclass fields: Tuple[attr.ib] = attr.fields(clazz) except TypeError: # Not a dataclass try: return class_schema(attr.dataclass(clazz)) except Exception: raise TypeError( f"{getattr(clazz, '__name__', repr(clazz))} is not a dataclass and cannot be turned into one." ) # Copy all public members of the dataclass to the schema attributes = { k: v for k, v in inspect.getmembers(clazz) if not k.startswith("_") } # Update the schema members to contain marshmallow fields instead of dataclass fields attributes.update(( field.name, field_for_schema(field.type, _get_field_default(field), field.metadata), ) for field in fields if field.init) schema_class = type(clazz.__name__, (_base_schema(clazz), ), attributes) return cast(Type[marshmallow.Schema], schema_class)