style(entity): Change the world “model” by “entity”

Description

Abstract

Rename BaseModel and BaseModelWithId to BaseEntity and BaseEntityWithId, and replace the word “model” by “entity” everywhere, in comments and function names.

Motivation

To keep things consistent, it’s better to have one and only one word to name a thing, and we started with entity, and it’s the name used in the DDD word.

Rationale

N/A

Info

Hash

c2dd0fc606636dd72c1b55d30095bbeb622b788d

Date

2020-10-04 21:07:00 +0200

Parents
  • style(mypy): Remove most “type: ignore” pragmas [76638c3d]2020-10-04 20:36:50 +0200

Children
  • fix(entity): `id` changed from `int` to `uuid4`, renamed to `identifier` [79f704bd]2020-10-05 10:51:49 +0200

Branches
Tags

(No tags)

Changes

docs/domain_contexts_diagrams.py

Type

Modified

Stats

+57 -57

@@ -1,6 +1,6 @@
 #!/usr/bin/env python

-"""Make the diagrams of models for each isshub domain contexts."""
+"""Make the diagrams of entities for each isshub domain contexts."""

 import importlib
 import os.path
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
     from attr import _Fields  # pylint: disable=no-name-in-module

 from isshub.domain import contexts
-from isshub.domain.utils.entity import BaseModel
+from isshub.domain.utils.entity import BaseEntity


 def import_submodules(
@@ -157,13 +157,13 @@ def render_enum(enum: Type[Enum]) -> Tuple[str, str]:
     )


