in isshub.domain.utils View Git history

entity

“entity” module

Package to handle isshub entities validation.

It is an adapter over the attrs external dependency.

instance_of_self()[source]

Return a validator checking that the field holds an instance of its own entity.

Returns

The instantiated validator

Return type

_InstanceOfSelfValidator

optional_field(field_type, relation_verbose_name=None)[source]

Define an optional field of the specified field_type.

Parameters
  • field_type (Union[type, str]) – The expected type of the field. Use the string “self” to reference the current field’s entity

  • relation_verbose_name (Optional[str]) – A verbose name to describe the relation between the entity linked to the field, and the entity pointed by field_type

Returns

An attrs attribute, with a default value set to None, and a validator checking that this field is optional and, if set, of the correct type.

Return type

Any

Raises

AssertionError – If field_type is a string and this string is not “self”

Examples

>>> from isshub.domain.utils.entity import optional_field, validated, BaseEntity
>>>
>>> @validated()
... class MyEntity(BaseEntity):
...     my_field: str = optional_field(str)
>>>
>>> from isshub.domain.utils.testing.validation import check_field_nullable
>>> check_field_nullable(MyEntity, 'my_field', my_field='foo')
required_field(field_type, frozen=False, relation_verbose_name=None)[source]

Define a required field of the specified field_type.

Parameters
  • field_type (Union[type, str]) – The expected type of the field. Use the string “self” to reference the current field’s entity

  • frozen (bool) – If set to False (the default), the field can be updated after being set at init time. If set to True, the field can be set at init time but cannot be changed later, else a FrozenAttributeError exception will be raised.

  • relation_verbose_name (Optional[str]) – A verbose name to describe the relation between the entity linked to the field, and the entity pointed by field_type

Returns

An attrs attribute, and a validator checking that this field is of the correct type.

Return type

Any

Raises

AssertionError – If field_type is a string and this string is not “self”

Examples

>>> from isshub.domain.utils.entity import required_field, validated, BaseEntity
>>>
>>> @validated()
... class MyEntity(BaseEntity):
...     my_field: str = required_field(str)
>>>
>>> from isshub.domain.utils.testing.validation import check_field_not_nullable
>>> check_field_not_nullable(MyEntity, 'my_field', my_field='foo')
validated()[source]

Decorate an entity to handle validation.

This will let attrs manage the class, using slots for fields, and forcing attributes to be passed as named arguments (this allows to not have to defined all required fields first, then optional ones, and resolves problems with inheritance where we can’t handle the order)

Returns

The decorated class.

Return type

type

Examples

>>> from isshub.domain.utils.entity import required_field, validated, BaseEntity
>>>
>>> @validated()
... class MyEntity(BaseEntity):
...     my_field: str = required_field(str)
>>>
>>> MyEntity.__slots__
('my_field',)
>>>
>>> instance = MyEntity()
Traceback (most recent call last):
    ...
TypeError: __init__() missing 1 required keyword-only argument: 'my_field'
>>> instance = MyEntity(my_field='foo')
>>> instance.my_field
'foo'
>>> instance.validate()
>>> instance.my_field = None
>>> instance.validate()
Traceback (most recent call last):
    ...
