Merge pull request #525 from pikers/msgspec_struct_updates
`msgspec` struct derivative updatesmaster
						commit
						a12b008a15
					
				| 
						 | 
					@ -1,5 +1,7 @@
 | 
				
			||||||
# piker: trading gear for hackers
 | 
					# piker: trading gear for hackers
 | 
				
			||||||
# Copyright (C) Guillermo Rodriguez (in stewardship for piker0)
 | 
					# Copyright (C) (in stewardship for pikers)
 | 
				
			||||||
 | 
					#  - Tyler Goodlet
 | 
				
			||||||
 | 
					#  - Guillermo Rodriguez
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# This program is free software: you can redistribute it and/or modify
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
# it under the terms of the GNU Affero General Public License as published by
 | 
					# it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
| 
						 | 
					@ -14,18 +16,22 @@
 | 
				
			||||||
# You should have received a copy of the GNU Affero General Public License
 | 
					# You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
					# along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""
 | 
					'''
 | 
				
			||||||
Built-in (extension) types.
 | 
					Extensions to built-in or (heavily used but 3rd party) friend-lib
 | 
				
			||||||
 | 
					types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""
 | 
					'''
 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
from pprint import pformat
 | 
					from pprint import pformat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import msgspec
 | 
					from msgspec import (
 | 
				
			||||||
 | 
					    msgpack,
 | 
				
			||||||
 | 
					    Struct,
 | 
				
			||||||
 | 
					    structs,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Struct(
 | 
					class Struct(
 | 
				
			||||||
    msgspec.Struct,
 | 
					    Struct,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # https://jcristharif.com/msgspec/structs.html#tagged-unions
 | 
					    # https://jcristharif.com/msgspec/structs.html#tagged-unions
 | 
				
			||||||
    # tag='pikerstruct',
 | 
					    # tag='pikerstruct',
 | 
				
			||||||
| 
						 | 
					@ -36,22 +42,14 @@ class Struct(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
    def to_dict(self) -> dict:
 | 
					    def to_dict(self) -> dict:
 | 
				
			||||||
        return {
 | 
					        '''
 | 
				
			||||||
            f: getattr(self, f)
 | 
					        Like it sounds.. direct delegation to:
 | 
				
			||||||
            for f in self.__struct_fields__
 | 
					        https://jcristharif.com/msgspec/api.html#msgspec.structs.asdict
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Lul, doesn't seem to work that well..
 | 
					        TODO: probably just drop this method since it's now a built-int method?
 | 
				
			||||||
    # def __repr__(self):
 | 
					 | 
				
			||||||
    #     # only turn on pprint when we detect a python REPL
 | 
					 | 
				
			||||||
    #     # at runtime B)
 | 
					 | 
				
			||||||
    #     if (
 | 
					 | 
				
			||||||
    #         hasattr(sys, 'ps1')
 | 
					 | 
				
			||||||
    #         # TODO: check if we're in pdb
 | 
					 | 
				
			||||||
    #     ):
 | 
					 | 
				
			||||||
    #         return self.pformat()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #     return super().__repr__()
 | 
					        '''
 | 
				
			||||||
 | 
					        return structs.asdict(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def pformat(self) -> str:
 | 
					    def pformat(self) -> str:
 | 
				
			||||||
        return f'Struct({pformat(self.to_dict())})'
 | 
					        return f'Struct({pformat(self.to_dict())})'
 | 
				
			||||||
| 
						 | 
					@ -60,30 +58,47 @@ class Struct(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        update: dict | None = None,
 | 
					        update: dict | None = None,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> msgspec.Struct:
 | 
					    ) -> Struct:
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        Validate-typecast all self defined fields, return a copy of us
 | 
					        Validate-typecast all self defined fields, return a copy of
 | 
				
			||||||
        with all such fields.
 | 
					        us with all such fields.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        This is kinda like the default behaviour in `pydantic.BaseModel`.
 | 
					        NOTE: This is kinda like the default behaviour in
 | 
				
			||||||
 | 
					        `pydantic.BaseModel` except a copy of the object is
 | 
				
			||||||
 | 
					        returned making it compat with `frozen=True`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        if update:
 | 
					        if update:
 | 
				
			||||||
            for k, v in update.items():
 | 
					            for k, v in update.items():
 | 
				
			||||||
                setattr(self, k, v)
 | 
					                setattr(self, k, v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # roundtrip serialize to validate
 | 
					        # NOTE: roundtrip serialize to validate
 | 
				
			||||||
        return msgspec.msgpack.Decoder(
 | 
					        # - enode to msgpack binary format,
 | 
				
			||||||
            type=type(self)
 | 
					        # - decode that back to a struct.
 | 
				
			||||||
        ).decode(
 | 
					        return msgpack.Decoder(type=type(self)).decode(
 | 
				
			||||||
            msgspec.msgpack.Encoder().encode(self)
 | 
					            msgpack.Encoder().encode(self)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # NOTE XXX: this won't work on frozen types!
 | 
					 | 
				
			||||||
    # use ``.copy()`` above in such cases.
 | 
					 | 
				
			||||||
    def typecast(
 | 
					    def typecast(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        # fields: list[str] | None = None,
 | 
					
 | 
				
			||||||
 | 
					        # TODO: allow only casting a named subset?
 | 
				
			||||||
 | 
					        # fields: set[str] | None = None,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ) -> None:
 | 
					    ) -> None:
 | 
				
			||||||
        for fname, ftype in self.__annotations__.items():
 | 
					        '''
 | 
				
			||||||
            setattr(self, fname, ftype(getattr(self, fname)))
 | 
					        Cast all fields using their declared type annotations
 | 
				
			||||||
 | 
					        (kinda like what `pydantic` does by default).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        NOTE: this of course won't work on frozen types, use
 | 
				
			||||||
 | 
					        ``.copy()`` above in such cases.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        # https://jcristharif.com/msgspec/api.html#msgspec.structs.fields
 | 
				
			||||||
 | 
					        fi: structs.FieldInfo
 | 
				
			||||||
 | 
					        for fi in structs.fields(self):
 | 
				
			||||||
 | 
					            setattr(
 | 
				
			||||||
 | 
					                self,
 | 
				
			||||||
 | 
					                fi.name,
 | 
				
			||||||
 | 
					                fi.type(getattr(self, fi.name)),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue