@@ -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
426425import inspect
427426import types
428427import 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
430443pythonx_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