TypeError: ("'my_field' must be <class 'str'> (got None that is a <class 'NoneType'>)...
class field_validator(field)[source]

Bases: object

Decorate an entity method to make it a validator of the given field.

Notes

It’s easier to implement as a function but we couldn’t make mypy work with it. Thanks to https://github.com/python/mypy/issues/1551#issuecomment-253978622

Parameters

field (Any) – The field to validate.

Examples

>>> from isshub.domain.utils.entity import field_validator, required_field, BaseEntity
>>>
>>> @validated()
... class MyEntity(BaseEntity):
...    my_field: str = required_field(str)
...
...    @field_validator(my_field)
...    def validate_my_field(self, field, value):
...        if value != 'foo':
...            raise ValueError(f'{self.__class__.__name__}.my_field must be "foo"')
>>>
>>> instance = MyEntity(my_field='bar')
Traceback (most recent call last):
    ...
ValueError: MyEntity.my_field must be "foo"
>>> instance = MyEntity(my_field='foo')
>>> instance.my_field
'foo'
>>> instance.my_field = 'bar'
>>> instance.validate()
Traceback (most recent call last):
    ...
ValueError: MyEntity.my_field must be "foo"
>>> instance.my_field = 'foo'
>>> instance.validate()
>>> instance.my_field
'foo'
validate_instance(instance)[source]

Validate a whole instance.

Parameters

instance (Any) – The instance to validate.

Raises

TypeError, ValueError – If a field in the instance is not valid.

Examples

>>> from isshub.domain.utils.entity import required_field, validate_instance, BaseEntity
>>>
>>> @validated()
... class MyEntity(BaseEntity):
...    my_field: str = required_field(str)
>>>
>>> instance = MyEntity(my_field='foo')
>>> validate_instance(instance)
>>> instance.my_field = None
>>> validate_instance(instance)
Traceback (most recent call last):
    ...
TypeError: ("'my_field' must be <class 'str'> (got None that is a <class 'NoneType'>)...
Return type

Any

validate_positive_integer(value, none_allowed, display_name)[source]

Validate that the given value is a positive integer (None accepted if none_allowed).

Parameters
  • value (Any) – The value to validate as a positive integer.

  • none_allowed (bool) – If True, the value can be None. If False, the value must be a positive integer.

  • display_name (str) – The name of the field to display in errors.

Raises
  • TypeError – If value is not of type int.

  • ValueError – If value is not a positive integer (ie > 0), or None if none_allowed is True.

Examples

>>> from isshub.domain.utils.entity import field_validator, required_field, BaseEntity
>>>
>>> @validated()
... class MyEntity(BaseEntity):
...    my_field: int = required_field(int)
...
...    @field_validator(my_field)
...    def validate_my_field(self, field, value):
...        validate_positive_integer(
...            value=value,
...            none_allowed=False,
...            display_name=f"{self.__class__.__name__}.my_field",
...        )
>>>
>>> instance = MyEntity(my_field='foo')
Traceback (most recent call last):
    ...
TypeError: ("'my_field' must be <class 'int'> (got 'foo' that is a <class 'str'>)...
>>> instance = MyEntity(my_field=-2)
Traceback (most recent call last):
    ...
ValueError: MyEntity.my_field must be a positive integer
>>> instance = MyEntity(my_field=0)
Traceback (most recent call last):
    ...
ValueError: MyEntity.my_field must be a positive integer
>>> instance = MyEntity(my_field=1.1)
Traceback (most recent call last):
    ...
TypeError: ("'my_field' must be <class 'int'> (got 1.1 that is a <class 'float'>)...
>>> instance = MyEntity(my_field=1)
>>> instance.my_field = -2
>>> instance.validate()
Traceback (most recent call last):
    ...
ValueError: MyEntity.my_field must be a positive integer
Return type

None

validate_uuid(value, none_allowed, display_name)[source]

Validate that the given value is a uuid (version 4) (None accepted if none_allowed).

Parameters
  • value (Any) – The value to validate as a uuid.

  • none_allowed (bool) – If True, the value can be None. If False, the value must be a uuid.

  • display_name (str) – The name of the field to display in errors.

Raises

TypeError – If value is not of type UUID version 4 .

Examples

>>> from uuid import UUID
>>> from isshub.domain.utils.entity import field_validator, required_field, BaseEntity
>>>
>>> @validated()
... class MyEntity(BaseEntity):
...    my_field: UUID = required_field(UUID)
...
...    @field_validator(my_field)
...    def validate_my_field(self, field, value):
...        validate_uuid(
...            value=value,
...            none_allowed=False,
...            display_name=f"{self.__class__.__name__}.my_field",
...        )
>>>
>>> instance = MyEntity(my_field='foo')
Traceback (most recent call last):
    ...
TypeError: ("'my_field' must be <class 'uuid.UUID'> (got 'foo' that is a <class 'str'>)...
>>> instance = MyEntity(my_field='7298d61a-f08f-4f83-b75e-934e786eb43d')
Traceback (most recent call last):
    ...
TypeError: ("'my_field' must be <class 'uuid.UUID'> (got '7298d61a-f08f-4f83-b75e-934e786eb43d' that is a <class 'str'>)...
>>> instance = MyEntity(my_field=UUID('19f49bc8-06e5-11eb-8465-bf44725d7bd3'))
Traceback (most recent call last):
    ...
TypeError: MyEntity.my_field must be a UUID version 4
>>> instance = MyEntity(my_field=UUID('7298d61a-f08f-4f83-b75e-934e786eb43d'))
>>> instance.my_field = UUID('19f49bc8-06e5-11eb-8465-bf44725d7bd3')
>>> instance.validate()
Traceback (most recent call last):
    ...
TypeError: MyEntity.my_field must be a UUID version 4
Return type

None

class BaseEntity[source]

Bases: object

A base entity without any field, that is able to validate itself.

validate()[source]

Validate all fields of the current instance.

Raises

TypeError, ValueError – If a field is not valid.

Return type

None

class BaseEntityWithIdentifier(*, identifier)[source]

Bases: isshub.domain.utils.entity.BaseEntity

A base entity with an identifier, that is able to validate itself.

Variables

identifier (UUID) – The identifier of the instance. Validated to be a UUID version 4.

identifier: uuid.UUID
validate_id_is_uuid(field, value)[source]

Validate that the BaseEntityWithIdentifier.identifier field is a uuid.

Parameters
  • field (Any) – The field to validate.

  • value (Any) – The value to validate for the field.

Return type

None