-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathservice.py
More file actions
212 lines (170 loc) · 7.34 KB
/
Copy pathservice.py
File metadata and controls
212 lines (170 loc) · 7.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
import os
import typing as t
from click import ClickException
from ellar.constants import ELLAR_CONFIG_MODULE, ELLAR_PY_PROJECT
from ellar.core import App, Config, ModuleBase
from ellar.helper.importer import import_from_string, module_import
from ellar.helper.module_loading import module_dir
from tomlkit import dumps as tomlkit_dumps, parse as tomlkit_parse, table
from tomlkit.items import Table
from ellar_cli.schema import EllarPyProjectSerializer
PY_PROJECT_TOML = "pyproject.toml"
ELLAR_DEFAULT_KEY = "default"
ELLAR_PROJECTS_KEY = "projects"
class EllarCLIException(ClickException):
pass
class EllarPyProject:
def __init__(self, ellar: Table = None) -> None:
self._ellar = ellar if ellar is not None else table()
self._projects = t.cast(
Table, self._ellar.setdefault(ELLAR_PROJECTS_KEY, table())
)
self._default_project = self._ellar.get(ELLAR_DEFAULT_KEY, None)
@classmethod
def get_or_create_ellar_py_project(
cls, py_project_table: Table
) -> "EllarPyProject":
ellar_py_project_table = py_project_table.setdefault(ELLAR_PY_PROJECT, table())
return cls(ellar_py_project_table)
@property
def has_default_project(self) -> bool:
return self._ellar.get(ELLAR_DEFAULT_KEY, None) is not None
@property
def default_project(self) -> str:
return self._ellar.get(ELLAR_DEFAULT_KEY, None)
@default_project.setter
def default_project(self, value: str) -> None:
self._ellar.update(default=value)
def get_projects(self) -> Table:
return self._projects
def get_project(self, project_name: str) -> Table:
return t.cast(Table, self._projects.get(project_name))
def get_or_create_project(self, project_name: str) -> Table:
project = self._projects.setdefault(project_name.lower(), table())
return t.cast(Table, project)
def has_project(self, project_name: t.Optional[str]) -> bool:
if not project_name:
return False
return project_name in self._projects
def get_root_node(self) -> Table:
return self._ellar
class EllarCLIService:
schema_cls: t.Type[EllarPyProjectSerializer] = EllarPyProjectSerializer
def __init__(
self,
*,
meta: t.Optional[EllarPyProjectSerializer] = None,
py_project_path: str,
cwd: str,
app_name: str = "ellar",
ellar_py_projects: EllarPyProject = None,
) -> None:
self._meta = meta
self.py_project_path = py_project_path
self.cwd = cwd
self.app = app_name
self.ellar_py_projects = ellar_py_projects or EllarPyProject()
@property
def has_meta(self) -> bool:
return self._meta is not None
@property
def project_meta(self) -> t.Optional[EllarPyProjectSerializer]:
return self._meta
@classmethod
def cwd_has_pyproject_file(cls) -> bool:
cwd = os.getcwd()
py_project_file_path = os.path.join(cwd, PY_PROJECT_TOML)
return os.path.exists(py_project_file_path)
@classmethod
def import_project_meta(
cls, project: t.Optional[str] = None
) -> t.Optional["EllarCLIService"]:
cwd = os.getcwd()
project_to_load: str = "ellar"
ellar_py_projects = None
py_project_file_path = os.path.join(cwd, PY_PROJECT_TOML)
if os.path.exists(py_project_file_path):
pyproject_table = EllarCLIService.read_py_project(py_project_file_path)
_ellar_pyproject_serializer: t.Optional[EllarPyProjectSerializer] = None
if pyproject_table and ELLAR_PY_PROJECT in pyproject_table:
ellar_py_projects = EllarPyProject(
pyproject_table.get(ELLAR_PY_PROJECT)
)
if ellar_py_projects.has_project(
project
) or ellar_py_projects.has_project(ellar_py_projects.default_project):
project_to_load = (
project # type: ignore
if ellar_py_projects.has_project(project)
else ellar_py_projects.default_project
)
_ellar_pyproject_serializer = cls.schema_cls.parse_obj(
ellar_py_projects.get_project(project_to_load)
)
return cls(
meta=_ellar_pyproject_serializer,
py_project_path=py_project_file_path,
cwd=cwd,
app_name=project_to_load,
ellar_py_projects=ellar_py_projects,
)
return None
def create_ellar_project_meta(self, project_name: str) -> None:
pyproject_table = EllarCLIService.read_py_project(self.py_project_path)
if pyproject_table is None:
raise EllarCLIException("Could not locate `pyproject.toml`")
ellar_py_project = self.ellar_py_projects.get_or_create_ellar_py_project(
pyproject_table
)
if ellar_py_project.has_project(project_name.lower()):
raise EllarCLIException(
f"project -> `{project_name}` already exist in ellar projects"
)
if not ellar_py_project.has_default_project:
ellar_py_project.default_project = project_name.lower()
ellar_new_project = ellar_py_project.get_or_create_project(project_name.lower())
ellar_new_project.add("project-name", project_name.lower())
ellar_new_project.add("application", f"{project_name}.server:application")
ellar_new_project.add("config", f"{project_name}.config:DevelopmentConfig")
ellar_new_project.add(
"root-module", f"{project_name}.root_module:ApplicationModule"
)
ellar_new_project.add("apps-module", f"{project_name}.apps")
# TODO: lock pyproject.toml file
EllarCLIService.write_py_project(self.py_project_path, pyproject_table)
@staticmethod
def read_py_project(path: str) -> t.Optional[Table]:
if os.path.exists(path):
with open(path, mode="r") as fp:
table_content = tomlkit_parse(fp.read())
return table_content
return None
@staticmethod
def write_py_project(path: str, content: Table) -> None:
with open(path, mode="w") as fw:
fw.writelines(tomlkit_dumps(content))
def import_application(self) -> "App":
assert self._meta
application_module = t.cast("App", import_from_string(self._meta.application))
return application_module
def import_configuration(self) -> t.Type["Config"]:
assert self._meta
config = t.cast(t.Type["Config"], import_from_string(self._meta.config))
return config
def get_application_config(self) -> "Config":
assert self._meta
config = Config(os.environ.get(ELLAR_CONFIG_MODULE, self._meta.config))
return config
def import_root_module(self) -> t.Type["ModuleBase"]:
assert self._meta
root_module = t.cast(
t.Type["ModuleBase"], import_from_string(self._meta.root_module)
)
return root_module
def import_apps_module(self) -> t.Any:
assert self._meta
apps_module = module_import(self._meta.apps_module)
return apps_module
def get_apps_module_path(self) -> str:
apps_module = self.import_apps_module()
return module_dir(apps_module) # type: ignore