Cannot use class variable to create instance in DI container
Closed this issue · 1 comments
hsheng commented
Python 3.12 and dependency-injector 4.45.0
Here is my code structure:
class MyModel(BaseModel):
...
# full class definition of EntityMapper
class EntityMapper:
def __init__(
self,
dict_to_model: Callable[[dict], DomainModel],
model_to_dict: Callable[[DomainModel], dict],
filter_to_query: Callable[[dict], dict],
):
self.dict_to_model = dict_to_model
self.model_to_dict = model_to_dict
self.filter_to_query = filter_to_query
class GenericDAO:
def __init__(self, model_type: type, collection_name: str, entity_mapper: EntityMapper):
...
...
class MyEntityMapperUtil:
@staticmethod
def model_to_dict(model: MyModel) -> dict:
...
@staticmethod
def dict_to_model(dict_from_mongodb: dict) -> MyModel:
...
@staticmethod
def filter_to_query(_filter: dict) -> dict:
...
entity_mapper = EntityMapper(
dict_to_model=dict_to_model,
model_to_dict=model_to_dict,
filter_to_query=filter_to_query,
)
my_entity_mapper_x1 = EntityMapper(
dict_to_model=MyEntityMapperUtil.dict_to_model,
model_to_dict=MyEntityMapperUtil.model_to_dict,
filter_to_query=MyEntityMapperUtil.filter_to_query,
)
my_entity_mapper_x2 = MyEntityMapperUtil.entity_mapper
class MyDIContainer(containers.DeclarativeContainer):
wiring_config = containers.WiringConfiguration(packages=["<package-name>")
**# This is OK**
my_dao = providers.Singleton(
GenericDAO,
MyModel,
"<my_collection>",
my_entity_mapper_x1, <--
)
**# This is OK**
my_dao = providers.Singleton(
GenericDAO,
MyModel,
"<my_collection>",
EntityMapper( <--
dict_to_model=MyEntityMapperUtil.dict_to_model,
model_to_dict=MyEntityMapperUtil.model_to_dict,
filter_to_query=MyEntityMapperUtil.filter_to_query,
),
)
**# This fails with NonCopyableArgumentError**
my_dao = providers.Singleton(
GenericDAO,
MyModel,
"<my_collection>",
MyEntityMapperUtil.entity_mapper, <--
)
**# This fails with NonCopyableArgumentError**
my_dao = providers.Singleton(
GenericDAO,
MyModel,
"<my_collection>",
my_entity_mapper_x2, <--
)
ZipFile commented
Provider arguments must be deepcopy-able. Given your particular example, if you check the stack trace, there should be an error TypeError: cannot pickle 'staticmethod' object. Which means, in MyEntityMapperUtil.entity_mapper you're referencing wrappers, not functions themselves (at this point, transformation to real static method not yet happened). And staticmethod instances are not deepcopy-able.
If you really want to keep your code structure, you can do something like this instead:
def filter_to_query(_filter: dict) -> dict:
...
- entity_mapper = EntityMapper(
- dict_to_model=dict_to_model,
- model_to_dict=model_to_dict,
- filter_to_query=filter_to_query,
- )
+MyEntityMapperUtil.entity_mapper = EntityMapper(
+ dict_to_model=MyEntityMapperUtil.dict_to_model,
+ model_to_dict=MyEntityMapperUtil.model_to_dict,
+ filter_to_query=MyEntityMapperUtil.filter_to_query,
+)
my_entity_mapper_x1 = EntityMapper(But on a personal note, I would recommend against any generic DAO/Repository implementations. It is difficult to get it right.