diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_sysconfig.py b/graalpython/com.oracle.graal.python.test/src/tests/test_sysconfig.py new file mode 100644 index 0000000000..ad0b480fa5 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_sysconfig.py @@ -0,0 +1,45 @@ +# Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +def test_sysconfig(): + import os, sysconfig + os.environ["PATH"] += os.pathsep + r" : \ " + os.pathsep; + sysconfig.get_config_vars() + # must not fail diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java index 9330b1765c..1d30ed8e18 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java @@ -67,16 +67,12 @@ import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; -import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.lang.management.ManagementFactory; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import java.util.logging.Level; @@ -130,6 +126,7 @@ import com.oracle.graal.python.builtins.objects.str.PString; import com.oracle.graal.python.builtins.objects.str.StringUtils; import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.lib.OsEnvironGetNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectGetItem; import com.oracle.graal.python.nodes.ErrorMessages; @@ -631,14 +628,22 @@ private static PKeyword[] fromToolchain() { private static int which() { CompilerAsserts.neverPartOfCompilation(); - String path = System.getenv("PATH"); + Env env = PythonContext.get(null).getEnv(); + TruffleString tspath = OsEnvironGetNode.executeUncached(T_PATH); + if (tspath == null) { + return -1; + } + String path = tspath.toJavaStringUncached(); if (path != null) { for (int i = 0; i < C_COMPILER_PRECEDENCE.length; i++) { int last = 0; - for (int j = path.indexOf(File.pathSeparatorChar); j != -1; j = path.indexOf(File.pathSeparatorChar, last)) { - Path resolvedProgramName = Paths.get(path.substring(last, j)).resolve(C_COMPILER_PRECEDENCE[i]); - if (Files.isExecutable(resolvedProgramName)) { - return i; + for (int j = path.indexOf(env.getPathSeparator()); j != -1; j = path.indexOf(env.getPathSeparator(), last)) { + try { + if (env.getPublicTruffleFile(path.substring(last, j)).resolve(C_COMPILER_PRECEDENCE[i]).isExecutable()) { + return i; + } + } catch (UnsupportedOperationException | IllegalArgumentException e) { + // skip } /* * next start is the char after the separator because we have "path0:path1" @@ -902,6 +907,7 @@ TruffleString doit(VirtualFrame frame, @Builtin(name = "java_assert", minNumOfPositionalArgs = 0) @GenerateNodeFactory abstract static class JavaAssertNode extends PythonBuiltinNode { + @SuppressWarnings("all") @Specialization Object doit() { boolean assertOn = false; @@ -1017,7 +1023,7 @@ abstract static class GetPythonHomePaths extends PythonBuiltinNode { @Specialization TruffleString get() { PythonContext context = getContext(); - TruffleString sep = TruffleString.fromJavaStringUncached(File.pathSeparator, TS_ENCODING); + TruffleString sep = TruffleString.fromJavaStringUncached(context.getEnv().getPathSeparator(), TS_ENCODING); return context.getStdlibHome().concatUncached(sep, TS_ENCODING, false).concatUncached(context.getCoreHome(), TS_ENCODING, false); } } @@ -1096,6 +1102,7 @@ Object foreignNumberList(Object number) { return new ForeignNumberList(number); } + @SuppressWarnings("static-method") @ExportLibrary(value = InteropLibrary.class, delegateTo = "number") static final class ForeignNumberList implements TruffleObject { final Object number; @@ -1140,7 +1147,7 @@ Object foreignWrapper(Object object) { return new ForeignWrapper(object); } - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "static-method"}) @ExportLibrary(value = InteropLibrary.class, delegateTo = "object") static final class ForeignWrapper implements TruffleObject { final Object object; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index f1554b130f..dea41cf8b9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -111,7 +111,6 @@ import static com.oracle.graal.python.nodes.ErrorMessages.WARN_IGNORE_UNIMPORTABLE_BREAKPOINT_S; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__; -import static com.oracle.graal.python.nodes.SpecialMethodNames.T_GET; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SIZEOF__; import static com.oracle.graal.python.nodes.StringLiterals.T_BACKSLASHREPLACE; import static com.oracle.graal.python.nodes.StringLiterals.T_BASE_PREFIX; @@ -189,6 +188,7 @@ import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.tuple.StructSequence; import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins; +import com.oracle.graal.python.lib.OsEnvironGetNode; import com.oracle.graal.python.lib.PyExceptionInstanceCheckNode; import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.lib.PyFloatCheckExactNode; @@ -222,7 +222,6 @@ import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.nodes.object.GetClassNode; -import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.nodes.util.ExceptionStateNodes.GetCaughtExceptionNode; import com.oracle.graal.python.runtime.PosixSupportLibrary; @@ -1703,21 +1702,6 @@ static Object doHook(VirtualFrame frame, PythonModule sys, Object obj, @GenerateNodeFactory abstract static class BreakpointHookNode extends PythonBuiltinNode { static final TruffleString T_VAL_PDB_SETTRACE = tsLiteral("pdb.set_trace"); - static final TruffleString T_MOD_OS = tsLiteral("os"); - static final TruffleString T_ATTR_ENVIRON = tsLiteral("environ"); - - private static TruffleString getEnvVar(VirtualFrame frame, Node inliningTarget, PyImportImport importNode, - PyObjectGetAttr getAttr, PyObjectCallMethodObjArgs callMethodObjArgs, - CastToTruffleStringNode castToStringNode) { - Object os = importNode.execute(frame, inliningTarget, T_MOD_OS); - final Object environ = getAttr.execute(frame, inliningTarget, os, T_ATTR_ENVIRON); - Object var = callMethodObjArgs.execute(frame, inliningTarget, environ, T_GET, T_PYTHONBREAKPOINT); - try { - return castToStringNode.execute(inliningTarget, var); - } catch (CannotCastException cce) { - return null; - } - } @Specialization Object doHook(VirtualFrame frame, Object[] args, PKeyword[] keywords, @@ -1725,16 +1709,14 @@ Object doHook(VirtualFrame frame, Object[] args, PKeyword[] keywords, @Cached CallNode callNode, @Cached PyObjectGetAttr getAttr, @Cached PyImportImport importNode, - @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached IsBuiltinObjectProfile attrErrorProfile, - @Cached CastToTruffleStringNode castToStringNode, @Cached BuiltinFunctions.IsInstanceNode isInstanceNode, @Cached WarningsModuleBuiltins.WarnNode warnNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode, @Cached TruffleString.LastIndexOfCodePointNode lastIndexOfCodePointNode, @Cached TruffleString.SubstringNode substringNode) { - TruffleString hookName = getEnvVar(frame, inliningTarget, importNode, getAttr, callMethodObjArgs, castToStringNode); + TruffleString hookName = OsEnvironGetNode.executeUncached(T_PYTHONBREAKPOINT); if (hookName == null || hookName.isEmpty()) { hookName = T_VAL_PDB_SETTRACE; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java index 5d6301ea7a..2f37a00137 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java @@ -45,8 +45,6 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; import static com.oracle.graal.python.builtins.modules.SSLModuleBuiltins.LOGGER; -import static com.oracle.graal.python.nodes.BuiltinNames.T_NT; -import static com.oracle.graal.python.nodes.BuiltinNames.T_POSIX; import static com.oracle.graal.python.nodes.ErrorMessages.S; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; @@ -89,26 +87,23 @@ import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; -import com.oracle.graal.python.builtins.PythonOS; import com.oracle.graal.python.builtins.modules.SSLModuleBuiltins; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary; import com.oracle.graal.python.builtins.objects.bytes.PBytes; import com.oracle.graal.python.builtins.objects.bytes.PBytesLike; -import com.oracle.graal.python.builtins.objects.common.HashingStorage; -import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.ToByteArrayNode; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.list.PList; -import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.socket.PSocket; import com.oracle.graal.python.builtins.objects.ssl.CertUtils.NeedsPasswordException; import com.oracle.graal.python.builtins.objects.ssl.CertUtils.NoCertificateFoundException; import com.oracle.graal.python.builtins.objects.str.StringNodes; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.lib.OsEnvironGetNode; import com.oracle.graal.python.lib.PyCallableCheckNode; import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyNumberIndexNode; @@ -119,7 +114,6 @@ import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -145,7 +139,6 @@ import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateNodeFactory; -import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; @@ -156,8 +149,6 @@ @CoreFunctions(extendClasses = PythonBuiltinClassType.PSSLContext) public final class SSLContextBuiltins extends PythonBuiltins { - private static final TruffleString T_ENVIRON = tsLiteral("environ"); - public static final TpSlots SLOTS = SSLContextBuiltinsSlotsGen.SLOTS; @Override @@ -618,27 +609,15 @@ static Object pha(@SuppressWarnings("unused") PSSLContext self) { @Builtin(name = "set_default_verify_paths", minNumOfPositionalArgs = 1) @GenerateNodeFactory abstract static class SetDefaultVerifyPathsNode extends PythonUnaryBuiltinNode { + static final TruffleString T_SSL_CERT_FILE = tsLiteral("SSL_CERT_FILE"); + static final TruffleString T_SSL_CERT_DIR = tsLiteral("SSL_CERT_DIR"); + @Specialization Object set(VirtualFrame frame, PSSLContext self, @Bind("this") Node inliningTarget, - @Cached PyUnicodeFSDecoderNode asPath, - @Cached("createEnvironLookup()") GetAttributeNode getAttribute, - @Cached HashingStorageGetItem getItem, - @Cached("createCertFileKey()") PBytes certFileKey, - @Cached("createCertDirKey()") PBytes certDirKey, - @Cached TruffleString.ToJavaStringNode toJavaStringNode) { - - PythonModule posix; - if (PythonOS.getPythonOS() == PythonOS.PLATFORM_WIN32) { - posix = getContext().lookupBuiltinModule(T_NT); - } else { - posix = getContext().lookupBuiltinModule(T_POSIX); - } - PDict environ = (PDict) getAttribute.executeObject(frame, posix); - HashingStorage storage = environ.getDictStorage(); - - TruffleFile file = toTruffleFile(frame, asPath, getItem.execute(frame, inliningTarget, storage, certFileKey), toJavaStringNode); - TruffleFile path = toTruffleFile(frame, asPath, getItem.execute(frame, inliningTarget, storage, certDirKey), toJavaStringNode); + @Cached PyUnicodeFSDecoderNode asPath) { + TruffleFile file = toTruffleFile(frame, asPath, OsEnvironGetNode.executeUncached(T_SSL_CERT_FILE)); + TruffleFile path = toTruffleFile(frame, asPath, OsEnvironGetNode.executeUncached(T_SSL_CERT_DIR)); if (file != null || path != null) { LOGGER.fine(() -> String.format("set_default_verify_paths file: %s. path: %s", file != null ? file.getPath() : "None", path != null ? path.getPath() : "None")); try { @@ -653,30 +632,13 @@ Object set(VirtualFrame frame, PSSLContext self, return PNone.NONE; } - @NeverDefault - @TruffleBoundary - protected PBytes createCertFileKey() { - return PFactory.createBytes(PythonLanguage.get(null), "SSL_CERT_FILE".getBytes()); - } - - @NeverDefault - @TruffleBoundary - protected PBytes createCertDirKey() { - return PFactory.createBytes(PythonLanguage.get(null), "SSL_CERT_DIR".getBytes()); - } - - @NeverDefault - protected static GetAttributeNode createEnvironLookup() { - return GetAttributeNode.create(T_ENVIRON); - } - - private TruffleFile toTruffleFile(VirtualFrame frame, PyUnicodeFSDecoderNode asPath, Object path, TruffleString.ToJavaStringNode toJavaStringNode) throws PException { + private TruffleFile toTruffleFile(VirtualFrame frame, PyUnicodeFSDecoderNode asPath, TruffleString path) throws PException { if (path == null) { return null; } TruffleFile file; try { - file = getContext().getEnv().getPublicTruffleFile(toJavaStringNode.execute(asPath.execute(frame, path))); + file = getContext().getEnv().getPublicTruffleFile(asPath.execute(frame, path).toJavaStringUncached()); if (!file.exists()) { return null; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/OsEnvironGetNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/OsEnvironGetNode.java new file mode 100644 index 0000000000..9b642cfa0c --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/OsEnvironGetNode.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.lib; + +import static com.oracle.graal.python.nodes.BuiltinNames.T_ENVIRON; +import static com.oracle.graal.python.nodes.BuiltinNames.T_OS; +import static com.oracle.graal.python.runtime.exception.PythonErrorType.KeyError; + +import com.oracle.graal.python.nodes.PNodeWithContext; +import com.oracle.graal.python.nodes.statement.AbstractImportNode; +import com.oracle.graal.python.nodes.util.CannotCastException; +import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; +import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.GenerateCached; +import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.strings.TruffleString; + +/** + * Helper to do os.environ.get(name) from Java. Only uncached for now. Returns null + * when the key is not found or the value is not a string. + */ +@GenerateUncached +@GenerateCached(false) +public abstract class OsEnvironGetNode extends PNodeWithContext { + protected abstract TruffleString execute(TruffleString name); + + public static TruffleString executeUncached(TruffleString name) { + return OsEnvironGetNodeGen.getUncached().execute(name); + } + + @TruffleBoundary + @Specialization + static TruffleString doLookup(TruffleString name) { + Object os = AbstractImportNode.importModule(T_OS); + Object environ = PyObjectGetAttr.executeUncached(os, T_ENVIRON); + try { + Object value = PyObjectGetItem.executeUncached(environ, name); + return CastToTruffleStringNode.executeUncached(value); + } catch (PException e) { + e.expectUncached(KeyError); + } catch (CannotCastException e) { + // treat as if not there + } + return null; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/BuiltinNames.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/BuiltinNames.java index 4294df525f..2ef49e3a44 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/BuiltinNames.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/BuiltinNames.java @@ -73,6 +73,10 @@ public abstract class BuiltinNames { public static final String J___NOTES__ = "__notes__"; public static final TruffleString T___NOTES__ = tsLiteral(J___NOTES__); + // os + public static final TruffleString T_OS = tsLiteral("os"); + public static final TruffleString T_ENVIRON = tsLiteral("environ"); + // sys public static final TruffleString T_TRACEBACKLIMIT = tsLiteral("tracebacklimit");