accli 2.0.6__tar.gz → 2.0.7__tar.gz
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.
- {accli-2.0.6 → accli-2.0.7}/PKG-INFO +1 -1
- {accli-2.0.6 → accli-2.0.7}/accli/AcceleratorTaskDispatcher.py +38 -7
- {accli-2.0.6 → accli-2.0.7}/accli/_version.py +1 -1
- {accli-2.0.6 → accli-2.0.7}/accli.egg-info/PKG-INFO +1 -1
- {accli-2.0.6 → accli-2.0.7}/LICENSE +0 -0
- {accli-2.0.6 → accli-2.0.7}/README.md +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/AcceleratorJobProjectService.py +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/AcceleratorTerminalCliProjectService.py +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/CsvRegionalTimeseriesValidator.py +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/__init__.py +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/__main__.py +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/cleanup-nfs.ps1 +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/cli.py +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/common.py +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/gui.py +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/mount_downloader.py +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/nfs_watcher.py +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/refresh_explorer.ps1 +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/setup-nfs.ps1 +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli/token.py +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli.egg-info/SOURCES.txt +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli.egg-info/dependency_links.txt +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli.egg-info/entry_points.txt +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli.egg-info/requires.txt +0 -0
- {accli-2.0.6 → accli-2.0.7}/accli.egg-info/top_level.txt +0 -0
- {accli-2.0.6 → accli-2.0.7}/pyproject.toml +0 -0
- {accli-2.0.6 → accli-2.0.7}/setup.cfg +0 -0
|
@@ -3,11 +3,12 @@ import zipfile
|
|
|
3
3
|
import hashlib
|
|
4
4
|
import shutil
|
|
5
5
|
import tempfile
|
|
6
|
+
import fnmatch
|
|
6
7
|
from typing import Optional, Dict, List
|
|
7
8
|
|
|
8
9
|
import requests
|
|
9
10
|
from functools import lru_cache
|
|
10
|
-
from pydantic import BaseModel, model_validator
|
|
11
|
+
from pydantic import BaseModel, model_validator, Field
|
|
11
12
|
from accli.AcceleratorTerminalCliProjectService import AcceleratorTerminalCliProjectService
|
|
12
13
|
from accli.token import (
|
|
13
14
|
get_token, get_server_url,
|
|
@@ -48,23 +49,38 @@ def get_file_sha1(file_path):
|
|
|
48
49
|
return sha1_hash.hexdigest()
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
def copy_tree(src, dst):
|
|
52
|
+
def copy_tree(src, dst, include=None, exclude=None, root_src=None):
|
|
52
53
|
"""Recursively copy from src to dst, excluding .git folders."""
|
|
54
|
+
if root_src is None:
|
|
55
|
+
root_src = src
|
|
53
56
|
for item in os.listdir(src):
|
|
54
57
|
src_path = os.path.join(src, item)
|
|
55
58
|
dst_path = os.path.join(dst, item)
|
|
59
|
+
rel_path = os.path.relpath(src_path, root_src)
|
|
56
60
|
|
|
57
61
|
if os.path.isdir(src_path):
|
|
58
62
|
if item == '.git':
|
|
59
63
|
continue
|
|
64
|
+
if exclude:
|
|
65
|
+
excludes = [e.strip() for e in exclude.split(',') if e.strip()]
|
|
66
|
+
if any(fnmatch.fnmatch(rel_path, p) or fnmatch.fnmatch(item, p) for p in excludes):
|
|
67
|
+
continue
|
|
60
68
|
os.makedirs(dst_path, exist_ok=True)
|
|
61
|
-
copy_tree(src_path, dst_path)
|
|
69
|
+
copy_tree(src_path, dst_path, include, exclude, root_src)
|
|
62
70
|
else:
|
|
71
|
+
if exclude:
|
|
72
|
+
excludes = [e.strip() for e in exclude.split(',') if e.strip()]
|
|
73
|
+
if any(fnmatch.fnmatch(rel_path, p) or fnmatch.fnmatch(item, p) for p in excludes):
|
|
74
|
+
continue
|
|
75
|
+
if include:
|
|
76
|
+
includes = [i.strip() for i in include.split(',') if i.strip()]
|
|
77
|
+
if not any(fnmatch.fnmatch(rel_path, p) or fnmatch.fnmatch(item, p) for p in includes):
|
|
78
|
+
continue
|
|
63
79
|
shutil.copy2(src_path, dst_path)
|
|
64
80
|
|
|
65
81
|
|
|
66
82
|
@lru_cache(maxsize=None)
|
|
67
|
-
def push_folder_job(directory):
|
|
83
|
+
def push_folder_job(directory, include=None, exclude=None):
|
|
68
84
|
server_url = get_server_url()
|
|
69
85
|
project_slug = get_project_slug()
|
|
70
86
|
_, access_token, _ = exchange_refresh_token(project_slug)
|
|
@@ -76,7 +92,7 @@ def push_folder_job(directory):
|
|
|
76
92
|
)
|
|
77
93
|
|
|
78
94
|
repo_dir = tempfile.mkdtemp()
|
|
79
|
-
copy_tree(directory, repo_dir)
|
|
95
|
+
copy_tree(directory, repo_dir, include, exclude)
|
|
80
96
|
|
|
81
97
|
if os.path.isfile(f'{repo_dir}/wkube.py'):
|
|
82
98
|
os.remove(f'{repo_dir}/wkube.py')
|
|
@@ -152,7 +168,9 @@ class WKubeTaskMeta(BaseModel):
|
|
|
152
168
|
class WKubeTaskKwargs(BaseModel):
|
|
153
169
|
docker_image: Optional[str] = None
|
|
154
170
|
|
|
155
|
-
job_folder: str = './'
|
|
171
|
+
job_folder: str = Field('./', description="Path to the directory containing job files. Must be a valid existing directory.")
|
|
172
|
+
include: Optional[str] = Field(None, description="Comma-separated wildcard patterns to include (e.g. '*.py, data/*')")
|
|
173
|
+
exclude: Optional[str] = Field(None, description="Comma-separated wildcard patterns to exclude (e.g. '*.txt, .venv/*')")
|
|
156
174
|
|
|
157
175
|
repo_url: Optional[str] = None # required when docker image is not present
|
|
158
176
|
repo_branch: Optional[str] = None # required when docker image is not present
|
|
@@ -172,6 +190,10 @@ class WKubeTaskKwargs(BaseModel):
|
|
|
172
190
|
result = super().model_dump(*args, **kwargs)
|
|
173
191
|
if 'job_folder' in result:
|
|
174
192
|
del result['job_folder']
|
|
193
|
+
if 'include' in result:
|
|
194
|
+
del result['include']
|
|
195
|
+
if 'exclude' in result:
|
|
196
|
+
del result['exclude']
|
|
175
197
|
return result
|
|
176
198
|
|
|
177
199
|
@model_validator(mode="before")
|
|
@@ -179,10 +201,19 @@ class WKubeTaskKwargs(BaseModel):
|
|
|
179
201
|
def validate_root(cls, values):
|
|
180
202
|
if not values.get('docker_image'):
|
|
181
203
|
job_folder = values.get('job_folder', './')
|
|
204
|
+
include = values.get('include')
|
|
205
|
+
exclude = values.get('exclude')
|
|
206
|
+
|
|
207
|
+
if not os.path.isdir(job_folder):
|
|
208
|
+
raise ValueError(
|
|
209
|
+
f"Invalid job_folder: '{job_folder}' does not exist or is not a directory. "
|
|
210
|
+
f"Help: job_folder must be a valid path. You can also use 'include' and 'exclude' "
|
|
211
|
+
f"with comma-separated wildcards (e.g., include='*.py, data/*', exclude='*.txt, .venv/*')."
|
|
212
|
+
)
|
|
182
213
|
|
|
183
214
|
if not (values.get('repo_url') and values.get('repo_branch')):
|
|
184
215
|
remote_url, branch_name = push_folder_job(
|
|
185
|
-
os.path.abspath(job_folder)
|
|
216
|
+
os.path.abspath(job_folder), include, exclude
|
|
186
217
|
)
|
|
187
218
|
values['repo_url'] = remote_url
|
|
188
219
|
values['repo_branch'] = branch_name
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|