sfapi-client 0.5.0__tar.gz → 0.6.0__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.
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/.github/workflows/mkdocs.yml +1 -1
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/PKG-INFO +1 -1
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_async/compute.py +10 -1
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_sync/compute.py +6 -1
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/conftest.py +25 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_jobs.py +24 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_jobs_async.py +25 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/.github/dependabot.yml +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/.github/workflows/black.yml +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/.github/workflows/pypi.yml +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/.github/workflows/pytest.yml +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/.github/workflows/ruff.yml +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/.gitignore +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/.pre-commit-config.yaml +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/LEGAL +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/LICENSE +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/README.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/community/contributing.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/examples/check_current_and_past_jobs.ipynb +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/examples/getting_bearer_token.ipynb +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/examples/run_job_and_check_status.ipynb +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/gen_examples.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/gen_sync.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/index.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/javascript/apiselector.js +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/quickstart/index.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/reference/SUMMARY.txt +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/reference/async/client/index.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/reference/async/compute/index.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/reference/async/exceptions/index.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/reference/async/groups/index.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/reference/async/jobs/index.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/reference/async/paths/index.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/reference/async/projects/index.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/reference/async/resources/index.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/docs/reference/async/users/index.md +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/mkdocs.yml +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/pyproject.toml +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/pytest.ini +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/requirements-dev.txt +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/scripts/run.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/__init__.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_async/__init__.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_async/client.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_async/groups.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_async/jobs.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_async/paths.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_async/projects.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_async/storage.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_async/users.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_compute.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_jobs.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_models/__init__.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_models/job_status_response_sacct.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_models/job_status_response_squeue.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_models/resources.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_monitor.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_sync/__init__.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_sync/client.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_sync/groups.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_sync/jobs.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_sync/paths.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_sync/projects.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_sync/storage.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_sync/users.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_utils.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/client.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/compute.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/exceptions.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/groups.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/jobs.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/paths.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/projects.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/resources.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/storage.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/users.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_api.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_api_async.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_client.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_client_async.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_compute.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_compute_async.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_groups.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_groups_async.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_key.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_paths.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_paths_async.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_projects.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_projects_async.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_resources.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_resources_async.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_storage.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_storage_async.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_users.py +0 -0
- {sfapi_client-0.5.0 → sfapi_client-0.6.0}/tests/test_users_async.py +0 -0
|
@@ -126,10 +126,13 @@ class AsyncCompute(ComputeBase):
|
|
|
126
126
|
return task.result
|
|
127
127
|
|
|
128
128
|
@check_auth
|
|
129
|
-
async def submit_job(
|
|
129
|
+
async def submit_job(
|
|
130
|
+
self, script: Union[str, AsyncRemotePath], args: Optional[List[str]] = None
|
|
131
|
+
) -> AsyncJobSqueue:
|
|
130
132
|
"""Submit a job to the compute resource
|
|
131
133
|
|
|
132
134
|
:param script: Path to file on the compute system, or script to run beginning with `#!`.
|
|
135
|
+
:param args: An optional list of command line arguments to pass to the script.
|
|
133
136
|
:return: Object containing information about the job, its job id, and status on the system.
|
|
134
137
|
"""
|
|
135
138
|
|
|
@@ -150,6 +153,12 @@ class AsyncCompute(ComputeBase):
|
|
|
150
153
|
raise SfApiError(f"Script path not present or is not a file, {script}")
|
|
151
154
|
|
|
152
155
|
data = {"job": script, "isPath": is_path}
|
|
156
|
+
if args is not None:
|
|
157
|
+
if not is_path:
|
|
158
|
+
raise ValueError(
|
|
159
|
+
"Command line arguments cannot be passed when the script is not a file."
|
|
160
|
+
)
|
|
161
|
+
data["args"] = args
|
|
153
162
|
|
|
154
163
|
r = await self.client.post(f"compute/jobs/{self.name}", data)
|
|
155
164
|
r.raise_for_status()
|
|
@@ -127,10 +127,11 @@ class Compute(ComputeBase):
|
|
|
127
127
|
return task.result
|
|
128
128
|
|
|
129
129
|
@check_auth
|
|
130
|
-
def submit_job(self, script: Union[str, RemotePath]) -> JobSqueue:
|
|
130
|
+
def submit_job(self, script: Union[str, RemotePath], args: Optional[List[str]] = None) -> JobSqueue:
|
|
131
131
|
"""Submit a job to the compute resource
|
|
132
132
|
|
|
133
133
|
:param script: Path to file on the compute system, or script to run beginning with `#!`.
|
|
134
|
+
:param args: An optional list of command line arguments to pass to the script.
|
|
134
135
|
:return: Object containing information about the job, its job id, and status on the system.
|
|
135
136
|
"""
|
|
136
137
|
|
|
@@ -151,6 +152,10 @@ class Compute(ComputeBase):
|
|
|
151
152
|
raise SfApiError(f"Script path not present or is not a file, {script}")
|
|
152
153
|
|
|
153
154
|
data = {"job": script, "isPath": is_path}
|
|
155
|
+
if args is not None:
|
|
156
|
+
if not is_path:
|
|
157
|
+
raise ValueError("Command line arguments cannot be passed when the script is not a file.")
|
|
158
|
+
data["args"] = args
|
|
154
159
|
|
|
155
160
|
r = self.client.post(f"compute/jobs/{self.name}", data)
|
|
156
161
|
r.raise_for_status()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import random
|
|
3
3
|
import string
|
|
4
|
+
from io import BytesIO
|
|
4
5
|
from typing import Optional, Union, Dict
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
@@ -23,6 +24,7 @@ class Settings(BaseSettings):
|
|
|
23
24
|
SFAPI_DEV_CLIENT_ID: Optional[str] = None
|
|
24
25
|
SFAPI_DEV_CLIENT_SECRET: Optional[Union[str, Dict]] = None
|
|
25
26
|
TEST_JOB_PATH: Optional[str] = None
|
|
27
|
+
TEST_JOB_ACCOUNT: Optional[str] = None
|
|
26
28
|
TEST_MACHINE: Machine = Machine.perlmutter
|
|
27
29
|
TEST_RESOURCE: Resource = Resource.spin
|
|
28
30
|
TEST_USERNAME: Optional[str] = None
|
|
@@ -75,6 +77,11 @@ def test_job_path():
|
|
|
75
77
|
return settings.TEST_JOB_PATH
|
|
76
78
|
|
|
77
79
|
|
|
80
|
+
@pytest.fixture
|
|
81
|
+
def test_job_account():
|
|
82
|
+
return settings.TEST_JOB_ACCOUNT
|
|
83
|
+
|
|
84
|
+
|
|
78
85
|
@pytest.fixture
|
|
79
86
|
def test_machine():
|
|
80
87
|
return settings.TEST_MACHINE
|
|
@@ -214,3 +221,21 @@ def empty_key_file(tmp_path_factory):
|
|
|
214
221
|
if temp_path.exists():
|
|
215
222
|
(temp_path / "nokey.pem").unlink(missing_ok=True)
|
|
216
223
|
temp_path.rmdir()
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@pytest.fixture
|
|
227
|
+
def test_arg_job_script(test_job_account, test_tmp_dir):
|
|
228
|
+
script = BytesIO(f"""#!/usr/bin/env bash
|
|
229
|
+
#SBATCH --nodes=1
|
|
230
|
+
#SBATCH --constraint=cpu
|
|
231
|
+
#SBATCH --account={test_job_account}
|
|
232
|
+
#SBATCH --qos=debug
|
|
233
|
+
#SBATCH --time=00:01:00
|
|
234
|
+
#SBATCH --output={test_tmp_dir}/slurm-%j.out
|
|
235
|
+
|
|
236
|
+
set -euo pipefail
|
|
237
|
+
|
|
238
|
+
echo "$@"
|
|
239
|
+
""".encode())
|
|
240
|
+
script.filename = "echo-args.sh"
|
|
241
|
+
return script
|
|
@@ -17,6 +17,30 @@ def test_submit(authenticated_client, test_job_path, test_machine):
|
|
|
17
17
|
assert state == JobState.COMPLETED
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def test_submit_with_args(
|
|
21
|
+
authenticated_client, test_machine, test_tmp_dir, test_arg_job_script
|
|
22
|
+
):
|
|
23
|
+
with authenticated_client as client:
|
|
24
|
+
machine = client.compute(test_machine)
|
|
25
|
+
|
|
26
|
+
[remote_dir] = machine.ls(test_tmp_dir, directory=True)
|
|
27
|
+
remote_script = remote_dir.upload(test_arg_job_script)
|
|
28
|
+
try:
|
|
29
|
+
args = ["1", "2", "3"]
|
|
30
|
+
|
|
31
|
+
job = machine.submit_job(remote_script, args)
|
|
32
|
+
|
|
33
|
+
state = job.complete()
|
|
34
|
+
|
|
35
|
+
assert state == JobState.COMPLETED
|
|
36
|
+
|
|
37
|
+
[output_path] = machine.ls(f"{test_tmp_dir}/slurm-{job.jobid}.out")
|
|
38
|
+
output = output_path.download().read()
|
|
39
|
+
assert "1 2 3" in output
|
|
40
|
+
finally:
|
|
41
|
+
machine.run(["rm", "-f", str(remote_script)])
|
|
42
|
+
|
|
43
|
+
|
|
20
44
|
def test_cancel(authenticated_client, test_job_path, test_machine):
|
|
21
45
|
with authenticated_client as client:
|
|
22
46
|
machine = client.compute(test_machine)
|
|
@@ -16,6 +16,31 @@ async def test_submit(async_authenticated_client, test_job_path, test_machine):
|
|
|
16
16
|
assert state == JobState.COMPLETED
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
@pytest.mark.asyncio
|
|
20
|
+
async def test_submit_with_args(
|
|
21
|
+
async_authenticated_client, test_machine, test_tmp_dir, test_arg_job_script
|
|
22
|
+
):
|
|
23
|
+
async with async_authenticated_client as client:
|
|
24
|
+
machine = await client.compute(test_machine)
|
|
25
|
+
|
|
26
|
+
[remote_dir] = await machine.ls(test_tmp_dir, directory=True)
|
|
27
|
+
remote_script = await remote_dir.upload(test_arg_job_script)
|
|
28
|
+
try:
|
|
29
|
+
args = ["1", "2", "3"]
|
|
30
|
+
|
|
31
|
+
job = await machine.submit_job(remote_script, args)
|
|
32
|
+
|
|
33
|
+
state = await job.complete()
|
|
34
|
+
|
|
35
|
+
assert state == JobState.COMPLETED
|
|
36
|
+
|
|
37
|
+
[output_path] = await machine.ls(f"{test_tmp_dir}/slurm-{job.jobid}.out")
|
|
38
|
+
output = (await output_path.download()).read()
|
|
39
|
+
assert "1 2 3" in output
|
|
40
|
+
finally:
|
|
41
|
+
await machine.run(["rm", "-f", str(remote_script)])
|
|
42
|
+
|
|
43
|
+
|
|
19
44
|
@pytest.mark.asyncio
|
|
20
45
|
async def test_cancel(async_authenticated_client, test_job_path, test_machine):
|
|
21
46
|
async with async_authenticated_client as client:
|
|
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
|
|
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
|
{sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_models/job_status_response_sacct.py
RENAMED
|
File without changes
|
{sfapi_client-0.5.0 → sfapi_client-0.6.0}/src/sfapi_client/_models/job_status_response_squeue.py
RENAMED
|
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
|
|
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
|