Skip to content

Commit 6de1741

Browse files
authored
Merge pull request #65 from python-ellar/new_command_template
New command template
2 parents 5ab49ad + 2515dbd commit 6de1741

File tree

21 files changed

+313
-24
lines changed

21 files changed

+313
-24
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.3.3"
3+
__version__ = "0.3.4"

ellar_cli/manage_commands/create_project.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ def __init__(
2828
ellar_cli_service: EllarCLIService,
2929
working_project_name: str,
3030
working_directory: str,
31+
plain: bool,
3132
**kwargs: t.Any,
3233
) -> None:
3334
super().__init__(
3435
**kwargs,
3536
working_project_name=working_project_name,
3637
working_directory=working_directory,
3738
)
39+
self._plain = plain
3840
self.ellar_cli_service = ellar_cli_service
3941
self._working_project_name = working_project_name
4042
if self._specified_directory:
@@ -90,15 +92,18 @@ def validate_project_name(self) -> None:
9092

9193
def on_scaffold_completed(self) -> None:
9294
_working_project_name = self._working_project_name
93-
94-
self.ellar_cli_service.create_ellar_project_meta(
95-
project_name=_working_project_name, prefix=self.prefix
96-
)
97-
print(
98-
f"`{self._working_project_name}` project scaffold completed. To start your server, run the command below"
99-
)
100-
print(f"ellar --project {self._working_project_name} runserver --reload")
101-
print("Happy coding!")
95+
if not self._plain:
96+
self.ellar_cli_service.create_ellar_project_meta(
97+
project_name=_working_project_name, prefix=self.prefix
98+
)
99+
print(
100+
f"`{self._working_project_name}` project scaffold completed. To start your server, run the command below"
101+
)
102+
print(f"ellar --project {self._working_project_name} runserver --reload")
103+
print("Happy coding!")
104+
else:
105+
print(f"`{self._working_project_name}` project scaffold completed.")
106+
print("Happy coding!")
102107

103108

104109
@eClick.argument("project_name", help="Project Name")
@@ -107,19 +112,23 @@ def on_scaffold_completed(self) -> None:
107112
help="The name of a new directory to scaffold the project into.",
108113
required=False,
109114
)
115+
@eClick.option(
116+
"--plain",
117+
is_flag=True,
118+
default=False,
119+
help="Create a new without including `pyproject.toml`.",
120+
)
110121
@eClick.pass_context
111122
def create_project(
112-
ctx: eClick.Context,
113-
project_name: str,
114-
directory: t.Optional[str],
123+
ctx: eClick.Context, project_name: str, directory: t.Optional[str], plain: bool
115124
):
116125
"""- Scaffolds Ellar Application -"""
117126

118127
ellar_project_meta = t.cast(t.Optional[EllarCLIService], ctx.meta.get(ELLAR_META))
119-
if not ellar_project_meta:
128+
if not ellar_project_meta and not plain:
120129
raise EllarCLIException("No pyproject.toml file found.")
121130

122-
if ellar_project_meta.ellar_py_projects.has_project(project_name):
131+
if not plain and ellar_project_meta.ellar_py_projects.has_project(project_name):
123132
raise EllarCLIException("Ellar Project already exist.")
124133

125134
schema = EllarScaffoldSchema.parse_file(project_template_json)
@@ -130,5 +139,6 @@ def create_project(
130139
ellar_cli_service=ellar_project_meta,
131140
specified_directory=directory,
132141
working_project_name=project_name.lower(),
142+
plain=plain,
133143
)
134144
project_template_scaffold.scaffold()

ellar_cli/manage_commands/new.py

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,21 @@
1616

1717

1818
conf_module_dir = module_dir(scaffolding)
19-
root_scaffold_template_path = os.path.join(conf_module_dir, "new_template")
20-
init_template_json = os.path.join(root_scaffold_template_path, "setup.json")
19+
new_template_template_path = os.path.join(conf_module_dir, "new_template")
20+
new_manage_template_template_path = os.path.join(conf_module_dir, "new_manage_template")
2121

2222

2323
class NewTemplateScaffold(FileTemplateScaffold):
2424
unwanted_chars = "".join(["-", ";", "!", "*", ":", " "])
2525

26-
def __init__(self, project_name: str = None, **kwargs: t.Any) -> None:
26+
def __init__(
27+
self, project_name: str = None, pyproject_enabled: bool = True, **kwargs: t.Any
28+
) -> None:
2729
super(NewTemplateScaffold, self).__init__(
2830
working_project_name=project_name, **kwargs
2931
)
3032
self._project_name = project_name
33+
self._pyproject_enabled = pyproject_enabled
3134

3235
def create_file(self, base_path: str, file_name: str, content: t.Any) -> None:
3336
_path = os.path.join(base_path, file_name.replace(".ellar", ".py"))
@@ -42,8 +45,12 @@ def create_file(self, base_path: str, file_name: str, content: t.Any) -> None:
4245
fw.writelines(refined_content)
4346

