Skip to content

Commit 5b84e7a

Browse files
committed
Added test for dynamic module config
1 parent eed7001 commit 5b84e7a

File tree

13 files changed

+296
-70
lines changed

13 files changed

+296
-70
lines changed

ellar/core/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from .factory import AppFactory
88
from .guard import BaseAPIKey, BaseAuthGuard, BaseHttpAuth, GuardCanActivate
99
from .main import App
10-
from .modules import DynamicModule, IModuleConfigure, ModuleBase, ModuleConfigure
10+
from .modules import DynamicModule, IModuleSetup, ModuleBase, ModuleSetup
1111
from .response import (
1212
FileResponse,
1313
HTMLResponse,
@@ -36,7 +36,7 @@
3636
"ModuleBase",
3737
"BaseAPIKey",
3838
"BaseAuthGuard",
39-
"IModuleConfigure",
39+
"IModuleSetup",
4040
"BaseHttpAuth",
4141
"GuardCanActivate",
4242
"Config",
@@ -53,7 +53,7 @@
5353
"Response",
5454
"Request",
5555
"WebSocket",
56-
"ModuleConfigure",
56+
"ModuleSetup",
5757
"DynamicModule",
5858
]
5959

ellar/core/factory.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from ellar.constants import MODULE_METADATA, MODULE_WATERMARK
99
from ellar.core import Config
1010
from ellar.core.main import App
11-
from ellar.core.modules import DynamicModule, ModuleBase, ModuleConfigure
11+
from ellar.core.modules import DynamicModule, ModuleBase, ModuleSetup
1212
from ellar.di import EllarInjector, ProviderConfig
1313
from ellar.reflect import reflect
1414

