Coverage for packages/kwai-core/src/kwai_core/domain/entity.py: 100%

23 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2024-01-01 00:00 +0000

1"""Module that defines a generic entity.""" 

2 

3from dataclasses import dataclass, field, fields, replace 

4from typing import ( 

5 Any, 

6 ClassVar, 

7 Self, 

8) 

9 

10from kwai_core.domain.value_objects.identifier import IntIdentifier 

11from kwai_core.domain.value_objects.traceable_time import TraceableTime 

12 

13 

14@dataclass(frozen=True, slots=True, eq=False) 

15class DataclassEntity: 

16 """A base class for an entity. 

17 

18 An entity is immutable, so it cannot be modified. A method of an entity that 

19 changes the entity must allways return a new entity instance with the changed 

20 data. The method replace of dataclasses can be used for this. 

21 

22 Currently, this is a separate class to make it possible to migrate to this 

23 new class. In the future, the Entity class will be removed and this class 

24 will be renamed to Entity. 

25 

26 By default, id is of type IntIdentifier. Overwrite ID in an entity class if 

27 another identifier should be used. 

28 

29 Attributes: 

30 id: The id of the entity. 

31 traceable_time: Keeps track of the creation and update timestamp of the entity. 

32 """ 

33 

34 ID: ClassVar = IntIdentifier 

35 

36 id: ID = None 

37 traceable_time: TraceableTime = field(default_factory=TraceableTime) 

38 version = 0 

39 

40 def __post_init__(self): 

41 """When is id is not set, a default id is created.""" 

42 if self.id is None: 

43 object.__setattr__(self, "id", self.ID()) 

44 

45 def set_id(self, id_: ID) -> Self: 

46 """Set the id for this entity. 

47 

48 This will raise a ValueError if the id was already set. 

49 If you need an entity with the same data but with another id, you should create 

50 a new entity with dataclasses.replace and replace the id. 

51 """ 

52 if not self.id.is_empty(): 

53 raise ValueError(f"{self.__class__.__name__} has already an ID: {self.id}") 

54 return replace(self, id=id_) 

55 

56 def shallow_dict(self) -> dict[str, Any]: 

57 """Return a dictionary representation of the entity. 

58 

59 !!! Note 

60 This method is not recursive. Use asdict from dataclasses when also 

61 nested fields must be returned as a dict. 

62 """ 

63 return {f.name: getattr(self, f.name) for f in fields(self)} 

64 

65 def __eq__(self, other: Any) -> bool: 

66 """Check if two entities are equal. 

67 

68 An entity equals another entity when the id is the same. 

69 """ 

70 return isinstance(other, type(self)) and other.id == self.id 

71 

72 def __hash__(self) -> int: 

73 """Generate a hash for this entity.""" 

74 return hash(self.id)