-def validate_model(
+def validate_entity(
     name: str,
-    model: Type[BaseModel],
+    entity: Type[BaseEntity],
     context: str,
-    linkable_models: Dict[str, Type[BaseModel]],
+    linkable_entities: Dict[str, Type[BaseEntity]],
 ) -> Dict[str, Tuple[Any, bool]]:
-    """Validate that we can handle the given model and its fields.
+    """Validate that we can handle the given entity and its fields.

     We only handle fields defined with a "type hint", restricted to:
     - the ones with a direct type
@@ -171,20 +171,20 @@ def validate_model(
       ``NoneType``)

     The direct type, if in the ``isshub`` namespace, must be in the given `context` (in the given
-    `linkable_models`.
+    `linkable_entities`.

     Parameters
     ----------
     name : str
-        The name of the `model`
-    model : Type[BaseModel]
-        The model to validate
+        The name of the `entity`
+    entity : Type[BaseEntity]
+        The entity to validate
     context : str
-        The name of the context, ie the name of the module containing the `model` and the
-        `linkable_models`
-    linkable_models : Dict[str, Type[BaseModel]]
-        A dict containing all the models the `model` to validate can link to, with their full python
-        path as keys, and the models themselves as values
+        The name of the context, ie the name of the module containing the `entity` and the
+        `linkable_entities`
+    linkable_entities : Dict[str, Type[BaseEntity]]
+        A dict containing all the entities the `entity` to validate can link to, with their full python
+        path as keys, and the entities themselves as values

     Returns
     -------
@@ -198,10 +198,10 @@ def validate_model(
         If the type is a ``Union`` of more than two types or with one not being ``NoneType``
     TypeError
         If the type is an object in the ``isshub`` namespace that is not in the given
-        `linkable_models` (except for enums, actually)
+        `linkable_entities` (except for enums, actually)

     """
-    types = get_type_hints(model)
+    types = get_type_hints(entity)
     fields = {}
     for field_name, field_type in types.items():
         required = True
@@ -223,10 +223,10 @@ def validate_model(
         if field_type.__module__.startswith("isshub") and not issubclass(
             field_type, Enum
         ):
-            if get_python_path(field_type) not in linkable_models:
+            if get_python_path(field_type) not in linkable_entities:
                 raise TypeError(
                     f"{name}.{field_name} : {field_type}"
-                    f" - It's not a valid model in context {context}"
+                    f" - It's not a valid entity in context {context}"
                 )

         fields[field_name] = (field_type, required)
@@ -241,12 +241,12 @@ def render_link(
     required: bool,
     attr_fields: "_Fields",
 ) -> str:
-    """Render a link between the field of a model to another class.
+    """Render a link between the field of an entity to another class.

     Parameters
     ----------
     source_name : str
-        The dot identifier of the source class. The source class is expected to be a entity model
+        The dot identifier of the source class. The source class is expected to be an entity class
     field_name : str
         The field in the source class that is linked to the dest class
     dest_name : str
@@ -275,49 +275,49 @@ def render_link(
     return f'{source_name}:{field_name} -> {dest_name}:__class__ [label="{link_label}"]'


-def render_model(
+def render_entity(
     name: str,
-    model: Type[BaseModel],
+    entity: Type[BaseEntity],
     context: str,
-    linkable_models: Dict[str, Type[BaseModel]],
+    linkable_entities: Dict[str, Type[BaseEntity]],
 ) -> Tuple[Dict[str, str], Set[str]]:
-    """Render the given `model` to be incorporated in a dot file, with links.
+    """Render the given `entity` to be incorporated in a dot file, with links.

     Parameters
     ----------
     name : str
-        The name of the `model`
-    model : Type[BaseModel]
-        The model to render
+        The name of the `entity`
+    entity : Type[BaseEntity]
+        The entity to render
     context : str
-        The name of the context, ie the name of the module containing the `model` and the
-        `linkable_models`
-    linkable_models : Dict[str, Type[BaseModel]]
-        A dict containing all the models the `model` to validate can link to, with their full python
-        path as keys, and the models themselves as values
+        The name of the context, ie the name of the module containing the `entity` and the
+        `linkable_entities`
+    linkable_entities : Dict[str, Type[BaseEntity]]
+        A dict containing all the entities the `entity` to validate can link to, with their full python
+        path as keys, and the entities themselves as values

     Returns
     -------
     Dict[str, str]
-        Lines representing the models (or enums) to render in the graph.
-        The keys are the dot identifier of the model (or enum), and the values are the line to put
+        Lines representing the entities (or enums) to render in the graph.
+        The keys are the dot identifier of the entity (or enum), and the values are the line to put
         in the dot file to render them.
-        There is at least one entry, the rendered `model`, but there can be more entries, if the
-        `model` is linked to some enums (we use a dict to let the caller to deduplicate enums with
-        the same identifiers if called from many models)
+        There is at least one entry, the rendered `entity`, but there can be more entries, if the
+        `entity` is linked to some enums (we use a dict to let the caller to deduplicate enums with
+        the same identifiers if called from many entities)
     Set[str]
-        Lines representing the links between the `model` and other models or enums.
+        Lines representing the links between the `entity` and other entities or enums.

     """
     lines = {}
     links = set()

     dot_name = get_dot_identifier(name)
-    attr_fields = attr.fields(model)
+    attr_fields = attr.fields(entity)
     fields = {}

-    for field_name, (field_type, required) in validate_model(
-        name, model, context, linkable_models
+    for field_name, (field_type, required) in validate_entity(
+        name, entity, context, linkable_entities
     ).items():

         link_to = None
@@ -341,50 +341,50 @@ def render_model(
     )
     lines[
         dot_name
-    ] = f'{dot_name} [label="<__class__> Model: {model.__name__}|{fields_parts}"]'
+    ] = f'{dot_name} [label="<__class__> Entity: {entity.__name__}|{fields_parts}"]'

     return lines, links


 def make_domain_context_graph(
-    context_name: str, subclasses: Dict[str, Type[BaseModel]], output_path: str
+    context_name: str, subclasses: Dict[str, Type[BaseEntity]], output_path: str
 ) -> None:
-    """Make the graph of models in the given contexts.
+    """Make the graph of entities in the given contexts.

     Parameters
     ----------
     context_name : str
         The name of the context, represented by the python path of its module
-    subclasses : Dict[str, Type[BaseModel]]
-        All the subclasses of ``BaseModel`` from which to extract the modules to render.
+    subclasses : Dict[str, Type[BaseEntity]]
+        All the subclasses of ``BaseEntity`` from which to extract the modules to render.
         Only subclasses present in the given context will be rendered.
     output_path : str
         The path where to save the generated graph

     """
-    # restrict the subclasses of ``BaseModel`` to the ones in the given module name
+    # restrict the subclasses of ``BaseEntity`` to the ones in the given module name
     context_subclasses = {
         subclass_name: subclass
         for subclass_name, subclass in subclasses.items()
         if subclass_name.startswith(context_name + ".")
     }

-    # render models and all links between them
-    model_lines, links = {}, set()
+    # render entities and all links between them
+    entity_lines, links = {}, set()
     for subclass_name, subclass in context_subclasses.items():
-        subclass_lines, subclass_links = render_model(
+        subclass_lines, subclass_links = render_entity(
             subclass_name,
             subclass,
             context_name,
             context_subclasses,
         )
-        model_lines.update(subclass_lines)
+        entity_lines.update(subclass_lines)
         links.update(subclass_links)

     # compose the content of the dot file
     dot_file_content = (
         """\
-digraph domain_context_models {
+digraph domain_context_entities {
   label = "Domain context [%s]"
   #labelloc = "t"
   rankdir=LR
@@ -392,7 +392,7 @@ digraph domain_context_models {
 """
         % context_name
     )
-    for line in tuple(model_lines.values()) + tuple(links):
+    for line in tuple(entity_lines.values()) + tuple(links):
         dot_file_content += f"  {line}\n"
     dot_file_content += "}"

@@ -403,7 +403,7 @@ digraph domain_context_models {


 def make_domain_contexts_diagrams(output_path: str) -> None:
-    """Make the diagrams of models for each domain contexts.
+    """Make the diagrams of entities for each domain contexts.

     Parameters
     ----------
@@ -411,9 +411,9 @@ def make_domain_contexts_diagrams(output_path: str) -> None:
         The path where to save the generated diagrams

     """
-    # we need to import all python files (except tests) to find all submodels of ``BaseModel``
+    # we need to import all python files (except tests) to find all subclasses of ``BaseEntity``
     import_submodules(contexts, skip_names=["tests"])
-    subclasses = get_final_subclasses(BaseModel)
+    subclasses = get_final_subclasses(BaseEntity)

     # we render each context independently, assuming that each one is directly at the root of
     # the ``contexts`` package

isshub/domain/contexts/code_repository/__init__.py

Type

Modified

Stats

+1 -1

@@ -1,6 +1,6 @@
 """Package to handle isshub domain code_repository context.

-The "code_repository" context defines every models that are related to code repositories (like
+The "code_repository" context defines every entities that are related to code repositories (like
 Github, Gitlab...):

 - repositories

isshub/domain/contexts/code_repository/entities/namespace/__init__.py

Type

Modified

Stats

+2 -2

@@ -4,7 +4,7 @@ import enum
 from typing import Any, Optional

 from isshub.domain.utils.entity import (
-    BaseModelWithId,
+    BaseEntityWithId,
     field_validator,
     optional_field,
     required_field,
@@ -21,7 +21,7 @@ class NamespaceKind(enum.Enum):


 @validated()
-class Namespace(BaseModelWithId):
+class Namespace(BaseEntityWithId):
     """A namespace can contain namespaces and repositories.

     Attributes

isshub/domain/contexts/code_repository/entities/repository/__init__.py

Type

Modified

Stats

+2 -2

@@ -1,11 +1,11 @@
 """Package defining the ``Repository`` entity."""

 from isshub.domain.contexts.code_repository.entities.namespace import Namespace
-from isshub.domain.utils.entity import BaseModelWithId, required_field, validated
+from isshub.domain.utils.entity import BaseEntityWithId, required_field, validated


 @validated()
-class Repository(BaseModelWithId):
+class Repository(BaseEntityWithId):
     """A repository holds code, issues, code requests...

     Attributes

isshub/domain/utils/entity.py

Type

Modified

Stats

+44 -42

@@ -31,7 +31,7 @@ else:
 class _InstanceOfSelfValidator(
     attr.validators._InstanceOfValidator  # type: ignore  # pylint: disable=protected-access
 ):
-    """Validator checking that the field holds an instance of its own model."""
+    """Validator checking that the field holds an instance of its own entity."""

     def __call__(self, inst, attr, value):  # type: ignore  # pylint: disable=redefined-outer-name
         """Validate that the `value` is an instance of the class of `inst`.
@@ -43,7 +43,7 @@ class _InstanceOfSelfValidator(


 def instance_of_self() -> _InstanceOfSelfValidator:
-    """Return a validator checking that the field holds an instance of its own model.
+    """Return a validator checking that the field holds an instance of its own entity.

     Returns
     -------
@@ -61,10 +61,11 @@ def optional_field(
     Parameters
     ----------
     field_type : Union[type, str]
-        The expected type of the field. Use the string "self" to reference the current field's model
+        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 model linked to the field, and the
-        model pointed by `field_type`
+        A verbose name to describe the relation between the entity linked to the field, and the
+        entity pointed by `field_type`

     Returns
     -------
@@ -79,14 +80,14 @@ def optional_field(

     Examples
     --------
-    >>> from isshub.domain.utils.entity import optional_field, validated, BaseModel
+    >>> from isshub.domain.utils.entity import optional_field, validated, BaseEntity
     >>>
     >>> @validated()
-    ... class MyModel(BaseModel):
+    ... class MyEntity(BaseEntity):
     ...     my_field: str = optional_field(str)
     >>>
     >>> from isshub.domain.utils.testing.validation import check_field_nullable
-    >>> check_field_nullable(MyModel, 'my_field', my_field='foo')
+    >>> check_field_nullable(MyEntity, 'my_field', my_field='foo')

     """
     metadata = {}
@@ -116,14 +117,15 @@ def required_field(
     Parameters
     ----------
     field_type : Union[type, str]
-        The expected type of the field. Use the string "self" to reference the current field's model
+        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 model linked to the field, and the
-        model pointed by `field_type`
+        A verbose name to describe the relation between the entity linked to the field, and the
+        entity pointed by `field_type`

     Returns
     -------
@@ -137,14 +139,14 @@ def required_field(

     Examples
     --------
-    >>> from isshub.domain.utils.entity import required_field, validated, BaseModel
+    >>> from isshub.domain.utils.entity import required_field, validated, BaseEntity
     >>>
     >>> @validated()
-    ... class MyModel(BaseModel):
+    ... class MyEntity(BaseEntity):
     ...     my_field: str = required_field(str)
     >>>
     >>> from isshub.domain.utils.testing.validation import check_field_not_nullable
-    >>> check_field_not_nullable(MyModel, 'my_field', my_field='foo')
+    >>> check_field_not_nullable(MyEntity, 'my_field', my_field='foo')

     """
     metadata = {}
@@ -179,20 +181,20 @@ def validated() -> Any:

     Examples
     --------
-    >>> from isshub.domain.utils.entity import required_field, validated, BaseModel
+    >>> from isshub.domain.utils.entity import required_field, validated, BaseEntity
     >>>
     >>> @validated()
-    ... class MyModel(BaseModel):
+    ... class MyEntity(BaseEntity):
     ...     my_field: str = required_field(str)
     >>>
-    >>> MyModel.__slots__
+    >>> MyEntity.__slots__
     ('my_field',)
     >>>
-    >>> instance = MyModel()
+    >>> instance = MyEntity()
     Traceback (most recent call last):
         ...
     TypeError: __init__() missing 1 required keyword-only argument: 'my_field'
-    >>> instance = MyModel(my_field='foo')
+    >>> instance = MyEntity(my_field='foo')
     >>> instance.my_field
     'foo'
     >>> instance.validate()
@@ -226,10 +228,10 @@ class field_validator:  # pylint: disable=invalid-name

     Examples
     --------
-    >>> from isshub.domain.utils.entity import field_validator, required_field, BaseModel
+    >>> from isshub.domain.utils.entity import field_validator, required_field, BaseEntity
     >>>
     >>> @validated()
-    ... class MyModel(BaseModel):
+    ... class MyEntity(BaseEntity):
     ...    my_field: str = required_field(str)
     ...
     ...    @field_validator(my_field)
@@ -237,18 +239,18 @@ class field_validator:  # pylint: disable=invalid-name
     ...        if value != 'foo':
     ...            raise ValueError(f'{self.__class__.__name__}.my_field must be "foo"')
     >>>
-    >>> instance = MyModel(my_field='bar')
+    >>> instance = MyEntity(my_field='bar')
     Traceback (most recent call last):
         ...
-    ValueError: MyModel.my_field must be "foo"
-    >>> instance = MyModel(my_field='foo')
+    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: MyModel.my_field must be "foo"
+    ValueError: MyEntity.my_field must be "foo"
     >>> instance.my_field = 'foo'
     >>> instance.validate()
     >>> instance.my_field
@@ -292,13 +294,13 @@ def validate_instance(instance: Any) -> Any:

     Examples
     --------
-    >>> from isshub.domain.utils.entity import required_field, validate_instance, BaseModel
+    >>> from isshub.domain.utils.entity import required_field, validate_instance, BaseEntity
     >>>
     >>> @validated()
-    ... class MyModel(BaseModel):
+    ... class MyEntity(BaseEntity):
     ...    my_field: str = required_field(str)
     >>>
-    >>> instance = MyModel(my_field='foo')
+    >>> instance = MyEntity(my_field='foo')
     >>> validate_instance(instance)
     >>> instance.my_field = None
     >>> validate_instance(instance)
@@ -333,10 +335,10 @@ def validate_positive_integer(

     Examples
     --------
-    >>> from isshub.domain.utils.entity import field_validator, required_field, BaseModel
+    >>> from isshub.domain.utils.entity import field_validator, required_field, BaseEntity
     >>>
     >>> @validated()
-    ... class MyModel(BaseModel):
+    ... class MyEntity(BaseEntity):
     ...    my_field: int = required_field(int)
     ...
     ...    @field_validator(my_field)
@@ -347,28 +349,28 @@ def validate_positive_integer(
     ...            display_name=f"{self.__class__.__name__}.my_field",
     ...        )
     >>>
-    >>> instance = MyModel(my_field='foo')
+    >>> 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 = MyModel(my_field=-2)
+    >>> instance = MyEntity(my_field=-2)
     Traceback (most recent call last):
         ...
-    ValueError: MyModel.my_field must be a positive integer
-    >>> instance = MyModel(my_field=0)
+    ValueError: MyEntity.my_field must be a positive integer
+    >>> instance = MyEntity(my_field=0)
     Traceback (most recent call last):
         ...
-    ValueError: MyModel.my_field must be a positive integer
-    >>> instance = MyModel(my_field=1.1)
+    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 = MyModel(my_field=1)
+    >>> instance = MyEntity(my_field=1)
     >>> instance.my_field = -2
     >>> instance.validate()
     Traceback (most recent call last):
         ...
-    ValueError: MyModel.my_field must be a positive integer
+    ValueError: MyEntity.my_field must be a positive integer

     """
     if none_allowed and value is None:
@@ -381,8 +383,8 @@ def validate_positive_integer(


 @validated()
-class BaseModel:
-    """A base model without any field, that is able to validate itself."""
+class BaseEntity:
+    """A base entity without any field, that is able to validate itself."""

     def validate(self) -> None:
         """Validate all fields of the current instance.
@@ -397,8 +399,8 @@ class BaseModel:


 @validated()
-class BaseModelWithId(BaseModel):
-    """A base model with an ``id``, that is able to validate itself.
+class BaseEntityWithId(BaseEntity):
+    """A base entity with an ``id``, that is able to validate itself.

     Attributes
     ----------

isshub/domain/utils/testing/validation.py

Type

Modified

Stats

+9 -9

@@ -6,7 +6,7 @@ import pytest

 from attr.exceptions import FrozenAttributeError

-from isshub.domain.utils.entity import BaseModel
+from isshub.domain.utils.entity import BaseEntity


 ValuesValidation = List[Tuple[Any, Optional[Type[Exception]]]]
@@ -30,12 +30,12 @@ positive_integer_only: ValuesValidation = integer_only + no_zero
 string_only: ValuesValidation = [("foo", None), (1, TypeError), (-0.1, TypeError)]


-def check_field(obj: BaseModel, field_name: str) -> None:
+def check_field(obj: BaseEntity, field_name: str) -> None:
     """Assert that the given `obj` has an attribute named `field_name`.

     Parameters
     ----------
-    obj : BaseModel
+    obj : BaseEntity
         The object to test
     field_name : str
         The field name to search for
@@ -50,7 +50,7 @@ def check_field(obj: BaseModel, field_name: str) -> None:


 def check_field_value(
-    factory: Callable[..., BaseModel],
+    factory: Callable[..., BaseEntity],
     field_name: str,
     value: Any,
     exception: Optional[Type[Exception]],
@@ -60,7 +60,7 @@ def check_field_value(

     Parameters
     ----------
-    factory : Callable[...,BaseModel]
+    factory : Callable[...,BaseEntity]
         The factory to use to create the object to test
     field_name : str
         The name of the field to check
@@ -109,13 +109,13 @@ def check_field_value(


 def check_field_not_nullable(
-    factory: Callable[..., BaseModel], field_name: str, **factory_kwargs: Any
+    factory: Callable[..., BaseEntity], field_name: str, **factory_kwargs: Any
 ) -> None:
     """Assert that an object cannot have a specific field set to ``None``.

     Parameters
     ----------
-    factory : Callable[...,BaseModel]
+    factory : Callable[...,BaseEntity]
         The factory to use to create the object to test
     field_name : str
         The name of the field to check
@@ -147,13 +147,13 @@ def check_field_not_nullable(


 def check_field_nullable(
-    factory: Callable[..., BaseModel], field_name: str, **factory_kwargs: Any
+    factory: Callable[..., BaseEntity], field_name: str, **factory_kwargs: Any
 ) -> None:
     """Assert that an object can have a specific field set to ``None``.

     Parameters
     ----------
-    factory : Callable[...,BaseModel]
+    factory : Callable[...,BaseEntity]
         The factory to use to create the object to test
     field_name : str
         The name of the field to check