Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New command template #65

Merged
merged 4 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Expand Up @@ -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:
Expand Down Expand Up @@ -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")
Expand All @@ -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)
Expand All @@ -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
Expand Up @@ -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"))
Expand All @@ -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,
Expand All @@ -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"))

Expand Down Expand Up @@ -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
Expand Up @@ -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
Expand Up @@ -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
Expand All @@ -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)
Expand Down
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