FastAPI-fastkit 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. fastapi_fastkit/__init__.py +10 -0
  2. fastapi_fastkit/__main__.py +8 -0
  3. fastapi_fastkit/backend.py +112 -0
  4. fastapi_fastkit/cli.py +432 -0
  5. fastapi_fastkit/core/__init__.py +0 -0
  6. fastapi_fastkit/core/exceptions.py +27 -0
  7. fastapi_fastkit/core/settings.py +107 -0
  8. fastapi_fastkit/fastapi_project_template/PROJECT_README_TEMPLATE.md +66 -0
  9. fastapi_fastkit/fastapi_project_template/README.md +20 -0
  10. fastapi_fastkit/fastapi_project_template/__init__.py +0 -0
  11. fastapi_fastkit/fastapi_project_template/fastapi-default/.env.test-tpl +3 -0
  12. fastapi_fastkit/fastapi_project_template/fastapi-default/.gitignore-tpl +191 -0
  13. fastapi_fastkit/fastapi_project_template/fastapi-default/README.md-tpl +17 -0
  14. fastapi_fastkit/fastapi_project_template/fastapi-default/main.py-tpl +21 -0
  15. fastapi_fastkit/fastapi_project_template/fastapi-default/requirements.txt-tpl +47 -0
  16. fastapi_fastkit/fastapi_project_template/fastapi-default/script/run-server.sh-tpl +2 -0
  17. fastapi_fastkit/fastapi_project_template/fastapi-default/script/run-test.sh-tpl +2 -0
  18. fastapi_fastkit/fastapi_project_template/fastapi-default/setup.cfg-tpl +13 -0
  19. fastapi_fastkit/fastapi_project_template/fastapi-default/setup.py-tpl +35 -0
  20. fastapi_fastkit/fastapi_project_template/fastapi-default/src/.DS_Store +0 -0
  21. fastapi_fastkit/fastapi_project_template/fastapi-default/src/__init__.py-tpl +88 -0
  22. fastapi_fastkit/fastapi_project_template/fastapi-default/src/core/__init__.py-tpl +0 -0
  23. fastapi_fastkit/fastapi_project_template/fastapi-default/src/core/settings.py-tpl +96 -0
  24. fastapi_fastkit/fastapi_project_template/fastapi-default/src/crud/__init__.py-tpl +0 -0
  25. fastapi_fastkit/fastapi_project_template/fastapi-default/src/crud/_base.py-tpl +92 -0
  26. fastapi_fastkit/fastapi_project_template/fastapi-default/src/crud/user.py-tpl +44 -0
  27. fastapi_fastkit/fastapi_project_template/fastapi-default/src/helper/__init__.py-tpl +0 -0
  28. fastapi_fastkit/fastapi_project_template/fastapi-default/src/helper/exceptions.py-tpl +94 -0
  29. fastapi_fastkit/fastapi_project_template/fastapi-default/src/helper/global_data.py-tpl +33 -0
  30. fastapi_fastkit/fastapi_project_template/fastapi-default/src/helper/logging.py-tpl +25 -0
  31. fastapi_fastkit/fastapi_project_template/fastapi-default/src/helper/pagination.py-tpl +71 -0
  32. fastapi_fastkit/fastapi_project_template/fastapi-default/src/mocks/__init__.py-tpl +0 -0
  33. fastapi_fastkit/fastapi_project_template/fastapi-default/src/mocks/mock_users.json-tpl +17 -0
  34. fastapi_fastkit/fastapi_project_template/fastapi-default/src/router/__init__.py-tpl +48 -0
  35. fastapi_fastkit/fastapi_project_template/fastapi-default/src/router/user.py-tpl +126 -0
  36. fastapi_fastkit/fastapi_project_template/fastapi-default/src/schemas/__init__.py-tpl +48 -0
  37. fastapi_fastkit/fastapi_project_template/fastapi-default/src/schemas/user.py-tpl +81 -0
  38. fastapi_fastkit/fastapi_project_template/fastapi-default/src/templates/index.html-tpl +27 -0
  39. fastapi_fastkit/fastapi_project_template/fastapi-default/src/utils/__init__.py-tpl +0 -0
  40. fastapi_fastkit/fastapi_project_template/fastapi-default/src/utils/documents.py-tpl +20 -0
  41. fastapi_fastkit/fastapi_project_template/fastapi-default/test/__init__.py-tpl +0 -0
  42. fastapi_fastkit/fastapi_project_template/fastapi-default/test/conftest.py-tpl +22 -0
  43. fastapi_fastkit/fastapi_project_template/fastapi-default/test/routes/__init__.py-tpl +0 -0
  44. fastapi_fastkit/fastapi_project_template/fastapi-default/test/routes/test_user.py-tpl +112 -0
  45. fastapi_fastkit/fastapi_project_template/fastapi-dockerized/__init__.py-tpl +0 -0
  46. fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/__init__.py-tpl +0 -0
  47. fastapi_fastkit/py.typed +0 -0
  48. fastapi_fastkit/utils/__init__.py +0 -0
  49. fastapi_fastkit/utils/inspector.py +15 -0
  50. fastapi_fastkit/utils/logging.py +33 -0
  51. fastapi_fastkit/utils/transducer.py +70 -0
  52. fastapi_fastkit-0.1.0.dist-info/METADATA +46 -0
  53. fastapi_fastkit-0.1.0.dist-info/RECORD +56 -0
  54. fastapi_fastkit-0.1.0.dist-info/WHEEL +4 -0
  55. fastapi_fastkit-0.1.0.dist-info/entry_points.txt +5 -0
  56. fastapi_fastkit-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,10 @@