@@ -26,7 +26,7 @@ class AppFactory:
2626
"""
2727

2828
@classmethod
29-
def get_all_modules(cls, module_config: ModuleConfigure) -> t.List[ModuleConfigure]:
29+
def get_all_modules(cls, module_config: ModuleSetup) -> t.List[ModuleSetup]:
3030
"""
3131
Gets all registered modules from a particular module in their order of dependencies
3232
:param module_config: Module Type
@@ -38,9 +38,7 @@ def get_all_modules(cls, module_config: ModuleConfigure) -> t.List[ModuleConfigu
3838
return module_dependency
3939

4040
@classmethod
41-
def read_all_module(
42-
cls, module_config: ModuleConfigure
43-
) -> t.Dict[t.Type, ModuleConfigure]:
41+
def read_all_module(cls, module_config: ModuleSetup) -> t.Dict[t.Type, ModuleSetup]:
4442
"""
4543
Retrieves all modules dependencies registered in another module
4644
:param module_config: Module Type
@@ -52,9 +50,11 @@ def read_all_module(
5250
module_dependency = OrderedDict()
5351
for module in modules:
5452
if isinstance(module, DynamicModule):
55-
module_config = ModuleConfigure(module.module)
53+
module_config = ModuleSetup(module.module)
54+
elif isinstance(module, ModuleSetup):
55+
module_config = module
5656
else:
57-
module_config = ModuleConfigure(module)
57+
module_config = ModuleSetup(module)
5858

5959
module_dependency[module_config.module] = module_config
6060
module_dependency.update(cls.read_all_module(module_config))
@@ -78,7 +78,7 @@ def _build_modules(
7878
MODULE_WATERMARK, app_module
7979
), "Only Module is allowed"
8080

81-
app_module_config = ModuleConfigure(app_module)
81+
app_module_config = ModuleSetup(app_module)
8282
module_dependency = cls.get_all_modules(app_module_config)
8383
routes = []
8484

@@ -90,14 +90,14 @@ def _build_modules(
9090
container=injector.container, config=config
9191
)
9292

93-
if not isinstance(module_ref, ModuleConfigure):
93+
if not isinstance(module_ref, ModuleSetup):
9494
module_ref.run_module_register_services()
9595
routes.extend(module_ref.routes)
9696

9797
injector.add_module(module_ref)
9898

9999
for module_config in injector.get_dynamic_modules():
100-
if injector.get_module(module_config.module):
100+
if injector.get_module(module_config.module): # pragma: no cover
101101
continue
102102

103103
module_ref = module_config.configure_with_factory(
@@ -144,7 +144,7 @@ def _create_app(
144144
module_changed = False
145145

146146
for module_config in injector.get_app_dependent_modules():
147-
if injector.get_module(module_config.module):
147+
if injector.get_module(module_config.module): # pragma: no cover
148148
continue
149149

150150
module_ref = module_config.configure_with_factory(

ellar/core/main.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,7 @@
2020
RequestVersioningMiddleware,
2121
TrustedHostMiddleware,
2222
)
23-
from ellar.core.modules import (
24-
DynamicModule,
25-
ModuleBase,
26-
ModuleConfigure,
27-
ModuleTemplateRef,
28-
)
23+
from ellar.core.modules import DynamicModule, ModuleBase, ModuleSetup, ModuleTemplateRef
2924
from ellar.core.routing import ApplicationRouter
3025
from ellar.core.templating import AppTemplating, Environment
3126
from ellar.core.versioning import VERSIONING, BaseAPIVersioning
@@ -139,9 +134,9 @@ def install_module(
139134
**init_kwargs: t.Any,
140135
) -> t.Union[T, ModuleBase]:
141136
if isinstance(module, DynamicModule):
142-
module_config = ModuleConfigure(module.module, init_kwargs=init_kwargs)
137+
module_config = ModuleSetup(module.module, init_kwargs=init_kwargs)
143138
else:
144-
module_config = ModuleConfigure(module, init_kwargs=init_kwargs)
139+
module_config = ModuleSetup(module, init_kwargs=init_kwargs)
145140

146141
module_ref = self.injector.get_module(module_config.module)
147142
if module_ref:

ellar/core/modules/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
from .base import ModuleBase
2-
from .config import DynamicModule, IModuleConfigure, ModuleConfigure
2+
from .config import DynamicModule, IModuleSetup, ModuleSetup
33
from .ref import ModulePlainRef, ModuleRefBase, ModuleTemplateRef
44

55
__all__ = [
66
"ModuleBase",
77
"ModulePlainRef",
88
"ModuleTemplateRef",
99
"ModuleRefBase",
10-
"ModuleConfigure",
11-
"IModuleConfigure",
10+
"IModuleSetup",
11+
"ModuleSetup",
1212
"DynamicModule",
1313
]

ellar/core/modules/config.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,20 @@
77
import ellar.core.conf as conf
88
import ellar.core.main as main
99
import ellar.di as di
10-
from ellar.constants import MODULE_METADATA, MODULE_REF_TYPES, MODULE_WATERMARK
10+
from ellar.constants import MODULE_METADATA, MODULE_REF_TYPES
1111
from ellar.reflect import reflect
1212

13+
from .base import ModuleBase
1314
from .ref import ModuleRefBase, create_module_ref_factor
1415

1516
if t.TYPE_CHECKING: # pragma: no cover
1617
from ellar.core import ControllerBase
1718

18-
from .base import ModuleBase
19-
2019

2120
@dataclasses.dataclass
2221
class DynamicModule:
2322
# Module type to be configured
24-
module: t.Type[t.Union["ModuleBase", "IModuleConfigure", t.Any]]
23+
module: t.Type[t.Union["ModuleBase", t.Any]]
2524

2625
providers: t.List[t.Union[t.Type, t.Any]] = dataclasses.field(
2726
default_factory=lambda: []
@@ -35,9 +34,7 @@ class DynamicModule:
3534
)
3635

3736
def __post_init__(self) -> None:
38-
if not reflect.get_metadata(MODULE_WATERMARK, self.module) or not isinstance(
39-
self.module, type
40-
):
37+
if not isinstance(self.module, type) or not issubclass(self.module, ModuleBase):
4138
raise Exception(f"{self.module.__name__} is not a valid Module")
4239

4340
kwargs = dict(
@@ -50,12 +47,14 @@ def __post_init__(self) -> None:
5047
MODULE_METADATA.ROUTERS,
5148
MODULE_METADATA.PROVIDERS,
5249
]:
53-
reflect.delete_metadata(key, self.module)
54-
reflect.define_metadata(key, kwargs[key], self.module)
50+
value = kwargs[key]
51+
if value:
52+
reflect.delete_metadata(key, self.module)
53+
reflect.define_metadata(key, value, self.module)
5554

5655

5756
@dataclasses.dataclass
58-
class ModuleConfigure:
57+
class ModuleSetup:
5958
"""
6059
ModuleConfigure is a way to configure a module late after the application has started.
6160
This is necessary for Module that requires some services available to configure them.
@@ -76,7 +75,7 @@ def module_a_configuration_factory(module: ModuleA, config: Config, foo: Foo):
7675
"""
7776

7877
# Module type to be configured
79-
module: t.Type[t.Union["ModuleBase", "IModuleConfigure", t.Any]]
78+
module: t.Type[t.Union[ModuleBase, "IModuleSetup", t.Any]]
8079

8180
# `inject` property holds collection types to be injected to `use_factory` method.
8281
# the order at which the types are defined becomes the order at which they are injected.
@@ -91,9 +90,7 @@ def module_a_configuration_factory(module: ModuleA, config: Config, foo: Foo):
9190
factory: t.Callable[..., DynamicModule] = None # type: ignore[assignment]
9291

9392
def __post_init__(self) -> None:
94-
if not reflect.get_metadata(MODULE_WATERMARK, self.module) or not isinstance(
95-
self.module, type
96-
):
93+
if not isinstance(self.module, type) or not issubclass(self.module, ModuleBase):
9794
raise Exception(f"{self.module.__name__} is not a valid Module")
9895

9996
if main.App in self.inject:
@@ -105,13 +102,17 @@ def has_factory_function(self) -> bool:
105102
# if we have a factory function, we need to check if the services to inject is just config
106103
# if so, then we can go ahead and have the configuration executed since at this level,
107104
# the config service is available to be injected.
108-
if len(self.inject) == 1 and self.inject[0] == conf.Config:
105+
inject_size = len(self.inject)
106+
if inject_size == 0:
107+
return False
108+
109+
if inject_size == 1 and self.inject[0] == conf.Config:
109110
return False
110111
return True
111112

112113
def get_module_ref(
113114
self, config: conf.Config, container: di.Container
114-
) -> t.Union["ModuleRefBase", "ModuleConfigure"]:
115+
) -> t.Union[ModuleRefBase, "ModuleSetup"]:
115116
if self.has_factory_function or self.ref_type == MODULE_REF_TYPES.APP_DEPENDENT:
116117
return self
117118

@@ -148,15 +149,15 @@ def _get_services(self, injector: di.EllarInjector) -> t.List:
148149
res.append(injector.get(service))
149150
return res
150151

151-
def __hash__(self) -> int:
152+
def __hash__(self) -> int: # pragma: no cover
152153
return hash(self.module)
153154

154155

155-
class IModuleConfigure:
156+
class IModuleSetup:
156157
"""Modules that must have a custom setup should inherit from IModuleConfigure"""
157158

158159
@classmethod
159160
@abstractmethod
160161
@t.no_type_check
161-
def module_configure(cls, *args: t.Any, **kwargs: t.Any) -> DynamicModule:
162-
"""Module Configure"""
162+
def setup(cls, *args: t.Any, **kwargs: t.Any) -> DynamicModule:
163+
"""Module Dynamic Setup"""

ellar/core/testclient.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
from ellar.core.routing import ModuleRouter
1010
from ellar.di import ProviderConfig
1111

12+
if t.TYPE_CHECKING: # pragma: no cover
13+
from ellar.core import GuardCanActivate
14+
1215

1316
class _TestingModule:
1417
def __init__(self, app: App) -> None:
@@ -36,33 +39,41 @@ class TestClientFactory:
3639
@classmethod
3740
def create_test_module(
3841
cls,
42+
modules: t.Sequence[t.Union[t.Type, t.Any]] = tuple(),
3943
controllers: t.Sequence[t.Union[t.Any]] = tuple(),
4044
routers: t.Sequence[ModuleRouter] = tuple(),
4145
providers: t.Sequence[ProviderConfig] = tuple(),
4246
template_folder: t.Optional[str] = None,
4347
base_directory: t.Optional[t.Union[str, Path]] = None,
4448
static_folder: str = "static",
49+
global_guards: t.List[
50+
t.Union[t.Type["GuardCanActivate"], "GuardCanActivate"]
51+
] = None,
4552
config_module: str = None,
4653
) -> _TestingModule:
4754
"""
4855
Create a TestingModule to test controllers and services in isolation
56+
:param modules:
4957
:param controllers:
5058
:param routers:
5159
:param providers:
5260
:param template_folder:
5361
:param base_directory:
5462
:param static_folder:
5563
:param config_module:
64+
:param global_guards:
5665
:return:
5766
"""
5867
app = AppFactory.create_app(
68+
modules=modules,
5969
controllers=controllers,
6070
routers=routers,
6171
providers=providers,
6272
template_folder=template_folder,
6373
base_directory=base_directory,
6474
static_folder=static_folder,
6575
config_module=config_module,
76+
global_guards=global_guards,
6677
)
6778
return _TestingModule(app=app)
6879

ellar/di/injector/ellar_injector.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
if t.TYPE_CHECKING: # pragma: no cover
1717
from ellar.core.modules import (
1818
ModuleBase,
19-
ModuleConfigure,
2019
ModuleRefBase,
20+
ModuleSetup,
2121
ModuleTemplateRef,
2222
)
2323

@@ -72,15 +72,15 @@ def get_modules(
7272

7373
def get_dynamic_modules(
7474
self,
75-
) -> t.Generator["ModuleConfigure", t.Any, None]:
75+
) -> t.Generator["ModuleSetup", t.Any, None]:
7676
for _, module_configure in self._modules[MODULE_REF_TYPES.DYNAMIC].items():
7777
yield module_configure
7878

7979
self._modules[MODULE_REF_TYPES.DYNAMIC].clear()
8080

8181
def get_app_dependent_modules(
8282
self,
83-
) -> t.Generator["ModuleConfigure", t.Any, None]:
83+
) -> t.Generator["ModuleSetup", t.Any, None]:
8484
for _, module_configure in self._modules[
8585
MODULE_REF_TYPES.APP_DEPENDENT
8686
].items():
@@ -104,9 +104,7 @@ def get_templating_modules(
104104
) -> t.Dict[t.Type["ModuleBase"], "ModuleTemplateRef"]:
105105
return self._modules.get(MODULE_REF_TYPES.TEMPLATE, {})
106106

107-
def add_module(
108-
self, module_ref: t.Union["ModuleRefBase", "ModuleConfigure"]
109-
) -> None:
107+
def add_module(self, module_ref: t.Union["ModuleRefBase", "ModuleSetup"]) -> None:
110108
self._modules[module_ref.ref_type].update({module_ref.module: module_ref})
111109

112110
@t.no_type_check

ellar/di/service_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def __init__(
3333
base_type: t.Union[t.Type[T], t.Type],
3434
*,
3535
use_value: T = None,
36-
use_class: t.Type[T] = None
36+
use_class: t.Union[t.Type[T], t.Any] = None
3737
):
3838
if use_value and use_class:
3939
raise DIImproperConfiguration(

0 commit comments

Comments
 (0)