Skip to content

Commit 345a4f7

Browse files
committed
Diff
1 parent 2378933 commit 345a4f7

File tree

1 file changed

+30
-15
lines changed

1 file changed

+30
-15
lines changed

c_src/pythonx.cpp

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -382,20 +382,19 @@ fine::Ok<> init(ErlNifEnv *env, std::string python_dl_path,
382382
// We set env vars to match Elixir at the time of initialization.
383383
// Note that the interpreter initializes its env vars from the OS
384384
// process, however we want to account for changes to env vars
385-
// such as `System.put_env/2`.
386-
387-
auto py_os = PyImport_AddModule("os");
388-
raise_if_failed(env, py_os);
389-
390-
auto py_os_environ = PyObject_GetAttrString(py_os, "environ");
391-
raise_if_failed(env, py_os_environ);
392-
auto py_os_environ_guard = PyDecRefGuard(py_os_environ);
393-
394-
auto py_os_environ_clear = PyObject_GetAttrString(py_os_environ, "clear");
395-
raise_if_failed(env, py_os_environ_clear);
396-
auto py_os_environ_clear_guard = PyDecRefGuard(py_os_environ_clear);
397-
auto result = PyObject_CallNoArgs(py_os_environ_clear);
398-
raise_if_failed(env, result);
385+
// such as `System.put_env/2` and `System.delete_env/1`.
386+
//
387+
// On Windows, there are special env vars, which can be read, but
388+
// cannot be set, so we don't want to loop over all envs and set
389+
// them. Instead, we want to diff the env vars and only mirror the
390+
// changes. Doing all of that with C API would be complex, so we
391+
// build a dict with the env vars, and then diff it in the eval
392+
// below (there is no need to overoptimise this, since init runs
393+
// only once, and we already eval anyway).
394+
395+
auto py_envs = PyDict_New();
396+
raise_if_failed(env, py_envs);
397+
auto py_envs_guard = PyDecRefGuard(py_envs);
399398

400399
for (const auto &[key, value] : envs) {
401400
auto py_key = PyUnicode_FromStringAndSize(
@@ -407,7 +406,7 @@ fine::Ok<> init(ErlNifEnv *env, std::string python_dl_path,
407406
raise_if_failed(env, py_value);
408407
auto py_value_guard = PyDecRefGuard(py_value);
409408

410-
auto result = PyObject_SetItem(py_os_environ, py_key, py_value);
409+
auto result = PyDict_SetItem(py_envs, py_key, py_value);
411410
raise_if_failed(env, result);
412411
}
413412

@@ -426,6 +425,20 @@ import sys
426425
import inspect
427426
import types
428427
import sys
428+
import os
429+
430+
431+
# Prepare env vars
432+
433+
to_remove = [key for key in os.environ if key not in envs]
434+
435+
for key in to_remove:
436+
os.environ.pop(key, None)
437+
438+
for key, value in envs.items():
439+
if os.environ.get(key, None) != value:
440+
os.environ[key] = value
441+
429442
430443
pythonx_handle_io_write = ctypes.CFUNCTYPE(
431444
None, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_bool
@@ -526,6 +539,8 @@ sys.modules["pythonx"] = pythonx
526539
py_globals, "pythonx_handle_send_tagged_object_ptr",
527540
py_pythonx_handle_send_tagged_object_ptr));
528541

542+
raise_if_failed(env, PyDict_SetItemString(py_globals, "envs", py_envs));
543+
529544
auto py_exec_args = PyTuple_Pack(2, py_code, py_globals);
530545
raise_if_failed(env, py_exec_args);
531546
auto py_exec_args_guard = PyDecRefGuard(py_exec_args);

0 commit comments

Comments
 (0)