viperx 0.9.26__py3-none-any.whl → 0.9.40__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 +52 -3
- viperx/core.py +71 -71
- viperx/templates/__init__.py.j2 +0 -3
- viperx/templates/config.yaml.j2 +3 -0
- viperx/templates/main.py.j2 +3 -3
- viperx/templates/pyproject.toml.j2 +20 -19
- viperx/templates/viperx_config.yaml.j2 +1 -1
- viperx/utils.py +17 -1
- {viperx-0.9.26.dist-info → viperx-0.9.40.dist-info}/METADATA +1 -1
- viperx-0.9.40.dist-info/RECORD +22 -0
- viperx-0.9.26.dist-info/RECORD +0 -22
- {viperx-0.9.26.dist-info → viperx-0.9.40.dist-info}/WHEEL +0 -0
- {viperx-0.9.26.dist-info → viperx-0.9.40.dist-info}/entry_points.txt +0 -0
viperx/config_engine.py
CHANGED
|
@@ -4,7 +4,7 @@ from rich.console import Console
|
|
|
4
4
|
from rich.panel import Panel
|
|
5
5
|
|
|
6
6
|
from viperx.core import ProjectGenerator
|
|
7
|
-
from viperx.constants import DEFAULT_LICENSE, DEFAULT_BUILDER, TYPE_CLASSIC, FRAMEWORK_PYTORCH
|
|
7
|
+
from viperx.constants import DEFAULT_LICENSE, DEFAULT_BUILDER, TYPE_CLASSIC, TYPE_ML, TYPE_DL, FRAMEWORK_PYTORCH
|
|
8
8
|
|
|
9
9
|
console = Console()
|
|
10
10
|
|
|
@@ -69,6 +69,7 @@ class ConfigEngine:
|
|
|
69
69
|
use_config=settings_conf.get("use_config", True),
|
|
70
70
|
use_tests=settings_conf.get("use_tests", True),
|
|
71
71
|
framework=settings_conf.get("framework", FRAMEWORK_PYTORCH),
|
|
72
|
+
scripts={project_name: f"{project_name}.main:main"}, # Simple default for hydration
|
|
72
73
|
verbose=self.verbose
|
|
73
74
|
)
|
|
74
75
|
# generate() expects parent dir, and will operate on parent/name (which is self.root_path)
|
|
@@ -84,9 +85,55 @@ class ConfigEngine:
|
|
|
84
85
|
else:
|
|
85
86
|
if target_dir.exists():
|
|
86
87
|
console.print(Panel(f"⚠️ [bold yellow]Directory exists but not initialized. Hydrating:[/bold yellow] {project_name}", border_style="yellow"))
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
# Prepare Scripts & Dependency Context
|
|
89
|
+
packages = workspace_conf.get("packages", [])
|
|
89
90
|
|
|
91
|
+
# --- Aggregate Global Dependencies ---
|
|
92
|
+
# Start with Root Settings
|
|
93
|
+
# Root is always present (ProjectGenerator uses these)
|
|
94
|
+
root_use_config = settings_conf.get("use_config", True)
|
|
95
|
+
root_use_env = settings_conf.get("use_env", False)
|
|
96
|
+
root_type = settings_conf.get("type", TYPE_CLASSIC)
|
|
97
|
+
root_framework = settings_conf.get("framework", FRAMEWORK_PYTORCH)
|
|
98
|
+
|
|
99
|
+
glob_has_config = root_use_config
|
|
100
|
+
glob_has_env = root_use_env
|
|
101
|
+
glob_is_ml_dl = root_type in [TYPE_ML, TYPE_DL]
|
|
102
|
+
glob_is_dl = root_type == TYPE_DL
|
|
103
|
+
glob_frameworks = {root_framework} if glob_is_dl else set()
|
|
104
|
+
|
|
105
|
+
project_scripts = {project_name: f"{project_name}.main:main"}
|
|
106
|
+
|
|
107
|
+
for pkg in packages:
|
|
108
|
+
# Scripts
|
|
109
|
+
pkg_name = pkg.get("name")
|
|
110
|
+
from viperx.utils import sanitize_project_name
|
|
111
|
+
pkg_name_clean = sanitize_project_name(pkg_name)
|
|
112
|
+
# CLI Command = Raw Name (e.g. test-classic) -> sanitized module path (test_classic.main:main)
|
|
113
|
+
project_scripts[pkg_name] = f"{pkg_name_clean}.main:main"
|
|
114
|
+
|
|
115
|
+
# Dependency Aggregation
|
|
116
|
+
# Inherit defaults if not defined in pkg
|
|
117
|
+
p_config = pkg.get("use_config", settings_conf.get("use_config", True))
|
|
118
|
+
p_env = pkg.get("use_env", settings_conf.get("use_env", False))
|
|
119
|
+
p_type = pkg.get("type", TYPE_CLASSIC)
|
|
120
|
+
p_framework = pkg.get("framework", FRAMEWORK_PYTORCH) # Defaults to pytorch if implicit
|
|
121
|
+
|
|
122
|
+
if p_config: glob_has_config = True
|
|
123
|
+
if p_env: glob_has_env = True
|
|
124
|
+
if p_type in [TYPE_ML, TYPE_DL]: glob_is_ml_dl = True
|
|
125
|
+
if p_type == TYPE_DL:
|
|
126
|
+
glob_is_dl = True
|
|
127
|
+
glob_frameworks.add(p_framework)
|
|
128
|
+
|
|
129
|
+
dep_context = {
|
|
130
|
+
"has_config": glob_has_config,
|
|
131
|
+
"has_env": glob_has_env,
|
|
132
|
+
"is_ml_dl": glob_is_ml_dl,
|
|
133
|
+
"is_dl": glob_is_dl,
|
|
134
|
+
"frameworks": list(glob_frameworks)
|
|
135
|
+
}
|
|
136
|
+
|
|
90
137
|
# Create Root (or Hydrate)
|
|
91
138
|
gen = ProjectGenerator(
|
|
92
139
|
name=project_name,
|
|
@@ -99,6 +146,8 @@ class ConfigEngine:
|
|
|
99
146
|
use_config=settings_conf.get("use_config", True),
|
|
100
147
|
use_tests=settings_conf.get("use_tests", True),
|
|
101
148
|
framework=settings_conf.get("framework", FRAMEWORK_PYTORCH),
|
|
149
|
+
scripts=project_scripts,
|
|
150
|
+
dependency_context=dep_context,
|
|
102
151
|
verbose=self.verbose
|
|
103
152
|
)
|
|
104
153
|
gen.generate(self.root_path)
|
viperx/core.py
CHANGED
|
@@ -34,12 +34,32 @@ class ProjectGenerator:
|
|
|
34
34
|
license: str = DEFAULT_LICENSE,
|
|
35
35
|
builder: str = DEFAULT_BUILDER,
|
|
36
36
|
framework: str = "pytorch",
|
|
37
|
+
scripts: Optional[dict] = None,
|
|
38
|
+
dependency_context: Optional[dict] = None,
|
|
37
39
|
verbose: bool = False):
|
|
38
40
|
self.raw_name = name
|
|
39
41
|
self.project_name = sanitize_project_name(name)
|
|
40
42
|
self.description = description or name
|
|
41
43
|
self.type = type
|
|
42
44
|
self.framework = framework
|
|
45
|
+
self.scripts = scripts or {}
|
|
46
|
+
# Dependency Context (Global workspace features)
|
|
47
|
+
self.dependency_context = dependency_context or {
|
|
48
|
+
"has_config": use_config,
|
|
49
|
+
"has_env": use_env,
|
|
50
|
+
"is_ml_dl": type in ["ml", "dl"],
|
|
51
|
+
"is_dl": type == "dl",
|
|
52
|
+
"frameworks": [framework] if type == "dl" else []
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Default script for the main package if none provided (and it's a root project mostly)
|
|
56
|
+
self.scripts = scripts or {}
|
|
57
|
+
# Default script for the main package if none provided (and it's a root project mostly)
|
|
58
|
+
if not self.scripts:
|
|
59
|
+
# Key = Raw Name (CLI command, e.g. test-classic)
|
|
60
|
+
# Value = Sanitized Path (Module, e.g. test_classic.main:main)
|
|
61
|
+
self.scripts = {self.raw_name: f"{self.project_name}.main:main"}
|
|
62
|
+
|
|
43
63
|
self.author = author
|
|
44
64
|
if not self.author or self.author == "Your Name":
|
|
45
65
|
self.author, self.author_email = get_author_from_git()
|
|
@@ -54,21 +74,44 @@ class ProjectGenerator:
|
|
|
54
74
|
self.use_tests = use_tests
|
|
55
75
|
self.verbose = verbose
|
|
56
76
|
|
|
57
|
-
# Detect System Python
|
|
58
|
-
self.
|
|
77
|
+
# Detect System Python (For logging/diagnostics)
|
|
78
|
+
self.system_python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
79
|
+
|
|
80
|
+
# Project Python Version (For requires-python in pyproject.toml)
|
|
81
|
+
# Driven by package constants to ensure compatibility/evolution
|
|
82
|
+
from viperx.constants import DEFAULT_PYTHON_VERSION
|
|
83
|
+
self.python_version = DEFAULT_PYTHON_VERSION
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# Validate Choices
|
|
87
|
+
from viperx.utils import validate_choice, check_builder_installed
|
|
88
|
+
from viperx.constants import PROJECT_TYPES, DL_FRAMEWORKS, TYPE_DL
|
|
59
89
|
|
|
90
|
+
try:
|
|
91
|
+
validate_choice(self.type, PROJECT_TYPES, "project type")
|
|
92
|
+
if self.type == TYPE_DL:
|
|
93
|
+
validate_choice(self.framework, DL_FRAMEWORKS, "framework")
|
|
94
|
+
|
|
95
|
+
# Validate Builder Existence & Support
|
|
96
|
+
if not check_builder_installed(self.builder):
|
|
97
|
+
from viperx.constants import SUPPORTED_BUILDERS
|
|
98
|
+
if self.builder not in SUPPORTED_BUILDERS:
|
|
99
|
+
console.print(f"[bold red]Error:[/bold red] Invalid builder '[bold]{self.builder}[/bold]'.")
|
|
100
|
+
console.print(f"Supported builders: [green]{', '.join(SUPPORTED_BUILDERS)}[/green]")
|
|
101
|
+
else:
|
|
102
|
+
console.print(f"[bold red]Error:[/bold red] The builder '[bold]{self.builder}[/bold]' is not installed or not in PATH.")
|
|
103
|
+
console.print(f"Please install it (e.g., `pip install {self.builder}` or `curl -LsSf https://astral.sh/uv/install.sh | sh` for uv).")
|
|
104
|
+
sys.exit(1)
|
|
105
|
+
|
|
106
|
+
except ValueError as e:
|
|
107
|
+
console.print(f"[bold red]Configuration Error:[/bold red] {e}")
|
|
108
|
+
sys.exit(1)
|
|
109
|
+
|
|
60
110
|
# Jinja Setup
|
|
61
111
|
self.env = Environment(
|
|
62
112
|
loader=PackageLoader("viperx", "templates"),
|
|
63
113
|
autoescape=select_autoescape()
|
|
64
114
|
)
|
|
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)
|
|
72
115
|
|
|
73
116
|
def log(self, message: str, style: str = "dim"):
|
|
74
117
|
if self.verbose:
|
|
@@ -88,7 +131,8 @@ class ProjectGenerator:
|
|
|
88
131
|
console.print(f"[bold green]Creating project {self.raw_name} ({self.type})...[/bold green]")
|
|
89
132
|
|
|
90
133
|
self.log(f"Target directory: {project_dir}")
|
|
91
|
-
self.log(f"Python
|
|
134
|
+
self.log(f"System Python: {self.system_python_version}")
|
|
135
|
+
self.log(f"Project Python: {self.python_version}")
|
|
92
136
|
|
|
93
137
|
|
|
94
138
|
def generate(self, target_dir: Path, is_subpackage: bool = False):
|
|
@@ -109,8 +153,13 @@ class ProjectGenerator:
|
|
|
109
153
|
)
|
|
110
154
|
else:
|
|
111
155
|
# Create new
|
|
156
|
+
# STRICT DIR NAMING: Use self.project_name (underscores) for directory
|
|
157
|
+
# But use self.raw_name (hyphens) for the package Name metadata if possible?
|
|
158
|
+
# uv init [NAME] creates directory NAME.
|
|
159
|
+
# If we want dir=test_classic but name=test-classic:
|
|
160
|
+
# uv init test_classic --name test-classic
|
|
112
161
|
subprocess.run(
|
|
113
|
-
["uv", "init", "--package", "--no-workspace", self.raw_name],
|
|
162
|
+
["uv", "init", "--package", "--no-workspace", self.project_name, "--name", self.raw_name],
|
|
114
163
|
check=True, cwd=target_dir, capture_output=True
|
|
115
164
|
)
|
|
116
165
|
console.print(" [blue]✓ Scaffolding created with uv init[/blue]")
|
|
@@ -153,7 +202,7 @@ class ProjectGenerator:
|
|
|
153
202
|
# 5. Git & Final Steps
|
|
154
203
|
console.print(f"\n[bold green]✓ Project {self.raw_name} created successfully![/bold green]")
|
|
155
204
|
if not is_subpackage:
|
|
156
|
-
console.print(f" [dim]cd {self.
|
|
205
|
+
console.print(f" [dim]cd {self.project_name} && uv sync[/dim]")
|
|
157
206
|
|
|
158
207
|
def _create_extra_dirs(self, root: Path, is_subpackage: bool = False):
|
|
159
208
|
if is_subpackage:
|
|
@@ -200,17 +249,16 @@ class ProjectGenerator:
|
|
|
200
249
|
"has_config": self.use_config,
|
|
201
250
|
"use_readme": self.use_readme,
|
|
202
251
|
"use_env": self.use_env,
|
|
252
|
+
"use_env": self.use_env,
|
|
203
253
|
"framework": self.framework,
|
|
254
|
+
"scripts": self.scripts,
|
|
204
255
|
}
|
|
256
|
+
# Merge dependency context overrides
|
|
257
|
+
context.update(self.dependency_context)
|
|
205
258
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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)")
|
|
259
|
+
# pyproject.toml (Overwrite uv's basic one to add our specific deps)
|
|
260
|
+
# Even subpackages need this if they are Workspace Members (which they are in our model)
|
|
261
|
+
self._render("pyproject.toml.j2", root / "pyproject.toml", context)
|
|
214
262
|
|
|
215
263
|
# Determine Package Root
|
|
216
264
|
if is_subpackage:
|
|
@@ -301,57 +349,9 @@ class ProjectGenerator:
|
|
|
301
349
|
"""Add a new package to an existing workspace."""
|
|
302
350
|
console.print(f"[bold green]Adding package {self.raw_name} to workspace...[/bold green]")
|
|
303
351
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return
|
|
308
|
-
|
|
309
|
-
# Read pyproject.toml
|
|
310
|
-
with open(pyproject_path, "r") as f:
|
|
311
|
-
content = f.read()
|
|
312
|
-
|
|
313
|
-
# Check if it's already a workspace
|
|
314
|
-
is_workspace = "[tool.uv.workspace]" in content
|
|
315
|
-
|
|
316
|
-
if not is_workspace:
|
|
317
|
-
console.print("[yellow]Upgrading project to Workspace...[/yellow]")
|
|
318
|
-
# Append workspace definition
|
|
319
|
-
with open(pyproject_path, "a") as f:
|
|
320
|
-
f.write(f"\n[tool.uv.workspace]\nmembers = [\"src/{self.raw_name}\"]\n")
|
|
321
|
-
self.log("Added [tool.uv.workspace] section")
|
|
322
|
-
else:
|
|
323
|
-
# Add member to specific list if it exists
|
|
324
|
-
# We use a simple regex approach to find 'members = [...]'
|
|
325
|
-
import re
|
|
326
|
-
members_pattern = r'members\s*=\s*\[(.*?)\]'
|
|
327
|
-
match = re.search(members_pattern, content, re.DOTALL)
|
|
328
|
-
|
|
329
|
-
if match:
|
|
330
|
-
current_members = match.group(1)
|
|
331
|
-
# Check if already present
|
|
332
|
-
if f'"{self.raw_name}"' in current_members or f"'{self.raw_name}'" in current_members:
|
|
333
|
-
self.log(f"Package {self.raw_name} is already in workspace members.")
|
|
334
|
-
else:
|
|
335
|
-
# Append new member
|
|
336
|
-
# We inject it into the list
|
|
337
|
-
self.log("Adding member to existing workspace list")
|
|
338
|
-
# Naively replace the closing bracket
|
|
339
|
-
# Better: parse, but for now robust string insertion
|
|
340
|
-
# Cleanest way without breaking formatting involves finding the last element
|
|
341
|
-
# Cleanest way without breaking formatting involves finding the last element
|
|
342
|
-
new_member = f', "src/{self.raw_name}"'
|
|
343
|
-
# Warning: This regex replace is basic. `uv` handles toml well, maybe we should just edit safely.
|
|
344
|
-
# Let's try to append to the end of the content of the list
|
|
345
|
-
new_content = re.sub(members_pattern, lambda m: f'members = [{m.group(1)}{new_member}]', content, flags=re.DOTALL)
|
|
346
|
-
with open(pyproject_path, "w") as f:
|
|
347
|
-
f.write(new_content)
|
|
348
|
-
else:
|
|
349
|
-
# Section exists but members key might be missing? Or weird formatting.
|
|
350
|
-
# Append to section?
|
|
351
|
-
# Safe fallback
|
|
352
|
-
console.print("[yellow]Warning: Could not parse members list. Adding manually at end.[/yellow]")
|
|
353
|
-
with open(pyproject_path, "a") as f:
|
|
354
|
-
f.write(f"\n# Added by viperx\n[tool.uv.workspace]\nmembers = [\"{self.raw_name}\"]\n")
|
|
352
|
+
# User Request: Do not modify root pyproject.toml to add workspace members.
|
|
353
|
+
# "uv est assez intelligent"
|
|
354
|
+
pass
|
|
355
355
|
|
|
356
356
|
# Generate the package in the root IF it doesn't exist
|
|
357
357
|
pkg_dir = workspace_root / SRC_DIR / self.raw_name
|
viperx/templates/__init__.py.j2
CHANGED
viperx/templates/config.yaml.j2
CHANGED
viperx/templates/main.py.j2
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from {{ package_name }} import hello
|
|
2
1
|
{% if has_config %}
|
|
3
2
|
from {{ package_name }} import SETTINGS
|
|
4
3
|
{% endif %}
|
|
5
4
|
|
|
6
5
|
def main():
|
|
7
|
-
hello()
|
|
8
6
|
{% if has_config %}
|
|
9
|
-
print(f"
|
|
7
|
+
print(f"Hi from {SETTINGS['project_name']}!")
|
|
8
|
+
{% else %}
|
|
9
|
+
print("Hi from viperx!")
|
|
10
10
|
{% endif %}
|
|
11
11
|
|
|
12
12
|
if __name__ == "__main__":
|
|
@@ -11,10 +11,14 @@ authors = [
|
|
|
11
11
|
]
|
|
12
12
|
license = { text = "{{ license }}" }
|
|
13
13
|
dependencies = [
|
|
14
|
+
{%- if has_config %}
|
|
14
15
|
"pyyaml>=6.0",
|
|
16
|
+
{%- endif %}
|
|
17
|
+
{%- if has_env %}
|
|
15
18
|
"python-dotenv>=1.0.0",
|
|
19
|
+
{%- endif %}
|
|
20
|
+
{%- if is_ml_dl %}
|
|
16
21
|
"kagglehub>=0.2.0",
|
|
17
|
-
{% if project_type == 'ml' %}
|
|
18
22
|
"numpy>=1.24.0",
|
|
19
23
|
"pandas>=2.0.0",
|
|
20
24
|
"scikit-learn>=1.3.0",
|
|
@@ -22,35 +26,32 @@ dependencies = [
|
|
|
22
26
|
"seaborn>=0.12.0",
|
|
23
27
|
"requests>=2.30.0",
|
|
24
28
|
"tqdm>=4.65.0",
|
|
25
|
-
{
|
|
26
|
-
{
|
|
29
|
+
{%- endif %}
|
|
30
|
+
{%- if is_dl %}
|
|
31
|
+
{%- if 'pytorch' in frameworks %}
|
|
27
32
|
"torch>=2.0.0",
|
|
28
33
|
"torchvision>=0.15.0",
|
|
29
|
-
{
|
|
34
|
+
{%- endif %}
|
|
35
|
+
{%- if 'tensorflow' in frameworks %}
|
|
30
36
|
"tensorflow>=2.13.0",
|
|
31
37
|
# "keras>=3.0.0", # Optional, included in tf usually
|
|
32
|
-
{
|
|
33
|
-
|
|
34
|
-
"pandas>=2.0.0",
|
|
35
|
-
"matplotlib>=3.7.0",
|
|
36
|
-
"seaborn>=0.12.0",
|
|
37
|
-
"requests>=2.30.0",
|
|
38
|
-
"tqdm>=4.65.0",
|
|
39
|
-
{% endif %}
|
|
38
|
+
{%- endif %}
|
|
39
|
+
{%- endif %}
|
|
40
40
|
]
|
|
41
41
|
|
|
42
|
+
[project.scripts]
|
|
43
|
+
{%- for name, entry in scripts.items() %}
|
|
44
|
+
{{ name }} = "{{ entry }}"
|
|
45
|
+
{%- endfor %}
|
|
46
|
+
|
|
42
47
|
[build-system]
|
|
43
|
-
{
|
|
48
|
+
{%- if builder == 'hatch' %}
|
|
44
49
|
requires = ["hatchling"]
|
|
45
50
|
build-backend = "hatchling.build"
|
|
46
|
-
{
|
|
51
|
+
{%- else %}
|
|
47
52
|
# Default: uv native build backend
|
|
48
53
|
requires = ["uv_build>=0.9.21,<0.10.0"]
|
|
49
54
|
build-backend = "uv_build"
|
|
50
|
-
{
|
|
55
|
+
{%- endif %}
|
|
51
56
|
|
|
52
|
-
{% if use_uv %}
|
|
53
|
-
[tool.uv]
|
|
54
|
-
managed = true
|
|
55
|
-
{% endif %}
|
|
56
57
|
|
viperx/utils.py
CHANGED
|
@@ -31,11 +31,27 @@ def validate_project_name(ctx, param, value):
|
|
|
31
31
|
raise BadParameter("Project name must contain only letters, numbers, underscores, and hyphens.")
|
|
32
32
|
return value
|
|
33
33
|
|
|
34
|
+
from viperx.constants import SUPPORTED_BUILDERS
|
|
35
|
+
|
|
36
|
+
def validate_choice(value: str, choices: list[str], name: str):
|
|
37
|
+
"""
|
|
38
|
+
Validate that a value is within the allowed choices.
|
|
39
|
+
Raises ValueError with a friendly message if invalid.
|
|
40
|
+
"""
|
|
41
|
+
if value not in choices:
|
|
42
|
+
raise ValueError(f"Invalid {name} '{value}'. Allowed: {', '.join(choices)}")
|
|
43
|
+
return value
|
|
44
|
+
|
|
34
45
|
def check_builder_installed(builder: str) -> bool:
|
|
35
46
|
"""
|
|
36
|
-
Check if the specified builder is installed.
|
|
47
|
+
Check if the specified builder is valid AND installed.
|
|
37
48
|
Uses shutil.which() for robust path detection.
|
|
38
49
|
"""
|
|
50
|
+
# 1. Validate against supported list
|
|
51
|
+
if builder not in SUPPORTED_BUILDERS:
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
# 2. Check existence
|
|
39
55
|
return shutil.which(builder) is not None
|
|
40
56
|
|
|
41
57
|
def get_author_from_git() -> tuple[str, str]:
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
viperx/__init__.py,sha256=mRrD-SK9PtwBhqgLzJrvefdViNsXCyBGknzFNsIou1I,51
|
|
2
|
+
viperx/config_engine.py,sha256=A5u1qUaWlgvXQReRHtF05--spPsd90XgT81zZYR2rBM,9908
|
|
3
|
+
viperx/constants.py,sha256=Mv5tFjFxe3tD4VDCEsXiwfiV7biTlzl45ALkHmiK33M,887
|
|
4
|
+
viperx/core.py,sha256=yEACR7U-z5h7_fuCKyy4uljGPMe4S-F1HO5H_XeqHNg,20308
|
|
5
|
+
viperx/licenses.py,sha256=TPsG1aqNAjtOsvuD6i3SiC5rUK9f3DslPHQkKiV7vxs,12482
|
|
6
|
+
viperx/main.py,sha256=N0pqLnZoWr00XjH1kS5dxbDKEqXvazsVspqLQ_-fF8k,10663
|
|
7
|
+
viperx/templates/Base.ipynb.j2,sha256=rlGCw7jIds3RiXFrG8D8oAjzXzzj1cayKjBI91k-1kA,3140
|
|
8
|
+
viperx/templates/Base_General.ipynb.j2,sha256=f5ZUu65khtvBeCofs3Lvzccn5Krl0nRGu0dv7SzLvzg,2716
|
|
9
|
+
viperx/templates/Base_Kaggle.ipynb.j2,sha256=gPkmFt4cA5UzhRU_gLZ1UzSjEa7Mgel001Eqiw94-fc,2698
|
|
10
|
+
viperx/templates/README.md.j2,sha256=TJNUwTcKnjALrP0MOSs8UBx8zNhI8erRHrc2-8nuMgQ,3445
|
|
11
|
+
viperx/templates/__init__.py.j2,sha256=j-i1NRnKdvk-EgjijSyx-rfOsxWOVSXCyHY2-JEwMDM,93
|
|
12
|
+
viperx/templates/config.py.j2,sha256=ExMbopPQPntmkNgbv9uKooGDjv82gh_XhyQmjJ8MiQg,1456
|
|
13
|
+
viperx/templates/config.yaml.j2,sha256=EyeUfIIGvNIyMSHayh4i_0XVxIeu4Wdf2I6uAgfmSf4,552
|
|
14
|
+
viperx/templates/data_loader.py.j2,sha256=hehewwvBAYJM-acKn6_T4cU5EvUPX7wHAurAQ3z3ET4,3696
|
|
15
|
+
viperx/templates/main.py.j2,sha256=cpXjtc9MS6CnGAZ0RK1jaB3xBqh4r5Q4iwscBVB2pcY,258
|
|
16
|
+
viperx/templates/pyproject.toml.j2,sha256=NGrgIJM_pzjbQ3OAgsT_y3mX2n8UzdAg_mo9AERhGmU,1297
|
|
17
|
+
viperx/templates/viperx_config.yaml.j2,sha256=z-teOb1hTYmoVdXBQqgr6kK5fQGkMpI6KsTzbeF0m4Q,1408
|
|
18
|
+
viperx/utils.py,sha256=c9FKL9eRsszNvinQd3zjvA2bRMVhEQI5F26aLN_PPZE,2578
|
|
19
|
+
viperx-0.9.40.dist-info/WHEEL,sha256=RRVLqVugUmFOqBedBFAmA4bsgFcROUBiSUKlERi0Hcg,79
|
|
20
|
+
viperx-0.9.40.dist-info/entry_points.txt,sha256=Is38BrTuf6unQCLgDE990Rjd9HyGldBSw4Uzy_nMePY,44
|
|
21
|
+
viperx-0.9.40.dist-info/METADATA,sha256=h7IGeSpqB1LJ__SFKtWl_PGaJ1tVoZCARLzynArtGEk,6782
|
|
22
|
+
viperx-0.9.40.dist-info/RECORD,,
|
viperx-0.9.26.dist-info/RECORD
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
viperx/__init__.py,sha256=mRrD-SK9PtwBhqgLzJrvefdViNsXCyBGknzFNsIou1I,51
|
|
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
|
-
viperx/licenses.py,sha256=TPsG1aqNAjtOsvuD6i3SiC5rUK9f3DslPHQkKiV7vxs,12482
|
|
6
|
-
viperx/main.py,sha256=N0pqLnZoWr00XjH1kS5dxbDKEqXvazsVspqLQ_-fF8k,10663
|
|
7
|
-
viperx/templates/Base.ipynb.j2,sha256=rlGCw7jIds3RiXFrG8D8oAjzXzzj1cayKjBI91k-1kA,3140
|
|
8
|
-
viperx/templates/Base_General.ipynb.j2,sha256=f5ZUu65khtvBeCofs3Lvzccn5Krl0nRGu0dv7SzLvzg,2716
|
|
9
|
-
viperx/templates/Base_Kaggle.ipynb.j2,sha256=gPkmFt4cA5UzhRU_gLZ1UzSjEa7Mgel001Eqiw94-fc,2698
|
|
10
|
-
viperx/templates/README.md.j2,sha256=TJNUwTcKnjALrP0MOSs8UBx8zNhI8erRHrc2-8nuMgQ,3445
|
|
11
|
-
viperx/templates/__init__.py.j2,sha256=uq8IYd0SGYX2-cpGb9q5197eYs-ode-g5y2NCGWU5YM,196
|
|
12
|
-
viperx/templates/config.py.j2,sha256=ExMbopPQPntmkNgbv9uKooGDjv82gh_XhyQmjJ8MiQg,1456
|
|
13
|
-
viperx/templates/config.yaml.j2,sha256=IYvjQUioZ9lyzVHVDgwrdjgM6s4pUnCFXwtnydoKe2k,485
|
|
14
|
-
viperx/templates/data_loader.py.j2,sha256=hehewwvBAYJM-acKn6_T4cU5EvUPX7wHAurAQ3z3ET4,3696
|
|
15
|
-
viperx/templates/main.py.j2,sha256=caGN54Hck_EcwT3Aqju52CBuGujSnzU2Hehw8mowzjI,277
|
|
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
|