init-app 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 (35) hide show
  1. create_app/__init__.py +101 -0
  2. create_app/templates/__init__.py +0 -0
  3. create_app/templates/bottle/__init__.py +0 -0
  4. create_app/templates/bottle/minimal/__init__.py +0 -0
  5. create_app/templates/bottle/minimal/structure.py +60 -0
  6. create_app/templates/bottle/production/__init__.py +0 -0
  7. create_app/templates/bottle/production/structure.py +173 -0
  8. create_app/templates/django/__init__.py +0 -0
  9. create_app/templates/django/drf/__init__.py +0 -0
  10. create_app/templates/django/drf/structure.py +152 -0
  11. create_app/templates/django/minimal/__init__.py +0 -0
  12. create_app/templates/django/minimal/structure.py +140 -0
  13. create_app/templates/pyramid/__init__.py +0 -0
  14. create_app/templates/pyramid/minimal/__init__.py +0 -0
  15. create_app/templates/pyramid/minimal/structure.py +60 -0
  16. create_app/templates/pyramid/production/__init__.py +0 -0
  17. create_app/templates/pyramid/production/structure.py +169 -0
  18. create_app/templates/tornado/__init__.py +0 -0
  19. create_app/templates/tornado/minimal/__init__.py +0 -0
  20. create_app/templates/tornado/minimal/structure.py +70 -0
  21. create_app/templates/tornado/production/__init__.py +0 -0
  22. create_app/templates/tornado/production/structure.py +144 -0
  23. create_app/ui/__init__.py +0 -0
  24. create_app/ui/loader.py +62 -0
  25. create_app/ui/logger.py +47 -0
  26. create_app/ui/prompts.py +39 -0
  27. init_app-0.1.0.dist-info/METADATA +36 -0
  28. init_app-0.1.0.dist-info/RECORD +35 -0
  29. init_app-0.1.0.dist-info/WHEEL +5 -0
  30. init_app-0.1.0.dist-info/entry_points.txt +2 -0
  31. init_app-0.1.0.dist-info/licenses/LICENSE +0 -0
  32. init_app-0.1.0.dist-info/top_level.txt +2 -0
  33. tests/__init__.py +0 -0
  34. tests/test_full_matrix.py +83 -0
  35. tests/test_logic.py +44 -0
