fluxqueue-cli 0.1.0b4__tar.gz → 0.1.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fluxqueue-cli
3
- Version: 0.1.0b4
3
+ Version: 0.1.1
4
4
  Summary: A command-line tool for installing and running FluxQueue workers
5
5
  Author-Email: Giorgi Merebashvili <mereba2627@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -9,9 +9,7 @@ Requires-Python: <3.15,>=3.11
9
9
  Requires-Dist: typer>=0.17.0
10
10
  Requires-Dist: requests>=2.29.0
11
11
  Provides-Extra: dev
12
- Requires-Dist: black; extra == "dev"
13
12
  Requires-Dist: ruff; extra == "dev"
14
- Requires-Dist: pdm; extra == "dev"
15
13
  Description-Content-Type: text/markdown
16
14
 
17
15
  ## FluxQueue CLI
@@ -34,7 +32,7 @@ fluxqueue worker install
34
32
 
35
33
  ## Starting a Worker
36
34
 
37
- To start a worker, provide the path to the module where your tasks are defined and exported:
35
+ To start a worker, provide the path to the module where your tasks are exported:
38
36
 
39
37
  ```bash
40
38
  fluxqueue start --tasks-module-path src/tasks
@@ -18,7 +18,7 @@ fluxqueue worker install
18
18
 
19
19
  ## Starting a Worker
20
20
 
21
- To start a worker, provide the path to the module where your tasks are defined and exported:
21
+ To start a worker, provide the path to the module where your tasks are exported:
22
22
 
23
23
  ```bash
24
24
  fluxqueue start --tasks-module-path src/tasks
@@ -21,26 +21,22 @@ dependencies = [
21
21
  "typer>=0.17.0",
22
22
  "requests>=2.29.0",
23
23
  ]
24
- version = "0.1.0b4"
24
+ version = "0.1.1"
25
25
 
26
26
  [project.scripts]
27
27
  fluxqueue = "fluxqueue_cli.cli:app"
28
28
 
29
29
  [project.optional-dependencies]
30
30
  dev = [
31
- "black",
32
31
  "ruff",
33
- "pdm",
34
32
  ]
35
33
 
36
34
  [tool.pdm.version]
37
35
  source = "file"
38
36
  path = "src/fluxqueue_cli/__init__.py"
39
37
 
40
- [tool.black]
41
- line-length = 79
42
- include = "\\.pyi?$"
43
- preview = true
38
+ [tool.ruff]
39
+ line-length = 88
44
40
 
45
41
  [tool.ruff.lint]