4447
def on_scaffold_completed(self) -> None:
48+
args = ["ellar", "create-project", self.get_project_name()]
49+
if self._pyproject_enabled:
50+
args.append("--plain")
51+
4552
popen_res = subprocess.run(
46-
["ellar", "create-project", self.get_project_name()],
53+
args,
4754
cwd=self.get_project_cwd(),
4855
stdout=subprocess.PIPE,
4956
stderr=subprocess.PIPE,
@@ -64,9 +71,13 @@ def on_scaffold_completed(self) -> None:
6471
f"{log_1}"
6572
)
6673
print("To start your server, run the command below")
67-
print(
68-
f"- ellar --project {project_working_project_name} runserver --reload\nHappy coding!"
69-
)
74+
if self._pyproject_enabled:
75+
print("- python manage.py runserver --reload\nHappy coding!")
76+
else:
77+
print(
78+
f"- ellar --project {project_working_project_name} runserver --reload\nHappy coding!"
79+
)
80+
7081
else: # pragma: no cover
7182
print(popen_res.stderr.decode("utf8"))
7283

@@ -128,14 +139,28 @@ def get_project_cwd(self) -> str:
128139
required=False,
129140
help="The name of a new directory to scaffold the project into. Scaffolding into an existing directory is only allowed if the directory is empty",
130141
)
131-
def new_command(project_name: str, directory: t.Optional[str]):
142+
@eClick.option(
143+
"--plain",
144+
is_flag=True,
145+
default=False,
146+
help="Create a new without including `pyproject.toml`.",
147+
)
148+
def new_command(project_name: str, directory: t.Optional[str], plain: bool):
132149
"""- Runs a complete Ellar project scaffold and creates all files required for managing you application -"""
150+
root_scaffold_template_path = new_template_template_path
151+
init_template_json = os.path.join(root_scaffold_template_path, "setup.json")
152+
153+
if plain:
154+
root_scaffold_template_path = new_manage_template_template_path
155+
init_template_json = os.path.join(root_scaffold_template_path, "setup.json")
156+
133157
schema = EllarScaffoldSchema.parse_file(init_template_json)
134158
init_template_scaffold = NewTemplateScaffold(
135159
schema=schema,
136160
working_directory=os.getcwd(),
137161
scaffold_ellar_template_root_path=root_scaffold_template_path,
138162
project_name=project_name,
139163
specified_directory=directory,
164+
pyproject_enabled=plain,
140165
)
141166
init_template_scaffold.scaffold()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# {{project_name}}
2+
Project Description
3+
4+
## Requirements
5+
Python >= 3.8
6+
ellar
7+
8+
## Project setup
9+
```
10+
pip install -r requirements.txt
11+
```
12+
13+
### Development Server
14+
```
15+
python manage.py runserver --reload
16+
```
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import os
2+
3+
from ellar.common.constants import ELLAR_CONFIG_MODULE
4+
from ellar_cli.main import create_ellar_cli
5+
6+
7+
if __name__ == '__main__':
8+
os.environ.setdefault(ELLAR_CONFIG_MODULE, "{{project_name}}.config:DevelopmentConfig")
9+
10+
# initialize Commandline program
11+
cli = create_ellar_cli('{{project_name}}.server:bootstrap')
12+
# start commandline execution
13+
cli(prog_name="Ellar Web Framework CLI")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from ellar.testing import Test, TestClient
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"context": ["folder_name", "project_name"],
3+
"files": [
4+
{
5+
"name": "folder_name",
6+
"is_directory": "true",
7+
"files": [
8+
{
9+
"name": "manage.ellar"
10+
},
11+
{
12+
"name": "README.md"
13+
},
14+
{
15+
"name": "tests",
16+
"is_directory": "true",
17+
"files": [
18+
{
19+
"name": "conftest.ellar"
20+
}
21+
]
22+
}
23+
]
24+
}
25+
]
26+
}

ellar_cli/scaffolding/project_template/project_name/root_module.ellar

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ from ellar.samples.modules import HomeModule
77
class ApplicationModule(ModuleBase):
88
@exception_handler(404)
99
def exception_404_handler(cls, ctx: IExecutionContext, exc: Exception) -> Response:
10-
return JSONResponse(dict(detail="Resource not found."), status_code=404)
10+
return JSONResponse({"detail": "Resource not found."}, status_code=404)

tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from uuid import uuid4
66

77
import pytest
8+
from ellar.common.constants import ELLAR_CONFIG_MODULE
89
from tomlkit import table
910

1011
from ellar_cli.service import EllarCLIService, EllarPyProject
@@ -18,6 +19,7 @@
1819
def change_os_dir():
1920
sys.path.append(sample_app_path)
2021
os.chdir(sample_app_path)
22+
os.environ.pop(ELLAR_CONFIG_MODULE, None)
2123
print(f"working director - {os.getcwd()}")
2224
yield
2325
sys.path.remove(sample_app_path)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# plain_project
2+
Project Description
3+
4+
## Requirements
5+
Python >= 3.8
6+
ellar
7+
8+
## Project setup
9+
```
10+
pip install -r requirements.txt
11+
```
12+
13+
### Development Server
14+
```
15+
python manage.py runserver --reload
16+
```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import os
2+
3+
from ellar.common.constants import ELLAR_CONFIG_MODULE
4+
5+
from ellar_cli.main import create_ellar_cli
6+
7+
os.environ.setdefault(ELLAR_CONFIG_MODULE, "plain_project.config:DevelopmentConfig")
8+
9+
10+
if __name__ == "__main__":
11+
# initialize Commandline program
12+
cli = create_ellar_cli("plain_project.server:bootstrap")
13+
# start commandline execution
14+
cli(prog_name="Ellar Web Framework CLI")

