Module jumpscale.core.base.fields
fields
have all possible fields where they can be used as a class variables for any Base classselfself.
Field instances will not hold the value or any date, they will be converted by Base to function descriptors, where the data itself resides in Base objects (instances).
Example:
from enum import Enum
class Permission(Base):
read_posts = fields.Boolean()
write_posts = fields.Boolean()
open_ticket = fields.Boolean()
class UserType(Enum):
USER = "user"
ADMIN = "admin"
class User(Base):
emails = fields.List(fields.String())
permissions = fields.List(fields.Object(Permission))
custom_config = fields.Typed(dict)
type = fields.Enum(UserType)
user = User()
emails = ["a@b.com"]
perm1 = Permission()
perm1.open_portal = True
permissions = [perm1]
user.emails = emails
user.permissions = permissions
user.save()
Every field type is responsible dealing with the actual value of this field in the Base objects (instances), mainly:
- validation
- converting from raw primitive types to special types if any, which would help in serialization
In addition to custom options every field can accept and define, they can be used in the following methods:
validate
: raises aValidationError
in case it's not valid.to_raw
: returns a raw (primitive type) object from a value of this fieldfrom_raw
: returns a new object of the field type (if any) from a raw value
No need for from_raw
to raise an error on e.g. type mismatch, as validate
will do the validation.
Expand source code
"""
`fields` have all possible fields where they can be used as a class variables for any Base classselfself.
Field instances will not hold the value or any date, they will be converted by Base to function descriptors,
where the data itself resides in Base objects (instances).
See `jumpscale.core.base.meta`.
Example:
```python
from enum import Enum
class Permission(Base):
read_posts = fields.Boolean()
write_posts = fields.Boolean()
open_ticket = fields.Boolean()
class UserType(Enum):
USER = "user"
ADMIN = "admin"
class User(Base):
emails = fields.List(fields.String())
permissions = fields.List(fields.Object(Permission))
custom_config = fields.Typed(dict)
type = fields.Enum(UserType)
user = User()
emails = ["a@b.com"]
perm1 = Permission()
perm1.open_portal = True
permissions = [perm1]
user.emails = emails
user.permissions = permissions
user.save()
```
Every field type is responsible dealing with the actual value of this field in the Base objects (instances), mainly:
* validation
* converting from raw primitive types to special types if any, which would help in serialization
In addition to custom options every field can accept and define, they can be used in the following methods:
* `validate`: raises a `ValidationError` in case it's not valid.
* `to_raw`: returns a raw (primitive type) object from a value of this field
* `from_raw`: returns a new object of the field type (if any) from a raw value
No need for `from_raw` to raise an error on e.g. type mismatch, as `validate` will do the validation.
"""
import arrow
import datetime
import ipaddress
import json
import re
import uuid
from urllib.parse import urlparse
from .factory import Factory as BaseFactory, StoredFactory
class ValidationError(Exception):
"""
base type for any validation error
"""
class Field:
def __init__(
self,
default=None,
required=False,
indexed=False,
readonly=False,
validators=None,
stored=True,
on_update=None,
compute=None,
**kwargs,
):
"""
Base field for all field types, have some common options that can be used any other field type too.
Args:
default (any, optional): default value. Defaults to None.
required (bool, optional): required or not. Defaults to False.
indexed (bool, optional): indexed or not. Defaults to False.
readonly (bool, optional): can only get the value. Defaults to False.
validators (list of function, optional): a list of functions that takes a value and raises ValidationError if not valid. Defaults to None.
stored (bool, optional): if the fields should be stored or not, useful for computed fields. Defaults to True.
on_update (callable, optional): a callable that takes the instance and new value on field updates. Defaults to None.
compute (callable, optional): a callable that takes the instance and returns a computed value for this field. `on_update` won't be called in this case. Defaults to None.
"""
self.default = default
self.required = required
self.indexed = indexed
self.readonly = readonly
self.kwargs = kwargs
self.validators = validators
if self.validators is None:
self.validators = []
self.stored = stored
self.on_update = on_update
self.compute = compute
def preprocess(self, value):
# TODO: make from/to raw methods only for serialization
# and let preprocess/validate do the cleanup/checking step
# and return the correct value that current field should hold
# so, from_raw and to_raw are used only for serialization
return value
def validate_with_name(self, value, name):
"""
validates the value using validate method and prepends the field name
in case an exception was thrown
Args:
value (any): in case value is not valid
name (str): the field name
Raises:
ValidationError: The original validation error with the field name prepended to the message
"""
try:
self.validate(value)
except ValidationError as e:
raise ValidationError(f"{name}: " + str(e))
def validate(self, value):
"""
validate value if required and call custom self.validators if any
Args:
value (any): in case value is not valid
Raises:
ValidationError: [description]
"""
if value is None:
if self.required:
raise ValidationError("field is required")
for validator in self.validators:
validator(value)
def from_raw(self, value):
"""
get the value of this field from primitive raw types
Args:
value (any): support value by this field type (if any)
Returns:
any: raw value
"""
return value
def to_raw(self, value):
"""
get the raw value of this field
Args:
value (any): current value of this field
Returns:
any: a primitive raw value
"""
return value
@property
def trigger_updates(self):
return callable(self.on_update)
@property
def computed(self):
return callable(self.compute)
class Typed(Field):
def __init__(self, type_, **kwargs):
"""
Base field for any type, it will hold values of `type_`
Args:
type_ (type): any type (class)
kwargs: any keyword arguments supported by `Field`
"""
self.type = type_
super().__init__(**kwargs)
def validate(self, value):
super().validate(value)
if value is not None:
if not isinstance(value, self.type):
raise ValidationError(f"value '{value}' is not of type {self.type.__name__}")
class Boolean(Typed):
def __init__(self, default=False, **kwargs):
"""
Boolean fields to hold a `bool` value.
values can be set using strings or numbers and will be converted to `bool` like:
- "on", "off"
- "yes", "no"
- "true", "false"
- 0, 1
- 0, 1+2j
Args:
default (bool, optional): default value. Defaults to False.
kwargs: any keyword arguments supported by `Field`
"""
super().__init__(type_=bool, default=default, **kwargs)
def from_raw(self, value):
"""
get bool value from strings and numbers
Args:
value (str or int or float or complex)
Returns:
bool: boolean value
"""
if isinstance(value, str):
value = value.lower().strip()
if value in ("yes", "on", "true"):
return True
if value in ("no", "off", "false"):
return False
elif isinstance(value, (int, float, complex)):
return bool(value)
# validate will do the check
return value
class Integer(Typed):
def __init__(self, default=0, min=None, max=None, **kwargs):
"""
Intger field, the same as `Typed`, but with a type of `int`
It can have a minimum value, if min is not set, it will ignore it.
values can be set using strings like (will be converted to `int`):
- "12", "1212 "
Args:
default (int, optional): default value. Defaults to 0.
min (int, optional): minimum value (inclusive). Defaults to None.
max (int, optional): maximum value (inclusive). Defaults to None.
kwargs: any keyword arguments supported by `Field`
"""
self.min = min
self.max = max
super().__init__(type_=int, default=default, min=min, **kwargs)
def validate(self, value):
super().validate(value)
if value and self.min is not None:
if value < self.min:
raise ValidationError(f"cannot set values less than {self.min}")
if value and self.max is not None:
if value > self.max:
raise ValidationError(f"cannot set values greater than {self.max}")
def from_raw(self, value):
if isinstance(value, str):
try:
value = int(value.strip())
except ValueError:
pass
return value
class Float(Typed):
def __init__(self, default=0.0, **kwargs):
"""
Same as `Integer` field, but with a type of `float`.
values can be set using strings like (will be converted to `float`):
- "12.3", " 1212.23 "
Args:
default (float, optional): default value. Defaults to 0.0.
kwargs: any keyword arguments supported by `Field`
"""
super().__init__(type_=float, default=default, min=min, **kwargs)
def from_raw(self, value):
if isinstance(value, str):
value = value.strip()
try:
value = float(value)
except ValueError:
pass
return value
class String(Typed):
def __init__(self, maxlen=None, allow_empty=True, **kwargs):
"""
Same as `Typed`, but with a type of `str`.
If maxlen is set, it will validate the length of the string.
Args:
maxlen (int): maximum length allowed. Defaults to None
allow_empty (bool): if empty string values are allowed or not. Defaults to True
kwargs: any keyword arguments supported by `Field`
"""
super().__init__(type_=str, **kwargs)
self.maxlen = maxlen
self.allow_empty = allow_empty
def validate(self, value):
super().validate(value)
if not self.allow_empty and value == "":
raise ValidationError("field does not allow empty values")
if self.maxlen is not None:
if value and len(value) > self.maxlen:
raise ValidationError(f"length of the string exceeds {self.maxlen}")
class Secret(String):
"""
Same as `String`, but encrypted by default.
Should be used with sensitive data.
Args:
kwargs: any keyword arguments supported by `String`
"""
class Object(Typed):
def __init__(self, type_, type_kwargs=None, **kwargs):
"""
An embedded Base object field of any type.
Args:
type_ (type): Base object type (class)
type_kwargs (dict, optional): kwargs as a dict to be passed to Base instance when created. Defaults to None.
kwargs: any keyword arguments supported by `Field`
"""
super().__init__(type_=type_, **kwargs)
self.type_kwargs = type_kwargs
if self.type_kwargs is None:
self.type_kwargs = {}
if not self.default:
# make it callable to create different objects
# not a single one as a default for all
self.default = lambda: self.type(**self.type_kwargs)
def validate(self, value):
"""
validate Base objects
Args:
value (Base): object
"""
super().validate(value)
if value:
value.validate()
def to_raw(self, obj):
"""
get raw value of an object as dict
Args:
obj (Base): base object
Returns:
dict: raw data
"""
if obj:
return obj._get_data()
def from_raw(self, data):
"""
get an object from dict
Args:
data (dict): data dict
Returns:
Base: base object
"""
if isinstance(data, dict):
return self.type(**data)
return data
class List(Field):
def __init__(self, field, **kwargs):
"""
A list field for any field types.
Args:
field (Field): a field instance of any fields, e.g. `fields.String(maxlen=14)`.
kwargs: any keyword arguments supported by `Field`
"""
self.field = field
super().__init__(**kwargs)
def validate(self, value):
"""
validate the value of every item in the list
Will just call the field.validate of the given field
"""
super().validate(value)
if value is None:
value = []
for item in value:
self.field.validate(item)
def to_raw(self, values):
"""
get a list of values as raw
Args:
values (list): list of items of field type
Returns:
list: list of raw values
"""
if not values:
return []
return [self.field.to_raw(value) for value in values]
def from_raw(self, values):
"""
get a list of field type from raw values
Args:
values (list): list of raw values
Returns:
list: list of objects of field type
"""
if not values:
return []
return [self.field.from_raw(value) for value in values]
class Enum(Typed):
def __init__(self, enum_type, **kwargs):
"""
Enum field, to be used with `enum.Enum`.
Example:
```python
class UserType(Enum):
USER = "user"
ADMIN = "admin"
class User(Base):
field = fields.Enum(UserType)
```
Will hold values of type `enum.Enum`.
Args:
enum_type (type): enum type (class)
kwargs: any keyword arguments supported by `Field`
"""
# default is the first value
default = next(iter(enum_type))
super().__init__(type_=enum_type, default=default, **kwargs)
self.enum_type = enum_type
def to_raw(self, enum_obj):
"""
get enum value
Args:
enum_obj (enum.Enum): enum object
Returns:
any: enum value
"""
if enum_obj:
return enum_obj.value
def from_raw(self, value):
"""
get an enum object from value
Args:
value (any): any value
Returns:
enum: enum object of enum type of the field
"""
try:
return self.enum_type(value)
except ValueError:
# let validate() do the validation
return value
class Email(String):
def __init__(self, default="", **kwargs):
"""
Email field, will validate the value of emails
will hold string email values.
Args:
default (str, optional): default value. Defaults to ""
kwargs: other keyword arguments supported by `string`
"""
super().__init__(default=default, **kwargs)
self.regex = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
def validate(self, value):
"""
check whether provided value is a valid email representation
Args:
value (str)
Raises:
ValidationError: in case the value is not a telephone
"""
super().validate(value)
if value and not re.match(self.regex, value):
raise ValidationError(f"'{value}' is not a valid Email address")
class Path(String):
# TODO: Validate that it is working on windows
def __init__(self, default="", **kwargs):
"""
Path field, will validate the value of file system paths
Will hold string path values.
Args:
default (str, optional): default value. Defaults to ""
kwargs: other keyword arguments supported by `Field`
"""
super().__init__(default, **kwargs)
self.regex = r"^(/[^/ ]*)+/?$"
def validate(self, value):
"""
check whether provided value is a valid path representation
Args:
value (str)
Raises:
ValidationError: in case the value is not a telephone
"""
super().validate(value)
if value and not re.match(self.regex, value):
raise ValidationError(f"'{value}' is not a valid Path")
class URL(String):
def __init__(self, default="", **kwargs):
"""
URL field, will validate the value of urls
Will hold string URLs.
Args:
default (str, optional): default value. Defaults to ""
kwargs: other keyword arguments supported by `Field`
"""
super().__init__(default=default, **kwargs)
self.regex = r"^(https?|ftp)://[^\s/$.?#].[^\s]*$"
def validate(self, value):
"""
check whether provided value is a valid URL representation
Args:
value (str)
Raises:
ValidationError: in case the value is not a telephone
"""
super().validate(value)
if value:
url = urlparse(value)
if not url.scheme or not url.netloc:
raise ValidationError(f"'{value}' is not a valid URL address")
class Tel(String):
def __init__(self, default="", **kwargs):
"""
Telephone field, will validate the value of telephone numbers
Will hold telephone values as strings.
It will strip any additional characters that are not numbers.
Args:
default (str, optional): default value. Defaults to ""
kwargs: other keyword arguments supported by `String`
"""
super().__init__(default=default, **kwargs)
self.regex = r"^\+?[0-9]{6,15}(?:x[0-9]+)?$"
def validate(self, value):
"""
check whether provided value is a valid telephone number representation
Args:
value (str)
Raises:
ValidationError: in case the value is not a telephone
"""
super().validate(value)
if value and not re.search(self.regex, value):
raise ValidationError(f"'{value}' is not a valid Telephone")
def from_raw(self, value):
"""clean the telephone value from unwanted signs like , - ( )"""
if value is not None:
value = value.replace(",", "")
value = value.replace("-", "")
value = value.replace("(", "")
value = value.replace(")", "")
value = value.replace(" ", "")
return value
return value
class IPMixin:
def is_a(self, value, *types):
"""
try creating any type of `types` from the given value
Args:
value (str)
Returns:
bool: `True` if any matched, `False` otherwise
"""
matched = False
for type_ in types:
try:
type_(value)
matched |= True
except ValueError:
continue
return matched
def is_ipv4(self, value):
"""
check if a given value is a v4 IP address
Args:
value (str): ip address
Returns:
bool: `True` if it's a valid v4 IP address
"""
return self.is_a(value, ipaddress.IPv4Address)
def is_ipv6(self, value):
"""
check if a given value is a v6 IP address
Args:
value (str): IP address
Returns:
bool: `True` if it's a valid v6 IP address
"""
return self.is_a(value, ipaddress.IPv6Address)
def is_ip(self, value):
"""
check if a given value is a v4/v6 IP address
Args:
value (str): IP address
Returns:
bool: `True` if it's a valid v4/v6 IP address
"""
return self.is_ipv4(value) or self.is_ipv6(value)
def is_iface(self, value):
"""
check if a given value is an IP interface
Args:
value (str): IP interface
Returns:
bool: `True` if it's a valid v4/v6 IP interface
"""
return self.is_a(value, ipaddress.IPv4Interface, ipaddress.IPv6Interface)
class IPAddress(IPMixin, String):
def __init__(self, default="0.0.0.0", **kwargs):
"""
IP address field, will validate the value of ip address (v4 and v6)
Will hold string ip addresses.
Args:
default (str, optional): default value. Defaults to ""
kwargs: any keyword arguments supported by `Field`
"""
super().__init__(default=default, **kwargs)
def validate(self, value):
"""
check whether provided value is a valid IPaddress representation
including IPv4,IPv6 and network
Args:
value (str)
Raises:
ValidationError: in case the value is not an IPAddress
"""
super().validate(value)
if value and not self.is_ip(value):
raise ValidationError(f"'{value}' is not a valid IP address")
def from_raw(self, value):
if isinstance(value, str):
if value.strip().lower() == "localhost":
value = "127.0.0.1"
return value
class IPRange(IPMixin, String):
def __init__(self, default="::/128", **kwargs):
"""
ip range field, will validate the value of ip ranges (v4 and v6)
will be stored as a string.
Args:
default (str, optional): default value. Defaults to ""
kwargs: any keyword arguments supported by `Field`
"""
super().__init__(default=default, **kwargs)
def validate(self, value):
"""
check whether provided value is a valid IPaddress representation
including IPv4,IPv6 and network
Args:
value (str)
Raises:
ValidationError: in case the value is not an IPAddress
"""
super().validate(value)
if value and not self.is_iface(value):
raise ValidationError(f"'{value}' is not a valid IP range/interface")
class Port(Integer):
def __init__(self, **kwargs):
"""
Port field, will check if the given port is within the range of 0-65535.
Will hold integers, but also accepts string values like `"8080"`.
Args:
kwargs: any keyword arguments supported by `Field`
"""
super().__init__(min=0, max=65535, **kwargs)
def validate(self, value):
try:
super().validate(value)
except ValidationError:
raise ValidationError("value is not in range of 0-65535")
class GUID(String):
def __init__(self, default=None, **kwargs):
"""
UUID v4 field, will be auto-generated by default.
Will hold the UUID as a string.
It accepts setting UUID value by many ways and converts them to strings:
- Strings like: `"12345678-1234-4678-9234-567812345678"`
- Bytes like: `b"\x12\x34\x56\x78" * 4`
- Integer like: `0x12345678123456781234567812345678`
- UUID objects of type uuid.UUID
Args:
default (str, optional): default value, will be auto-generated if None. Defaults to None
kwargs: other keyword arguments supported by `String`
"""
if not default:
default = lambda: str(uuid.uuid4())
super().__init__(default=default, **kwargs)
def validate(self, value):
super().validate(value)
if not value:
return
try:
uuid.UUID(value, version=4)
except ValueError as valexc:
raise ValidationError(f"''{value}'' is invalid, {valexc}") from valexc
def from_raw(self, value):
"""
convert string, bytes, int to UUID object, then to a string
Args:
value (int or str or bytes or uuid.UUID): [description]
Returns:
str: UUID string
"""
if isinstance(value, uuid.UUID):
return str(value)
try:
uuid_obj = None
if isinstance(value, int):
uuid_obj = uuid.UUID(int=value, version=4)
elif isinstance(value, str):
uuid_obj = uuid.UUID(value, version=4)
elif isinstance(value, bytes):
uuid_obj = uuid.UUID(bytes=value, version=4)
if uuid_obj:
return str(uuid_obj)
# return as is to be validated later
return value
except ValueError:
# failed to get uuid from raw value
# return as is to be validated later
return value
class DateTimeMixin:
def get_arrow_obj(self, value):
"""
get an arrow object from int, float and str and `datetime.time` objects.
Args:
value (int or float or str): timestamp (utc) or e.g. "1998-01-03"
Returns:
arrow.Arrow: arrow object in utc
"""
if isinstance(value, datetime.time):
# convert to string, as there's no direct way i know of
# to convert from datetime.time objects to arrow directly
value = value.strftime(self.format)
if isinstance(value, str):
return arrow.Arrow.strptime(value, self.format).to("utc")
return arrow.get(value)
def get_timestamp(self, obj):
"""
get a utc timestamp from datetime/date/time objects
Args:
obj (datetime.datetime or datetime.date or datetime.time): date/time or datetime object
Returns:
int or float: utc timestamp
"""
return arrow.get(obj).to("utc").timestamp
def from_raw(self, value):
"""
get a datetime object from a numberic (epoch) or string value
Args:
value (str or int or float): value as a number or a string
Returns:
datetime.datetime or datetime.date or datetime.time: datetime or date/time object
"""
if isinstance(value, (int, float, str)):
try:
obj = self.get_arrow_obj(value)
if self.type == datetime.datetime:
return obj.datetime
elif self.type == datetime.date:
return obj.date()
elif self.type == datetime.time:
return obj.time()
except (ValueError, arrow.parser.ParserError):
# will be caught by validate
pass
return value
def to_raw(self, dt_obj):
"""
get a utc timestamp from datetime object
Args:
dt_obj (datetime.datetime or datetime.date or datetime.time): datetime object
Returns:
int or float: utc timestamp
"""
if dt_obj:
return self.get_arrow_obj(dt_obj).to("utc").timestamp
def validate(self, value):
if isinstance(self.from_raw(value), str):
# cannot convert from string, still an invalid format
raise ValidationError(f"'{value}' is not in the format of '{self.format}'")
super().validate(value)
class DateTime(DateTimeMixin, Typed):
# maybe add something like auto_now and auto_today for date/time fields
def __init__(self, default=None, format_=None, **kwargs):
"""
datetime field, will hold datetime.datetime objects.
values can be set using strings in the given `format_` too like "12/1/2020" or a utc timestamp,
they will converted to objects.
Args:
default (datetime): default value. Defaults to None.
format_ (str, optional): datetime format. Defaults to "%Y-%m-%d %H:%M" if None.
kwargs: any keyword arguments supported by `Field`
"""
super().__init__(type_=datetime.datetime, default=default, **kwargs)
if not format_:
format_ = "%Y-%m-%d %H:%M"
self.format = format_
class Date(DateTimeMixin, Typed):
def __init__(self, default=None, format_=None, **kwargs):
"""
date field, will hold datetime.date objects.
values can be set using strings in the given `format_` too like "12/1/2020" or a utc timestamp,
they will converted to objects.
Args:
default (date): default value. Defaults to None.
format_ (str, optional): date format. Defaults to "%Y-%m-%d" if None.
kwargs: any keyword arguments supported by `Field`
"""
super().__init__(type_=datetime.date, default=default, **kwargs)
if not format_:
format_ = "%Y-%m-%d"
self.format = format_
class Time(DateTimeMixin, Typed):
def __init__(self, default=None, format_=None, **kwargs):
"""
time field, will hold utc datetime.time objects
values can be set using strings in the given `format_` too like "12:13" or a utc timestamp,
they will converted to objects.
Args:
default (date): default value. Defaults to None.
format_ (str, optional): time format. Defaults to "%H:%M" if None.
kwargs: any keyword arguments supported by `Field`
"""
super().__init__(default=default, type_=datetime.time, **kwargs)
if not format_:
format_ = "%H:%M"
self.format = format_
class Bytes(Typed):
def __init__(self, default=b"", encoding="utf-8", **kwargs):
"""
same as string field, but will hold `bytes`.
Args:
default (b"", optional): default value. Defaults to b""
encoding: encoding to be used when serializing the value. Defaults to "utf-8"
kwargs: any keyword arguments supported by `Field`
"""
self.encoding = encoding
super().__init__(default=default, type_=bytes, **kwargs)
def from_raw(self, value):
if isinstance(value, str):
return value.encode(self.encoding)
return value
def to_raw(self, value):
if value:
return value.decode(self.encoding)
class Json(String):
def __init__(self, default="{}", **kwargs):
"""
Json field, will check if the value is a valid json string.
It will hold json strings, if the value is not string, it will be converted.
Args:
default (str, optional): default value. Defaults to "{}"
kwargs: other keyword arguments supported by `String`
"""
super().__init__(default=default, **kwargs)
def validate(self, value):
"""
check whether provided value is a valid json
Args:
value (str)
Raises:
ValidationError: in case the value isn't a valid json
"""
super().validate(value)
if not value:
return
# if it's a string, try to load it
try:
json.loads(value)
except Exception as e:
raise ValidationError(f"'{value}' isn't a valid json, {e}") from e
def from_raw(self, value):
"""
convert non-string values to json string
Args:
value (any): non-string value
Returns:
str: a json string
"""
if not isinstance(value, (str, bytes, bytearray)):
try:
value = json.dumps(value)
except:
pass
return value
class Factory(Field):
def __init__(self, type_, factory_type=None, **kwargs):
"""
A factory field for any `Base` type, also, you can specify your factory type/class
Example:
```python
class User(Base):
name = fields.String()
class Server(Base):
users = fields.Factory(User)
```
Another example with a custom factory class:
```python
class User(Base):
name = fields.String()
class UserFactory(StoredFactory):
def list_from_remote(self):
# list users from remote storage
# ...
class Server(Base):
users = fields.Factory(User, factory_type=UserFactory)
```
Args:
type_ (Base): any base type to be used by the factory
factory_type (`BaseFactory`, optional): factory class/type. Defaults to None.
"""
# value type will be factory
# make sure stored default value is True
kwargs["stored"] = kwargs.get("stored", True)
super().__init__(readonly=True, **kwargs)
# but we keep the type of any Base class
# so, we can init a Factory with it
self.type = type_
if factory_type:
self.factory_type = factory_type
else:
if self.stored:
self.factory_type = StoredFactory
else:
self.factory_type = BaseFactory
def validate(self, value):
super().validate(value)
if not isinstance(value, self.factory_type):
raise ValidationError(f"factory type is not {self.factory_type}")
def from_raw(self, value):
return value
def to_raw(self, value):
return None
Classes
class Boolean (default=False, **kwargs)
-
Boolean fields to hold a
bool
value.values can be set using strings or numbers and will be converted to
bool
like:- "on", "off"
- "yes", "no"
- "true", "false"
- 0, 1
- 0, 1+2j
Args
default
:bool
, optional- default value. Defaults to False.
kwargs
- any keyword arguments supported by
Field
Expand source code
class Boolean(Typed): def __init__(self, default=False, **kwargs): """ Boolean fields to hold a `bool` value. values can be set using strings or numbers and will be converted to `bool` like: - "on", "off" - "yes", "no" - "true", "false" - 0, 1 - 0, 1+2j Args: default (bool, optional): default value. Defaults to False. kwargs: any keyword arguments supported by `Field` """ super().__init__(type_=bool, default=default, **kwargs) def from_raw(self, value): """ get bool value from strings and numbers Args: value (str or int or float or complex) Returns: bool: boolean value """ if isinstance(value, str): value = value.lower().strip() if value in ("yes", "on", "true"): return True if value in ("no", "off", "false"): return False elif isinstance(value, (int, float, complex)): return bool(value) # validate will do the check return value
Ancestors
Methods
def from_raw(self, value)
-
get bool value from strings and numbers
Args
value (str or int or float or complex)
Returns
bool
- boolean value
Expand source code
def from_raw(self, value): """ get bool value from strings and numbers Args: value (str or int or float or complex) Returns: bool: boolean value """ if isinstance(value, str): value = value.lower().strip() if value in ("yes", "on", "true"): return True if value in ("no", "off", "false"): return False elif isinstance(value, (int, float, complex)): return bool(value) # validate will do the check return value
Inherited members
class Bytes (default=b'', encoding='utf-8', **kwargs)
-
same as string field, but will hold
bytes
.Args
- default (b"", optional): default value. Defaults to b""
encoding
- encoding to be used when serializing the value. Defaults to "utf-8"
kwargs
- any keyword arguments supported by
Field
Expand source code
class Bytes(Typed): def __init__(self, default=b"", encoding="utf-8", **kwargs): """ same as string field, but will hold `bytes`. Args: default (b"", optional): default value. Defaults to b"" encoding: encoding to be used when serializing the value. Defaults to "utf-8" kwargs: any keyword arguments supported by `Field` """ self.encoding = encoding super().__init__(default=default, type_=bytes, **kwargs) def from_raw(self, value): if isinstance(value, str): return value.encode(self.encoding) return value def to_raw(self, value): if value: return value.decode(self.encoding)
Ancestors
Inherited members
class Date (default=None, format_=None, **kwargs)
-
date field, will hold datetime.date objects.
values can be set using strings in the given
format_
too like "12/1/2020" or a utc timestamp, they will converted to objects.Args
default
:date
- default value. Defaults to None.
format_
:str
, optional- date format. Defaults to "%Y-%m-%d" if None.
kwargs
- any keyword arguments supported by
Field
Expand source code
class Date(DateTimeMixin, Typed): def __init__(self, default=None, format_=None, **kwargs): """ date field, will hold datetime.date objects. values can be set using strings in the given `format_` too like "12/1/2020" or a utc timestamp, they will converted to objects. Args: default (date): default value. Defaults to None. format_ (str, optional): date format. Defaults to "%Y-%m-%d" if None. kwargs: any keyword arguments supported by `Field` """ super().__init__(type_=datetime.date, default=default, **kwargs) if not format_: format_ = "%Y-%m-%d" self.format = format_
Ancestors
Inherited members
class DateTime (default=None, format_=None, **kwargs)
-
datetime field, will hold datetime.datetime objects.
values can be set using strings in the given
format_
too like "12/1/2020" or a utc timestamp, they will converted to objects.Args
default
:datetime
- default value. Defaults to None.
format_
:str
, optional- datetime format. Defaults to "%Y-%m-%d %H:%M" if None.
kwargs
- any keyword arguments supported by
Field
Expand source code
class DateTime(DateTimeMixin, Typed): # maybe add something like auto_now and auto_today for date/time fields def __init__(self, default=None, format_=None, **kwargs): """ datetime field, will hold datetime.datetime objects. values can be set using strings in the given `format_` too like "12/1/2020" or a utc timestamp, they will converted to objects. Args: default (datetime): default value. Defaults to None. format_ (str, optional): datetime format. Defaults to "%Y-%m-%d %H:%M" if None. kwargs: any keyword arguments supported by `Field` """ super().__init__(type_=datetime.datetime, default=default, **kwargs) if not format_: format_ = "%Y-%m-%d %H:%M" self.format = format_
Ancestors
Inherited members
class DateTimeMixin
-
Expand source code
class DateTimeMixin: def get_arrow_obj(self, value): """ get an arrow object from int, float and str and `datetime.time` objects. Args: value (int or float or str): timestamp (utc) or e.g. "1998-01-03" Returns: arrow.Arrow: arrow object in utc """ if isinstance(value, datetime.time): # convert to string, as there's no direct way i know of # to convert from datetime.time objects to arrow directly value = value.strftime(self.format) if isinstance(value, str): return arrow.Arrow.strptime(value, self.format).to("utc") return arrow.get(value) def get_timestamp(self, obj): """ get a utc timestamp from datetime/date/time objects Args: obj (datetime.datetime or datetime.date or datetime.time): date/time or datetime object Returns: int or float: utc timestamp """ return arrow.get(obj).to("utc").timestamp def from_raw(self, value): """ get a datetime object from a numberic (epoch) or string value Args: value (str or int or float): value as a number or a string Returns: datetime.datetime or datetime.date or datetime.time: datetime or date/time object """ if isinstance(value, (int, float, str)): try: obj = self.get_arrow_obj(value) if self.type == datetime.datetime: return obj.datetime elif self.type == datetime.date: return obj.date() elif self.type == datetime.time: return obj.time() except (ValueError, arrow.parser.ParserError): # will be caught by validate pass return value def to_raw(self, dt_obj): """ get a utc timestamp from datetime object Args: dt_obj (datetime.datetime or datetime.date or datetime.time): datetime object Returns: int or float: utc timestamp """ if dt_obj: return self.get_arrow_obj(dt_obj).to("utc").timestamp def validate(self, value): if isinstance(self.from_raw(value), str): # cannot convert from string, still an invalid format raise ValidationError(f"'{value}' is not in the format of '{self.format}'") super().validate(value)
Subclasses
Methods
def from_raw(self, value)
-
get a datetime object from a numberic (epoch) or string value
Args
value
:str
orint
orfloat
- value as a number or a string
Returns
datetime.datetime
ordatetime.date
ordatetime.time
- datetime or date/time object
Expand source code
def from_raw(self, value): """ get a datetime object from a numberic (epoch) or string value Args: value (str or int or float): value as a number or a string Returns: datetime.datetime or datetime.date or datetime.time: datetime or date/time object """ if isinstance(value, (int, float, str)): try: obj = self.get_arrow_obj(value) if self.type == datetime.datetime: return obj.datetime elif self.type == datetime.date: return obj.date() elif self.type == datetime.time: return obj.time() except (ValueError, arrow.parser.ParserError): # will be caught by validate pass return value
def get_arrow_obj(self, value)
-
get an arrow object from int, float and str and
datetime.time
objects.Args
value
:int
orfloat
orstr
- timestamp (utc) or e.g. "1998-01-03"
Returns
arrow.Arrow
- arrow object in utc
Expand source code
def get_arrow_obj(self, value): """ get an arrow object from int, float and str and `datetime.time` objects. Args: value (int or float or str): timestamp (utc) or e.g. "1998-01-03" Returns: arrow.Arrow: arrow object in utc """ if isinstance(value, datetime.time): # convert to string, as there's no direct way i know of # to convert from datetime.time objects to arrow directly value = value.strftime(self.format) if isinstance(value, str): return arrow.Arrow.strptime(value, self.format).to("utc") return arrow.get(value)
def get_timestamp(self, obj)
-
get a utc timestamp from datetime/date/time objects
Args
obj
:datetime.datetime
ordatetime.date
ordatetime.time
- date/time or datetime object
Returns
int
orfloat
- utc timestamp
Expand source code
def get_timestamp(self, obj): """ get a utc timestamp from datetime/date/time objects Args: obj (datetime.datetime or datetime.date or datetime.time): date/time or datetime object Returns: int or float: utc timestamp """ return arrow.get(obj).to("utc").timestamp
def to_raw(self, dt_obj)
-
get a utc timestamp from datetime object
Args
dt_obj
:datetime.datetime
ordatetime.date
ordatetime.time
- datetime object
Returns
int
orfloat
- utc timestamp
Expand source code
def to_raw(self, dt_obj): """ get a utc timestamp from datetime object Args: dt_obj (datetime.datetime or datetime.date or datetime.time): datetime object Returns: int or float: utc timestamp """ if dt_obj: return self.get_arrow_obj(dt_obj).to("utc").timestamp
def validate(self, value)
-
Expand source code
def validate(self, value): if isinstance(self.from_raw(value), str): # cannot convert from string, still an invalid format raise ValidationError(f"'{value}' is not in the format of '{self.format}'") super().validate(value)
class Email (default='', **kwargs)
-
Email field, will validate the value of emails
will hold string email values.
Args
default
:str
, optional- default value. Defaults to ""
kwargs
- other keyword arguments supported by
string
Expand source code
class Email(String): def __init__(self, default="", **kwargs): """ Email field, will validate the value of emails will hold string email values. Args: default (str, optional): default value. Defaults to "" kwargs: other keyword arguments supported by `string` """ super().__init__(default=default, **kwargs) self.regex = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" def validate(self, value): """ check whether provided value is a valid email representation Args: value (str) Raises: ValidationError: in case the value is not a telephone """ super().validate(value) if value and not re.match(self.regex, value): raise ValidationError(f"'{value}' is not a valid Email address")
Ancestors
Methods
def validate(self, value)
-
check whether provided value is a valid email representation
Args
value (str)
Raises
ValidationError
- in case the value is not a telephone
Expand source code
def validate(self, value): """ check whether provided value is a valid email representation Args: value (str) Raises: ValidationError: in case the value is not a telephone """ super().validate(value) if value and not re.match(self.regex, value): raise ValidationError(f"'{value}' is not a valid Email address")
Inherited members
class Enum (enum_type, **kwargs)
-
Enum field, to be used with
enum.Enum
.Example:
class UserType(Enum): USER = "user" ADMIN = "admin" class User(Base): field = fields.Enum(UserType)
Will hold values of type
enum.Enum
.Args
enum_type
:type
- enum type (class)
kwargs
- any keyword arguments supported by
Field
Expand source code
class Enum(Typed): def __init__(self, enum_type, **kwargs): """ Enum field, to be used with `enum.Enum`. Example: ```python class UserType(Enum): USER = "user" ADMIN = "admin" class User(Base): field = fields.Enum(UserType) ``` Will hold values of type `enum.Enum`. Args: enum_type (type): enum type (class) kwargs: any keyword arguments supported by `Field` """ # default is the first value default = next(iter(enum_type)) super().__init__(type_=enum_type, default=default, **kwargs) self.enum_type = enum_type def to_raw(self, enum_obj): """ get enum value Args: enum_obj (enum.Enum): enum object Returns: any: enum value """ if enum_obj: return enum_obj.value def from_raw(self, value): """ get an enum object from value Args: value (any): any value Returns: enum: enum object of enum type of the field """ try: return self.enum_type(value) except ValueError: # let validate() do the validation return value
Ancestors
Methods
def from_raw(self, value)
-
get an enum object from value
Args
value
:any
- any value
Returns
enum
- enum object of enum type of the field
Expand source code
def from_raw(self, value): """ get an enum object from value Args: value (any): any value Returns: enum: enum object of enum type of the field """ try: return self.enum_type(value) except ValueError: # let validate() do the validation return value
def to_raw(self, enum_obj)
-
get enum value
Args
enum_obj
:enum.Enum
- enum object
Returns
any
- enum value
Expand source code
def to_raw(self, enum_obj): """ get enum value Args: enum_obj (enum.Enum): enum object Returns: any: enum value """ if enum_obj: return enum_obj.value
Inherited members
class Factory (type_, factory_type=None, **kwargs)
-
A factory field for any
Base
type, also, you can specify your factory type/classExample:
class User(Base): name = fields.String() class Server(Base): users = fields.Factory(User)
Another example with a custom factory class:
class User(Base): name = fields.String() class UserFactory(StoredFactory): def list_from_remote(self): # list users from remote storage # ... class Server(Base): users = fields.Factory(User, factory_type=UserFactory)
Args
type_
:Base
- any base type to be used by the factory
factory_type (
BaseFactory
, optional): factory class/type. Defaults to None.Expand source code
class Factory(Field): def __init__(self, type_, factory_type=None, **kwargs): """ A factory field for any `Base` type, also, you can specify your factory type/class Example: ```python class User(Base): name = fields.String() class Server(Base): users = fields.Factory(User) ``` Another example with a custom factory class: ```python class User(Base): name = fields.String() class UserFactory(StoredFactory): def list_from_remote(self): # list users from remote storage # ... class Server(Base): users = fields.Factory(User, factory_type=UserFactory) ``` Args: type_ (Base): any base type to be used by the factory factory_type (`BaseFactory`, optional): factory class/type. Defaults to None. """ # value type will be factory # make sure stored default value is True kwargs["stored"] = kwargs.get("stored", True) super().__init__(readonly=True, **kwargs) # but we keep the type of any Base class # so, we can init a Factory with it self.type = type_ if factory_type: self.factory_type = factory_type else: if self.stored: self.factory_type = StoredFactory else: self.factory_type = BaseFactory def validate(self, value): super().validate(value) if not isinstance(value, self.factory_type): raise ValidationError(f"factory type is not {self.factory_type}") def from_raw(self, value): return value def to_raw(self, value): return None
Ancestors
Inherited members
class Field (default=None, required=False, indexed=False, readonly=False, validators=None, stored=True, on_update=None, compute=None, **kwargs)
-
Base field for all field types, have some common options that can be used any other field type too.
Args
default
:any
, optional- default value. Defaults to None.
required
:bool
, optional- required or not. Defaults to False.
indexed
:bool
, optional- indexed or not. Defaults to False.
readonly
:bool
, optional- can only get the value. Defaults to False.
validators
:list
offunction
, optional- a list of functions that takes a value and raises ValidationError if not valid. Defaults to None.
stored
:bool
, optional- if the fields should be stored or not, useful for computed fields. Defaults to True.
on_update
:callable
, optional- a callable that takes the instance and new value on field updates. Defaults to None.
compute
:callable
, optional- a callable that takes the instance and returns a computed value for this field.
on_update
won't be called in this case. Defaults to None.
Expand source code
class Field: def __init__( self, default=None, required=False, indexed=False, readonly=False, validators=None, stored=True, on_update=None, compute=None, **kwargs, ): """ Base field for all field types, have some common options that can be used any other field type too. Args: default (any, optional): default value. Defaults to None. required (bool, optional): required or not. Defaults to False. indexed (bool, optional): indexed or not. Defaults to False. readonly (bool, optional): can only get the value. Defaults to False. validators (list of function, optional): a list of functions that takes a value and raises ValidationError if not valid. Defaults to None. stored (bool, optional): if the fields should be stored or not, useful for computed fields. Defaults to True. on_update (callable, optional): a callable that takes the instance and new value on field updates. Defaults to None. compute (callable, optional): a callable that takes the instance and returns a computed value for this field. `on_update` won't be called in this case. Defaults to None. """ self.default = default self.required = required self.indexed = indexed self.readonly = readonly self.kwargs = kwargs self.validators = validators if self.validators is None: self.validators = [] self.stored = stored self.on_update = on_update self.compute = compute def preprocess(self, value): # TODO: make from/to raw methods only for serialization # and let preprocess/validate do the cleanup/checking step # and return the correct value that current field should hold # so, from_raw and to_raw are used only for serialization return value def validate_with_name(self, value, name): """ validates the value using validate method and prepends the field name in case an exception was thrown Args: value (any): in case value is not valid name (str): the field name Raises: ValidationError: The original validation error with the field name prepended to the message """ try: self.validate(value) except ValidationError as e: raise ValidationError(f"{name}: " + str(e)) def validate(self, value): """ validate value if required and call custom self.validators if any Args: value (any): in case value is not valid Raises: ValidationError: [description] """ if value is None: if self.required: raise ValidationError("field is required") for validator in self.validators: validator(value) def from_raw(self, value): """ get the value of this field from primitive raw types Args: value (any): support value by this field type (if any) Returns: any: raw value """ return value def to_raw(self, value): """ get the raw value of this field Args: value (any): current value of this field Returns: any: a primitive raw value """ return value @property def trigger_updates(self): return callable(self.on_update) @property def computed(self): return callable(self.compute)
Subclasses
Instance variables
var computed
-
Expand source code
@property def computed(self): return callable(self.compute)
var trigger_updates
-
Expand source code
@property def trigger_updates(self): return callable(self.on_update)
Methods
def from_raw(self, value)
-
get the value of this field from primitive raw types
Args
value
:any
- support value by this field type (if any)
Returns
any
- raw value
Expand source code
def from_raw(self, value): """ get the value of this field from primitive raw types Args: value (any): support value by this field type (if any) Returns: any: raw value """ return value
def preprocess(self, value)
-
Expand source code
def preprocess(self, value): # TODO: make from/to raw methods only for serialization # and let preprocess/validate do the cleanup/checking step # and return the correct value that current field should hold # so, from_raw and to_raw are used only for serialization return value
def to_raw(self, value)
-
get the raw value of this field
Args
value
:any
- current value of this field
Returns
any
- a primitive raw value
Expand source code
def to_raw(self, value): """ get the raw value of this field Args: value (any): current value of this field Returns: any: a primitive raw value """ return value
def validate(self, value)
-
validate value if required and call custom self.validators if any
Args
value
:any
- in case value is not valid
Raises
ValidationError
- [description]
Expand source code
def validate(self, value): """ validate value if required and call custom self.validators if any Args: value (any): in case value is not valid Raises: ValidationError: [description] """ if value is None: if self.required: raise ValidationError("field is required") for validator in self.validators: validator(value)
def validate_with_name(self, value, name)
-
validates the value using validate method and prepends the field name in case an exception was thrown
Args
value
:any
- in case value is not valid
name (str): the field name
Raises
ValidationError
- The original validation error with the field name prepended to the message
Expand source code
def validate_with_name(self, value, name): """ validates the value using validate method and prepends the field name in case an exception was thrown Args: value (any): in case value is not valid name (str): the field name Raises: ValidationError: The original validation error with the field name prepended to the message """ try: self.validate(value) except ValidationError as e: raise ValidationError(f"{name}: " + str(e))
class Float (default=0.0, **kwargs)
-
Same as
Integer
field, but with a type offloat
.values can be set using strings like (will be converted to
float
):- "12.3", " 1212.23 "
Args
default
:float
, optional- default value. Defaults to 0.0.
kwargs
- any keyword arguments supported by
Field
Expand source code
class Float(Typed): def __init__(self, default=0.0, **kwargs): """ Same as `Integer` field, but with a type of `float`. values can be set using strings like (will be converted to `float`): - "12.3", " 1212.23 " Args: default (float, optional): default value. Defaults to 0.0. kwargs: any keyword arguments supported by `Field` """ super().__init__(type_=float, default=default, min=min, **kwargs) def from_raw(self, value): if isinstance(value, str): value = value.strip() try: value = float(value) except ValueError: pass return value
Ancestors
Inherited members
class GUID (default=None, **kwargs)
-
UUID v4 field, will be auto-generated by default.
Will hold the UUID as a string.
It accepts setting UUID value by many ways and converts them to strings:
- Strings like:
"12345678-1234-4678-9234-567812345678"
- Bytes like:
b"4Vx" * 4
- Integer like:
0x12345678123456781234567812345678
- UUID objects of type uuid.UUID
Args
default
:str
, optional- default value, will be auto-generated if None. Defaults to None
kwargs
- other keyword arguments supported by
String
Expand source code
class GUID(String): def __init__(self, default=None, **kwargs): """ UUID v4 field, will be auto-generated by default. Will hold the UUID as a string. It accepts setting UUID value by many ways and converts them to strings: - Strings like: `"12345678-1234-4678-9234-567812345678"` - Bytes like: `b"\x12\x34\x56\x78" * 4` - Integer like: `0x12345678123456781234567812345678` - UUID objects of type uuid.UUID Args: default (str, optional): default value, will be auto-generated if None. Defaults to None kwargs: other keyword arguments supported by `String` """ if not default: default = lambda: str(uuid.uuid4()) super().__init__(default=default, **kwargs) def validate(self, value): super().validate(value) if not value: return try: uuid.UUID(value, version=4) except ValueError as valexc: raise ValidationError(f"''{value}'' is invalid, {valexc}") from valexc def from_raw(self, value): """ convert string, bytes, int to UUID object, then to a string Args: value (int or str or bytes or uuid.UUID): [description] Returns: str: UUID string """ if isinstance(value, uuid.UUID): return str(value) try: uuid_obj = None if isinstance(value, int): uuid_obj = uuid.UUID(int=value, version=4) elif isinstance(value, str): uuid_obj = uuid.UUID(value, version=4) elif isinstance(value, bytes): uuid_obj = uuid.UUID(bytes=value, version=4) if uuid_obj: return str(uuid_obj) # return as is to be validated later return value except ValueError: # failed to get uuid from raw value # return as is to be validated later return value
Ancestors
Methods
def from_raw(self, value)
-
convert string, bytes, int to UUID object, then to a string
Args
value
:int
orstr
orbytes
oruuid.UUID
- [description]
Returns
str
- UUID string
Expand source code
def from_raw(self, value): """ convert string, bytes, int to UUID object, then to a string Args: value (int or str or bytes or uuid.UUID): [description] Returns: str: UUID string """ if isinstance(value, uuid.UUID): return str(value) try: uuid_obj = None if isinstance(value, int): uuid_obj = uuid.UUID(int=value, version=4) elif isinstance(value, str): uuid_obj = uuid.UUID(value, version=4) elif isinstance(value, bytes): uuid_obj = uuid.UUID(bytes=value, version=4) if uuid_obj: return str(uuid_obj) # return as is to be validated later return value except ValueError: # failed to get uuid from raw value # return as is to be validated later return value
Inherited members
- Strings like:
class IPAddress (default='0.0.0.0', **kwargs)
-
IP address field, will validate the value of ip address (v4 and v6)
Will hold string ip addresses.
Args
default
:str
, optional- default value. Defaults to ""
kwargs
- any keyword arguments supported by
Field
Expand source code
class IPAddress(IPMixin, String): def __init__(self, default="0.0.0.0", **kwargs): """ IP address field, will validate the value of ip address (v4 and v6) Will hold string ip addresses. Args: default (str, optional): default value. Defaults to "" kwargs: any keyword arguments supported by `Field` """ super().__init__(default=default, **kwargs) def validate(self, value): """ check whether provided value is a valid IPaddress representation including IPv4,IPv6 and network Args: value (str) Raises: ValidationError: in case the value is not an IPAddress """ super().validate(value) if value and not self.is_ip(value): raise ValidationError(f"'{value}' is not a valid IP address") def from_raw(self, value): if isinstance(value, str): if value.strip().lower() == "localhost": value = "127.0.0.1" return value
Ancestors
Methods
def validate(self, value)
-
check whether provided value is a valid IPaddress representation including IPv4,IPv6 and network
Args
value (str)
Raises
ValidationError
- in case the value is not an IPAddress
Expand source code
def validate(self, value): """ check whether provided value is a valid IPaddress representation including IPv4,IPv6 and network Args: value (str) Raises: ValidationError: in case the value is not an IPAddress """ super().validate(value) if value and not self.is_ip(value): raise ValidationError(f"'{value}' is not a valid IP address")
Inherited members
class IPMixin
-
Expand source code
class IPMixin: def is_a(self, value, *types): """ try creating any type of `types` from the given value Args: value (str) Returns: bool: `True` if any matched, `False` otherwise """ matched = False for type_ in types: try: type_(value) matched |= True except ValueError: continue return matched def is_ipv4(self, value): """ check if a given value is a v4 IP address Args: value (str): ip address Returns: bool: `True` if it's a valid v4 IP address """ return self.is_a(value, ipaddress.IPv4Address) def is_ipv6(self, value): """ check if a given value is a v6 IP address Args: value (str): IP address Returns: bool: `True` if it's a valid v6 IP address """ return self.is_a(value, ipaddress.IPv6Address) def is_ip(self, value): """ check if a given value is a v4/v6 IP address Args: value (str): IP address Returns: bool: `True` if it's a valid v4/v6 IP address """ return self.is_ipv4(value) or self.is_ipv6(value) def is_iface(self, value): """ check if a given value is an IP interface Args: value (str): IP interface Returns: bool: `True` if it's a valid v4/v6 IP interface """ return self.is_a(value, ipaddress.IPv4Interface, ipaddress.IPv6Interface)
Subclasses
Methods
def is_a(self, value, *types)
-
try creating any type of
types
from the given valueArgs
value (str)
Returns
bool
True
if any matched,False
otherwise
Expand source code
def is_a(self, value, *types): """ try creating any type of `types` from the given value Args: value (str) Returns: bool: `True` if any matched, `False` otherwise """ matched = False for type_ in types: try: type_(value) matched |= True except ValueError: continue return matched
def is_iface(self, value)
-
check if a given value is an IP interface
Args
value
:str
- IP interface
Returns
bool
True
if it's a valid v4/v6 IP interface
Expand source code
def is_iface(self, value): """ check if a given value is an IP interface Args: value (str): IP interface Returns: bool: `True` if it's a valid v4/v6 IP interface """ return self.is_a(value, ipaddress.IPv4Interface, ipaddress.IPv6Interface)
def is_ip(self, value)
-
check if a given value is a v4/v6 IP address
Args
value
:str
- IP address
Returns
bool
True
if it's a valid v4/v6 IP address
Expand source code
def is_ip(self, value): """ check if a given value is a v4/v6 IP address Args: value (str): IP address Returns: bool: `True` if it's a valid v4/v6 IP address """ return self.is_ipv4(value) or self.is_ipv6(value)
def is_ipv4(self, value)
-
check if a given value is a v4 IP address
Args
value
:str
- ip address
Returns
bool
True
if it's a valid v4 IP address
Expand source code
def is_ipv4(self, value): """ check if a given value is a v4 IP address Args: value (str): ip address Returns: bool: `True` if it's a valid v4 IP address """ return self.is_a(value, ipaddress.IPv4Address)
def is_ipv6(self, value)
-
check if a given value is a v6 IP address
Args
value
:str
- IP address
Returns
bool
True
if it's a valid v6 IP address
Expand source code
def is_ipv6(self, value): """ check if a given value is a v6 IP address Args: value (str): IP address Returns: bool: `True` if it's a valid v6 IP address """ return self.is_a(value, ipaddress.IPv6Address)
class IPRange (default='::/128', **kwargs)
-
ip range field, will validate the value of ip ranges (v4 and v6)
will be stored as a string.
Args
default
:str
, optional- default value. Defaults to ""
kwargs
- any keyword arguments supported by
Field
Expand source code
class IPRange(IPMixin, String): def __init__(self, default="::/128", **kwargs): """ ip range field, will validate the value of ip ranges (v4 and v6) will be stored as a string. Args: default (str, optional): default value. Defaults to "" kwargs: any keyword arguments supported by `Field` """ super().__init__(default=default, **kwargs) def validate(self, value): """ check whether provided value is a valid IPaddress representation including IPv4,IPv6 and network Args: value (str) Raises: ValidationError: in case the value is not an IPAddress """ super().validate(value) if value and not self.is_iface(value): raise ValidationError(f"'{value}' is not a valid IP range/interface")
Ancestors
Methods
def validate(self, value)
-
check whether provided value is a valid IPaddress representation including IPv4,IPv6 and network
Args
value (str)
Raises
ValidationError
- in case the value is not an IPAddress
Expand source code
def validate(self, value): """ check whether provided value is a valid IPaddress representation including IPv4,IPv6 and network Args: value (str) Raises: ValidationError: in case the value is not an IPAddress """ super().validate(value) if value and not self.is_iface(value): raise ValidationError(f"'{value}' is not a valid IP range/interface")
Inherited members
class Integer (default=0, min=None, max=None, **kwargs)
-
Intger field, the same as
Typed
, but with a type ofint
It can have a minimum value, if min is not set, it will ignore it.
values can be set using strings like (will be converted to
int
):- "12", "1212 "
Args
default
:int
, optional- default value. Defaults to 0.
min
:int
, optional- minimum value (inclusive). Defaults to None.
max
:int
, optional- maximum value (inclusive). Defaults to None.
kwargs
- any keyword arguments supported by
Field
Expand source code
class Integer(Typed): def __init__(self, default=0, min=None, max=None, **kwargs): """ Intger field, the same as `Typed`, but with a type of `int` It can have a minimum value, if min is not set, it will ignore it. values can be set using strings like (will be converted to `int`): - "12", "1212 " Args: default (int, optional): default value. Defaults to 0. min (int, optional): minimum value (inclusive). Defaults to None. max (int, optional): maximum value (inclusive). Defaults to None. kwargs: any keyword arguments supported by `Field` """ self.min = min self.max = max super().__init__(type_=int, default=default, min=min, **kwargs) def validate(self, value): super().validate(value) if value and self.min is not None: if value < self.min: raise ValidationError(f"cannot set values less than {self.min}") if value and self.max is not None: if value > self.max: raise ValidationError(f"cannot set values greater than {self.max}") def from_raw(self, value): if isinstance(value, str): try: value = int(value.strip()) except ValueError: pass return value
Ancestors
Subclasses
Inherited members
class Json (default='{}', **kwargs)
-
Json field, will check if the value is a valid json string.
It will hold json strings, if the value is not string, it will be converted.
Args
default
:str
, optional- default value. Defaults to "{}"
kwargs
- other keyword arguments supported by
String
Expand source code
class Json(String): def __init__(self, default="{}", **kwargs): """ Json field, will check if the value is a valid json string. It will hold json strings, if the value is not string, it will be converted. Args: default (str, optional): default value. Defaults to "{}" kwargs: other keyword arguments supported by `String` """ super().__init__(default=default, **kwargs) def validate(self, value): """ check whether provided value is a valid json Args: value (str) Raises: ValidationError: in case the value isn't a valid json """ super().validate(value) if not value: return # if it's a string, try to load it try: json.loads(value) except Exception as e: raise ValidationError(f"'{value}' isn't a valid json, {e}") from e def from_raw(self, value): """ convert non-string values to json string Args: value (any): non-string value Returns: str: a json string """ if not isinstance(value, (str, bytes, bytearray)): try: value = json.dumps(value) except: pass return value
Ancestors
Methods
def from_raw(self, value)
-
convert non-string values to json string
Args
value
:any
- non-string value
Returns
str
- a json string
Expand source code
def from_raw(self, value): """ convert non-string values to json string Args: value (any): non-string value Returns: str: a json string """ if not isinstance(value, (str, bytes, bytearray)): try: value = json.dumps(value) except: pass return value
def validate(self, value)
-
check whether provided value is a valid json
Args
value (str)
Raises
ValidationError
- in case the value isn't a valid json
Expand source code
def validate(self, value): """ check whether provided value is a valid json Args: value (str) Raises: ValidationError: in case the value isn't a valid json """ super().validate(value) if not value: return # if it's a string, try to load it try: json.loads(value) except Exception as e: raise ValidationError(f"'{value}' isn't a valid json, {e}") from e
Inherited members
class List (field, **kwargs)
-
A list field for any field types.
Args
Expand source code
class List(Field): def __init__(self, field, **kwargs): """ A list field for any field types. Args: field (Field): a field instance of any fields, e.g. `fields.String(maxlen=14)`. kwargs: any keyword arguments supported by `Field` """ self.field = field super().__init__(**kwargs) def validate(self, value): """ validate the value of every item in the list Will just call the field.validate of the given field """ super().validate(value) if value is None: value = [] for item in value: self.field.validate(item) def to_raw(self, values): """ get a list of values as raw Args: values (list): list of items of field type Returns: list: list of raw values """ if not values: return [] return [self.field.to_raw(value) for value in values] def from_raw(self, values): """ get a list of field type from raw values Args: values (list): list of raw values Returns: list: list of objects of field type """ if not values: return [] return [self.field.from_raw(value) for value in values]
Ancestors
Methods
def from_raw(self, values)
-
get a list of field type from raw values
Args
values
:list
- list of raw values
Returns
list
- list of objects of field type
Expand source code
def from_raw(self, values): """ get a list of field type from raw values Args: values (list): list of raw values Returns: list: list of objects of field type """ if not values: return [] return [self.field.from_raw(value) for value in values]
def to_raw(self, values)
-
get a list of values as raw
Args
values
:list
- list of items of field type
Returns
list
- list of raw values
Expand source code
def to_raw(self, values): """ get a list of values as raw Args: values (list): list of items of field type Returns: list: list of raw values """ if not values: return [] return [self.field.to_raw(value) for value in values]
def validate(self, value)
-
validate the value of every item in the list Will just call the field.validate of the given field
Expand source code
def validate(self, value): """ validate the value of every item in the list Will just call the field.validate of the given field """ super().validate(value) if value is None: value = [] for item in value: self.field.validate(item)
Inherited members
class Object (type_, type_kwargs=None, **kwargs)
-
An embedded Base object field of any type.
Args
type_
:type
- Base object type (class)
type_kwargs
:dict
, optional- kwargs as a dict to be passed to Base instance when created. Defaults to None.
kwargs
- any keyword arguments supported by
Field
Expand source code
class Object(Typed): def __init__(self, type_, type_kwargs=None, **kwargs): """ An embedded Base object field of any type. Args: type_ (type): Base object type (class) type_kwargs (dict, optional): kwargs as a dict to be passed to Base instance when created. Defaults to None. kwargs: any keyword arguments supported by `Field` """ super().__init__(type_=type_, **kwargs) self.type_kwargs = type_kwargs if self.type_kwargs is None: self.type_kwargs = {} if not self.default: # make it callable to create different objects # not a single one as a default for all self.default = lambda: self.type(**self.type_kwargs) def validate(self, value): """ validate Base objects Args: value (Base): object """ super().validate(value) if value: value.validate() def to_raw(self, obj): """ get raw value of an object as dict Args: obj (Base): base object Returns: dict: raw data """ if obj: return obj._get_data() def from_raw(self, data): """ get an object from dict Args: data (dict): data dict Returns: Base: base object """ if isinstance(data, dict): return self.type(**data) return data
Ancestors
Methods
def from_raw(self, data)
-
get an object from dict
Args
data
:dict
- data dict
Returns
Base
- base object
Expand source code
def from_raw(self, data): """ get an object from dict Args: data (dict): data dict Returns: Base: base object """ if isinstance(data, dict): return self.type(**data) return data
def to_raw(self, obj)
-
get raw value of an object as dict
Args
obj
:Base
- base object
Returns
dict
- raw data
Expand source code
def to_raw(self, obj): """ get raw value of an object as dict Args: obj (Base): base object Returns: dict: raw data """ if obj: return obj._get_data()
def validate(self, value)
-
validate Base objects
Args
value
:Base
- object
Expand source code
def validate(self, value): """ validate Base objects Args: value (Base): object """ super().validate(value) if value: value.validate()
Inherited members
class Path (default='', **kwargs)
-
Path field, will validate the value of file system paths
Will hold string path values.
Args
default
:str
, optional- default value. Defaults to ""
kwargs
- other keyword arguments supported by
Field
Expand source code
class Path(String): # TODO: Validate that it is working on windows def __init__(self, default="", **kwargs): """ Path field, will validate the value of file system paths Will hold string path values. Args: default (str, optional): default value. Defaults to "" kwargs: other keyword arguments supported by `Field` """ super().__init__(default, **kwargs) self.regex = r"^(/[^/ ]*)+/?$" def validate(self, value): """ check whether provided value is a valid path representation Args: value (str) Raises: ValidationError: in case the value is not a telephone """ super().validate(value) if value and not re.match(self.regex, value): raise ValidationError(f"'{value}' is not a valid Path")
Ancestors
Methods
def validate(self, value)
-
check whether provided value is a valid path representation
Args
value (str)
Raises
ValidationError
- in case the value is not a telephone
Expand source code
def validate(self, value): """ check whether provided value is a valid path representation Args: value (str) Raises: ValidationError: in case the value is not a telephone """ super().validate(value) if value and not re.match(self.regex, value): raise ValidationError(f"'{value}' is not a valid Path")
Inherited members
class Port (**kwargs)
-
Port field, will check if the given port is within the range of 0-65535.
Will hold integers, but also accepts string values like
"8080"
.Args
kwargs
- any keyword arguments supported by
Field
Expand source code
class Port(Integer): def __init__(self, **kwargs): """ Port field, will check if the given port is within the range of 0-65535. Will hold integers, but also accepts string values like `"8080"`. Args: kwargs: any keyword arguments supported by `Field` """ super().__init__(min=0, max=65535, **kwargs) def validate(self, value): try: super().validate(value) except ValidationError: raise ValidationError("value is not in range of 0-65535")
Ancestors
Inherited members
class Secret (maxlen=None, allow_empty=True, **kwargs)
-
Same as
String
, but encrypted by default.Should be used with sensitive data.
Args
kwargs
- any keyword arguments supported by
String
Same as
Typed
, but with a type ofstr
.If maxlen is set, it will validate the length of the string.
Args
maxlen
:int
- maximum length allowed. Defaults to None
allow_empty
:bool
- if empty string values are allowed or not. Defaults to True
kwargs
- any keyword arguments supported by
Field
Expand source code
class Secret(String): """ Same as `String`, but encrypted by default. Should be used with sensitive data. Args: kwargs: any keyword arguments supported by `String` """
Ancestors
Inherited members
class String (maxlen=None, allow_empty=True, **kwargs)
-
Same as
Typed
, but with a type ofstr
.If maxlen is set, it will validate the length of the string.
Args
maxlen
:int
- maximum length allowed. Defaults to None
allow_empty
:bool
- if empty string values are allowed or not. Defaults to True
kwargs
- any keyword arguments supported by
Field
Expand source code
class String(Typed): def __init__(self, maxlen=None, allow_empty=True, **kwargs): """ Same as `Typed`, but with a type of `str`. If maxlen is set, it will validate the length of the string. Args: maxlen (int): maximum length allowed. Defaults to None allow_empty (bool): if empty string values are allowed or not. Defaults to True kwargs: any keyword arguments supported by `Field` """ super().__init__(type_=str, **kwargs) self.maxlen = maxlen self.allow_empty = allow_empty def validate(self, value): super().validate(value) if not self.allow_empty and value == "": raise ValidationError("field does not allow empty values") if self.maxlen is not None: if value and len(value) > self.maxlen: raise ValidationError(f"length of the string exceeds {self.maxlen}")
Ancestors
Subclasses
Inherited members
class Tel (default='', **kwargs)
-
Telephone field, will validate the value of telephone numbers
Will hold telephone values as strings.
It will strip any additional characters that are not numbers.
Args
default
:str
, optional- default value. Defaults to ""
kwargs
- other keyword arguments supported by
String
Expand source code
class Tel(String): def __init__(self, default="", **kwargs): """ Telephone field, will validate the value of telephone numbers Will hold telephone values as strings. It will strip any additional characters that are not numbers. Args: default (str, optional): default value. Defaults to "" kwargs: other keyword arguments supported by `String` """ super().__init__(default=default, **kwargs) self.regex = r"^\+?[0-9]{6,15}(?:x[0-9]+)?$" def validate(self, value): """ check whether provided value is a valid telephone number representation Args: value (str) Raises: ValidationError: in case the value is not a telephone """ super().validate(value) if value and not re.search(self.regex, value): raise ValidationError(f"'{value}' is not a valid Telephone") def from_raw(self, value): """clean the telephone value from unwanted signs like , - ( )""" if value is not None: value = value.replace(",", "") value = value.replace("-", "") value = value.replace("(", "") value = value.replace(")", "") value = value.replace(" ", "") return value return value
Ancestors
Methods
def from_raw(self, value)
-
clean the telephone value from unwanted signs like , - ( )
Expand source code
def from_raw(self, value): """clean the telephone value from unwanted signs like , - ( )""" if value is not None: value = value.replace(",", "") value = value.replace("-", "") value = value.replace("(", "") value = value.replace(")", "") value = value.replace(" ", "") return value return value
def validate(self, value)
-
check whether provided value is a valid telephone number representation
Args
value (str)
Raises
ValidationError
- in case the value is not a telephone
Expand source code
def validate(self, value): """ check whether provided value is a valid telephone number representation Args: value (str) Raises: ValidationError: in case the value is not a telephone """ super().validate(value) if value and not re.search(self.regex, value): raise ValidationError(f"'{value}' is not a valid Telephone")
Inherited members
class Time (default=None, format_=None, **kwargs)
-
time field, will hold utc datetime.time objects
values can be set using strings in the given
format_
too like "12:13" or a utc timestamp, they will converted to objects.Args
default
:date
- default value. Defaults to None.
format_
:str
, optional- time format. Defaults to "%H:%M" if None.
kwargs
- any keyword arguments supported by
Field
Expand source code
class Time(DateTimeMixin, Typed): def __init__(self, default=None, format_=None, **kwargs): """ time field, will hold utc datetime.time objects values can be set using strings in the given `format_` too like "12:13" or a utc timestamp, they will converted to objects. Args: default (date): default value. Defaults to None. format_ (str, optional): time format. Defaults to "%H:%M" if None. kwargs: any keyword arguments supported by `Field` """ super().__init__(default=default, type_=datetime.time, **kwargs) if not format_: format_ = "%H:%M" self.format = format_
Ancestors
Inherited members
class Typed (type_, **kwargs)
-
Base field for any type, it will hold values of
type_
Args
type_
:type
- any type (class)
kwargs
- any keyword arguments supported by
Field
Expand source code
class Typed(Field): def __init__(self, type_, **kwargs): """ Base field for any type, it will hold values of `type_` Args: type_ (type): any type (class) kwargs: any keyword arguments supported by `Field` """ self.type = type_ super().__init__(**kwargs) def validate(self, value): super().validate(value) if value is not None: if not isinstance(value, self.type): raise ValidationError(f"value '{value}' is not of type {self.type.__name__}")
Ancestors
Subclasses
Inherited members
class URL (default='', **kwargs)
-
URL field, will validate the value of urls
Will hold string URLs.
Args
default
:str
, optional- default value. Defaults to ""
kwargs
- other keyword arguments supported by
Field
Expand source code
class URL(String): def __init__(self, default="", **kwargs): """ URL field, will validate the value of urls Will hold string URLs. Args: default (str, optional): default value. Defaults to "" kwargs: other keyword arguments supported by `Field` """ super().__init__(default=default, **kwargs) self.regex = r"^(https?|ftp)://[^\s/$.?#].[^\s]*$" def validate(self, value): """ check whether provided value is a valid URL representation Args: value (str) Raises: ValidationError: in case the value is not a telephone """ super().validate(value) if value: url = urlparse(value) if not url.scheme or not url.netloc: raise ValidationError(f"'{value}' is not a valid URL address")
Ancestors
Methods
def validate(self, value)
-
check whether provided value is a valid URL representation
Args
value (str)
Raises
ValidationError
- in case the value is not a telephone
Expand source code
def validate(self, value): """ check whether provided value is a valid URL representation Args: value (str) Raises: ValidationError: in case the value is not a telephone """ super().validate(value) if value: url = urlparse(value) if not url.scheme or not url.netloc: raise ValidationError(f"'{value}' is not a valid URL address")
Inherited members
class ValidationError (*args, **kwargs)
-
base type for any validation error
Expand source code
class ValidationError(Exception): """ base type for any validation error """
Ancestors
- builtins.Exception
- builtins.BaseException