46
42
  select = [
@@ -0,0 +1 @@
1
+ __version__ = "0.1.1"
@@ -78,11 +78,21 @@ def start(
78
78
  @worker_app.command(name="install")
79
79
  def worker_install(
80
80
  version: Annotated[
81
+ str | None,
82
+ typer.Option(help="Version to install. If omitted, installs the latest."),
83
+ ] = None,
84
+ path: Annotated[
81
85
  str | None,
82
86
  typer.Option(
83
- help="Version to install. If omitted, installs the latest."
87
+ help="Custom destination path. Otherwise installation will require sudo permissions."
84
88
  ),
85
89
  ] = None,
90
+ force: Annotated[
91
+ bool,
92
+ typer.Option(
93
+ help="In case of already installed worker in the destination path using this flag will force the installation."
94
+ ),
95
+ ] = False,
86
96
  ):
87
97
  """
88
98
  Download and install [bold]fluxqueue-worker[/bold].
@@ -95,16 +105,14 @@ def worker_install(
95
105
  "Invalid version. Please use versions like: 0.1.0, 0.2.3"
96
106
  )
97
107
 
98
- download_and_install(version)
108
+ download_and_install(version, path, force)
99
109
 
100
110
 
101
111
  @worker_app.command(name="update")
102
112
  def worker_update(
103
113
  version: Annotated[
104
114
  str | None,
105
- typer.Option(
106
- help="Version to update to. If omitted, updates to the latest."
107
- ),
115
+ typer.Option(help="Version to update to. If omitted, updates to the latest."),
108
116
  ] = None,
109
117
  no_backup: Annotated[
110
118
  bool,
@@ -0,0 +1,18 @@
1
+ import os
2
+ import tomllib
3
+ from pathlib import Path
4
+
5
+
6
+ def get_fluxqueue_config():
7
+ toml_path = Path(os.getcwd()) / "pyproject.toml"
8
+
9
+ if not toml_path.exists():
10
+ return {}
11
+
12
+ try:
13
+ with open(toml_path, "rb") as f:
14
+ data = tomllib.load(f)
15
+ return data.get("tool", {}).get("fluxqueue_cli", {})
16
+
17
+ except (tomllib.TOMLDecodeError, PermissionError):
18
+ return {}
@@ -3,12 +3,15 @@ import platform
3
3
  import shutil
4
4
  import subprocess
5
5
  import sys
6
+ import sysconfig
6
7
  import tarfile
7
8
  import tempfile
9
+ import zipfile
8
10
  from pathlib import Path
9
11
 
10
12
  import requests
11
13
 
14
+ from fluxqueue_cli.config import get_fluxqueue_config
12
15
  from fluxqueue_cli.exceptions import (
13
16
  AlreadyInstalledError,
14
17
  BinaryNotFoundError,
@@ -19,9 +22,14 @@ from fluxqueue_cli.exceptions import (
19
22
 
20
23
  REPO = "CCXLV/fluxqueue"
21
24
  BINARY_NAME = "fluxqueue-worker"
22
- INSTALL_DIR = "/usr/local/bin" if platform.system() != "Windows" else "C:\\bin"
25
+ if platform.system() == "Windows":
26
+ user_profile = os.environ.get("USERPROFILE", os.path.expanduser("~"))
27
+ install_dir = os.path.join(user_profile, ".fluxqueue", "bin")
28
+ INSTALL_DIR = install_dir
29
+ else:
30
+ INSTALL_DIR = "/usr/local/bin"
23
31
 
24
- # TODO: Refactor the headers since the token won't be needed when launched to public
32
+ # TODO: Add startup wrapper support
25
33
 
26
34
 
27
35
  def start_worker(
@@ -32,6 +40,9 @@ def start_worker(
32
40
  queue: str,
33
41
  save_dead_tasks=False,
34
42
  ):
43
+ config = get_fluxqueue_config()
44
+ worker_path = config.get("worker_path", "fluxqueue-worker")
45
+
35
46
  # fmt: off
36
47
  arguments = [
37
48
  "--concurrency", str(concurrency),
@@ -44,18 +55,28 @@ def start_worker(
44
55
  if save_dead_tasks:
45
56
  arguments.append("--save-dead-tasks")
46
57
 
58
+ lib_dir = sysconfig.get_config_var("LIBDIR")
59
+ env = os.environ.copy()
60
+ if lib_dir:
61
+ current_ld = env.get("LD_LIBRARY_PATH", "")
62
+ env["LD_LIBRARY_PATH"] = f"{lib_dir}:{current_ld}" if current_ld else lib_dir
63
+
47
64
  subprocess.run(
48
- ["fluxqueue-worker", *arguments],
65
+ [worker_path, *arguments],
49
66
  stdin=sys.stdin,
50
67
  stdout=sys.stdout,
51
68
  stderr=sys.stderr,
69
+ env=env,
52
70
  )
53
71
 
54
72
 
55
73
  def get_worker_version() -> str | None:
74
+ config = get_fluxqueue_config()
75
+ worker_path = config.get("worker_path", "fluxqueue-worker")
76
+
56
77
  try:
57
78
  result = subprocess.run(
58
- ["fluxqueue-worker", "--version"],
79
+ [worker_path, "--version"],
59
80
  check=True,
60
81
  capture_output=True,
61
82
  text=True,
@@ -140,14 +161,11 @@ def download_worker_binary(version: str | None = None):
140
161
 
141
162
 
142
163
  def install_worker(
143
- *, actual_file_name: str, temp_file_path: str, overwrite=False
164
+ *,
165
+ dest_path: Path,
166
+ actual_file_name: str,
167
+ temp_file_path: str,
144
168
  ):
145
- dest_path = Path(INSTALL_DIR) / BINARY_NAME
146
- if dest_path.exists() and not overwrite:
147
- raise FileExistsError(
148
- f"fluxqueue-worker is already installed at {dest_path}"
149
- )
150
-
151
169
  # Linux
152
170
  if actual_file_name.endswith(".tar.gz"):
153
171
  with tarfile.open(temp_file_path, "r:gz") as tar:
@@ -161,27 +179,103 @@ def install_worker(
161
179
  except:
162
180
  delete_installed_files(temp_file_path)
163
181
  raise
164
- # TODO: Implement Windows
182
+ # macOS + Windows
183
+ elif actual_file_name.endswith(".zip"):
184
+ with tempfile.TemporaryDirectory() as extract_temp_dir:
185
+ with zipfile.ZipFile(temp_file_path, "r") as zip_file:
186
+ zip_file.extractall(extract_temp_dir)
187
+
188
+ exe_name = (
189
+ f"{BINARY_NAME}.exe" if platform.system() == "Windows" else BINARY_NAME
190
+ )
191
+ found_binary = None
192
+
193
+ for root, _dirs, files in os.walk(extract_temp_dir):
194
+ if exe_name in files:
195
+ found_binary = Path(root) / exe_name
196
+ break
197
+
198
+ if not found_binary:
199
+ for root, _dirs, files in os.walk(extract_temp_dir):
200
+ for file in files:
201
+ if BINARY_NAME in file and (
202
+ platform.system() != "Windows" or file.endswith(".exe")
203
+ ):
204
+ found_binary = Path(root) / file
205
+ break
206
+ if found_binary:
207
+ break
208
+
209
+ if not found_binary:
210
+ try:
211
+ os.remove(temp_file_path)
212
+ except PermissionError:
213
+ raise PermissionError(
214
+ f"Permission denied: Could not remove temporary file {temp_file_path}. "
215
+ "Please remove it manually."
216
+ ) from None
217
+ except OSError as e:
218
+ if e.errno == 13: # Permission denied (EACCES)
219
+ raise PermissionError(
220
+ f"Permission denied: Could not remove temporary file {temp_file_path}. "
221
+ "Please remove it manually."
222
+ ) from e
223
+ raise BinaryNotFoundError(
224
+ f"Could not find {exe_name} in the downloaded archive."
225
+ )
226
+
227
+ dest_path.parent.mkdir(parents=True, exist_ok=True)
228
+ shutil.copy2(found_binary, dest_path)
229
+
230
+ if platform.system() != "Windows":
231
+ os.chmod(dest_path, 0o755)
232
+ try:
233
+ os.remove(temp_file_path)
234
+ except PermissionError:
235
+ raise PermissionError(
236
+ f"Permission denied: Could not remove temporary file {temp_file_path}. "
237
+ "Please remove it manually."
238
+ ) from None
239
+ except OSError as e:
240
+ if e.errno == 13: # Permission denied (EACCES)
241
+ raise PermissionError(
242
+ f"Permission denied: Could not remove temporary file {temp_file_path}. "
243
+ "Please remove it manually."
244
+ ) from e
245
+ raise
165
246
  else:
166
247
  delete_installed_files(temp_file_path)
167
- raise NotImplementedError(
168
- "Only .tar.gz installation is implemented yet."
169
- )
248
+ raise NotImplementedError("Only .tar.gz installation is implemented yet.")
249
+
250
+ print(f"Successfully installed {BINARY_NAME} to {dest_path}")
170
251
 
171
- print(f"Successfully installed {BINARY_NAME} to {INSTALL_DIR}")
172
252
 
253
+ def download_and_install(
254
+ version: str | None = None, custom_dest_path: str | None = None, force: bool = False
255
+ ):
256
+ dest_path = get_destination_path(custom_dest_path, force)
173
257
 
174
- def download_and_install(version: str | None = None):
175
- if shutil.which("fluxqueue-worker"):
258
+ if not custom_dest_path and shutil.which("fluxqueue-worker"):
176
259
  raise AlreadyInstalledError(
177
260
  "fluxqueue-worker is already installed, use `fluxqueue worker update` command to update it."
178
261
  )
179
262
 
180
263
  temp_path, target_name = download_worker_binary(version)
181
- install_worker(actual_file_name=target_name, temp_file_path=temp_path)
264
+ install_worker(
265
+ dest_path=dest_path,
266
+ actual_file_name=target_name,
267
+ temp_file_path=temp_path,
268
+ )
269
+
182
270
 
271
+ def update_worker(
272
+ *,
273
+ version: str | None = None,
274
+ custom_dest_path: str | None = None,
275
+ no_backup: bool = False,
276
+ ):
277
+ dest_path = get_destination_path(custom_dest_path, no_backup)
183
278
 
184
- def update_worker(*, version: str | None = None, no_backup: bool = False):
185
279
  current_version = get_worker_version()
186
280
 
187
281
  if not current_version:
@@ -193,21 +287,37 @@ def update_worker(*, version: str | None = None, no_backup: bool = False):
193
287
  if version:
194
288
  print(f"Desired Version: {version}")
195
289
 
196
- dest_path = os.path.join(INSTALL_DIR, BINARY_NAME)
290
+ backup_suffix = ".exe.backup" if platform.system() == "Windows" else ".backup"
291
+
197
292
  if not no_backup:
198
293
  new_name = os.path.join(
199
- INSTALL_DIR, f"{BINARY_NAME}-{current_version}.backup"
294
+ INSTALL_DIR, f"{BINARY_NAME}-{current_version}{backup_suffix}"
200
295
  )
201
296
  os.rename(dest_path, new_name)
202
297
 
203
298
  temp_path, target_name = download_worker_binary(version)
204
299
  install_worker(
300
+ dest_path=dest_path,
205
301
  actual_file_name=target_name,
206
302
  temp_file_path=temp_path,
207
- overwrite=no_backup,
208
303
  )
209
304
 
210
305
 
306
+ def get_destination_path(custom_dest_path: str | None = None, force: bool = False):
307
+ dest_dir = Path(custom_dest_path) if custom_dest_path else Path(INSTALL_DIR)
308
+ if platform.system() == "Windows":
309
+ dest_path = dest_dir / f"{BINARY_NAME}.exe"
310
+ else:
311
+ dest_path = dest_dir / BINARY_NAME
312
+
313
+ if dest_path.exists() and not force:
314
+ raise FileExistsError(
315
+ f"fluxqueue-worker is already installed at {dest_path}. Use --force flag for forcing the installation."
316
+ )
317
+
318
+ return dest_path
319
+
320
+
211
321
  def delete_installed_files(temp_path: str):
212
322
  os.remove(BINARY_NAME)
213
323
  os.remove(temp_path)
@@ -1 +0,0 @@
1
- __version__ = "0.1.0b4"
File without changes