Skip to content

Commit

Permalink
Merge pull request #65 from python-ellar/new_command_template
Browse files Browse the repository at this point in the history
New command template
  • Loading branch information
eadwinCode authored Jan 22, 2024
2 parents 5ab49ad + 2515dbd commit 6de1741
Showing 21 changed files with 313 additions and 24 deletions.
2 changes: 1 addition & 1 deletion ellar_cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Ellar CLI Tool for Scaffolding Ellar Projects, Modules and also running Ellar Commands"""

__version__ = "0.3.3"
__version__ = "0.3.4"
38 changes: 24 additions & 14 deletions ellar_cli/manage_commands/create_project.py
Original file line number Diff line number Diff line change
@@ -28,13 +28,15 @@ def __init__(
ellar_cli_service: EllarCLIService,
working_project_name: str,
working_directory: str,
plain: bool,
**kwargs: t.Any,
) -> None:
super().__init__(
**kwargs,
working_project_name=working_project_name,
working_directory=working_directory,
)
self._plain = plain
self.ellar_cli_service = ellar_cli_service
self._working_project_name = working_project_name
if self._specified_directory:
@@ -90,15 +92,18 @@ def validate_project_name(self) -> None:

def on_scaffold_completed(self) -> None:
_working_project_name = self._working_project_name

self.ellar_cli_service.create_ellar_project_meta(
project_name=_working_project_name, prefix=self.prefix
)
print(
f"`{self._working_project_name}` project scaffold completed. To start your server, run the command below"
)
print(f"ellar --project {self._working_project_name} runserver --reload")
print("Happy coding!")
if not self._plain:
self.ellar_cli_service.create_ellar_project_meta(
project_name=_working_project_name, prefix=self.prefix
)
print(
f"`{self._working_project_name}` project scaffold completed. To start your server, run the command below"
)
print(f"ellar --project {self._working_project_name} runserver --reload")
print("Happy coding!")
else:
print(f"`{self._working_project_name}` project scaffold completed.")
print("Happy coding!")


@eClick.argument("project_name", help="Project Name")
@@ -107,19 +112,23 @@ def on_scaffold_completed(self) -> None:
help="The name of a new directory to scaffold the project into.",
required=False,
)
@eClick.option(
"--plain",
is_flag=True,
default=False,
help="Create a new without including `pyproject.toml`.",
)
@eClick.pass_context
def create_project(
ctx: eClick.Context,
project_name: str,
directory: t.Optional[str],
ctx: eClick.Context, project_name: str, directory: t.Optional[str], plain: bool
):
"""- Scaffolds Ellar Application -"""

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

if ellar_project_meta.ellar_py_projects.has_project(project_name):
if not plain and ellar_project_meta.ellar_py_projects.has_project(project_name):
raise EllarCLIException("Ellar Project already exist.")

schema = EllarScaffoldSchema.parse_file(project_template_json)
@@ -130,5 +139,6 @@ def create_project(
ellar_cli_service=ellar_project_meta,
specified_directory=directory,
working_project_name=project_name.lower(),
plain=plain,
)
project_template_scaffold.scaffold()
41 changes: 33 additions & 8 deletions ellar_cli/manage_commands/new.py
Original file line number Diff line number Diff line change
@@ -16,18 +16,21 @@


conf_module_dir = module_dir(scaffolding)
root_scaffold_template_path = os.path.join(conf_module_dir, "new_template")
init_template_json = os.path.join(root_scaffold_template_path, "setup.json")
new_template_template_path = os.path.join(conf_module_dir, "new_template")
new_manage_template_template_path = os.path.join(conf_module_dir, "new_manage_template")


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

def __init__(self, project_name: str = None, **kwargs: t.Any) -> None:
def __init__(
self, project_name: str = None, pyproject_enabled: bool = True, **kwargs: t.Any
) -> None:
super(NewTemplateScaffold, self).__init__(
working_project_name=project_name, **kwargs
)
self._project_name = project_name
self._pyproject_enabled = pyproject_enabled

def create_file(self, base_path: str, file_name: str, content: t.Any) -> None:
_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:
fw.writelines(refined_content)

def on_scaffold_completed(self) -> None:
args = ["ellar", "create-project", self.get_project_name()]
if self._pyproject_enabled:
args.append("--plain")

popen_res = subprocess.run(
["ellar", "create-project", self.get_project_name()],
args,
cwd=self.get_project_cwd(),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
@@ -64,9 +71,13 @@ def on_scaffold_completed(self) -> None:
f"{log_1}"
)
print("To start your server, run the command below")
print(
f"- ellar --project {project_working_project_name} runserver --reload\nHappy coding!"
)
if self._pyproject_enabled:
print("- python manage.py runserver --reload\nHappy coding!")
else:
print(
f"- ellar --project {project_working_project_name} runserver --reload\nHappy coding!"
)

else: # pragma: no cover
print(popen_res.stderr.decode("utf8"))

@@ -128,14 +139,28 @@ def get_project_cwd(self) -> str:
required=False,
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",
)
def new_command(project_name: str, directory: t.Optional[str]):
@eClick.option(
"--plain",
is_flag=True,
default=False,
help="Create a new without including `pyproject.toml`.",
)
def new_command(project_name: str, directory: t.Optional[str], plain: bool):
"""- Runs a complete Ellar project scaffold and creates all files required for managing you application -"""
root_scaffold_template_path = new_template_template_path
init_template_json = os.path.join(root_scaffold_template_path, "setup.json")

if plain:
root_scaffold_template_path = new_manage_template_template_path
init_template_json = os.path.join(root_scaffold_template_path, "setup.json")

schema = EllarScaffoldSchema.parse_file(init_template_json)
init_template_scaffold = NewTemplateScaffold(
schema=schema,
working_directory=os.getcwd(),
scaffold_ellar_template_root_path=root_scaffold_template_path,
project_name=project_name,
specified_directory=directory,
pyproject_enabled=plain,
)
init_template_scaffold.scaffold()
16 changes: 16 additions & 0 deletions ellar_cli/scaffolding/new_manage_template/folder_name/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# {{project_name}}
Project Description

## Requirements
Python >= 3.8
ellar

## Project setup
```
pip install -r requirements.txt
```

### Development Server
```
python manage.py runserver --reload
```
13 changes: 13 additions & 0 deletions ellar_cli/scaffolding/new_manage_template/folder_name/manage.ellar
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import os

from ellar.common.constants import ELLAR_CONFIG_MODULE
from ellar_cli.main import create_ellar_cli


if __name__ == '__main__':
os.environ.setdefault(ELLAR_CONFIG_MODULE, "{{project_name}}.config:DevelopmentConfig")

# initialize Commandline program
cli = create_ellar_cli('{{project_name}}.server:bootstrap')
# start commandline execution
cli(prog_name="Ellar Web Framework CLI")
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from ellar.testing import Test, TestClient
26 changes: 26 additions & 0 deletions ellar_cli/scaffolding/new_manage_template/setup.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"context": ["folder_name", "project_name"],
"files": [
{
"name": "folder_name",
"is_directory": "true",
"files": [
{
"name": "manage.ellar"
},
{
"name": "README.md"
},
{
"name": "tests",
"is_directory": "true",
"files": [
{
"name": "conftest.ellar"
}
]
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -7,4 +7,4 @@ from ellar.samples.modules import HomeModule
class ApplicationModule(ModuleBase):
@exception_handler(404)
def exception_404_handler(cls, ctx: IExecutionContext, exc: Exception) -> Response:
return JSONResponse(dict(detail="Resource not found."), status_code=404)
return JSONResponse({"detail": "Resource not found."}, status_code=404)
2 changes: 2 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
from uuid import uuid4

import pytest
from ellar.common.constants import ELLAR_CONFIG_MODULE
from tomlkit import table

from ellar_cli.service import EllarCLIService, EllarPyProject
@@ -18,6 +19,7 @@
def change_os_dir():
sys.path.append(sample_app_path)
os.chdir(sample_app_path)
os.environ.pop(ELLAR_CONFIG_MODULE, None)
print(f"working director - {os.getcwd()}")
yield
sys.path.remove(sample_app_path)
16 changes: 16 additions & 0 deletions tests/sample_app/plain_project/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# plain_project
Project Description

## Requirements
Python >= 3.8
ellar

## Project setup
```
pip install -r requirements.txt
```

### Development Server
```
python manage.py runserver --reload
```
14 changes: 14 additions & 0 deletions tests/sample_app/plain_project/manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import os

from ellar.common.constants import ELLAR_CONFIG_MODULE

from ellar_cli.main import create_ellar_cli

os.environ.setdefault(ELLAR_CONFIG_MODULE, "plain_project.config:DevelopmentConfig")


if __name__ == "__main__":
# initialize Commandline program
cli = create_ellar_cli("plain_project.server:bootstrap")
# start commandline execution
cli(prog_name="Ellar Web Framework CLI")
Empty file.
70 changes: 70 additions & 0 deletions tests/sample_app/plain_project/plain_project/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""
Application Configurations
Default Ellar Configurations are exposed here through `ConfigDefaultTypesMixin`
Make changes and define your own configurations specific to your application
export ELLAR_CONFIG_MODULE=exy.config:DevelopmentConfig
"""

