Skip to content

Commit f372e4b

Browse files
authored
Merge pull request #102 from python-ellar/scaffold_fixing
Fix: Scaffold Template Refactor
2 parents 0a8825e + 7cb9a1c commit f372e4b

File tree

23 files changed

+90
-88
lines changed

23 files changed

+90
-88
lines changed

ellar_cli/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Ellar CLI Tool for Scaffolding Ellar Projects, Modules and also running Ellar Commands"""
22

3-
__version__ = "0.4.1"
3+
__version__ = "0.4.2b1"

ellar_cli/click/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@
4242
from click.utils import get_binary_stream as get_binary_stream
4343
from click.utils import get_text_stream as get_text_stream
4444
from click.utils import open_file as open_file
45-
from ellar.threading import run_as_async
45+
from ellar.threading import run_as_sync
4646

4747
from .argument import Argument
4848
from .command import Command
4949
from .group import AppContextGroup, EllarCommandGroup
50-
from .util import with_app_context
50+
from .util import with_injector_context
5151

5252

5353
def argument(
@@ -90,14 +90,14 @@ def group(
9090

9191
__all__ = [
9292
"argument",
93-
"run_as_async",
93+
"run_as_sync",
9494
"command",
9595
"Argument",
9696
"Option",
9797
"option",
9898
"AppContextGroup",
9999
"EllarCommandGroup",
100-
"with_app_context",
100+
"with_injector_context",
101101
"Context",
102102
"Group",
103103
"Parameter",

ellar_cli/click/group.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,41 @@
99
from ellar_cli.service import EllarCLIService, EllarCLIServiceWithPyProject
1010

1111
from .command import Command
12-
from .util import with_app_context
12+
from .util import with_injector_context
1313

1414

1515
class AppContextGroup(click.Group):
1616
"""This works similar to a regular click.Group, but it
1717
changes the behavior of the command decorator so that it
18-
automatically wraps the functions in `pass_with_app_context`.
18+
automatically wraps the functions in `with_injector_context`.
1919
"""
2020

2121
command_class = Command
2222

23-
def command(self, *args: t.Any, **kwargs: t.Any) -> t.Callable: # type:ignore[override]
23+
def command( # type:ignore[override]
24+
self, *args: t.Any, **kwargs: t.Any
25+
) -> t.Union[t.Callable[[t.Callable[..., t.Any]], click.Command], click.Command]:
2426
"""The same with click.Group command.
25-
It only decorates the command function to be run under `applicationContext`.
26-
It's disabled by passing `with_app_context=False`.
27+
It only decorates the command function to be run under `ellar.core.with_injector_context`.
28+
It's disabled by passing `with_injector_context=False`.
2729
"""
28-
wrap_for_ctx = kwargs.pop("with_app_context", True)
30+
wrap_for_ctx = kwargs.pop("with_injector_context", True)
2931

3032
def decorator(f: t.Callable) -> t.Any:
3133
if wrap_for_ctx:
32-
f = with_app_context(f)
33-
return click.Group.command(self, *args, **kwargs)(f)
34+
f = with_injector_context(f)
35+
return super(AppContextGroup, self).command(*args, **kwargs)(f)
3436

3537
return decorator
3638

37-
def group(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
39+
def group( # type:ignore[override]
40+
self, *args: t.Any, **kwargs: t.Any
41+
) -> t.Union[t.Callable[[t.Callable[..., t.Any]], click.Group], click.Group]:
3842
"""This works exactly like the method of the same name on a regular click.Group but it defaults
3943
the group class to `AppGroup`.
4044
"""
41-
kwargs.setdefault("cls", AppContextGroup)
42-
return click.Group.group(self, *args, **kwargs)
45+
kwargs.setdefault("cls", self.__class__)
46+
return super(AppContextGroup, self).group(*args, **kwargs) # type:ignore[no-any-return]
4347

4448

4549
class EllarCommandGroup(AppContextGroup):
@@ -62,7 +66,9 @@ def _load_application_commands(self, ctx: click.Context) -> None:
6266
if self._cli_meta:
6367
application = self._cli_meta.import_application()
6468

65-
module_configs = (i for i in application.injector.get_modules().keys())
69+
module_configs = (
70+
i for i in application.injector.tree_manager.modules.keys()
71+
)
6672

6773
ctx.meta[ELLAR_META] = self._cli_meta
6874
else:
@@ -77,12 +83,10 @@ def _load_application_commands(self, ctx: click.Context) -> None:
7783
ctx.meta[ELLAR_META] = meta_
7884

7985
if meta_ and meta_.has_meta:
80-
module_configs = (
81-
module_config.module
82-
for module_config in AppFactory.get_all_modules(
83-
ModuleSetup(meta_.import_root_module())
84-
)
86+
tree_manager = AppFactory.read_all_module(
87+
ModuleSetup(meta_.import_root_module())
8588
)
89+
module_configs = tree_manager.modules.keys()
8690
self._find_commands_from_modules(module_configs)
8791

8892
def _find_commands_from_modules(

ellar_cli/click/util.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
from ellar_cli.service import EllarCLIService
1111

1212

13-
def with_app_context(f: t.Callable) -> t.Any:
13+
def with_injector_context(f: t.Callable) -> t.Any:
1414
"""
15-
Wraps a callback so that it's guaranteed to be executed with application context.
15+
Wraps a callback so that it's guaranteed to be executed with `ellar.core.with_injector_context` contextmanager.
1616
"""
1717

1818
@click.pass_context

ellar_cli/main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ def _app_cli(ctx: click.Context, **kwargs: t.Any) -> None:
5252
ctx.meta[ELLAR_PROJECT_NAME] = kwargs["project"]
5353

5454
if not app_import_string:
55-
_app_cli.command(name="new")(new_command)
56-
_app_cli.command(name="create-project")(create_project)
55+
_app_cli.add_command(new_command)
56+
_app_cli.add_command(create_project)
5757

58-
_app_cli.command(context_settings={"auto_envvar_prefix": "UVICORN"})(runserver)
59-
_app_cli.command(name="create-module")(create_module)
58+
_app_cli.add_command(runserver)
59+
_app_cli.add_command(create_module)
6060

6161
return _app_cli # type:ignore[no-any-return]
6262

ellar_cli/manage_commands/create_module.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,14 @@ def get_scaffolding_context(self, working_project_name: str) -> t.Dict:
7171
return template_context
7272

7373

74+
@eClick.command(name="create-module")
7475
@eClick.argument("module_name")
7576
@eClick.argument(
7677
"directory",
7778
help="The name of a new directory to scaffold the module into.",
7879
required=False,
7980
)
81+
@eClick.with_injector_context
8082
def create_module(module_name: str, directory: t.Optional[str]):
8183
"""- Scaffolds Ellar Application Module -"""
8284

ellar_cli/manage_commands/create_project.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ def on_scaffold_completed(self) -> None:
106106
print("Happy coding!")
107107

108108

109+
@eClick.command(name="create-project")
109110
@eClick.argument("project_name", help="Project Name")
110111
@eClick.argument(
111112
"directory",
@@ -119,6 +120,7 @@ def on_scaffold_completed(self) -> None:
119120
help="Create a new without including `pyproject.toml`.",
120121
)
121122
@eClick.pass_context
123+
@eClick.with_injector_context
122124
def create_project(
123125
ctx: eClick.Context, project_name: str, directory: t.Optional[str], plain: bool
124126
):

ellar_cli/manage_commands/new.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def get_project_cwd(self) -> str:
130130
return os.path.join(self._working_directory, self._working_project_name)
131131

132132

133+
@eClick.command(name="new")
133134
@eClick.argument(
134135
"project_name",
135136
help="Project Module Name. Defaults to `project-name` if not set",
@@ -145,6 +146,7 @@ def get_project_cwd(self) -> str:
145146
default=False,
146147
help="Create a new without including `pyproject.toml`.",
147148
)
149+
@eClick.with_injector_context
148150
def new_command(project_name: str, directory: t.Optional[str], plain: bool):
149151
"""- Runs a complete Ellar project scaffold and creates all files required for managing you application -"""
150152
root_scaffold_template_path = new_template_template_path

ellar_cli/manage_commands/runserver.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
INTERFACE_CHOICES = eClick.Choice(INTERFACES)
3838

3939

40+
@eClick.command(name="runserver", context_settings={"auto_envvar_prefix": "UVICORN"})
4041
@eClick.option(
4142
"--host",
4243
type=str,

ellar_cli/scaffolding/module_template/module_name/module.ellar

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
base_directory=`default is the `{{module_name}}` folder`
1313
)
1414
class MyModule(ModuleBase):
15-
def register_providers(self, container: Container) -> None:
15+
def register_providers(self, moduleRef: ModuleRefBase) -> None:
1616
# for more complicated provider registrations
1717
pass
1818

1919
"""
2020
from ellar.common import Module
21-
from ellar.core import ModuleBase
21+
from ellar.core.modules import ModuleBase, ModuleRefBase
2222
from ellar.di import Container
2323

