Skip to content

Commit

Permalink
Remove attrs dependency and use pydantic instead (#168)
Browse files Browse the repository at this point in the history
Co-authored-by: konstantin <konstantin.klein@hochfrequenz.de>
  • Loading branch information
OLILHR and hf-kklein authored Jan 31, 2024
1 parent 9969327 commit 2be3806
Show file tree
Hide file tree
Showing 6 changed files with 22 additions and 36 deletions.
1 change: 0 additions & 1 deletion requirements.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
attrs
bo4e>=0.5.0
pydantic>=2.0.0
typeguard
Expand Down
2 changes: 0 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#
annotated-types==0.5.0
# via pydantic
attrs==23.2.0
# via -r requirements.in
bidict==0.22.1
# via
# -r requirements.in
Expand Down
1 change: 0 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ zip_safe = False
include_package_data = True
python_requires = >=3.10
install_requires =
attrs
bo4e
pydantic>=2.0.0
typeguard>=4.0.1
Expand Down
35 changes: 14 additions & 21 deletions src/bomf/loader/entityloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,63 +9,56 @@
from pathlib import Path
from typing import Awaitable, Callable, Generic, Optional, TypeVar

import attrs
from generics import get_filled_type
from pydantic import BaseModel, TypeAdapter, ValidationError # pylint:disable=no-name-in-module
from pydantic import BaseModel, TypeAdapter, ConfigDict, ValidationError # pylint:disable=no-name-in-module

_TargetEntity = TypeVar("_TargetEntity")


@attrs.define(auto_attribs=True, kw_only=True)
class EntityLoadingResult: # pylint:disable=too-few-public-methods
class EntityLoadingResult(BaseModel): # pylint:disable=too-few-public-methods
"""
Information gathered while loading a _TargetEntity into the target system.
"""

id_in_target_system: Optional[str] = attrs.field(
validator=attrs.validators.optional(attrs.validators.instance_of(str)), default=None
)
model_config = ConfigDict(arbitrary_types_allowed=True)

id_in_target_system: Optional[str] = None
"""
the optional ID of the entity in the target system (e.g. if a new (GU)ID is generated upon loading)
"""
polling_task: Optional[Awaitable] = attrs.field(default=None)
polling_task: Optional[Awaitable] = None
"""
If this task is awaited it means, that the target system is done with processing the request.
A possible use case is that the target system responds with something like an event ID which can be used to poll
an endpoint until it returns the expected result.
"""


@attrs.define(auto_attribs=True, kw_only=True)
class LoadingSummary(ABC, Generic[_TargetEntity]): # pylint:disable=too-few-public-methods
class LoadingSummary(BaseModel, ABC, Generic[_TargetEntity]): # pylint:disable=too-few-public-methods
"""
Each instance of _TargetEntity that is loaded to the target system results in a LoadingSummary.
It is a summary that reports to calling code.
"""

was_loaded_successfully: bool = attrs.field(validator=attrs.validators.instance_of(bool))
model_config = ConfigDict(arbitrary_types_allowed=True)

was_loaded_successfully: bool
"""
true iff the instance has been loaded successfully
"""
loaded_at: Optional[datetime] = attrs.field(
validator=attrs.validators.optional(attrs.validators.instance_of(datetime)), default=None
)
loaded_at: Optional[datetime] = None
"""
point in time at which the loading (without verification) has completed; if not None
"""
verified_at: Optional[datetime] = attrs.field(
validator=attrs.validators.optional(attrs.validators.instance_of(datetime)), default=None
)
verified_at: Optional[datetime] = None
"""
point in time at which the loading of this entity has been verified (or None if not)
"""
id_in_target_system: Optional[str] = attrs.field(
validator=attrs.validators.optional(attrs.validators.instance_of(str)), default=None
)
id_in_target_system: Optional[str] = None
"""
the optional ID of the entity in the target system (e.g. if a new (GU)ID is generated upon loading)
"""
loading_error: Optional[Exception] = attrs.field(default=None)
loading_error: Optional[Exception] = None


class EntityLoader(ABC, Generic[_TargetEntity]): # pylint:disable=too-few-public-methods
Expand Down
10 changes: 4 additions & 6 deletions src/bomf/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from abc import ABC
from typing import TypeVar, Union

import attrs
from bo4e.bo.geschaeftsobjekt import Geschaeftsobjekt
from bo4e.com.com import COM
from pydantic import BaseModel, Field # pylint: disable=no-name-in-module
Expand All @@ -26,8 +25,7 @@


# pylint:disable=too-few-public-methods
@attrs.define(kw_only=True, auto_attribs=True)
class BusinessObjectRelation:
class BusinessObjectRelation(BaseModel):
"""
A business object relation describes the relation between two business object.
E.g. a relation could have the type "has_melo" where relation_part_a is a bo4e.bo.Vertrag
Expand All @@ -37,7 +35,7 @@ class BusinessObjectRelation:
necessary relation information.
"""

relation_type: enum.Enum = attrs.field()
relation_type: enum.Enum
"""
The relation type describes how two business objects relate to each other.
This is not (only) about cardinality. It's about being able to model different relations between objects.
Expand All @@ -48,12 +46,12 @@ class BusinessObjectRelation:
All these relation types are 1:1 relations between business partners and adresses, yet they all carry different
meaning which we'd like to distinguish in our data.
"""
relation_part_a: Bo4eTyp = attrs.field()
relation_part_a: Bo4eTyp
"""
one Business Object or COM
"""

relation_part_b: Bo4eTyp = attrs.field()
relation_part_b: Bo4eTyp
"""
another Business Object or COM
"""
Expand Down
9 changes: 4 additions & 5 deletions unittests/test_mapper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Optional, Type

import attrs
from pydantic import BaseModel
import pytest # type:ignore[import]
from bo4e.bo.marktlokation import Marktlokation
from bo4e.bo.messlokation import Messlokation
Expand All @@ -15,10 +15,9 @@ class _NotImplementedBo4eDataSetMixin:
"""


@attrs.define(kw_only=True, auto_attribs=True)
class _MaLoAndMeLo(_NotImplementedBo4eDataSetMixin):
malo: Marktlokation = attrs.field()
melo: Messlokation = attrs.field()
class _MaLoAndMeLo(BaseModel, _NotImplementedBo4eDataSetMixin):
malo: Marktlokation
melo: Messlokation

def get_business_object(self, bo_type: Type[Bo4eTyp], specification: Optional[str] = None) -> Bo4eTyp:
# pyling:disable=fixme
Expand Down

0 comments on commit 2be3806

Please sign in to comment.