viperx 0.9.14__py3-none-any.whl → 0.9.26__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.
- viperx/config_engine.py +1 -1
- viperx/constants.py +5 -0
- viperx/core.py +51 -15
- viperx/main.py +54 -32
- viperx/templates/config.py.j2 +2 -0
- viperx/templates/pyproject.toml.j2 +3 -6
- viperx/templates/viperx_config.yaml.j2 +1 -0
- viperx/utils.py +21 -6
- {viperx-0.9.14.dist-info → viperx-0.9.26.dist-info}/METADATA +1 -1
- {viperx-0.9.14.dist-info → viperx-0.9.26.dist-info}/RECORD +12 -12
- {viperx-0.9.14.dist-info → viperx-0.9.26.dist-info}/WHEEL +0 -0
- {viperx-0.9.14.dist-info → viperx-0.9.26.dist-info}/entry_points.txt +0 -0
viperx/config_engine.py
CHANGED
|
@@ -129,7 +129,7 @@ class ConfigEngine:
|
|
|
129
129
|
author=project_conf.get("author", "Your Name"), # Inherit author
|
|
130
130
|
use_env=pkg.get("use_env", settings_conf.get("use_env", False)), # Inherit settings or default False
|
|
131
131
|
use_config=pkg.get("use_config", settings_conf.get("use_config", True)), # Inherit or default True
|
|
132
|
-
use_readme=pkg.get("use_readme",
|
|
132
|
+
use_readme=pkg.get("use_readme", False),
|
|
133
133
|
use_tests=pkg.get("use_tests", settings_conf.get("use_tests", True)),
|
|
134
134
|
framework=pkg.get("framework", FRAMEWORK_PYTORCH),
|
|
135
135
|
verbose=self.verbose
|
viperx/constants.py
CHANGED
|
@@ -33,3 +33,8 @@ PROJECT_TYPES = [TYPE_CLASSIC, TYPE_ML, TYPE_DL]
|
|
|
33
33
|
FRAMEWORK_PYTORCH = "pytorch"
|
|
34
34
|
FRAMEWORK_TENSORFLOW = "tensorflow"
|
|
35
35
|
DL_FRAMEWORKS = [FRAMEWORK_PYTORCH, FRAMEWORK_TENSORFLOW]
|
|
36
|
+
|
|
37
|
+
# Builders
|
|
38
|
+
BUILDER_UV = "uv"
|
|
39
|
+
BUILDER_HATCH = "hatch"
|
|
40
|
+
SUPPORTED_BUILDERS = [BUILDER_UV, BUILDER_HATCH]
|
viperx/core.py
CHANGED
|
@@ -62,6 +62,13 @@ class ProjectGenerator:
|
|
|
62
62
|
loader=PackageLoader("viperx", "templates"),
|
|
63
63
|
autoescape=select_autoescape()
|
|
64
64
|
)
|
|
65
|
+
|
|
66
|
+
# Validate Builder
|
|
67
|
+
from viperx.utils import check_builder_installed
|
|
68
|
+
if not check_builder_installed(self.builder):
|
|
69
|
+
console.print(f"[bold red]Error:[/bold red] The requested builder '[bold]{self.builder}[/bold]' is not installed or not in PATH.")
|
|
70
|
+
console.print(f"Please install it (e.g., `pip install {self.builder}` or `curl -LsSf https://astral.sh/uv/install.sh | sh` for uv).")
|
|
71
|
+
sys.exit(1)
|
|
65
72
|
|
|
66
73
|
def log(self, message: str, style: str = "dim"):
|
|
67
74
|
if self.verbose:
|
|
@@ -136,6 +143,13 @@ class ProjectGenerator:
|
|
|
136
143
|
# 4. Overwrite/Add Files
|
|
137
144
|
self._generate_files(project_dir, is_subpackage)
|
|
138
145
|
|
|
146
|
+
|
|
147
|
+
# Cleanup extra files for subpackages
|
|
148
|
+
if is_subpackage:
|
|
149
|
+
for f in [".gitignore", ".python-version"]:
|
|
150
|
+
if (project_dir / f).exists():
|
|
151
|
+
(project_dir / f).unlink()
|
|
152
|
+
|
|
139
153
|
# 5. Git & Final Steps
|
|
140
154
|
console.print(f"\n[bold green]✓ Project {self.raw_name} created successfully![/bold green]")
|
|
141
155
|
if not is_subpackage:
|
|
@@ -189,8 +203,14 @@ class ProjectGenerator:
|
|
|
189
203
|
"framework": self.framework,
|
|
190
204
|
}
|
|
191
205
|
|
|
192
|
-
|
|
193
|
-
|
|
206
|
+
if not is_subpackage:
|
|
207
|
+
# pyproject.toml (Overwrite uv's basic one to add our specific deps)
|
|
208
|
+
self._render("pyproject.toml.j2", root / "pyproject.toml", context)
|
|
209
|
+
else:
|
|
210
|
+
# Subpackages: remove default pyproject.toml from uv init
|
|
211
|
+
if (root / "pyproject.toml").exists():
|
|
212
|
+
(root / "pyproject.toml").unlink()
|
|
213
|
+
self.log("Removed default pyproject.toml (Subpackage: Code Only)")
|
|
194
214
|
|
|
195
215
|
# Determine Package Root
|
|
196
216
|
if is_subpackage:
|
|
@@ -210,19 +230,33 @@ class ProjectGenerator:
|
|
|
210
230
|
self._render("__init__.py.j2", pkg_root / "__init__.py", context)
|
|
211
231
|
|
|
212
232
|
# README.md
|
|
213
|
-
|
|
214
|
-
|
|
233
|
+
# README.md
|
|
234
|
+
if not is_subpackage:
|
|
235
|
+
# Root Project: Respect use_readme
|
|
236
|
+
if self.use_readme:
|
|
237
|
+
self._render("README.md.j2", root / "README.md", context)
|
|
238
|
+
else:
|
|
239
|
+
if (root / "README.md").exists():
|
|
240
|
+
(root / "README.md").unlink()
|
|
241
|
+
self.log("Removed default README.md (requested --no-readme)")
|
|
215
242
|
else:
|
|
216
|
-
if
|
|
243
|
+
# Subpackage: Default False, but if True, generate it
|
|
244
|
+
if self.use_readme:
|
|
245
|
+
self._render("README.md.j2", root / "README.md", context)
|
|
246
|
+
elif (root / "README.md").exists():
|
|
247
|
+
# Cleanup default README from uv init if we didn't request one
|
|
217
248
|
(root / "README.md").unlink()
|
|
218
|
-
|
|
249
|
+
|
|
219
250
|
|
|
220
251
|
# LICENSE
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
252
|
+
if not is_subpackage:
|
|
253
|
+
license_text = LICENSES.get(self.license, LICENSES["MIT"])
|
|
254
|
+
license_text = license_text.format(year=datetime.now().year, author=self.author)
|
|
255
|
+
with open(root / "LICENSE", "w") as f:
|
|
256
|
+
f.write(license_text)
|
|
257
|
+
self.log(f"Generated LICENSE ({self.license})")
|
|
258
|
+
elif (root / "LICENSE").exists():
|
|
259
|
+
(root / "LICENSE").unlink()
|
|
226
260
|
|
|
227
261
|
# Config files
|
|
228
262
|
if self.use_config:
|
|
@@ -249,10 +283,12 @@ class ProjectGenerator:
|
|
|
249
283
|
self.log(f"Created .env and .env.example in {pkg_root.relative_to(root)}")
|
|
250
284
|
|
|
251
285
|
# .gitignore
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
286
|
+
# Only for Root
|
|
287
|
+
if not is_subpackage:
|
|
288
|
+
with open(root / ".gitignore", "a") as f:
|
|
289
|
+
# Add data/ to gitignore but allow .gitkeep
|
|
290
|
+
f.write("\n# ViperX specific\n.ipynb_checkpoints/\n# Isolated Env\nsrc/**/.env\n# Data (Local)\ndata/*\n!data/.gitkeep\n")
|
|
291
|
+
self.log("Updated .gitignore")
|
|
256
292
|
|
|
257
293
|
def _render(self, template_name: str, target_path: Path, context: dict):
|
|
258
294
|
template = self.env.get_template(template_name)
|
viperx/main.py
CHANGED
|
@@ -14,9 +14,14 @@ from viperx.constants import (
|
|
|
14
14
|
DL_FRAMEWORKS,
|
|
15
15
|
FRAMEWORK_PYTORCH,
|
|
16
16
|
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
import importlib.metadata
|
|
18
|
+
try:
|
|
19
|
+
version = importlib.metadata.version("viperx")
|
|
20
|
+
except importlib.metadata.PackageNotFoundError:
|
|
21
|
+
version = "unknown"
|
|
22
|
+
|
|
23
|
+
HELP_TEXT = f"""
|
|
24
|
+
[bold green]ViperX[/bold green] (v{version}): Professional Python Project Initializer
|
|
20
25
|
.
|
|
21
26
|
|
|
22
27
|
Automates the creation of professional-grade Python projects using `uv`.
|
|
@@ -28,31 +33,15 @@ app = typer.Typer(
|
|
|
28
33
|
add_completion=False,
|
|
29
34
|
no_args_is_help=True,
|
|
30
35
|
rich_markup_mode="markdown",
|
|
31
|
-
epilog="Made with ❤️
|
|
36
|
+
epilog="Made with ❤️ by KpihX"
|
|
32
37
|
)
|
|
33
38
|
|
|
34
39
|
# Global state for verbose flag
|
|
35
40
|
state = {"verbose": False}
|
|
36
41
|
console = Console(force_terminal=True)
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
"""
|
|
40
|
-
**ViperX**: Professional Python Project Initializer.
|
|
41
|
-
|
|
42
|
-
Automates the creation of professional-grade Python projects using `uv`.
|
|
43
|
-
Supports Standard Libraries, Machine Learning, and Deep Learning templates.
|
|
44
|
-
"""
|
|
45
|
-
if verbose:
|
|
46
|
-
state["verbose"] = True
|
|
47
|
-
console.print("[dim]Verbose mode enabled[/dim]")
|
|
48
|
-
|
|
49
43
|
def version_callback(value: bool):
|
|
50
44
|
if value:
|
|
51
|
-
import importlib.metadata
|
|
52
|
-
try:
|
|
53
|
-
version = importlib.metadata.version("viperx")
|
|
54
|
-
except importlib.metadata.PackageNotFoundError:
|
|
55
|
-
version = "unknown"
|
|
56
45
|
console.print(f"ViperX CLI Version: [bold green]{version}[/bold green]")
|
|
57
46
|
raise typer.Exit()
|
|
58
47
|
|
|
@@ -79,9 +68,21 @@ def cli_callback(
|
|
|
79
68
|
if verbose:
|
|
80
69
|
state["verbose"] = True
|
|
81
70
|
console.print("[dim]Verbose mode enabled[/dim]")
|
|
71
|
+
|
|
72
|
+
|
|
82
73
|
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
|
|
75
|
+
# Config Management Group (The Main Entry Point)
|
|
76
|
+
config_app = typer.Typer(
|
|
77
|
+
help="Manage Declarative Configuration (viperx.yaml).",
|
|
78
|
+
no_args_is_help=False, # Allow running without subcommands (acts as apply)
|
|
79
|
+
)
|
|
80
|
+
app.add_typer(config_app, name="config")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@config_app.callback(invoke_without_command=True)
|
|
84
|
+
def config_main(
|
|
85
|
+
ctx: typer.Context,
|
|
85
86
|
# --- Config Driven Mode ---
|
|
86
87
|
config: Path = typer.Option(
|
|
87
88
|
None, "--config", "-c",
|
|
@@ -115,10 +116,17 @@ def init(
|
|
|
115
116
|
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose logging"),
|
|
116
117
|
):
|
|
117
118
|
"""
|
|
118
|
-
Initialize
|
|
119
|
+
**Configure & Initialize**: Apply configuration to create or update a project.
|
|
119
120
|
|
|
120
|
-
|
|
121
|
+
usage: [bold]viperx config [OPTIONS][/bold]
|
|
122
|
+
[bold]viperx config get[/bold]
|
|
121
123
|
"""
|
|
124
|
+
# Check if a subcommand (like 'get') is invoked
|
|
125
|
+
if ctx.invoked_subcommand is not None:
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
# --- Apply Logic (Former Init) ---
|
|
129
|
+
|
|
122
130
|
# 1. Declarative Mode
|
|
123
131
|
if config:
|
|
124
132
|
if not config.exists():
|
|
@@ -131,6 +139,10 @@ def init(
|
|
|
131
139
|
|
|
132
140
|
# 2. Imperative Mode (Validation)
|
|
133
141
|
if not name:
|
|
142
|
+
# Implicitly show help if no options provided?
|
|
143
|
+
# Or error out. User expects correct run.
|
|
144
|
+
# If user runs `viperx config` with NO args, and NO config, what happens?
|
|
145
|
+
# "Missing option name".
|
|
134
146
|
console.print("[bold red]Error:[/bold red] Missing option '--name' / '-n'. Required in manual mode.")
|
|
135
147
|
raise typer.Exit(code=1)
|
|
136
148
|
|
|
@@ -156,12 +168,8 @@ def init(
|
|
|
156
168
|
# Generate in current directory
|
|
157
169
|
generator.generate(Path.cwd())
|
|
158
170
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
help="Manage Declarative Configuration (viperx.yaml).",
|
|
162
|
-
no_args_is_help=True
|
|
163
|
-
)
|
|
164
|
-
app.add_typer(config_app, name="config")
|
|
171
|
+
|
|
172
|
+
|
|
165
173
|
|
|
166
174
|
@config_app.command("get")
|
|
167
175
|
def config_get(
|
|
@@ -212,7 +220,7 @@ def package_add(
|
|
|
212
220
|
),
|
|
213
221
|
use_env: bool = typer.Option(True, "--env/--no-env", help="Generate .env file"),
|
|
214
222
|
use_config: bool = typer.Option(True, "--embed-config/--no-embed-config", help="Generate embedded config"),
|
|
215
|
-
use_readme: bool = typer.Option(
|
|
223
|
+
use_readme: bool = typer.Option(False, "--readme/--no-readme", help="Generate README.md"),
|
|
216
224
|
verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose logging"),
|
|
217
225
|
):
|
|
218
226
|
"""
|
|
@@ -275,4 +283,18 @@ def package_update(
|
|
|
275
283
|
generator.update_package(Path.cwd())
|
|
276
284
|
|
|
277
285
|
if __name__ == "__main__":
|
|
278
|
-
|
|
286
|
+
try:
|
|
287
|
+
app()
|
|
288
|
+
except SystemExit as e:
|
|
289
|
+
if e.code != 0:
|
|
290
|
+
# On error (non-zero exit), display help as requested
|
|
291
|
+
from typer.main import get_command
|
|
292
|
+
import click
|
|
293
|
+
cli = get_command(app)
|
|
294
|
+
# Create a dummy context to render help
|
|
295
|
+
# We print it to stderr or stdout? Console prints to stdout usually.
|
|
296
|
+
# User wants it displayed immediately.
|
|
297
|
+
with click.Context(cli) as ctx:
|
|
298
|
+
console.print("\n")
|
|
299
|
+
console.print(cli.get_help(ctx))
|
|
300
|
+
raise
|
viperx/templates/config.py.j2
CHANGED
|
@@ -28,6 +28,7 @@ def get_config(key: str, default: Any = None) -> Any:
|
|
|
28
28
|
"""Retrieve a value from the globally loaded settings."""
|
|
29
29
|
return SETTINGS.get(key, default)
|
|
30
30
|
|
|
31
|
+
{% if project_type != 'classic' %}
|
|
31
32
|
def get_dataset_path(notebook_name: str, key: str = "datasets", extension: str = ".csv") -> str | None:
|
|
32
33
|
"""
|
|
33
34
|
Helper for notebook data loading.
|
|
@@ -38,3 +39,4 @@ def get_dataset_path(notebook_name: str, key: str = "datasets", extension: str =
|
|
|
38
39
|
if not dataset_name:
|
|
39
40
|
return None
|
|
40
41
|
return f"{dataset_name}{extension}"
|
|
42
|
+
{% endif %}
|
|
@@ -43,13 +43,10 @@ dependencies = [
|
|
|
43
43
|
{% if builder == 'hatch' %}
|
|
44
44
|
requires = ["hatchling"]
|
|
45
45
|
build-backend = "hatchling.build"
|
|
46
|
-
{% elif builder == 'flit' %}
|
|
47
|
-
requires = ["flit_core>=3.2,<4"]
|
|
48
|
-
build-backend = "flit_core.buildapi"
|
|
49
46
|
{% else %}
|
|
50
|
-
# Default
|
|
51
|
-
requires = ["
|
|
52
|
-
build-backend = "
|
|
47
|
+
# Default: uv native build backend
|
|
48
|
+
requires = ["uv_build>=0.9.21,<0.10.0"]
|
|
49
|
+
build-backend = "uv_build"
|
|
53
50
|
{% endif %}
|
|
54
51
|
|
|
55
52
|
{% if use_uv %}
|
viperx/utils.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import shutil
|
|
3
|
-
import subprocess
|
|
4
3
|
from rich.console import Console
|
|
5
4
|
|
|
6
5
|
console = Console()
|
|
@@ -32,16 +31,32 @@ def validate_project_name(ctx, param, value):
|
|
|
32
31
|
raise BadParameter("Project name must contain only letters, numbers, underscores, and hyphens.")
|
|
33
32
|
return value
|
|
34
33
|
|
|
34
|
+
def check_builder_installed(builder: str) -> bool:
|
|
35
|
+
"""
|
|
36
|
+
Check if the specified builder is installed.
|
|
37
|
+
Uses shutil.which() for robust path detection.
|
|
38
|
+
"""
|
|
39
|
+
return shutil.which(builder) is not None
|
|
40
|
+
|
|
35
41
|
def get_author_from_git() -> tuple[str, str]:
|
|
36
42
|
"""
|
|
37
43
|
Attempt to get author name and email from git config.
|
|
38
44
|
Returns (name, email) or defaults.
|
|
39
45
|
"""
|
|
40
46
|
try:
|
|
47
|
+
# Check if git is installed first
|
|
48
|
+
if not shutil.which("git"):
|
|
49
|
+
return "Nameless", "nameless@example.com"
|
|
50
|
+
|
|
41
51
|
import git
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
# Robust way using git command wrapper
|
|
53
|
+
reader = git.Git().config
|
|
54
|
+
name = reader("--global", "--get", "user.name")
|
|
55
|
+
email = reader("--global", "--get", "user.email")
|
|
56
|
+
|
|
57
|
+
# strip newlines if any
|
|
58
|
+
return (name.strip() if name else "Nameless",
|
|
59
|
+
email.strip() if email else "nameless@example.com")
|
|
46
60
|
except Exception:
|
|
47
|
-
|
|
61
|
+
# Fallback if git call fails
|
|
62
|
+
return "Nameless", "nameless@example.com"
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
viperx/__init__.py,sha256=mRrD-SK9PtwBhqgLzJrvefdViNsXCyBGknzFNsIou1I,51
|
|
2
|
-
viperx/config_engine.py,sha256=
|
|
3
|
-
viperx/constants.py,sha256=
|
|
4
|
-
viperx/core.py,sha256=
|
|
2
|
+
viperx/config_engine.py,sha256=9P9xxWIYWhL_QFRyvi601D3_4HBHn4RA20dreGwdSX4,7341
|
|
3
|
+
viperx/constants.py,sha256=Mv5tFjFxe3tD4VDCEsXiwfiV7biTlzl45ALkHmiK33M,887
|
|
4
|
+
viperx/core.py,sha256=R0IzxNTL_Coyz1bCTc2QAnQM3FK36ADV86NVQXuXmBM,20438
|
|
5
5
|
viperx/licenses.py,sha256=TPsG1aqNAjtOsvuD6i3SiC5rUK9f3DslPHQkKiV7vxs,12482
|
|
6
|
-
viperx/main.py,sha256=
|
|
6
|
+
viperx/main.py,sha256=N0pqLnZoWr00XjH1kS5dxbDKEqXvazsVspqLQ_-fF8k,10663
|
|
7
7
|
viperx/templates/Base.ipynb.j2,sha256=rlGCw7jIds3RiXFrG8D8oAjzXzzj1cayKjBI91k-1kA,3140
|
|
8
8
|
viperx/templates/Base_General.ipynb.j2,sha256=f5ZUu65khtvBeCofs3Lvzccn5Krl0nRGu0dv7SzLvzg,2716
|
|
9
9
|
viperx/templates/Base_Kaggle.ipynb.j2,sha256=gPkmFt4cA5UzhRU_gLZ1UzSjEa7Mgel001Eqiw94-fc,2698
|
|
10
10
|
viperx/templates/README.md.j2,sha256=TJNUwTcKnjALrP0MOSs8UBx8zNhI8erRHrc2-8nuMgQ,3445
|
|
11
11
|
viperx/templates/__init__.py.j2,sha256=uq8IYd0SGYX2-cpGb9q5197eYs-ode-g5y2NCGWU5YM,196
|
|
12
|
-
viperx/templates/config.py.j2,sha256=
|
|
12
|
+
viperx/templates/config.py.j2,sha256=ExMbopPQPntmkNgbv9uKooGDjv82gh_XhyQmjJ8MiQg,1456
|
|
13
13
|
viperx/templates/config.yaml.j2,sha256=IYvjQUioZ9lyzVHVDgwrdjgM6s4pUnCFXwtnydoKe2k,485
|
|
14
14
|
viperx/templates/data_loader.py.j2,sha256=hehewwvBAYJM-acKn6_T4cU5EvUPX7wHAurAQ3z3ET4,3696
|
|
15
15
|
viperx/templates/main.py.j2,sha256=caGN54Hck_EcwT3Aqju52CBuGujSnzU2Hehw8mowzjI,277
|
|
16
|
-
viperx/templates/pyproject.toml.j2,sha256=
|
|
17
|
-
viperx/templates/viperx_config.yaml.j2,sha256=
|
|
18
|
-
viperx/utils.py,sha256=
|
|
19
|
-
viperx-0.9.
|
|
20
|
-
viperx-0.9.
|
|
21
|
-
viperx-0.9.
|
|
22
|
-
viperx-0.9.
|
|
16
|
+
viperx/templates/pyproject.toml.j2,sha256=9s3wbXZjoA_643feZAlS5-7YMQLxVUd6XLPhDusyuJY,1287
|
|
17
|
+
viperx/templates/viperx_config.yaml.j2,sha256=a2H0N_W8sq0Zrjio4El7kFjOYQUATf_VdD9zK3NzG3U,1405
|
|
18
|
+
viperx/utils.py,sha256=yCJMclJT10_Vc8A1dx_lP0OSs7crCnVNLP46SCpxmog,2054
|
|
19
|
+
viperx-0.9.26.dist-info/WHEEL,sha256=RRVLqVugUmFOqBedBFAmA4bsgFcROUBiSUKlERi0Hcg,79
|
|
20
|
+
viperx-0.9.26.dist-info/entry_points.txt,sha256=Is38BrTuf6unQCLgDE990Rjd9HyGldBSw4Uzy_nMePY,44
|
|
21
|
+
viperx-0.9.26.dist-info/METADATA,sha256=GuapQRE5fpVYvo6_5i54EI8uA1AplAIAAsEaSiBTty4,6782
|
|
22
|
+
viperx-0.9.26.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|