2424
from .controllers import {{module_name | capitalize}}Controller
@@ -34,5 +34,5 @@ class {{module_name | capitalize}}Module(ModuleBase):
3434
{{module_name | capitalize}} Module
3535
"""
3636

37-
def register_providers(self, container: Container) -> None:
37+
def register_providers(self, moduleRef: ModuleRefBase) -> None:
3838
"""for more complicated provider registrations, use container.register_instance(...) """

ellar_cli/scaffolding/module_template/setup.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@
1414
{
1515
"name": "module.ellar"
1616
},
17-
{
18-
"name": "routers.ellar"
19-
},
2017
{
2118
"name": "schemas.ellar"
2219
},

ellar_cli/scaffolding/project_template/project_name/config.ellar

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export ELLAR_CONFIG_MODULE={{project_name}}.config:DevelopmentConfig
99
import typing as t
1010

1111
from ellar.pydantic import ENCODERS_BY_TYPE as encoders_by_type
12+
from starlette.requests import Request
1213
from starlette.middleware import Middleware
1314
from ellar.common import IExceptionHandler, JSONResponse
1415
from ellar.core import ConfigDefaultTypesMixin
@@ -29,6 +30,15 @@ class BaseConfig(ConfigDefaultTypesMixin):
2930
# https://jinja.palletsprojects.com/en/3.0.x/api/#high-level-api
3031
JINJA_TEMPLATES_OPTIONS: t.Dict[str, t.Any] = {}
3132

33+
# Injects context to jinja templating context values
34+
TEMPLATES_CONTEXT_PROCESSORS:t.List[
35+
t.Union[str, t.Callable[[t.Union[Request]], t.Dict[str, t.Any]]]
36+
] = [
37+
"ellar.core.templating.context_processors:request_context",
38+
"ellar.core.templating.context_processors:user",
39+
"ellar.core.templating.context_processors:request_state",
40+
]
41+
3242
# Application route versioning scheme
3343
VERSIONING_SCHEME: BaseAPIVersioning = DefaultAPIVersioning()
3444

@@ -51,14 +61,24 @@ class BaseConfig(ConfigDefaultTypesMixin):
5161
ALLOWED_HOSTS: t.List[str] = ["*"]
5262

5363
# Application middlewares
54-
MIDDLEWARE: t.Sequence[Middleware] = []
64+
MIDDLEWARE: t.Union[str, Middleware] = [
65+
"ellar.core.middleware.trusted_host:trusted_host_middleware",
66+
"ellar.core.middleware.cors:cors_middleware",
67+
"ellar.core.middleware.errors:server_error_middleware",
68+
"ellar.core.middleware.versioning:versioning_middleware",
69+
"ellar.auth.middleware.session:session_middleware",
70+
"ellar.auth.middleware.auth:identity_middleware",
71+
"ellar.core.middleware.exceptions:exception_middleware",
72+
]
5573

5674
# A dictionary mapping either integer status codes,
5775
# or exception class types onto callables which handle the exceptions.
5876
# Exception handler callables should be of the form
59-
# `handler(context:IExecutionContext, exc: Exception) -> response`
77+
# `handler(context:IExecutionContext, exc: Exception) -> response`
6078
# and may be either standard functions, or async functions.
61-
EXCEPTION_HANDLERS: t.List[IExceptionHandler] = []
79+
EXCEPTION_HANDLERS: t.Union[str, IExceptionHandler] = [
80+
"ellar.core.exceptions:error_404_handler"
81+
]
6282

6383
# Object Serializer custom encoders
6484
SERIALIZER_CUSTOM_ENCODER: t.Dict[
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
from ellar.common import Module, exception_handler, IExecutionContext, JSONResponse, Response
1+
from ellar.common import Module
22
from ellar.core import ModuleBase
33
from ellar.samples.modules import HomeModule
44

55

66
@Module(modules=[HomeModule])
77
class ApplicationModule(ModuleBase):
8-
@exception_handler(404)
9-
def exception_404_handler(cls, ctx: IExecutionContext, exc: Exception) -> Response:
10-
return JSONResponse({"detail": "Resource not found."}, status_code=404)
8+
pass

ellar_cli/scaffolding/project_template/setup.json

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,6 @@
55
"name": "project_name",
66
"is_directory": "true",
77
"files": [
8-
{
9-
"name": "core",
10-
"is_directory": "true",
11-
"files": [
12-
{
13-
"name": "__init__.ellar"
14-
}
15-
]
16-
},
17-
{
18-
"name": "domain",
19-
"is_directory": "true",
20-
"files": [
21-
{
22-
"name": "__init__.ellar"
23-
}
24-
]
25-
},
268
{
279
"name": "__init__.ellar"
2810
},

ellar_cli/service/cli.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
import typing as t
66

77
from ellar.app import App
8-
from ellar.app.context import ApplicationContext
98
from ellar.common.constants import ELLAR_CONFIG_MODULE
10-
from ellar.core import Config, ModuleBase
9+
from ellar.core import Config, ModuleBase, injector_context
10+
from ellar.di import EllarInjector
1111
from ellar.utils.importer import import_from_string, module_import
1212
from tomlkit import dumps as tomlkit_dumps
1313
from tomlkit import parse as tomlkit_parse
@@ -232,9 +232,9 @@ def import_root_module(self) -> t.Type["ModuleBase"]:
232232
return self._store.root_module
233233

234234
@_export_ellar_config_module
235-
def get_application_context(self) -> ApplicationContext:
235+
def get_application_context(self) -> t.AsyncGenerator[EllarInjector, t.Any]:
236236
app = t.cast(App, self.import_application())
237-
return app.application_context()
237+
return injector_context(app.injector)
238238

239239

240240
class EllarCLIServiceWithPyProject(EllarCLIService):

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ classifiers = [
4242
dependencies = [
4343
# exclude 0.11.2 and 0.11.3 due to https://github.com/sdispater/tomlkit/issues/225
4444
"tomlkit >=0.11.1,<1.0.0,!=0.11.2,!=0.11.3",
45+
"ellar >= 0.8b1",
4546
"uvicorn[standard] == 0.30.4",
46-
"ellar >= 0.7.4",
4747
"click >= 8.1.7",
4848
]
4949

tests/click/test_app_context_group.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from ellar.app import current_injector
2-
from ellar.core import Config
1+
from ellar.core import Config, current_injector
32

43
from ellar_cli.click.group import AppContextGroup
54

@@ -11,7 +10,7 @@ def app_group():
1110
pass
1211

1312

14-
@app_group.command(with_app_context=False)
13+
@app_group.command(with_injector_context=False)
1514
def invoke_without_app_context():
1615
assert current_injector.get(Config)
1716
print("Application Context wont be initialized.")

tests/sample_app/example_project/commands.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from ellar.app import current_injector
2-
from ellar.core import Config
1+
from ellar.core import Config, current_injector
32

43
import ellar_cli.click as click
54

@@ -27,7 +26,7 @@ def say_hello():
2726

2827

2928
@db.command(name="command-with-context")
30-
@click.with_app_context
29+
@click.with_injector_context
3130
def command_with_app_context():
3231
print(
3332
f"Running a command with application context - {current_injector.get(Config).APPLICATION_NAME}"

tests/sample_app/plain_project/plain_project/core/__init__.py

Whitespace-only changes.

tests/sample_app/plain_project/plain_project/domain/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)