create_app/__init__.py ADDED
@@ -0,0 +1,101 @@
1
+ __version__ = "1.0.0"
2
+
3
+ APP_NAME = "py-create"
4
+ APP_TAGLINE = "Python Backend Project Generator"
5
+
6
+ # ✅ Supported Frameworks
7
+ FRAMEWORKS = [
8
+ "Python",
9
+ "Flask",
10
+ "FastAPI",
11
+ "Django",
12
+ "Bottle",
13
+ "Falcon",
14
+ "Tornado",
15
+ "Pyramid",
16
+ "Sanic",
17
+ ]
18
+
19
+ # ✅ Project Types for specialized Frameworks (Flask, FastAPI, etc.)
20
+ # We use "Standard" as the default high-quality starting point
21
+ PROJECT_STRUCTURES = ["Standard", "Production"]
22
+
23
+ STRUCTURE_DESCRIPTIONS = {
24
+ "Standard": "Clean, modern foundation with essential configurations",
25
+ "Production": "Enterprise-ready layout with tests, logs, and advanced scaling",
26
+ }
27
+
28
+ # ✅ Django Specifics
29
+ DJANGO_PROJECT_TYPES = ["Standard", "drf"]
30
+
31
+ DJANGO_DESCRIPTIONS = {
32
+ "Standard": "Full Django project with default configuration",
33
+ "drf": "Django project with REST Framework ready for API development",
34
+ }
35
+
36
+ # ✅ Python Specifics (The "Swiss Army Knife" category)
37
+ PYTHON_PROJECT_TYPES = [
38
+ "Standard", # Basic clean setup
39
+ "CLI Application", # Command-line tool structure
40
+ "Library", # PyPI-ready package structure
41
+ "ML Labs", # Data Science (TF, PyHive, MLflow)
42
+ ]
43
+
44
+ PYTHON_DESCRIPTIONS = {
45
+ "Standard": "Refined universal foundation with a clean structure",
46
+ "CLI Application": "Professional CLI tool structure (Click/Rich integrated)",
47
+ "Library": "Standardized PyPI-ready package structure (PEP 621)",
48
+ "ML Labs": "Modern Data Science lab (TensorFlow, PyHive, MLflow tracking)",
49
+ }
50
+
51
+ # ✅ Environment & Database
52
+ VENV_OPTIONS = ["Yes (Recommended)", "No"]
53
+
54
+ DATABASE_OPTIONS = ["None", "SQLAlchemy", "PostgreSQL", "MySQL", "MongoDB"]
55
+
56
+ DATABASE_DESCRIPTIONS = {
57
+ "None": "No database integration",
58
+ "SQLAlchemy": "Database toolkit / ORM (flexible backend support)",
59
+ "PostgreSQL": "Powerful production-grade relational database",
60
+ "MySQL": "Popular relational database",
61
+ "MongoDB": "NoSQL document database",
62
+ }
63
+
64
+ # ✅ Technical Configs
65
+ DEFAULT_PORTS = {
66
+ "Flask": "5000",
67
+ "FastAPI": "8000",
68
+ "Django": "8000",
69
+ "Sanic": "8000",
70
+ "Tornado": "8888",
71
+ "Falcon": "8000",
72
+ "Bottle": "8080",
73
+ "Pyramid": "6543",
74
+ }
75
+
76
+ # 🚀 LIFT CORE COMPONENTS (Public API)
77
+ from create_app.ui.loader import Spinner
78
+ from create_app.ui.prompts import ask_project_details
79
+ from create_app.cli.engine import ProjectEngine
80
+ from create_app.generator.generator import generate_project
81
+
82
+ # ✅ Public API Contract
83
+ __all__ = [
84
+ "APP_NAME",
85
+ "APP_TAGLINE",
86
+ "FRAMEWORKS",
87
+ "DJANGO_PROJECT_TYPES",
88
+ "DJANGO_DESCRIPTIONS",
89
+ "PROJECT_STRUCTURES",
90
+ "PYTHON_PROJECT_TYPES",
91
+ "PYTHON_DESCRIPTIONS",
92
+ "STRUCTURE_DESCRIPTIONS",
93
+ "VENV_OPTIONS",
94
+ "DATABASE_OPTIONS",
95
+ "DATABASE_DESCRIPTIONS",
96
+ "DEFAULT_PORTS",
97
+ "Spinner",
98
+ "ask_project_details",
99
+ "ProjectEngine",
100
+ "generate_project",
101
+ ]
File without changes
File without changes
File without changes
@@ -0,0 +1,60 @@
1
+ from pathlib import Path
2
+ from create_app.generator.renderer import render_template
3
+
4
+
5
+ def generate(project_root: Path, context: dict):
6
+ """
7
+ Bottle Minimal Structure Generator
8
+
9
+ Creates:
10
+
11
+ project/
12
+ ├── app.py
13
+ ├── __init__.py
14
+ ├── requirements.txt
15
+ ├── .env
16
+ ├── README.md
17
+ └── .gitignore
18
+ """
19
+
20
+ # ✅ Ensure project root exists
21
+ project_root.mkdir(parents=True, exist_ok=True)
22
+
23
+ # ✅ Entry Point (Rendered from Template)
24
+ render_template(
25
+ "bottle/minimal/entry.py.tpl",
26
+ project_root / "app.py",
27
+ context,
28
+ )
29
+
30
+ # ✅ Common Files
31
+
32
+ render_template(
33
+ "common/__init__.py.tpl",
34
+ project_root / "__init__.py",
35
+ context,
36
+ )
37
+
38
+ render_template(
39
+ "common/requirements.txt.tpl",
40
+ project_root / "requirements.txt",
41
+ context,
42
+ )
43
+
44
+ render_template(
45
+ "common/.env.tpl",
46
+ project_root / ".env",
47
+ context,
48
+ )
49
+
50
+ render_template(
51
+ "common/README.md.tpl",
52
+ project_root / "README.md",
53
+ context,
54
+ )
55
+
56
+ render_template(
57
+ "common/gitignore.tpl",
58
+ project_root / ".gitignore",
59
+ context,
60
+ )
File without changes
@@ -0,0 +1,173 @@
1
+ from pathlib import Path
2
+ import shutil
3
+
4
+ from create_app.generator.renderer import render_template
5
+
6
+
7
+ # ✅ TEMPLATE ROOT 😈🔥
8
+ TEMPLATE_ROOT = Path(__file__).parents[2]
9
+ # → create_app/templates/
10
+
11
+ TEMPLATES_UI_DIR = TEMPLATE_ROOT / "common" / "template" / "bottle"
12
+ STATIC_UI_DIR = TEMPLATE_ROOT / "common" / "static"
13
+
14
+
15
+ # ✅ Copy Shared UI 😈🔥
16
+ def copy_ui(project_root: Path):
17
+
18
+ shutil.copytree(
19
+ TEMPLATES_UI_DIR,
20
+ project_root / "views", # ✅ Bottle uses views
21
+ dirs_exist_ok=True,
22
+ )
23
+
24
+ shutil.copytree(
25
+ STATIC_UI_DIR,
26
+ project_root / "static",
27
+ dirs_exist_ok=True,
28
+ )
29
+
30
+
31
+ def generate(project_root: Path, context: dict):
32
+ """
33
+ Bottle Production Grade Generator 😈🔥
34
+ Clean layered architecture + Shared UI
35
+ """
36
+
37
+ project_root.mkdir(parents=True, exist_ok=True)
38
+
39
+ # ✅ Core Directory Layout
40
+ folders = [
41
+ "config",
42
+ "routes",
43
+ "services",
44
+ "models",
45
+ "schemas",
46
+ "middleware",
47
+ "utils",
48
+ "logs",
49
+ "tests",
50
+ "views",
51
+ "static",
52
+ ]
53
+
54
+ for folder in folders:
55
+ (project_root / folder).mkdir(exist_ok=True)
56
+
57
+ # ✅ Static Subfolders 👍
58
+ for folder in ["css", "js", "assets"]:
59
+ (project_root / "static" / folder).mkdir(parents=True, exist_ok=True)
60
+
61
+ # ✅ Python Packages 👍
62
+ for folder in [
63
+ "config",
64
+ "routes",
65
+ "services",
66
+ "models",
67
+ "schemas",
68
+ "middleware",
69
+ "utils",
70
+ "tests",
71
+ ]:
72
+ (project_root / folder / "__init__.py").touch()
73
+
74
+ # ✅ ENTRYPOINT 😈🔥
75
+ render_template(
76
+ "bottle/production/entry.py.tpl",
77
+ project_root / "app.py",
78
+ context,
79
+ )
80
+
81
+ # ✅ CONFIG FILE 👍
82
+ (project_root / "config" / "settings.py").write_text(
83
+ """
84
+ import os
85
+
86
+
87
+ class Settings:
88
+ debug = os.getenv("DEBUG", "True") == "True"
89
+ host = os.getenv("HOST", "127.0.0.1")
90
+ port = int(os.getenv("PORT", 8080))
91
+
92
+
93
+ settings = Settings()
94
+ """.strip() + "\n"
95
+ )
96
+
97
+ # ✅ ROUTES REGISTRY 👍
98
+ (project_root / "routes" / "__init__.py").write_text(
99
+ """
100
+ from .health import register_health
101
+ from .auth import register_auth
102
+ from .api import register_api
103
+
104
+
105
+ def register_routes(app):
106
+ register_health(app)
107
+ register_auth(app)
108
+ register_api(app)
109
+ """.strip() + "\n"
110
+ )
111
+
112
+ # ✅ ROUTES 👍
113
+
114
+ (project_root / "routes" / "health.py").write_text(
115
+ """
116
+ from bottle import response
117
+
118
+
119
+ def register_health(app):
120
+
121
+ @app.get("/health")
122
+ def health():
123
+ response.content_type = "application/json"
124
+ return {"status": "healthy"}
125
+ """.strip() + "\n"
126
+ )
127
+
128
+ (project_root / "routes" / "auth.py").write_text(
129
+ """
130
+ def register_auth(app):
131
+
132
+ @app.get("/auth")
133
+ def auth():
134
+ return {"message": "Auth route ready"}
135
+ """.strip() + "\n"
136
+ )
137
+
138
+ (project_root / "routes" / "api.py").write_text(
139
+ """
140
+ from bottle import template
141
+
142
+
143
+ def register_api(app):
144
+
145
+ @app.get("/")
146
+ def index():
147
+ return template("index") # ✅ Loads Shared UI index.tpl
148
+ """.strip() + "\n"
149
+ )
150
+
151
+ # ✅ PLACEHOLDERS 👍
152
+ (project_root / "services" / "example_service.py").touch()
153
+ (project_root / "models" / "example_model.py").touch()
154
+ (project_root / "schemas" / "example_schema.py").touch()
155
+ (project_root / "middleware" / "example_middleware.py").touch()
156
+ (project_root / "utils" / "helpers.py").touch()
157
+
158
+ # ✅ LOG FILE 👍
159
+ (project_root / "logs" / "app.log").touch()
160
+
161
+ # ✅ TEST FILE 👍
162
+ (project_root / "tests" / "test_health.py").touch()
163
+
164
+ # ✅ ⭐ COPY SHARED UI ⭐ 😈🔥
165
+ copy_ui(project_root)
166
+
167
+ # 🔥 COMMON FILES 🔥
168
+ render_template("common/requirements.txt.tpl", project_root / "requirements.txt", context)
169
+ render_template("common/.env.tpl", project_root / ".env", context)
170
+ render_template("common/README.md.tpl", project_root / "README.md", context)
171
+ render_template("common/gitignore.tpl", project_root / ".gitignore", context)
172
+
173
+ return project_root
File without changes
File without changes
@@ -0,0 +1,152 @@
1
+ import sys
2
+ import subprocess
3
+ import re
4
+ from pathlib import Path
5
+
6
+ from create_app.generator.renderer import render_template
7
+
8
+ # ✅ Ensure Django Installed 😈🔥
9
+ def ensure_django():
10
+ try:
11
+ subprocess.run(
12
+ [sys.executable, "-m", "django", "--version"],
13
+ stdout=subprocess.DEVNULL,
14
+ stderr=subprocess.DEVNULL,
15
+ check=True,
16
+ )
17
+ except subprocess.SubprocessError:
18
+ subprocess.run(
19
+ [sys.executable, "-m", "pip", "install", "django"],
20
+ check=True,
21
+ )
22
+
23
+
24
+ # ✅ Patch settings.py 😈🔥
25
+ def patch_settings(settings_path: Path, context: dict):
26
+ if not settings_path.exists():
27
+ return
28
+
29
+ content = settings_path.read_text()
30
+
31
+ # ✅ Ensure import os exists 😈🔥
32
+ if "import os" not in content:
33
+ content = re.sub(
34
+ r"(from pathlib import Path.*\n)",
35
+ r"\1import os\n",
36
+ content,
37
+ )
38
+
39
+ # ✅ Replace SECRET KEY / DEBUG / HOSTS
40
+ secret_block = render_template(
41
+ "django/drf/secret.tpl",
42
+ None,
43
+ context,
44
+ raw=True,
45
+ )
46
+
47
+ content = re.sub(
48
+ r"SECRET_KEY\s*=.*",
49
+ secret_block,
50
+ content,
51
+ )
52
+
53
+ # ✅ Inject Installed Apps
54
+ apps_block = render_template(
55
+ "django/drf/apps.py.tpl",
56
+ None,
57
+ context,
58
+ raw=True,
59
+ )
60
+
61
+ pattern = r"INSTALLED_APPS\s*=\s*\[(.*?)\]"
62
+ match = re.search(pattern, content, re.DOTALL)
63
+
64
+ if not match:
65
+ raise RuntimeError("INSTALLED_APPS not found")
66
+
67
+ existing = match.group(1).strip()
68
+ updated = existing + "\n" + apps_block
69
+
70
+ content = re.sub(
71
+ pattern,
72
+ f"INSTALLED_APPS = [\n{updated}\n]",
73
+ content,
74
+ flags=re.DOTALL,
75
+ )
76
+
77
+ # ✅ Append DRF Config
78
+ drf_config = render_template(
79
+ "django/drf/rf.py.tpl",
80
+ None,
81
+ context,
82
+ raw=True,
83
+ )
84
+
85
+ content += "\n\n" + drf_config
86
+ settings_path.write_text(content)
87
+
88
+
89
+ # ✅ Overwrite urls.py 😈🔥
90
+ def overwrite_urls(urls_path: Path, context: dict):
91
+ if not urls_path.exists():
92
+ return
93
+
94
+ urls_content = render_template(
95
+ "django/drf/urls.tpl",
96
+ None,
97
+ context,
98
+ raw=True,
99
+ )
100
+ urls_path.write_text(urls_content)
101
+
102
+
103
+ # ✅ MAIN GENERATOR 🚀
104
+ def generate(project_root: Path, context: dict):
105
+ project_name = context["project_name"]
106
+ app_name = context["app_name"]
107
+ base_path = project_root.parent
108
+
109
+ base_path.mkdir(parents=True, exist_ok=True)
110
+
111
+ ensure_django()
112
+
113
+ # ✅ Step 1 — Create Project 😈🔥
114
+ subprocess.run(
115
+ [sys.executable, "-m", "django", "startproject", project_name],
116
+ cwd=base_path,
117
+ check=True,
118
+ )
119
+
120
+ project_dir = base_path / project_name
121
+
122
+ # 🛡️ GUARD: Create folders if they don't exist (Fixes Pytest Mocks)
123
+ project_dir.mkdir(parents=True, exist_ok=True)
124
+ (project_dir / project_name).mkdir(exist_ok=True)
125
+
126
+ # ✅ Step 2 — Create App 👍
127
+ subprocess.run(
128
+ [sys.executable, "manage.py", "startapp", app_name],
129
+ cwd=project_dir,
130
+ check=True,
131
+ )
132
+
133
+ # 🛡️ GUARD: Ensure app folder exists for tests
134
+ (project_dir / app_name).mkdir(exist_ok=True)
135
+
136
+ # ✅ Step 3 — Patch Settings 😈🔥
137
+ settings_path = project_dir / project_name / "settings.py"
138
+ patch_settings(settings_path, context)
139
+
140
+ # ✅ Step 4 — Overwrite URLs 😈🔥
141
+ urls_path = project_dir / project_name / "urls.py"
142
+ overwrite_urls(urls_path, context)
143
+
144
+ # ✅ Step 5 — Common Files 🔥
145
+ render_template("common/requirements.txt.tpl", project_dir / "requirements.txt", context)
146
+ render_template("common/.env.tpl", project_dir / ".env", context)
147
+ render_template("common/README.md.tpl", project_dir / "README.md", context)
148
+
149
+ # Using the dot naming convention for consistency
150
+ render_template("common/gitignore.tpl", project_dir / ".gitignore", context)
151
+
152
+ return project_dir
File without changes
@@ -0,0 +1,140 @@
1
+ import sys
2
+ import subprocess
3
+ import re
4
+ import shutil
5
+ from pathlib import Path
6
+
7
+ from create_app.generator.renderer import render_template
8
+
9
+ TEMPLATE_DIR = Path(__file__).parent
10
+ TEMPLATE_ROOT = Path(__file__).resolve().parents[2]
11
+
12
+
13
+ TEMPLATES_UI_DIR = TEMPLATE_ROOT / "common" / "template" / "django"
14
+ STATIC_UI_DIR = TEMPLATE_ROOT / "common" / "static"
15
+
16
+
17
+ def load_dependencies():
18
+ dependency_file = TEMPLATE_DIR / "requirements.txt"
19
+ return dependency_file.read_text().strip() if dependency_file.exists() else ""
20
+
21
+
22
+ # ✅ Copy Shared UI 😈🔥
23
+ def copy_ui(project_dir: Path):
24
+ """Copies global templates and static files to the Django project root."""
25
+ templates_dest = project_dir / "templates"
26
+ static_dest = project_dir / "static"
27
+
28
+ if TEMPLATES_UI_DIR.exists():
29
+ shutil.copytree(TEMPLATES_UI_DIR, templates_dest, dirs_exist_ok=True)
30
+ else:
31
+ print(f"⚠ Shared Templates not found → {TEMPLATES_UI_DIR}")
32
+
33
+ if STATIC_UI_DIR.exists():
34
+ shutil.copytree(STATIC_UI_DIR, static_dest, dirs_exist_ok=True)
35
+
36
+
37
+ # ✅ Patch Django settings.py 😈🔥
38
+ def patch_settings(settings_path: Path, context: dict):
39
+ if not settings_path.exists():
40
+ return
41
+
42
+ content = settings_path.read_text()
43
+ app_name = context["app_name"]
44
+
45
+ # ✅ Ensure import os
46
+ if "import os" not in content:
47
+ content = content.replace(
48
+ "from pathlib import Path",
49
+ "from pathlib import Path\nimport os",
50
+ )
51
+
52
+ # ✅ Configure Templates DIR to look at root/templates
53
+ content = re.sub(
54
+ r"'DIRS': \[(.*?)\]",
55
+ "'DIRS': [BASE_DIR / 'templates'],",
56
+ content,
57
+ flags=re.DOTALL,
58
+ )
59
+
60
+ # ✅ Register Installed App
61
+ apps_pattern = r"INSTALLED_APPS\s*=\s*\[(.*?)\]"
62
+ match = re.search(apps_pattern, content, re.DOTALL)
63
+
64
+ if match:
65
+ existing_apps = match.group(1)
66
+ if f"'{app_name}'" not in existing_apps:
67
+ updated_apps = existing_apps.strip() + f"\n '{app_name}',\n"
68
+ content = re.sub(
69
+ apps_pattern,
70
+ f"INSTALLED_APPS = [\n {updated_apps}]",
71
+ content,
72
+ flags=re.DOTALL,
73
+ )
74
+
75
+ settings_path.write_text(content)
76
+
77
+
78
+ def generate(project_root: Path, context: dict):
79
+ """
80
+ Django Standard Generator 😈🔥
81
+ Now with Shared UI and Test-Safety Guards
82
+ """
83
+ project_name = context["project_name"]
84
+ app_name = context["app_name"]
85
+ base_path = project_root.parent
86
+
87
+ # ✅ Remove empty scaffold folder
88
+ if project_root.exists() and not any(project_root.iterdir()):
89
+ project_root.rmdir()
90
+
91
+ # ✅ 1. Create Django Project
92
+ subprocess.run(
93
+ [sys.executable, "-m", "django", "startproject", project_name],
94
+ cwd=base_path,
95
+ check=True,
96
+ )
97
+
98
+ # 🛡️ GUARD: Ensure project_dir exists for the next steps (needed for MOCK tests)
99
+ project_dir = base_path / project_name
100
+ project_dir.mkdir(parents=True, exist_ok=True)
101
+ (project_dir / project_name).mkdir(exist_ok=True)
102
+
103
+ # ✅ 2. Create Django App
104
+ subprocess.run(
105
+ [sys.executable, "manage.py", "startapp", app_name],
106
+ cwd=project_dir,
107
+ check=True,
108
+ )
109
+
110
+ # 🛡️ GUARD: Ensure app folder exists before writing files
111
+ app_dir = project_dir / app_name
112
+ app_dir.mkdir(exist_ok=True)
113
+
114
+ # ✅ 3. Patch Settings
115
+ settings_path = project_dir / project_name / "settings.py"
116
+ if settings_path.exists():
117
+ patch_settings(settings_path, context)
118
+
119
+ # ✅ 4. Setup View logic
120
+ views_file = app_dir / "views.py"
121
+ views_file.write_text(f"""from django.shortcuts import render
122
+
123
+ def index(request):
124
+ return render(request, 'index.html')
125
+ """)
126
+
127
+ # ✅ 5. Copy Shared UI (index.html, CSS, JS) 😈🔥
128
+ copy_ui(project_dir)
129
+
130
+ # ✅ 6. Common Project Files
131
+ context.update({"dependencies": load_dependencies(), "entrypoint": "manage.py"})
132
+
133
+ render_template("common/requirements.txt.tpl", project_dir / "requirements.txt", context)
134
+ render_template("common/.env.tpl", project_dir / ".env", context)
135
+ render_template("common/README.md.tpl", project_dir / "README.md", context)
136
+
137
+ # Ensure this matches your physical file: .gitignore.tpl
138
+ render_template("common/gitignore.tpl", project_dir / ".gitignore", context)
139
+
140
+ return project_dir
File without changes
File without changes