Skip to content

Commit c502010

Browse files
author
Franziska Geiger
committed
[GR-15977] Add support of environment variables(os.environ) to os.exec
PullRequest: graalpython/529
2 parents 5acf98e + bb881ed commit c502010

File tree

2 files changed

+43
-26
lines changed

2 files changed

+43
-26
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_posix.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,16 @@
3838
# SOFTWARE.
3939

4040
import unittest
41+
import os
42+
import sys
43+
import posix
44+
import stat
4145

4246

4347
class PosixTests(unittest.TestCase):
4448

4549
def test_uname(self):
4650
# just like cpython, a simple smoke test
47-
import posix
4851
uname = posix.uname()
4952
self.assertRaises(TypeError, lambda: posix.uname(1))
5053
self.assertIsNotNone(uname.sysname)
@@ -56,28 +59,31 @@ def test_uname(self):
5659
def test_execv(self):
5760
# test creates a shell script, which again creates a file, to ensure script execution
5861
# Both files are deleted again in the end
59-
import os
60-
import sys
6162
new_file_path, cwd = self.create_file()
62-
# os.execv(new_file_path, [new_file_path, 'the_input'])
6363
os.system("%s -c \"import os; os.execv('%s', ['%s', 'the_input'])\"" % (sys.executable, new_file_path, new_file_path))
6464
assert os.path.isfile(cwd + '/test.txt')
6565
self.delete_file(new_file_path, cwd)
6666

6767
def test_execl(self):
6868
# test creates a shell script, which again creates a file, to ensure script execution
6969
# Both files are deleted again in the end
70-
import os
71-
import sys
7270
new_file_path, cwd = self.create_file()
73-
# os.execl(new_file_path, [new_file_path, 'the_input'])
7471
os.system("%s -c \"import os; os.execl('%s', *['%s', 'the_input'])\"" % (sys.executable, new_file_path, new_file_path))
7572
assert os.path.isfile(cwd + '/test.txt')
7673
self.delete_file(new_file_path, cwd)
7774

75+
def test_execv_with_env(self):
76+
new_file_path, cwd = self.create_file()
77+
with open(new_file_path, 'w') as script:
78+
script.write('#!/bin/sh\n')
79+
script.write('echo $ENV_VAR > {}/test.txt\n'.format(cwd))
80+
os.system("%s -c \"import os; os.environ['ENV_VAR']='the_text'; os.execv('%s', ['%s', 'the_input'])\"" % (sys.executable, new_file_path, new_file_path))
81+
assert os.path.isfile(cwd + '/test.txt')
82+
with open(cwd+'/test.txt', 'r') as result:
83+
assert 'the_text' in result.readline()
84+
self.delete_file(new_file_path, cwd)
85+
7886
def create_file(self):
79-
import os
80-
import stat
8187
cwd = os.getcwd()
8288
new_file_path = os.path.join(cwd , 'myscript.sh')
8389
with open(new_file_path, 'w') as script:
@@ -90,6 +96,5 @@ def create_file(self):
9096
return new_file_path, cwd
9197

9298
def delete_file(self, new_file_path, cwd):
93-
import os
9499
os.remove(new_file_path)
95100
os.remove(cwd + '/test.txt')

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltinsFactory.ConvertPathlikeObjectNodeGen;
101101
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltinsFactory.StatNodeFactory;
102102
import com.oracle.graal.python.builtins.objects.PNone;
103+
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes;
103104
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes.ToBytesNode;
104105
import com.oracle.graal.python.builtins.objects.bytes.PByteArray;
105106
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
@@ -288,34 +289,36 @@ public void postInitialize(PythonCore core) {
288289
((PDict) environAttr).setDictStorage(environ.getDictStorage());
289290
}
290291

