|
60 | 60 | import com.oracle.graal.python.builtins.PythonBuiltins;
|
61 | 61 | import com.oracle.graal.python.builtins.objects.PNone;
|
62 | 62 | import com.oracle.graal.python.builtins.objects.bytes.BytesNodes;
|
| 63 | +import com.oracle.graal.python.builtins.objects.bytes.BytesUtils; |
63 | 64 | import com.oracle.graal.python.builtins.objects.bytes.PBytes;
|
64 | 65 | import com.oracle.graal.python.builtins.objects.bytes.PBytesLike;
|
65 | 66 | import com.oracle.graal.python.builtins.objects.common.SequenceNodes.LenNode;
|
@@ -282,13 +283,24 @@ public void postInitialize(PythonCore core) {
|
282 | 283 | // fill the environ dictionary with the current environment
|
283 | 284 | Map<String, String> getenv = System.getenv();
|
284 | 285 | PDict environ = core.factory().createDict();
|
| 286 | + String pyenvLauncherKey = "__PYVENV_LAUNCHER__"; |
285 | 287 | for (Entry<String, String> entry : getenv.entrySet()) {
|
286 | 288 | String value;
|
287 |
| - if ("__PYVENV_LAUNCHER__".equals(entry.getKey())) { |
| 289 | + if (pyenvLauncherKey.equals(entry.getKey())) { |
288 | 290 | // On Mac, the CPython launcher uses this env variable to specify the real Python
|
289 | 291 | // executable. It will be honored by packages like "site". So, if it is set, we
|
290 | 292 | // overwrite it with our executable to ensure that subprocesses will use us.
|
291 | 293 | value = core.getContext().getOption(PythonOptions.Executable);
|
| 294 | + |
| 295 | + try { |
| 296 | + PosixSupportLibrary posixLib = PosixSupportLibrary.getUncached(); |
| 297 | + Object posixSupport = core.getContext().getPosixSupport(); |
| 298 | + Object k = posixLib.createPathFromString(posixSupport, pyenvLauncherKey); |
| 299 | + Object v = posixLib.createPathFromString(posixSupport, value); |
| 300 | + posixLib.setenv(posixSupport, k, v, true); |
| 301 | + } catch (PosixException e) { |
| 302 | + // TODO handle error |
| 303 | + } |
292 | 304 | } else {
|
293 | 305 | value = entry.getValue();
|
294 | 306 | }
|
@@ -318,6 +330,55 @@ public PTuple generic(VirtualFrame frame, Object cls, Object sequence, Object di
|
318 | 330 | }
|
319 | 331 | }
|
320 | 332 |
|
| 333 | + @Builtin(name = "putenv", minNumOfPositionalArgs = 2, parameterNames = {"name", "value"}) |
| 334 | + @ArgumentClinic(name = "name", conversionClass = FsConverterNode.class) |
| 335 | + @ArgumentClinic(name = "value", conversionClass = FsConverterNode.class) |
| 336 | + @GenerateNodeFactory |
| 337 | + public abstract static class PutenvNode extends PythonBinaryClinicBuiltinNode { |
| 338 | + |
| 339 | + @Override |
| 340 | + protected ArgumentClinicProvider getArgumentClinic() { |
| 341 | + return PosixModuleBuiltinsClinicProviders.PutenvNodeClinicProviderGen.INSTANCE; |
| 342 | + } |
| 343 | + |
| 344 | + @Specialization |
| 345 | + PNone putenv(VirtualFrame frame, PBytes nameBytes, PBytes valueBytes, |
| 346 | + @Cached BytesNodes.ToBytesNode toBytesNode, |
| 347 | + @Cached SysModuleBuiltins.AuditNode auditNode, |
| 348 | + @CachedLibrary("getPosixSupport()") PosixSupportLibrary posixLib) { |
| 349 | + // Unlike in other posix builtins, we go through str -> bytes -> byte[] -> String |
| 350 | + // conversions for emulated backend because the bytes version after fsencode conversion |
| 351 | + // is subject to sys.audit. |
| 352 | + byte[] name = toBytesNode.execute(nameBytes); |
| 353 | + byte[] value = toBytesNode.execute(valueBytes); |
| 354 | + Object nameOpaque = checkNull(posixLib.createPathFromBytes(getPosixSupport(), name)); |
| 355 | + Object valueOpaque = checkNull(posixLib.createPathFromBytes(getPosixSupport(), value)); |
| 356 | + checkEqualSign(name); |
| 357 | + auditNode.audit("os.putenv", nameBytes, valueBytes); |
| 358 | + try { |
| 359 | + posixLib.setenv(getPosixSupport(), nameOpaque, valueOpaque, true); |
| 360 | + } catch (PosixException e) { |
| 361 | + throw raiseOSErrorFromPosixException(frame, e); |
| 362 | + } |
| 363 | + return PNone.NONE; |
| 364 | + } |
| 365 | + |
| 366 | + private Object checkNull(Object value) { |
| 367 | + if (value == null) { |
| 368 | + throw raise(ValueError, ErrorMessages.EMBEDDED_NULL_BYTE); |
| 369 | + } |
| 370 | + return value; |
| 371 | + } |
| 372 | + |
| 373 | + private void checkEqualSign(byte[] bytes) { |
| 374 | + for (byte b : bytes) { |
| 375 | + if (b == '=') { |
| 376 | + throw raise(ValueError, ErrorMessages.ILLEGAL_ENVIRONMENT_VARIABLE_NAME); |
| 377 | + } |
| 378 | + } |
| 379 | + } |
| 380 | + } |
| 381 | + |
321 | 382 | @Builtin(name = "execv", minNumOfPositionalArgs = 3, declaresExplicitSelf = true)
|
322 | 383 | @GenerateNodeFactory
|
323 | 384 | public abstract static class ExecvNode extends PythonBuiltinNode {
|
@@ -2057,6 +2118,29 @@ protected static boolean isPath(Object obj) {
|
2057 | 2118 | // ------------------
|
2058 | 2119 | // Helpers
|
2059 | 2120 |
|
| 2121 | + /** |
| 2122 | + * Helper node that accepts either str or bytes and converts it to {@code PBytes}. |
| 2123 | + */ |
| 2124 | + abstract static class StringOrBytesToBytesNode extends PythonBuiltinBaseNode { |
| 2125 | + abstract PBytes execute(Object obj); |
| 2126 | + |
| 2127 | + @Specialization |
| 2128 | + PBytes doString(String str) { |
| 2129 | + return factory().createBytes(BytesUtils.utf8StringToBytes(str)); |
| 2130 | + } |
| 2131 | + |
| 2132 | + @Specialization |
| 2133 | + PBytes doPString(PString pstr, |
| 2134 | + @Cached CastToJavaStringNode castToJavaStringNode) { |
| 2135 | + return doString(castToJavaStringNode.execute(pstr)); |
| 2136 | + } |
| 2137 | + |
| 2138 | + @Specialization |
| 2139 | + PBytes doBytes(PBytes bytes) { |
| 2140 | + return bytes; |
| 2141 | + } |
| 2142 | + } |
| 2143 | + |
2060 | 2144 | abstract static class ConvertToTimespecBaseNode extends PythonBuiltinBaseNode {
|
2061 | 2145 | abstract void execute(VirtualFrame frame, Object obj, long[] timespec, int offset);
|
2062 | 2146 | }
|
@@ -2213,6 +2297,20 @@ public static PBytes opaquePathToBytes(Object opaquePath, PosixSupportLibrary po
|
2213 | 2297 | // ------------------
|
2214 | 2298 | // Converters
|
2215 | 2299 |
|
| 2300 | + public abstract static class FsConverterNode extends ArgumentCastNodeWithRaise { |
| 2301 | + @Specialization |
| 2302 | + PBytes convert(VirtualFrame frame, Object value, |
| 2303 | + @Cached FspathNode fspathNode, |
| 2304 | + @Cached StringOrBytesToBytesNode stringOrBytesToBytesNode) { |
| 2305 | + return stringOrBytesToBytesNode.execute(fspathNode.call(frame, value)); |
| 2306 | + } |
| 2307 | + |
| 2308 | + @ClinicConverterFactory |
| 2309 | + public static FsConverterNode create() { |
| 2310 | + return PosixModuleBuiltinsFactory.FsConverterNodeGen.create(); |
| 2311 | + } |
| 2312 | + } |
| 2313 | + |
2216 | 2314 | /**
|
2217 | 2315 | * Equivalent of CPython's {@code path_converter()}. Always returns an {@code int}. If the
|
2218 | 2316 | * parameter is omitted, returns {@link PosixSupportLibrary#DEFAULT_DIR_FD}.
|
|
0 commit comments