Source code for typedpy.extfields.extfields

"""
Additional types of fields: datefield, datetime, timestring, DateString,
Hostname, etc.
"""
import json
from datetime import datetime, date, time
import re

from typedpy.commons import wrap_val
from typedpy.structures import TypedField
from typedpy.fields import SerializableField, String

EmailAddress = String(pattern=r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9]+$)")


[docs]class JSONString(String): """ A string of a valid JSON """ def __set__(self, instance, value): json.loads(value) super().__set__(instance, value)
[docs]class IPV4(String): """ A string field of a valid IP version 4 """ _ipv4_re = re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") def __set__(self, instance, value): if IPV4._ipv4_re.match(value) and all( 0 <= int(component) <= 255 for component in value.split(".") ): super().__set__(instance, value) else: raise ValueError( f"{self._name}: Got {wrap_val(value)}; wrong format for IP version 4" ) def to_json_schema(self) -> dict: return {"type": "string", "format": "ipv4"} @classmethod def from_json_schema(cls, schema: dict): return "IPV4()" if schema == {"type": "string", "format": "ipv4"} else None
[docs]class HostName(String): """ A string field of a valid host name """ _host_name_re = re.compile(r"^[A-Za-z0-9][A-Za-z0-9\.\-]{1,255}$") def __set__(self, instance, value): if not HostName._host_name_re.match(value): raise ValueError( f"{self._name}: Got {wrap_val(value)}; wrong format for hostname" ) components = value.split(".") for component in components: if len(component) > 63: raise ValueError( f"{self._name}: Got {wrap_val(value)}; wrong format for hostname" ) super().__set__(instance, value) def to_json_schema(self) -> dict: return {"type": "string", "format": "hostname"} @classmethod def from_json_schema(cls, schema: dict): return ( "HostName()" if schema == {"type": "string", "format": "hostname"} else None )
[docs]class DateString(String): """ A string field of the format '%Y-%m-%d' that can be converted to a date Arguments: date_format(str): optional an alternative date format """ _ty = str def __init__(self, *args, date_format="%Y-%m-%d", **kwargs): self._format = date_format super().__init__(*args, **kwargs) def __set__(self, instance, value): super().__set__(instance, value) try: datetime.strptime(value, self._format) except ValueError as ex: raise ValueError( f"{self._name}: Got {wrap_val(value)}; {ex.args[0]}" ) from ex def to_json_schema(self) -> dict: return {"type": "string", "format": "date"} @classmethod def from_json_schema(cls, schema: dict): return ( "DateString()" if schema == {"type": "string", "format": "date"} else None )
[docs]class TimeString(TypedField): """ A string field of the format '%H:%M:%S' that can be converted to a time """ _ty = str def __set__(self, instance, value): super().__set__(instance, value) try: datetime.strptime(value, "%H:%M:%S") except ValueError as ex: raise ValueError( f"{self._name}: Got {wrap_val(value)}; {ex.args[0]}" ) from ex def to_json_schema(self) -> dict: return {"type": "string", "format": "time"} @classmethod def from_json_schema(cls, schema: dict): return ( "TimeString()" if schema == {"type": "string", "format": "time"} else None ) def serialize(self, value): return value
[docs]class DateField(SerializableField): """ A datetime.date field. Can accept either a date object, or a string that can be converted to a date, using the date_format in the constructor. Arguments: date_format(str): optional The date format used to convert to/from a string. Default is '%Y-%m-%d' Example: .. code-block:: python class Foo(Structure): date = DateField foo(date = date.today()) foo(date = "2020-01-31") This is a SerializableField, thus can be serialized/deserialized. """ def __init__(self, *args, date_format="%Y-%m-%d", **kwargs): self._date_format = date_format super().__init__(*args, **kwargs) def serialize(self, value): return value.strftime(self._date_format) def deserialize(self, value): try: return datetime.strptime(value, self._date_format).date() except ValueError as ex: raise ValueError(f"{self._name}: Got { wrap_val(value)}; {str(ex)}") from ex def __set__(self, instance, value): if isinstance(value, str): as_date = self.deserialize(value) super().__set__(instance, as_date) elif isinstance(value, datetime): super().__set__(instance, value.date()) elif isinstance(value, date): super().__set__(instance, value) else: raise TypeError( f"{self._name}: Got {wrap_val(value)}; Expected date, datetime, or str" ) @property def get_type(self): return date def to_json_schema(self) -> dict: return {"type": "string", "format": "date"} @classmethod def from_json_schema(cls, schema: dict): return "DateField()" if schema == {"type": "string", "format": "date"} else None
class TimeField(SerializableField): def __init__(self, format_str="%H:%M:%S", **kwargs): self._format = format_str super().__init__(**kwargs) def __set__(self, instance, value): parsed_time = value if isinstance(value, time) else self.deserialize(value) super().__set__(instance, parsed_time) def serialize(self, value: time): return value.strftime(self._format) def deserialize(self, value): try: return datetime.strptime(value, self._format).time() except ValueError as ex: raise ValueError(f"{self._name}: Got {wrap_val(value)}; {str(ex)}") from ex @property def get_type(self): return time def to_json_schema(self) -> dict: return {"type": "string", "format": "time"} @classmethod def from_json_schema(cls, schema: dict): return "TimeField()" if schema == {"type": "string", "format": "time"} else None
[docs]class DateTime(SerializableField): """ A datetime.datetime field. Can accept either a datetime object, or a string that can be converted to a date, using the date_format in the constructor. Arguments: datetime_format(str): optional The format used to convert to/from a string. Default is '%m/%d/%y %H:%M:%S' Example: .. code-block:: python class Foo(Structure): timestamp = DateTime foo(timestamp = datetime.now()) foo(timestamp = "01/31/20 07:15:45") This is a SerializableField, thus can be serialized/deserialized. """ def __init__(self, *args, datetime_format="%m/%d/%y %H:%M:%S", **kwargs): self._datetime_format = datetime_format super().__init__(*args, **kwargs) def serialize(self, value: datetime): return value.strftime(self._datetime_format) def deserialize(self, value): try: if isinstance(value, int) and 2000000000 > value > 1000000000: return datetime.fromtimestamp(value) return datetime.strptime(value, self._datetime_format) except ValueError as ex: raise ValueError(f"{self._name}: Got {wrap_val(value)}; {str(ex)}") from ex def __set__(self, instance, value): if isinstance(value, datetime): super().__set__(instance, value) elif isinstance(value, (str, int)): as_datetime = self.deserialize(value) super().__set__(instance, as_datetime) else: raise TypeError( f"{self._name}: Got {wrap_val(value)}; Expected datetime or str" ) @property def get_type(self): return datetime