import typing as t

from ellar.common import IExceptionHandler, JSONResponse
from ellar.core import ConfigDefaultTypesMixin
from ellar.core.versioning import BaseAPIVersioning, DefaultAPIVersioning
from ellar.pydantic import ENCODERS_BY_TYPE as encoders_by_type
from starlette.middleware import Middleware


class BaseConfig(ConfigDefaultTypesMixin):
DEBUG: bool = False

DEFAULT_JSON_CLASS: t.Type[JSONResponse] = JSONResponse
SECRET_KEY: str = "ellar_fmk86xpbSS12NsbGTGw52xek6OHmRUn466hQ61iFBV4"

# injector auto_bind = True allows you to resolve types that are not registered on the container
# For more info, read: https://injector.readthedocs.io/en/latest/index.html
INJECTOR_AUTO_BIND = False

# jinja Environment options
# https://jinja.palletsprojects.com/en/3.0.x/api/#high-level-api
JINJA_TEMPLATES_OPTIONS: t.Dict[str, t.Any] = {}

# Application route versioning scheme
VERSIONING_SCHEME: BaseAPIVersioning = DefaultAPIVersioning()

# Enable or Disable Application Router route searching by appending backslash
REDIRECT_SLASHES: bool = False

# Define references to static folders in python packages.
# eg STATIC_FOLDER_PACKAGES = [('boostrap4', 'statics')]
STATIC_FOLDER_PACKAGES: t.Optional[t.List[t.Union[str, t.Tuple[str, str]]]] = []