tests/sample_app/plain_project/plain_project/__init__.py

Whitespace-only changes.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""
2+
Application Configurations
3+
Default Ellar Configurations are exposed here through `ConfigDefaultTypesMixin`
4+
Make changes and define your own configurations specific to your application
5+
6+
export ELLAR_CONFIG_MODULE=exy.config:DevelopmentConfig
7+
"""
8+
9+
import typing as t
10+
11+
from ellar.common import IExceptionHandler, JSONResponse
12+
from ellar.core import ConfigDefaultTypesMixin
13+
from ellar.core.versioning import BaseAPIVersioning, DefaultAPIVersioning
14+
from ellar.pydantic import ENCODERS_BY_TYPE as encoders_by_type
15+
from starlette.middleware import Middleware
16+
17+
18+
class BaseConfig(ConfigDefaultTypesMixin):
19+
DEBUG: bool = False
20+
21+
DEFAULT_JSON_CLASS: t.Type[JSONResponse] = JSONResponse
22+
SECRET_KEY: str = "ellar_fmk86xpbSS12NsbGTGw52xek6OHmRUn466hQ61iFBV4"
23+
24+
# injector auto_bind = True allows you to resolve types that are not registered on the container
25+
# For more info, read: https://injector.readthedocs.io/en/latest/index.html
26+
INJECTOR_AUTO_BIND = False
27+
28+
# jinja Environment options
29+
# https://jinja.palletsprojects.com/en/3.0.x/api/#high-level-api
30+
JINJA_TEMPLATES_OPTIONS: t.Dict[str, t.Any] = {}
31+
32+
# Application route versioning scheme
33+
VERSIONING_SCHEME: BaseAPIVersioning = DefaultAPIVersioning()
34+
35+
# Enable or Disable Application Router route searching by appending backslash
36+
REDIRECT_SLASHES: bool = False
37+
38+
# Define references to static folders in python packages.
39+
# eg STATIC_FOLDER_PACKAGES = [('boostrap4', 'statics')]
40+
STATIC_FOLDER_PACKAGES: t.Optional[t.List[t.Union[str, t.Tuple[str, str]]]] = []
41+
42+
# Define references to static folders defined within the project
43+
STATIC_DIRECTORIES: t.Optional[t.List[t.Union[str, t.Any]]] = []
44+
45+
# static route path
46+
STATIC_MOUNT_PATH: str = "/static"
47+
48+
CORS_ALLOW_ORIGINS: t.List[str] = ["*"]
49+
CORS_ALLOW_METHODS: t.List[str] = ["*"]
50+
CORS_ALLOW_HEADERS: t.List[str] = ["*"]
51+
ALLOWED_HOSTS: t.List[str] = ["*"]
52+
53+
# Application middlewares
54+
MIDDLEWARE: t.Sequence[Middleware] = []
55+
56+
# A dictionary mapping either integer status codes,
57+
# or exception class types onto callables which handle the exceptions.
58+
# Exception handler callables should be of the form
59+
# `handler(context:IExecutionContext, exc: Exception) -> response`
60+
# and may be either standard functions, or async functions.
61+
EXCEPTION_HANDLERS: t.List[IExceptionHandler] = []
62+
63+
# Object Serializer custom encoders
64+
SERIALIZER_CUSTOM_ENCODER: t.Dict[
65+
t.Any, t.Callable[[t.Any], t.Any]
66+
] = encoders_by_type
67+
68+
69+
class DevelopmentConfig(BaseConfig):
70+
DEBUG: bool = True

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.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from ellar.app import current_app
2+
from ellar.common import (
3+
IExecutionContext,
4+
JSONResponse,
5+
Module,
6+
Response,
7+
exception_handler,
8+
)
9+
from ellar.core import Config, ModuleBase
10+
from ellar.samples.modules import HomeModule
11+
12+
import ellar_cli.click as click
13+
14+
15+
@click.command()
16+
@click.with_app_context
17+
def plain_project():
18+
"""Project 2 Custom Command"""
19+
assert isinstance(current_app.config, Config)
20+
print("Plain Project Command works. Executed within application context")
21+
22+
23+
@Module(modules=[HomeModule], commands=[plain_project])
24+
class ApplicationModule(ModuleBase):
25+
@exception_handler(404)
26+
def exception_404_handler(cls, ctx: IExecutionContext, exc: Exception) -> Response:
27+
return JSONResponse({"detail": "Resource not found."}, status_code=404)

0 commit comments

Comments
 (0)