Skip to content

Commit def4639

Browse files
committed
Create helper to access os.environ from builtins and fix reading PATH to determine toolchain
1 parent bc94b45 commit def4639

File tree

6 files changed

+160
-71
lines changed

6 files changed

+160
-71
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
41+
def test_sysconfig():
42+
import os, sysconfig
43+
os.environ["PATH"] += os.pathsep + r" : \ " + os.pathsep;
44+
sysconfig.get_config_vars()
45+
# must not fail

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import java.lang.reflect.Method;
7676
import java.nio.charset.StandardCharsets;
7777
import java.nio.file.Files;
78+
import java.nio.file.InvalidPathException;
7879
import java.nio.file.Path;
7980
import java.nio.file.Paths;
8081
import java.util.Arrays;
@@ -130,6 +131,7 @@
130131
import com.oracle.graal.python.builtins.objects.str.PString;
131132
import com.oracle.graal.python.builtins.objects.str.StringUtils;
132133
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
134+
import com.oracle.graal.python.lib.OsEnvironGetNode;
133135
import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs;
134136
import com.oracle.graal.python.lib.PyObjectGetItem;
135137
import com.oracle.graal.python.nodes.ErrorMessages;
@@ -631,14 +633,22 @@ private static PKeyword[] fromToolchain() {
631633

632634
private static int which() {
633635
CompilerAsserts.neverPartOfCompilation();
634-
String path = System.getenv("PATH");
636+
Env env = PythonContext.get(null).getEnv();
637+
TruffleString tspath = OsEnvironGetNode.executeUncached(T_PATH);
638+
if (tspath == null) {
639+
return -1;
640+
}
641+
String path = tspath.toJavaStringUncached();
635642
if (path != null) {
636643
for (int i = 0; i < C_COMPILER_PRECEDENCE.length; i++) {
637644
int last = 0;
638645
for (int j = path.indexOf(File.pathSeparatorChar); j != -1; j = path.indexOf(File.pathSeparatorChar, last)) {
639-
Path resolvedProgramName = Paths.get(path.substring(last, j)).resolve(C_COMPILER_PRECEDENCE[i]);
640-
if (Files.isExecutable(resolvedProgramName)) {
641-
return i;
646+
try {
647+
if (env.getPublicTruffleFile(path.substring(last, j)).resolve(C_COMPILER_PRECEDENCE[i]).isExecutable()) {
648+
return i;
649+
}
650+
} catch (UnsupportedOperationException | IllegalArgumentException e) {
651+
// skip
642652
}
643653
/*
644654
* next start is the char after the separator because we have "path0:path1"

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

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@
111111
import static com.oracle.graal.python.nodes.ErrorMessages.WARN_IGNORE_UNIMPORTABLE_BREAKPOINT_S;
112112
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___;
113113
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__;
114-
import static com.oracle.graal.python.nodes.SpecialMethodNames.T_GET;
115114
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SIZEOF__;
116115
import static com.oracle.graal.python.nodes.StringLiterals.T_BACKSLASHREPLACE;
117116
import static com.oracle.graal.python.nodes.StringLiterals.T_BASE_PREFIX;
@@ -189,6 +188,7 @@
189188
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
190189
import com.oracle.graal.python.builtins.objects.tuple.StructSequence;
191190
import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins;
191+
import com.oracle.graal.python.lib.OsEnvironGetNode;
192192
import com.oracle.graal.python.lib.PyExceptionInstanceCheckNode;
193193
import com.oracle.graal.python.lib.PyFloatAsDoubleNode;
194194
import com.oracle.graal.python.lib.PyFloatCheckExactNode;
@@ -222,7 +222,6 @@
222222
import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
223223
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
224224
import com.oracle.graal.python.nodes.object.GetClassNode;
225-
import com.oracle.graal.python.nodes.util.CannotCastException;
226225
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
227226
import com.oracle.graal.python.nodes.util.ExceptionStateNodes.GetCaughtExceptionNode;
228227
import com.oracle.graal.python.runtime.PosixSupportLibrary;
@@ -1703,38 +1702,21 @@ static Object doHook(VirtualFrame frame, PythonModule sys, Object obj,
17031702
@GenerateNodeFactory
17041703
abstract static class BreakpointHookNode extends PythonBuiltinNode {
17051704
static final TruffleString T_VAL_PDB_SETTRACE = tsLiteral("pdb.set_trace");
1706-
static final TruffleString T_MOD_OS = tsLiteral("os");
1707-
static final TruffleString T_ATTR_ENVIRON = tsLiteral("environ");
1708-
1709-
private static TruffleString getEnvVar(VirtualFrame frame, Node inliningTarget, PyImportImport importNode,
1710-
PyObjectGetAttr getAttr, PyObjectCallMethodObjArgs callMethodObjArgs,
1711-
CastToTruffleStringNode castToStringNode) {
1712-
Object os = importNode.execute(frame, inliningTarget, T_MOD_OS);
1713-
final Object environ = getAttr.execute(frame, inliningTarget, os, T_ATTR_ENVIRON);
1714-
Object var = callMethodObjArgs.execute(frame, inliningTarget, environ, T_GET, T_PYTHONBREAKPOINT);
1715-
try {
1716-
return castToStringNode.execute(inliningTarget, var);
1717-
} catch (CannotCastException cce) {
1718-
return null;
1719-
}
1720-
}
17211705

17221706
@Specialization
17231707
Object doHook(VirtualFrame frame, Object[] args, PKeyword[] keywords,
17241708
@Bind("this") Node inliningTarget,
17251709
@Cached CallNode callNode,
17261710
@Cached PyObjectGetAttr getAttr,
17271711
@Cached PyImportImport importNode,
1728-
@Cached PyObjectCallMethodObjArgs callMethodObjArgs,
17291712
@Cached IsBuiltinObjectProfile attrErrorProfile,
1730-
@Cached CastToTruffleStringNode castToStringNode,
17311713
@Cached BuiltinFunctions.IsInstanceNode isInstanceNode,
17321714
@Cached WarningsModuleBuiltins.WarnNode warnNode,
17331715
@Cached TruffleString.CodePointLengthNode codePointLengthNode,
17341716
@Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode,
17351717
@Cached TruffleString.LastIndexOfCodePointNode lastIndexOfCodePointNode,
17361718
@Cached TruffleString.SubstringNode substringNode) {
1737-
TruffleString hookName = getEnvVar(frame, inliningTarget, importNode, getAttr, callMethodObjArgs, castToStringNode);
1719+
TruffleString hookName = OsEnvironGetNode.executeUncached(T_PYTHONBREAKPOINT);
17381720
if (hookName == null || hookName.isEmpty()) {
17391721
hookName = T_VAL_PDB_SETTRACE;
17401722
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java

Lines changed: 9 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@
4545
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
4646
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError;
4747
import static com.oracle.graal.python.builtins.modules.SSLModuleBuiltins.LOGGER;
48-
import static com.oracle.graal.python.nodes.BuiltinNames.T_NT;
49-
import static com.oracle.graal.python.nodes.BuiltinNames.T_POSIX;
5048
import static com.oracle.graal.python.nodes.ErrorMessages.S;
5149
import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
5250
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
@@ -89,26 +87,23 @@
8987
import com.oracle.graal.python.builtins.CoreFunctions;
9088
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
9189
import com.oracle.graal.python.builtins.PythonBuiltins;
92-
import com.oracle.graal.python.builtins.PythonOS;
9390
import com.oracle.graal.python.builtins.modules.SSLModuleBuiltins;
9491
import com.oracle.graal.python.builtins.objects.PNone;
9592
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary;
9693
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
9794
import com.oracle.graal.python.builtins.objects.bytes.PBytesLike;
98-
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
99-
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem;
10095
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.ToByteArrayNode;
10196
import com.oracle.graal.python.builtins.objects.dict.PDict;
10297
import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum;
10398
import com.oracle.graal.python.builtins.objects.function.PKeyword;
10499
import com.oracle.graal.python.builtins.objects.list.PList;
105-
import com.oracle.graal.python.builtins.objects.module.PythonModule;
106100
import com.oracle.graal.python.builtins.objects.socket.PSocket;
107101
import com.oracle.graal.python.builtins.objects.ssl.CertUtils.NeedsPasswordException;
108102
import com.oracle.graal.python.builtins.objects.ssl.CertUtils.NoCertificateFoundException;
109103
import com.oracle.graal.python.builtins.objects.str.StringNodes;
110104
import com.oracle.graal.python.builtins.objects.type.TpSlots;
111105
import com.oracle.graal.python.builtins.objects.type.TypeNodes;
106+
import com.oracle.graal.python.lib.OsEnvironGetNode;
112107
import com.oracle.graal.python.lib.PyCallableCheckNode;
113108
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
114109
import com.oracle.graal.python.lib.PyNumberIndexNode;
@@ -119,7 +114,6 @@
119114
import com.oracle.graal.python.nodes.PGuards;
120115
import com.oracle.graal.python.nodes.PNodeWithContext;
121116
import com.oracle.graal.python.nodes.PRaiseNode;
122-
import com.oracle.graal.python.nodes.attributes.GetAttributeNode;
123117
import com.oracle.graal.python.nodes.call.CallNode;
124118
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
125119
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
@@ -145,7 +139,6 @@
145139
import com.oracle.truffle.api.dsl.Cached.Shared;
146140
import com.oracle.truffle.api.dsl.Fallback;
147141
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
148-
import com.oracle.truffle.api.dsl.NeverDefault;
149142
import com.oracle.truffle.api.dsl.NodeFactory;
150143
import com.oracle.truffle.api.dsl.Specialization;
151144
import com.oracle.truffle.api.frame.VirtualFrame;
@@ -156,8 +149,6 @@
156149
@CoreFunctions(extendClasses = PythonBuiltinClassType.PSSLContext)
157150
public final class SSLContextBuiltins extends PythonBuiltins {
158151

159-
private static final TruffleString T_ENVIRON = tsLiteral("environ");
160-
161152
public static final TpSlots SLOTS = SSLContextBuiltinsSlotsGen.SLOTS;
162153

163154
@Override
@@ -618,27 +609,15 @@ static Object pha(@SuppressWarnings("unused") PSSLContext self) {
618609
@Builtin(name = "set_default_verify_paths", minNumOfPositionalArgs = 1)
619610
@GenerateNodeFactory
620611
abstract static class SetDefaultVerifyPathsNode extends PythonUnaryBuiltinNode {
612+
static final TruffleString T_SSL_CERT_FILE = tsLiteral("SSL_CERT_FILE");
613+
static final TruffleString T_SSL_CERT_DIR = tsLiteral("SSL_CERT_DIR");
614+
621615
@Specialization
622616
Object set(VirtualFrame frame, PSSLContext self,
623617
@Bind("this") Node inliningTarget,
624-
@Cached PyUnicodeFSDecoderNode asPath,
625-
@Cached("createEnvironLookup()") GetAttributeNode getAttribute,
626-
@Cached HashingStorageGetItem getItem,
627-
@Cached("createCertFileKey()") PBytes certFileKey,
628-
@Cached("createCertDirKey()") PBytes certDirKey,
629-
@Cached TruffleString.ToJavaStringNode toJavaStringNode) {
630-
631-
PythonModule posix;
632-
if (PythonOS.getPythonOS() == PythonOS.PLATFORM_WIN32) {
633-
posix = getContext().lookupBuiltinModule(T_NT);
634-
} else {
635-
posix = getContext().lookupBuiltinModule(T_POSIX);
636-
}
637-
PDict environ = (PDict) getAttribute.executeObject(frame, posix);
638-
HashingStorage storage = environ.getDictStorage();
639-
640-
TruffleFile file = toTruffleFile(frame, asPath, getItem.execute(frame, inliningTarget, storage, certFileKey), toJavaStringNode);
641-
TruffleFile path = toTruffleFile(frame, asPath, getItem.execute(frame, inliningTarget, storage, certDirKey), toJavaStringNode);
618+
@Cached PyUnicodeFSDecoderNode asPath) {
619+
TruffleFile file = toTruffleFile(frame, asPath, OsEnvironGetNode.executeUncached(T_SSL_CERT_FILE));
620+
TruffleFile path = toTruffleFile(frame, asPath, OsEnvironGetNode.executeUncached(T_SSL_CERT_DIR));
642621
if (file != null || path != null) {
643622
LOGGER.fine(() -> String.format("set_default_verify_paths file: %s. path: %s", file != null ? file.getPath() : "None", path != null ? path.getPath() : "None"));
644623
try {
@@ -653,30 +632,13 @@ Object set(VirtualFrame frame, PSSLContext self,
653632
return PNone.NONE;
654633
}
655634

656-
@NeverDefault
657-
@TruffleBoundary
658-
protected PBytes createCertFileKey() {
659-
return PFactory.createBytes(PythonLanguage.get(null), "SSL_CERT_FILE".getBytes());
660-
}
661-
662-
@NeverDefault
663-
@TruffleBoundary
664-
protected PBytes createCertDirKey() {
665-
return PFactory.createBytes(PythonLanguage.get(null), "SSL_CERT_DIR".getBytes());
666-
}
667-
668-
@NeverDefault
669-
protected static GetAttributeNode createEnvironLookup() {
670-
return GetAttributeNode.create(T_ENVIRON);
671-
}
672-
673-
private TruffleFile toTruffleFile(VirtualFrame frame, PyUnicodeFSDecoderNode asPath, Object path, TruffleString.ToJavaStringNode toJavaStringNode) throws PException {
635+
private TruffleFile toTruffleFile(VirtualFrame frame, PyUnicodeFSDecoderNode asPath, TruffleString path) throws PException {
674636
if (path == null) {
675637
return null;
676638
}
677639
TruffleFile file;
678640
try {
679-
file = getContext().getEnv().getPublicTruffleFile(toJavaStringNode.execute(asPath.execute(frame, path)));
641+
file = getContext().getEnv().getPublicTruffleFile(asPath.execute(frame, path).toJavaStringUncached());
680642
if (!file.exists()) {
681643
return null;
682644
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.lib;
42+
43+
import static com.oracle.graal.python.nodes.BuiltinNames.T_ENVIRON;
44+
import static com.oracle.graal.python.nodes.BuiltinNames.T_OS;
45+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.KeyError;
46+
47+
import com.oracle.graal.python.nodes.PNodeWithContext;
48+
import com.oracle.graal.python.nodes.statement.AbstractImportNode;
49+
import com.oracle.graal.python.nodes.util.CannotCastException;
50+
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
51+
import com.oracle.graal.python.runtime.exception.PException;
52+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
53+
import com.oracle.truffle.api.dsl.GenerateCached;
54+
import com.oracle.truffle.api.dsl.GenerateUncached;
55+
import com.oracle.truffle.api.dsl.Specialization;
56+
import com.oracle.truffle.api.strings.TruffleString;
57+
58+
/**
59+
* Helper to do os.environ.get(name) from Java. Only uncached for now. Returns <code>null</code>
60+
* when the key is not found or the value is not a string.
61+
*/
62+
@GenerateUncached
63+
@GenerateCached(false)
64+
public abstract class OsEnvironGetNode extends PNodeWithContext {
65+
protected abstract TruffleString execute(TruffleString name);
66+
67+
public static TruffleString executeUncached(TruffleString name) {
68+
return OsEnvironGetNodeGen.getUncached().execute(name);
69+
}
70+
71+
@TruffleBoundary
72+
@Specialization
73+
static TruffleString doLookup(TruffleString name) {
74+
Object os = AbstractImportNode.importModule(T_OS);
75+
Object environ = PyObjectGetAttr.executeUncached(os, T_ENVIRON);
76+
try {
77+
Object value = PyObjectGetItem.executeUncached(environ, name);
78+
return CastToTruffleStringNode.executeUncached(value);
79+
} catch (PException e) {
80+
e.expectUncached(KeyError);
81+
} catch (CannotCastException e) {
82+
// treat as if not there
83+
}
84+
return null;
85+
}
86+
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/BuiltinNames.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ public abstract class BuiltinNames {
7373
public static final String J___NOTES__ = "__notes__";
7474
public static final TruffleString T___NOTES__ = tsLiteral(J___NOTES__);
7575

76+
// os
77+
public static final TruffleString T_OS = tsLiteral("os");
78+
public static final TruffleString T_ENVIRON = tsLiteral("environ");
79+
7680
// sys
7781
public static final TruffleString T_TRACEBACKLIMIT = tsLiteral("tracebacklimit");
7882

0 commit comments

Comments
 (0)