# Define references to static folders defined within the project
STATIC_DIRECTORIES: t.Optional[t.List[t.Union[str, t.Any]]] = []

# static route path
STATIC_MOUNT_PATH: str = "/static"

CORS_ALLOW_ORIGINS: t.List[str] = ["*"]
CORS_ALLOW_METHODS: t.List[str] = ["*"]
CORS_ALLOW_HEADERS: t.List[str] = ["*"]
ALLOWED_HOSTS: t.List[str] = ["*"]

# Application middlewares
MIDDLEWARE: t.Sequence[Middleware] = []

# A dictionary mapping either integer status codes,
# or exception class types onto callables which handle the exceptions.
# Exception handler callables should be of the form
# `handler(context:IExecutionContext, exc: Exception) -> response`
# and may be either standard functions, or async functions.
EXCEPTION_HANDLERS: t.List[IExceptionHandler] = []

# Object Serializer custom encoders
SERIALIZER_CUSTOM_ENCODER: t.Dict[
t.Any, t.Callable[[t.Any], t.Any]
] = encoders_by_type


class DevelopmentConfig(BaseConfig):
DEBUG: bool = True
Empty file.
Empty file.
27 changes: 27 additions & 0 deletions tests/sample_app/plain_project/plain_project/root_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from ellar.app import current_app
from ellar.common import (
IExecutionContext,
JSONResponse,
Module,
Response,
exception_handler,
)
from ellar.core import Config, ModuleBase
from ellar.samples.modules import HomeModule

import ellar_cli.click as click


@click.command()
@click.with_app_context
def plain_project():
"""Project 2 Custom Command"""
assert isinstance(current_app.config, Config)
print("Plain Project Command works. Executed within application context")


@Module(modules=[HomeModule], commands=[plain_project])
class ApplicationModule(ModuleBase):
@exception_handler(404)
def exception_404_handler(cls, ctx: IExecutionContext, exc: Exception) -> Response:
return JSONResponse({"detail": "Resource not found."}, status_code=404)
Loading

0 comments on commit 6de1741

Please sign in to comment.