Skip to content

Commit 71a85b2

Browse files
authored
Merge pull request #469 from simvue-io/add_process_cwd
Add cwd option to add_process
2 parents 163c4d0 + fdd10ac commit 71a85b2

File tree

3 files changed

+69
-2
lines changed

3 files changed

+69
-2
lines changed

simvue/executor.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def _execute_process(
4040
completion_callback: typing.Optional[CompletionCallback] = None,
4141
completion_trigger: typing.Optional[multiprocessing.synchronize.Event] = None,
4242
environment: typing.Optional[typing.Dict[str, str]] = None,
43+
cwd: typing.Optional[pathlib.Path] = None,
4344
) -> tuple[subprocess.Popen, typing.Optional[threading.Thread]]:
4445
thread_out = None
4546

@@ -51,6 +52,7 @@ def _execute_process(
5152
stderr=err,
5253
universal_newlines=True,
5354
env=environment,
55+
cwd=cwd,
5456
)
5557

5658
if completion_callback or completion_trigger:
@@ -138,6 +140,7 @@ def add_process(
138140
script: typing.Optional[pathlib.Path] = None,
139141
input_file: typing.Optional[pathlib.Path] = None,
140142
env: typing.Optional[typing.Dict[str, str]] = None,
143+
cwd: typing.Optional[pathlib.Path] = None,
141144
completion_callback: typing.Optional[CompletionCallback] = None,
142145
completion_trigger: typing.Optional[multiprocessing.synchronize.Event] = None,
143146
**kwargs,
@@ -186,6 +189,8 @@ def callback_function(status_code: int, std_out: str, std_err: str) -> None:
186189
you should provide it as such and perform the upload manually, by default None
187190
env : typing.Dict[str, str], optional
188191
environment variables for process
192+
cwd: typing.Optional[pathlib.Path], optional
193+
working directory to execute the process within
189194
completion_callback : typing.Callable | None, optional
190195
callback to run when process terminates
191196
completion_trigger : multiprocessing.Event | None, optional
@@ -252,6 +257,7 @@ def callback_function(status_code: int, std_out: str, std_err: str) -> None:
252257
completion_callback,
253258
completion_trigger,
254259
env,
260+
cwd,
255261
)
256262
)
257263

simvue/run.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,7 @@ def add_process(
660660
] = None,
661661
completion_trigger: typing.Optional[multiprocessing.synchronize.Event] = None,
662662
env: typing.Optional[typing.Dict[str, str]] = None,
663+
cwd: typing.Optional[pathlib.Path] = None,
663664
**cmd_kwargs,
664665
) -> None:
665666
"""Add a process to be executed to the executor.
@@ -703,10 +704,10 @@ def callback_function(status_code: int, std_out: str, std_err: str) -> None:
703704
positional argument, by default None
704705
*positional_arguments : Any, ..., optional
705706
all other positional arguments are taken to be part of the command to execute
706-
script : str | None, optional
707+
script : pydantic.FilePath | None, optional
707708
the script to run, note this only work if the script is not an option, if this is the case
708709
you should provide it as such and perform the upload manually, by default None
709-
input_file : str | None, optional
710+
input_file : pydantic.FilePath | None, optional
710711
the input file to run, note this only work if the input file is not an option, if this is the case
711712
you should provide it as such and perform the upload manually, by default None
712713
completion_callback : typing.Callable | None, optional
@@ -715,6 +716,9 @@ def callback_function(status_code: int, std_out: str, std_err: str) -> None:
715716
this trigger event is set when the processes completes
716717
env : typing.Dict[str, str], optional
717718
environment variables for process
719+
cwd: typing.Optional[pathlib.Path], optional
720+
working directory to execute the process within. Note that executable, input and script file paths should
721+
be absolute or relative to the directory where this method is called, not relative to the new working directory.
718722
**kwargs : Any, ..., optional
719723
all other keyword arguments are interpreted as options to the command
720724
"""
@@ -773,6 +777,7 @@ def callback_function(status_code: int, std_out: str, std_err: str) -> None:
773777
completion_callback=completion_callback, # type: ignore
774778
completion_trigger=completion_trigger,
775779
env=env,
780+
cwd=cwd,
776781
**cmd_kwargs,
777782
)
778783

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import unittest
2+
import uuid
3+
import tempfile
4+
import simvue
5+
from simvue import Run
6+
import os
7+
import filecmp
8+
import time
9+
class TestRunProcess(unittest.TestCase):
10+
def test_processes_cwd(self):
11+
"""Check that cwd argument works correctly in add_process.
12+
13+
Create a temporary directory, and a python file inside that directory. Check that if only the file name
14+
is passed to add_process as the script, and the directory is specified as the cwd argument, that the process
15+
runs correctly and the script is uploaded as expected.
16+
"""
17+
with tempfile.TemporaryDirectory() as temp_dir:
18+
with tempfile.NamedTemporaryFile(dir=temp_dir, suffix=".py") as temp_file:
19+
with open(temp_file.name, "w") as out_f:
20+
out_f.writelines([
21+
"import os\n",
22+
"f = open('new_file.txt', 'w')\n",
23+
"f.write('Test Line')\n",
24+
"f.close()"
25+
])
26+
import pdb; pdb.set_trace()
27+
28+
with Run() as run:
29+
run.init(
30+
name='test-%s' % str(uuid.uuid4()),
31+
folder='/test-%s' % str(uuid.uuid4())
32+
)
33+
run_id = run._id
34+
run.add_process(
35+
identifier="sleep_10_process",
36+
executable="python",
37+
script=temp_file.name,
38+
cwd=temp_dir
39+
)
40+
time.sleep(1)
41+
run.save_file(os.path.join(temp_dir, "new_file.txt"), 'output')
42+
43+
client = simvue.Client()
44+
45+
# Check that the script was uploaded to the run correctly
46+
os.makedirs(os.path.join(temp_dir, "downloaded"))
47+
client.get_artifact_as_file(run_id, os.path.basename(temp_file.name), path=os.path.join(temp_dir, "downloaded"))
48+
assert filecmp.cmp(os.path.join(temp_dir, "downloaded", os.path.basename(temp_file.name)), temp_file.name)
49+
50+
client.get_artifact_as_file(run_id, "new_file.txt", path=os.path.join(temp_dir, "downloaded"))
51+
new_file = open(os.path.join(temp_dir, "downloaded", "new_file.txt"), "r")
52+
assert new_file.read() == "Test Line"
53+
new_file.close()
54+
55+
if __name__ == '__main__':
56+
unittest.main()

0 commit comments

Comments
 (0)