1
+ __version__ = "0.0.1"
2
+
3
+ import os
4
+
5
+ from rich.console import Console
6
+
7
+ if "PYTEST_CURRENT_TEST" in os.environ:
8
+ console = Console(no_color=True)
9
+ else:
10
+ console = Console()
@@ -0,0 +1,8 @@
1
+ # --------------------------------------------------------------------------
2
+ # FastAPI-fastkit package main routine
3
+ #
4
+ # @author bnbong
5
+ # --------------------------------------------------------------------------
6
+ from fastapi_fastkit.cli import fastkit_cli
7
+
8
+ fastkit_cli()
@@ -0,0 +1,112 @@
1
+ # --------------------------------------------------------------------------
2
+ # The Module defines backend operations for FastAPI-fastkit CLI.
3
+ #
4
+ # @author bnbong bbbong9@gmail.com
5
+ # --------------------------------------------------------------------------
6
+ import os
7
+ import re
8
+ from logging import getLogger
9
+ from typing import Any
10
+
11
+ import click
12
+ from click.core import Context
13
+ from rich.panel import Panel
14
+ from rich.table import Table
15
+ from rich.text import Text
16
+
17
+ from fastapi_fastkit.core.exceptions import TemplateExceptions
18
+
19
+ from . import console
20
+
21
+ logger = getLogger(__name__)
22
+
23
+ REGEX = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b"
24
+
25
+
26
+ def print_error(message: str, title: str = "Error") -> None:
27
+ """Print an error message with specified output style."""
28
+ error_text = Text()
29
+ error_text.append("❌ ", style="bold red")
30
+ error_text.append(message)
31
+ console.print(Panel(error_text, border_style="red", title=title))
32
+
33
+
34
+ def print_success(message: str, title: str = "Success") -> None:
35
+ """Print a success message with specified output style."""
36
+ success_text = Text()
37
+ success_text.append("✨ ", style="bold yellow")
38
+ success_text.append(message, style="bold green")
39
+ console.print(Panel(success_text, border_style="green", title=title))
40
+
41
+
42
+ def print_warning(message: str, title: str = "Warning") -> None:
43
+ """Print a warning message with specified output style."""
44
+ warning_text = Text()
45
+ warning_text.append("⚠️ ", style="bold yellow")
46
+ warning_text.append(message)
47
+ console.print(Panel(warning_text, border_style="yellow", title=title))
48
+
49
+
50
+ def create_info_table(
51
+ title: str, data: dict[str, str], show_header: bool = False
52
+ ) -> Table:
53
+ """Create a table for displaying information."""
54
+ table = Table(title=title, show_header=show_header, title_style="bold magenta")
55
+ table.add_column("Field", style="cyan")
56
+ table.add_column("Value", style="green")
57
+
58
+ for key, value in data.items():
59
+ table.add_row(key, value)
60
+
61
+ return table
62
+
63
+
64
+ def validate_email(ctx: Context, param: Any, value: Any) -> Any:
65
+ """Validate email format."""
66
+ try:
67
+ if not re.match(REGEX, value):
68
+ raise ValueError(value)
69
+ return value
70
+ except ValueError as e:
71
+ print_error(f"Incorrect email address given: {e}")
72
+ value = click.prompt(param.prompt)
73
+ return validate_email(ctx, param, value)
74
+
75
+
76
+ def inject_project_metadata(
77
+ target_dir: str,
78
+ project_name: str,
79
+ author: str,
80
+ author_email: str,
81
+ description: str,
82
+ ) -> None:
83
+ """Inject project metadata."""
84
+ try:
85
+ main_py_path = os.path.join(target_dir, "main.py")
86
+ setup_py_path = os.path.join(target_dir, "setup.py")
87
+
88
+ with open(main_py_path, "r+") as f:
89
+ content = f.read()
90
+ content = content.replace("app_title", f'"{project_name}"')
91
+ content = content.replace("app_description", f'"{description}"')
92
+ f.seek(0)
93
+ f.write(content)
94
+ f.truncate()
95
+
96
+ with open(setup_py_path, "r+") as f:
97
+ content = f.read()
98
+ content = content.replace("<project_name>", project_name, 1)
99
+ content = content.replace("<description>", description, 1)
100
+ content = content.replace("<author>", author, 1)
101
+ content = content.replace("<author_email>", author_email, 1)
102
+ f.seek(0)
103
+ f.write(content)
104
+ f.truncate()
105
+ except Exception as e:
106
+ print_error(f"Error during metadata injection: {e}")
107
+ raise TemplateExceptions("Failed to inject metadata")
108
+
109
+
110
+ # TODO : modify this function
111
+ # def read_template_stack() -> Union[list, None]:
112
+ # pass
fastapi_fastkit/cli.py ADDED
@@ -0,0 +1,432 @@
1
+ # --------------------------------------------------------------------------
2
+ # The Module defines main and core CLI operations for FastAPI-fastkit.
3
+ #
4
+ # @author bnbong bbbong9@gmail.com
5
+ # --------------------------------------------------------------------------
6
+ import os
7
+ import shutil
8
+ import subprocess
9
+ from logging import getLogger
10
+ from typing import Union
11
+
12
+ import click
13
+ from click.core import BaseCommand, Context
14
+ from rich import print
15
+ from rich.panel import Panel
16
+
17
+ from fastapi_fastkit.core.exceptions import CLIExceptions
18
+ from fastapi_fastkit.core.settings import FastkitConfig
19
+ from fastapi_fastkit.utils.inspector import delete_project
20
+ from fastapi_fastkit.utils.logging import setup_logging
21
+ from fastapi_fastkit.utils.transducer import copy_and_convert_template
22
+
23
+ from . import __version__, console
24
+ from .backend import (
25
+ create_info_table,
26
+ inject_project_metadata,
27
+ print_error,
28
+ print_success,
29
+ print_warning,
30
+ validate_email,
31
+ )
32
+
33
+ logger = getLogger(__name__)
34
+
35
+
36
+ @click.group()
37
+ @click.option("--debug/--no-debug", default=False)
38
+ @click.version_option(__version__, prog_name="fastapi-fastkit")
39
+ @click.pass_context
40
+ def fastkit_cli(ctx: Context, debug: bool) -> Union["BaseCommand", None]:
41
+ """
42
+ main FastAPI-fastkit CLI operation group
43
+
44
+ :param ctx: context of passing configurations (NOT specify it at CLI)
45
+ :type ctx: <Object click.Context>
46
+ :param debug: parameter from CLI
47
+ :return: None(will be wrapped with click.core.BaseCommand via @click decorator)
48
+ """
49
+ settings = FastkitConfig()
50
+
51
+ ctx.ensure_object(dict)
52
+
53
+ if debug:
54
+ print_warning("running at debugging mode!!")
55
+ settings.set_debug_mode()
56
+
57
+ ctx.obj["settings"] = settings
58
+
59
+ setup_logging(settings=settings)
60
+
61
+ return None
62
+
63
+
64
+ @fastkit_cli.command()
65
+ @click.pass_context
66
+ def echo(ctx: Context) -> None:
67
+ """
68
+ About FastAPI-fastkit
69
+
70
+ :param ctx: context of passing configurations (NOT specify it at CLI)
71
+ :type ctx: <Object click.Context>
72
+ :return: None
73
+ """
74
+ fastkit_info = f"""
75
+ ⚡️ FastAPI fastkit - fastest [bold]FastAPI[/bold] initializer. ⚡️
76
+
77
+ Deploy FastAPI app foundation instantly at your local!
78
+
79
+ ---
80
+ - Project Maintainer : [link=mailto:bbbong9@gmail.com]bnbong(JunHyeok Lee)[/link]
81
+ - Current Version : {__version__}
82
+ - Github : [link]https://github.com/bnbong/FastAPI-fastkit[/link]
83
+ """
84
+ settings = ctx.obj["settings"]
85
+ description_panel = Panel(fastkit_info, title="About FastAPI-fastkit")
86
+ click.echo(print(description_panel))
87
+
88
+ if settings.DEBUG_MODE:
89
+ debug_output = f"FASTKIT_PROJECT_ROOT: {settings.FASTKIT_PROJECT_ROOT}\nUSER_WORKSPACE: {settings.USER_WORKSPACE}"
90
+
91
+ click.echo(debug_output)
92
+
93
+
94
+ @fastkit_cli.command()
95
+ def list_templates() -> None:
96
+ """
97
+ Display the list of available templates.
98
+ """
99
+ settings = FastkitConfig()
100
+ template_dir = settings.FASTKIT_TEMPLATE_ROOT
101
+
102
+ if not os.path.exists(template_dir):
103
+ print_error("Template directory not found.")
104
+ return
105
+
106
+ templates = [
107
+ d
108
+ for d in os.listdir(template_dir)
109
+ if os.path.isdir(os.path.join(template_dir, d)) and d != "__pycache__"
110
+ ]
111
+
112
+ if not templates:
113
+ print_warning("No available templates.")
114
+ return
115
+
116
+ table = create_info_table(
117
+ "Available Templates", {template: "No description" for template in templates}
118
+ )
119
+
120
+ for template in templates:
121
+ template_path = os.path.join(template_dir, template)
122
+ readme_path = os.path.join(template_path, "README.md-tpl")
123
+
124
+ description = "No description"
125
+ if os.path.exists(readme_path):
126
+ with open(readme_path, "r") as f:
127
+ first_line = f.readline().strip()
128
+ if first_line.startswith("# "):
129
+ description = first_line[2:]
130
+
131
+ table.add_row(template, description)
132
+
133
+ console.print(table)
134
+
135
+
136
+ @fastkit_cli.command(context_settings={"ignore_unknown_options": True})
137
+ @click.argument("template", default="fastapi-default")
138
+ @click.option(
139
+ "--project-name",
140
+ prompt="Enter the project name",
141
+ help="The name of the new FastAPI project.",
142
+ )
143
+ @click.option(
144
+ "--author", prompt="Enter the author name", help="The name of the project author."
145
+ )
146
+ @click.option(
147
+ "--author-email",
148
+ prompt="Enter the author email",
149
+ help="The email of the project author.",
150
+ type=str,
151
+ callback=validate_email,
152
+ )
153
+ @click.option(
154
+ "--description",
155
+ prompt="Enter the project description",
156
+ help="The description of the new FastAPI project.",
157
+ )
158
+ @click.pass_context
159
+ def startup(
160
+ ctx: Context,
161
+ template: str,
162
+ project_name: str,
163
+ author: str,
164
+ author_email: str,
165
+ description: str,
166
+ ) -> None:
167
+ """
168
+ Create a new FastAPI project from templates and inject metadata.
169
+
170
+ :param ctx: Click context object
171
+ :param template: Template name
172
+ :param project_name: Project name for the new project
173
+ :param author: Author name
174
+ :param author_email: Author email
175
+ :param description: Project description
176
+ :return: None
177
+ """
178
+ settings = ctx.obj["settings"]
179
+
180
+ template_dir = settings.FASTKIT_TEMPLATE_ROOT
181
+ click.echo(f"Deploying FastAPI project using '{template}' template")
182
+ target_template = os.path.join(template_dir, template)
183
+ print(f"Template path: {target_template}")
184
+
185
+ if not os.path.exists(target_template):
186
+ print_error(f"Template '{template}' does not exist in '{template_dir}'.")
187
+ raise CLIExceptions(
188
+ f"Template '{template}' does not exist in '{template_dir}'."
189
+ )
190
+ table = create_info_table(
191
+ "Project Information",
192
+ {
193
+ "Project Name": project_name,
194
+ "Author": author,
195
+ "Author Email": author_email,
196
+ "Description": description,
197
+ },
198
+ )
199
+
200
+ console.print("\n")
201
+ console.print(table)
202
+ # click.echo("Project Stack: [FastAPI, Uvicorn, SQLAlchemy, Docker (optional)]") # TODO : impl this?
203
+
204
+ confirm = click.confirm(
205
+ "\nDo you want to proceed with project creation?", default=False
206
+ )
207
+ if not confirm:
208
+ print_error("Project creation aborted!")
209
+ return
210
+
211
+ try:
212
+ user_local = settings.USER_WORKSPACE
213
+ project_dir = os.path.join(user_local, project_name)
214
+
215
+ click.echo(f"FastAPI template project will deploy at '{user_local}'")
216
+
217
+ copy_and_convert_template(target_template, user_local, project_name)
218
+
219
+ inject_project_metadata(
220
+ project_dir, project_name, author, author_email, description
221
+ )
222
+
223
+ print_success(
224
+ f"FastAPI project '{project_name}' from '{template}' has been created and saved to {user_local}!"
225
+ )
226
+
227
+ except Exception as e:
228
+ print_error(f"Error during project creation: {e}")
229
+
230
+
231
+ @fastkit_cli.command(context_settings={"ignore_unknown_options": True})
232
+ @click.option(
233
+ "--project-name",
234
+ prompt="Enter project name",
235
+ help="Name of the new FastAPI project",
236
+ )
237
+ @click.option(
238
+ "--stack",
239
+ type=click.Choice(["minimal", "standard", "full"]),
240
+ prompt="Select stack",
241
+ help="Project stack configuration",
242
+ )
243
+ def startproject(project_name: str, stack: str) -> None:
244
+ """
245
+ Start a new FastAPI project.
246
+ Dependencies will be automatically installed based on the selected stack.
247
+
248
+ :param project_name: Project name
249
+ :param stack: Project stack configuration
250
+ :return: None
251
+ """
252
+ settings = FastkitConfig()
253
+ project_dir = os.path.join(settings.USER_WORKSPACE, project_name)
254
+
255
+ if os.path.exists(project_dir):
256
+ print_error(f"Error: Project '{project_name}' already exists.")
257
+ return
258
+
259
+ try:
260
+ os.makedirs(project_dir)
261
+
262
+ table = create_info_table(
263
+ f"Creating Project: {project_name}", {"Component": "Status"}
264
+ )
265
+
266
+ dependencies = {
267
+ "minimal": ["fastapi", "uvicorn"],
268
+ "standard": ["fastapi", "uvicorn", "sqlalchemy", "alembic", "pytest"],
269
+ "full": [
270
+ "fastapi",
271
+ "uvicorn",
272
+ "sqlalchemy",
273
+ "alembic",
274
+ "pytest",
275
+ "redis",
276
+ "celery",
277
+ "docker-compose",
278
+ ],
279
+ }
280
+
281
+ with open(os.path.join(project_dir, "requirements.txt"), "w") as f:
282
+ for dep in dependencies[stack]:
283
+ f.write(f"{dep}\n")
284
+ table.add_row(dep, "✓")
285
+
286
+ console.print(table)
287
+
288
+ with console.status("[bold green]Setting up project environment..."):
289
+ console.print("[yellow]Creating virtual environment...[/yellow]")
290
+ subprocess.run(["python", "-m", "venv", os.path.join(project_dir, "venv")])
291
+
292
+ console.print("[yellow]Installing dependencies...[/yellow]")
293
+ subprocess.run(
294
+ ["pip", "install", "-r", "requirements.txt"], cwd=project_dir
295
+ )
296
+
297
+ print_success(f"Project '{project_name}' has been created successfully!")
298
+
299
+ except Exception as e:
300
+ print_error(f"Error during project creation: {e}")
301
+ shutil.rmtree(project_dir, ignore_errors=True)
302
+
303
+
304
+ def is_fastkit_project(project_dir: str) -> bool:
305
+ """
306
+ Check if the project was created with fastkit.
307
+ Inspects the contents of the setup.py file.
308
+
309
+ :param project_dir: Project directory
310
+ :return: True if the project was created with fastkit, False otherwise
311
+ """
312
+ setup_py = os.path.join(project_dir, "setup.py")
313
+ if not os.path.exists(setup_py):
314
+ return False
315
+
316
+ try:
317
+ with open(setup_py, "r") as f:
318
+ content = f.read()
319
+ return "FastAPI-fastkit" in content
320
+ except:
321
+ return False
322
+
323
+
324
+ @fastkit_cli.command()
325
+ @click.argument("project_name")
326
+ @click.pass_context
327
+ def deleteproject(ctx: Context, project_name: str) -> None:
328
+ """
329
+ Delete a FastAPI project.
330
+
331
+ :param ctx: Click context object
332
+ :param project_name: Project name
333
+ :return: None
334
+ """
335
+ settings = ctx.obj["settings"]
336
+ user_local = settings.USER_WORKSPACE
337
+ project_dir = os.path.join(user_local, project_name)
338
+
339
+ if not os.path.exists(project_dir):
340
+ print_error(f"Project '{project_name}' does not exist in '{user_local}'.")
341
+ return
342
+
343
+ if not is_fastkit_project(project_dir):
344
+ print_error(f"'{project_name}' is not a FastAPI-fastkit project.")
345
+ return
346
+
347
+ confirm = click.confirm(
348
+ f"\nDo you want to delete project '{project_name}' at '{project_dir}'?",
349
+ default=False,
350
+ )
351
+ if not confirm:
352
+ print_error("Project deletion cancelled!")
353
+ return
354
+
355
+ try:
356
+ delete_project(project_dir)
357
+ print_success(f"Project '{project_name}' has been deleted successfully!")
358
+
359
+ except Exception as e:
360
+ print_error(f"Error during project deletion: {e}")
361
+
362
+
363
+ @fastkit_cli.command()
364
+ @click.option(
365
+ "--host",
366
+ default="127.0.0.1",
367
+ show_default=True,
368
+ help="Host to bind the server",
369
+ )
370
+ @click.option(
371
+ "--port",
372
+ default=8000,
373
+ show_default=True,
374
+ help="Port to bind the server",
375
+ )
376
+ @click.option(
377
+ "--reload/--no-reload",
378
+ default=True,
379
+ show_default=True,
380
+ help="Enable/disable auto-reload on code changes",
381
+ )
382
+ @click.option(
383
+ "--workers",
384
+ default=1,
385
+ show_default=True,
386
+ help="Number of worker processes",
387
+ )
388
+ @click.pass_context
389
+ def runserver(
390
+ ctx: Context,
391
+ host: str = "127.0.0.1",
392
+ port: int = 8000,
393
+ reload: bool = True,
394
+ workers: int = 1,
395
+ ) -> None:
396
+ """
397
+ Run the FastAPI server for the current project.
398
+ [1.1.0 update TODO] Alternative Point : using FastAPI-fastkit's 'fastapi dev' command
399
+
400
+ :param ctx: Click context object
401
+ :param host: Host address to bind the server to
402
+ :param port: Port number to bind the server to
403
+ :param reload: Enable or disable auto-reload
404
+ :return: None
405
+ """
406
+ settings = ctx.obj["settings"]
407
+ project_dir = settings.USER_WORKSPACE
408
+
409
+ app_path = os.path.join(project_dir, "main.py")
410
+ if not os.path.exists(app_path):
411
+ print_error(f"Could not find 'main.py' in '{project_dir}'.")
412
+ return
413
+
414
+ command = [
415
+ "uvicorn",
416
+ "main:app",
417
+ "--host",
418
+ host,
419
+ "--port",
420
+ str(port),
421
+ "--workers",
422
+ str(workers),
423
+ ]
424
+
425
+ if reload:
426
+ command.append("--reload")
427
+
428
+ try:
429
+ print_success(f"Starting FastAPI server at {host}:{port}...")
430
+ subprocess.run(command, check=True)
431
+ except subprocess.CalledProcessError as e:
432
+ print_error(f"Failed to start FastAPI server.\n{e}")
File without changes
@@ -0,0 +1,27 @@
1
+ # --------------------------------------------------------------------------
2
+ # The Module defines exceptions that occurs from CLI operations.
3
+ #
4
+ # @author bnbong bbbong9@gmail.com
5
+ # --------------------------------------------------------------------------
6
+ class CLIExceptions(Exception):
7
+ """
8
+ Exceptions occurs from CLI operations
9
+ """
10
+
11
+ pass
12
+
13
+
14
+ class TemplateExceptions(Exception):
15
+ """
16
+ Exceptions occurs from deploying FastAPI templates
17
+ """
18
+
19
+ pass
20
+
21
+
22
+ class BackendExceptions(Exception):
23
+ """
24
+ Exceptions occurs from fastkit backend
25
+ """
26
+
27
+ pass
@@ -0,0 +1,107 @@
1
+ # --------------------------------------------------------------------------
2
+ # The Module defines fastapi-fastkit project's general Env settings.
3
+ #
4
+ # @author bnbong bbbong9@gmail.com
5
+ # --------------------------------------------------------------------------
6
+ import os
7
+ from pathlib import Path
8
+
9
+ from .exceptions import BackendExceptions
10
+
11
+
12
+ class FastkitConfig:
13
+ # Overridable values
14
+ FASTKIT_PROJECT_ROOT: str = "" # default : None (will be overridden)
15
+ FASTKIT_TEMPLATE_ROOT: str = "" # default : None (will be overridden)
16
+ LOG_FILE_PATH: str = "" # default : None (will be overridden)
17
+ USER_WORKSPACE: str = "" # default : None (will be overridden)
18
+
19
+ # Default Options
20
+ DEBUG_MODE: bool = False
21
+ LOGGING_LEVEL: str = "DEBUG"
22
+
23
+ # Testing Options
24
+ TEST_SERVER_PORT: int = 8000
25
+ TEST_DEFAULT_TERMINAL_WIDTH: int = 80
26
+ TEST_MAX_TERMINAL_WIDTH: int = 1000
27
+
28
+ def set_debug_mode(self, debug_mode: bool = True) -> None:
29
+ self.DEBUG_MODE = debug_mode
30
+
31
+ @staticmethod
32
+ def __get_fastapi_fastkit_root() -> Path:
33
+ """
34
+ Returns the root directory of the installed FastAPI-fastkit package.
35
+ For development: returns the project root
36
+ For installed package: returns the package installation directory
37
+ """
38
+ try:
39
+ import fastapi_fastkit
40
+
41
+ package_root = Path(fastapi_fastkit.__file__).parent.parent
42
+ if package_root.name == "site-packages":
43
+ return package_root / "fastapi_fastkit"
44
+ return package_root
45
+ except ImportError:
46
+ # Fallback for development environment
47
+ return Path(__file__).parent.parent.parent.parent
48
+
49
+ @staticmethod
50
+ def __get_template_root() -> Path:
51
+ """
52
+ Returns the template directory of the installed FastAPI-fastkit package.
53
+ For development: returns the project template directory
54
+ For installed package: returns the package template directory
55
+ """
56
+ try:
57
+ import fastapi_fastkit
58
+
59
+ package_root = Path(fastapi_fastkit.__file__).parent
60
+ template_dir = package_root / "fastapi_project_template"
61
+ if template_dir.exists():
62
+ return template_dir
63
+ # If inside site-packages
64
+ if package_root.parent.name == "site-packages":
65
+ return (
66
+ package_root.parent / "fastapi_fastkit" / "fastapi_project_template"
67
+ )
68
+ return package_root.parent / "fastapi_project_template"
69
+ except ImportError:
70
+ # Fallback for development environment
71
+ return Path(__file__).parent.parent / "fastapi_project_template"
72
+
73
+ @classmethod
74
+ def __init__(cls) -> None:
75
+ """
76
+ Initialize the configuration by performing important checks and setups.
77
+ Override directories to correct position.
78
+ """
79
+ cls.FASTKIT_PROJECT_ROOT = str(cls.__get_fastapi_fastkit_root())
80
+ cls.FASTKIT_TEMPLATE_ROOT = str(cls.__get_template_root())
81
+ cls.LOG_FILE_PATH = os.path.join(
82
+ cls.FASTKIT_PROJECT_ROOT, "logs", "fastkit.log"
83
+ )
84
+ cls.USER_WORKSPACE = os.getcwd()
85
+
86
+ # Validate the configurations
87
+ cls._validate()
88
+
89
+ @classmethod
90
+ def _validate(cls) -> None:
91
+ """
92
+ Validate the configuration settings to ensure that they are correct.
93
+ Raises an error if validation fails.
94
+ """
95
+ if not cls.FASTKIT_PROJECT_ROOT or not os.path.isdir(cls.FASTKIT_PROJECT_ROOT):
96
+ raise BackendExceptions(
97
+ "FASTKIT_PROJECT_ROOT is not allocated to valid directory."
98
+ )
99
+
100
+ if not cls.FASTKIT_TEMPLATE_ROOT or not os.path.isdir(cls.FASTKIT_PROJECT_ROOT):
101
+ raise BackendExceptions(
102
+ "FASTKIT_TEMPLATE_ROOT is not allocated to valid directory."
103
+ )
104
+
105
+ log_dir = os.path.dirname(cls.LOG_FILE_PATH)
106
+ if not os.path.exists(log_dir):
107
+ os.makedirs(log_dir)