1
- # TODO : make logic with click(cli operation) & rich(decorate console outputs and indicate manuals)
2
1
# --------------------------------------------------------------------------
3
2
# The Module defines main and core CLI operations for FastAPI-fastkit.
4
3
#
17
16
18
17
from rich import print
19
18
from rich .panel import Panel
19
+ from rich .console import Console
20
20
21
21
from . import __version__
22
- from .backend import validate_email , inject_project_metadata
22
+ from .backend import (
23
+ validate_email ,
24
+ inject_project_metadata ,
25
+ print_error ,
26
+ print_success ,
27
+ print_warning ,
28
+ create_info_table ,
29
+ )
23
30
from fastapi_fastkit .utils .logging import setup_logging
24
31
from fastapi_fastkit .core .settings import FastkitConfig
25
32
from fastapi_fastkit .core .exceptions import CLIExceptions
29
36
30
37
logger = getLogger (__name__ )
31
38
39
+ console = Console ()
40
+
32
41
33
42
@click .group ()
34
43
@click .option ("--debug/--no-debug" , default = False )
@@ -48,13 +57,7 @@ def fastkit_cli(ctx: Context, debug: bool) -> Union["BaseCommand", None]:
48
57
ctx .ensure_object (dict )
49
58
50
59
if debug :
51
- warning_panel = Panel (
52
- "running at debugging mode!!" ,
53
- title = "❗️ Warning ❗" ,
54
- style = "yellow" ,
55
- highlight = True ,
56
- )
57
- click .echo (print (warning_panel ))
60
+ print_warning ("running at debugging mode!!" )
58
61
settings .set_debug_mode ()
59
62
60
63
ctx .obj ["settings" ] = settings
@@ -98,27 +101,28 @@ def echo(ctx: Context) -> None:
98
101
def list_templates () -> None :
99
102
"""
100
103
Display the list of available templates.
101
-
102
- :return: None
103
104
"""
104
105
settings = FastkitConfig ()
105
106
template_dir = settings .FASTKIT_TEMPLATE_ROOT
106
107
107
108
if not os .path .exists (template_dir ):
108
- click . echo ("Template directory not found." )
109
+ print_error ("Template directory not found." )
109
110
return
110
111
111
112
templates = [
112
113
d
113
114
for d in os .listdir (template_dir )
114
- if os .path .isdir (os .path .join (template_dir , d ))
115
+ if os .path .isdir (os .path .join (template_dir , d )) and d != "__pycache__"
115
116
]
116
117
117
118
if not templates :
118
- click . echo ("No available templates." )
119
+ print_warning ("No available templates." )
119
120
return
120
121
121
- click .echo ("\n Available templates:" )
122
+ table = create_info_table (
123
+ "Available Templates" , {template : "No description" for template in templates }
124
+ )
125
+
122
126
for template in templates :
123
127
template_path = os .path .join (template_dir , template )
124
128
readme_path = os .path .join (template_path , "README.md-tpl" )
@@ -130,7 +134,9 @@ def list_templates() -> None:
130
134
if first_line .startswith ("# " ):
131
135
description = first_line [2 :]
132
136
133
- click .echo (f"- { template } : { description } " )
137
+ table .add_row (template , description )
138
+
139
+ console .print (table )
134
140
135
141
136
142
@fastkit_cli .command (context_settings = {"ignore_unknown_options" : True })
@@ -183,21 +189,29 @@ def startup(
183
189
print (f"Template path: { target_template } " )
184
190
185
191
if not os .path .exists (target_template ):
192
+ print_error (f"Template '{ template } ' does not exist in '{ template_dir } '." )
186
193
raise CLIExceptions (
187
- f"Error: Template '{ template } ' does not exist in '{ template_dir } '."
194
+ f"Template '{ template } ' does not exist in '{ template_dir } '."
188
195
)
189
- click .echo (f"\n Project Name: { project_name } " )
190
- click .echo (f"Author: { author } " )
191
- click .echo (f"Author Email: { author_email } " )
192
- click .echo (f"Description: { description } " )
193
- # read_template_stack()
196
+ table = create_info_table (
197
+ "Project Information" ,
198
+ {
199
+ "Project Name" : project_name ,
200
+ "Author" : author ,
201
+ "Author Email" : author_email ,
202
+ "Description" : description ,
203
+ },
204
+ )
205
+
206
+ console .print ("\n " )
207
+ console .print (table )
194
208
# click.echo("Project Stack: [FastAPI, Uvicorn, SQLAlchemy, Docker (optional)]") # TODO : impl this?
195
209
196
210
confirm = click .confirm (
197
211
"\n Do you want to proceed with project creation?" , default = False
198
212
)
199
213
if not confirm :
200
- click . echo ("Project creation aborted!" )
214
+ print_error ("Project creation aborted!" )
201
215
return
202
216
203
217
try :
@@ -212,11 +226,12 @@ def startup(
212
226
project_dir , project_name , author , author_email , description
213
227
)
214
228
215
- click . echo (
229
+ print_success (
216
230
f"FastAPI project '{ project_name } ' from '{ template } ' has been created and saved to { user_local } !"
217
231
)
232
+
218
233
except Exception as e :
219
- click . echo (f"Error during project creation: { e } " )
234
+ print_error (f"Error during project creation: { e } " )
220
235
221
236
222
237
@fastkit_cli .command (context_settings = {"ignore_unknown_options" : True })
@@ -244,12 +259,16 @@ def startproject(project_name: str, stack: str) -> None:
244
259
project_dir = os .path .join (settings .USER_WORKSPACE , project_name )
245
260
246
261
if os .path .exists (project_dir ):
247
- click . echo (f"Error: Project '{ project_name } ' already exists." )
262
+ print_error (f"Error: Project '{ project_name } ' already exists." )
248
263
return
249
264
250
265
try :
251
266
os .makedirs (project_dir )
252
267
268
+ table = create_info_table (
269
+ f"Creating Project: { project_name } " , {"Component" : "Status" }
270
+ )
271
+
253
272
dependencies = {
254
273
"minimal" : ["fastapi" , "uvicorn" ],
255
274
"standard" : ["fastapi" , "uvicorn" , "sqlalchemy" , "alembic" , "pytest" ],
@@ -268,15 +287,23 @@ def startproject(project_name: str, stack: str) -> None:
268
287
with open (os .path .join (project_dir , "requirements.txt" ), "w" ) as f :
269
288
for dep in dependencies [stack ]:
270
289
f .write (f"{ dep } \n " )
290
+ table .add_row (dep , "✓" )
271
291
272
- click .echo ("Creating virtual environment and installing dependencies..." )
273
- subprocess .run (["python" , "-m" , "venv" , os .path .join (project_dir , "venv" )])
274
- subprocess .run (["pip" , "install" , "-r" , "requirements.txt" ], cwd = project_dir )
292
+ console .print (table )
275
293
276
- click .echo (f"Project '{ project_name } ' has been created successfully!" )
294
+ with console .status ("[bold green]Setting up project environment..." ):
295
+ console .print ("[yellow]Creating virtual environment...[/yellow]" )
296
+ subprocess .run (["python" , "-m" , "venv" , os .path .join (project_dir , "venv" )])
297
+
298
+ console .print ("[yellow]Installing dependencies...[/yellow]" )
299
+ subprocess .run (
300
+ ["pip" , "install" , "-r" , "requirements.txt" ], cwd = project_dir
301
+ )
302
+
303
+ print_success (f"Project '{ project_name } ' has been created successfully!" )
277
304
278
305
except Exception as e :
279
- click . echo (f"Error during project creation: { e } " )
306
+ print_error (f"Error during project creation: { e } " )
280
307
shutil .rmtree (project_dir , ignore_errors = True )
281
308
282
309
@@ -316,28 +343,27 @@ def deleteproject(ctx: Context, project_name: str) -> None:
316
343
project_dir = os .path .join (user_local , project_name )
317
344
318
345
if not os .path .exists (project_dir ):
319
- click . echo (f"Error: Project '{ project_name } ' does not exist in '{ user_local } '." )
346
+ print_error (f"Project '{ project_name } ' does not exist in '{ user_local } '." )
320
347
return
321
348
322
349
if not is_fastkit_project (project_dir ):
323
- click . echo (f"Error: '{ project_name } ' is not a FastAPI-fastkit project." )
350
+ print_error (f"'{ project_name } ' is not a FastAPI-fastkit project." )
324
351
return
325
352
326
353
confirm = click .confirm (
327
354
f"\n Do you want to delete project '{ project_name } ' at '{ project_dir } '?" ,
328
355
default = False ,
329
356
)
330
357
if not confirm :
331
- click . echo ("Project deletion cancelled!" )
358
+ print_error ("Project deletion cancelled!" )
332
359
return
333
360
334
361
try :
335
362
delete_project (project_dir )
336
- click .echo (
337
- f"Project '{ project_name } ' has been successfully deleted from '{ user_local } '."
338
- )
363
+ print_success (f"Project '{ project_name } ' has been deleted successfully!" )
364
+
339
365
except Exception as e :
340
- click . echo (f"Error during project deletion: { e } " )
366
+ print_error (f"Error during project deletion: { e } " )
341
367
342
368
343
369
@fastkit_cli .command ()
@@ -388,7 +414,7 @@ def runserver(
388
414
389
415
app_path = os .path .join (project_dir , "main.py" )
390
416
if not os .path .exists (app_path ):
391
- click . echo (f"Error: Could not find 'main.py' in '{ project_dir } '." )
417
+ print_error (f"Could not find 'main.py' in '{ project_dir } '." )
392
418
return
393
419
394
420
command = [
@@ -406,7 +432,7 @@ def runserver(
406
432
command .append ("--reload" )
407
433
408
434
try :
409
- click . echo (f"Starting FastAPI server at { host } :{ port } ..." )
435
+ print_success (f"Starting FastAPI server at { host } :{ port } ..." )
410
436
subprocess .run (command , check = True )
411
437
except subprocess .CalledProcessError as e :
412
- click . echo (f"Error: Failed to start FastAPI server.\n { e } " )
438
+ print_error (f"Failed to start FastAPI server.\n { e } " )
0 commit comments