291-
@Builtin(name = "execv", minNumOfPositionalArgs = 2)
292+
@Builtin(name = "execv", minNumOfPositionalArgs = 3, declaresExplicitSelf = true)
292293
@GenerateNodeFactory
293294
public abstract static class ExecvNode extends PythonBuiltinNode {
294295

296+
@Child private BytesNodes.ToBytesNode toBytes = BytesNodes.ToBytesNode.create();
297+
295298
@Specialization
296-
Object execute(String path, PList args) {
297-
return doExecute(path, args);
299+
Object execute(PythonModule thisModule, String path, PList args) {
300+
return doExecute(thisModule, path, args);
298301
}
299302

300303
@Specialization
301-
Object execute(PString path, PTuple args) {
302-
return execute(path.getValue(), args);
304+
Object execute(PythonModule thisModule, PString path, PTuple args) {
305+
return execute(thisModule, path.getValue(), args);
303306
}
304307

305308
@Specialization
306-
Object execute(String path, PTuple args) {
309+
Object execute(PythonModule thisModule, String path, PTuple args) {
307310
// in case of execl the PList happens to be in the tuples first entry
308311
Object list = GetItemDynamicNode.getUncached().execute(args.getSequenceStorage(), 0);
309-
return doExecute(path, list instanceof PList ? (PList) list : args);
312+
return doExecute(thisModule, path, list instanceof PList ? (PList) list : args);
310313
}
311314

312315
@Specialization
313-
Object execute(PString path, PList args) {
314-
return doExecute(path.getValue(), args);
316+
Object execute(PythonModule thisModule, PString path, PList args) {
317+
return doExecute(thisModule, path.getValue(), args);
315318
}
316319

317320
@TruffleBoundary
318-
Object doExecute(String path, PSequence args) {
321+
Object doExecute(PythonModule thisModule, String path, PSequence args) {
319322
try {
320323
if (!getContext().isExecutableAccessAllowed()) {
321324
throw raise(OSError, "executable access denied");
@@ -328,22 +331,32 @@ Object doExecute(String path, PSequence args) {
328331
for (int i = 0; i < size; i++) {
329332
cmd[i] = GetItemDynamicNode.getUncached().execute(args.getSequenceStorage(), i).toString();
330333
}
331-
Runtime rt = Runtime.getRuntime();
332-
Process pr = rt.exec(cmd);
333-
// retrieve output from executed script
334+
PDict environ = (PDict) thisModule.getAttribute("environ");
335+
ProcessBuilder builder = new ProcessBuilder(cmd);
336+
Map<String, String> environment = builder.environment();
337+
environ.entries().forEach(entry -> {
338+
environment.put(new String(toBytes.execute(null, entry.key)), new String(toBytes.execute(null, entry.value)));
339+
});
340+
Process pr = builder.start();
334341
BufferedReader bfr = new BufferedReader(new InputStreamReader(pr.getInputStream()));
335342
OutputStream stream = getContext().getEnv().out();
336343
String line = "";
337344
while ((line = bfr.readLine()) != null) {
338345
stream.write(line.getBytes());
346+
stream.write("\n".getBytes());
347+
}
348+
BufferedReader stderr = new BufferedReader(new InputStreamReader(pr.getErrorStream()));
349+
OutputStream errStream = getContext().getEnv().err();
350+
line = "";
351+
while ((line = stderr.readLine()) != null) {
352+
errStream.write(line.getBytes());
353+
errStream.write("\n".getBytes());
339354
}
340-
341355
try {
342356
pr.waitFor();
343357
} catch (InterruptedException e) {
344358
throw new IOException(e);
345359
}
346-
347360
throw new PythonExitException(this, pr.exitValue());
348361
} catch (IOException e) {
349362
throw raise(PythonErrorType.ValueError, "Could not execute script '%s'", e.getMessage());
@@ -362,7 +375,6 @@ String cwd() {
362375
return "";
363376
}
364377
}
365-
366378
}
367379

368380
@Builtin(name = "chdir", minNumOfPositionalArgs = 1)

0 commit comments

Comments
 (0)