I'm trying to use python to set environment variables that will persist in Pythons parent environment, even after python exits back to the shell, but will not persist once the parent shell is closed. Opening a new shell should require that the python script be run again in order to set the environment correctly.
Based off the recommendations from this post, I'm trying to do this using the win32com python library. Unfortunately, I have very little experience with the win32 api.
Basically, I need a way to get a handle to the current environment, and set environment variables in such a way that they will persist in python's parent environment, but will not persist after the parent environment exits.
The linked post tells how to change the default environment which will affect new processes. It manipulates registry values.
"A process can never directly change the environment variables of another process that is not a child of that process", says MS documentation. So you'll never reach your stated goal from within a child process, Python or not.
It is impossible to change the environment of the parent, by design. The best you can do is have your program emit commands that alter the environment, and then the caller of your program needs to eval the output of your command.
Related
A bit of a random problem here. I set an environment variable using PowerShell like this:
$env:GOOGLE_ELEVATION_API="the_api-key"
Then in python I attempt to read it with this simple script:
from os import environ
key = environ.get("GOOGLE_ELEVATION_API")
This returns None. If I query my environment variables in PowerShell it is there. If I query it through python with os.environ it is not.
None of the results I found make reference that this should be an issue, neither on the PowerShell side nor on python's side. I have not restarted my machine since I honestly do not believe this is what should be done. I did restart my IDE in the hope that it is somehow caching the environment but thankfully it does not.
Any pointers would be great!
Setting an environment variable via PowerShell's $env: namespace defines it for the current process only.
Any Python scripts invoked directly from a PowerShell session would see such variables, however, because child processes inherit the caller's environment variables.
To persistently define a Windows environment variable at the machine level that all processes see, you must use .NET APIs directly (as of PowerShell 7.1):
# Note: Requires ELEVATION, due to setting at the *machine* level.
[Environment]::SetEnvironmentVariable('GOOGLE_ELEVATION_API', 'the_api-key', 'Machine')
Note that only future PowerShell sessions will see this variable, and the need to restart an application in order for it to see the new variable typically also applies to other running applications.
(The exception are those applications that actively listen for the WM_SETTINGCHANGE message and refresh their environment in response, which is what the Windows GUI shell (explorer.exe) does.)
For more information about persistently setting environment variables on Windows, see this answer.
I've created Windows system variable using below code:
import os
os.environ['BJT'] = 'HELLO'
But I can't see it in Advanced settings \ system variables Also I can't see it when I try to print it:
import os
print(os.environ['BJT'])
I thought that when I create system variable using os.environ it is created exacly like when I do it in system settings. Is it possible to create system variable from python code and access it even when I restart computer?
You need to call the system (with admin privileges) to create a system variable, you can use subprocess:
import subprocess
subprocess.run(['setx', 'BJT', '"HELLO"', '/M'])
Prior to python3.5 you will need to use process.call instead
There is a misunderstanding with what environment is. It is just a mapping of (string) variables that a process can pass to its children. Specifically a process can change its own environment (which will be used by its future children) but this will not change its parent's environment, nor even the environment of its already existing children if any.
In addition, Windows provides system and user environment variables which are used as the initial environment of any process. This are not changet by os.environ nor by putenv but only from the Windows API or the shell command setx.
While there are many questions regarding accessing/ setting env variables in python, I could not find the answer for my particular scenario.
I have a shell script that when called it does the export of a bunch of env variables that are used later on.
When you need those variables to be available from your current session, then you would do
. ./script_exp_var.sh
that does, say
export MYVAR = MYVAL
then if you run python, you could access it with os.environ.get('MYVAR').
My question is how to invoke the script from python and then to have to access the env variables that called script just exported. Is it possible at all and if yes how?
Note: I know I could set the env var from python using os.environ["MYVAR"] = MYVAL but I would like to use the existing logic in my ./script_exp_var.sh because it exports many variables.
Also making sure to execute the script first and then execute the python is also not an option for my scenario.
You can't. Environment variables are copied from parent to child, never back to the parent.
If you execute a shell script from python then the environment variables will be set in that shell process (the child) and python will be unaware of them.
You could write a parser to read the shell commands from python, but that's a lot of work.
Better to write a shell script with the settings in that and then call the python program as a child of the script.
Alternatively, write a shell script that echoes the values back to python which can be picked-up using a pipe.
i have a Python script here and it is called from the Windows CMD.
It is executing some commands and also changing the Windows environment variables. Now after i changed them with the command "setx". I have to restart another Shell so the new variables are loaded into it.
Is it possible that the main shell from which i called my script can update the variables itself ?
Or is it possible to start another shell with the new variables and the script will continue in the new opened shell ?
Thanks
Each process has its own environment. When a process starts another process, the new process gets a (eventually modified) copy of its parent environment.
The rule is :
a process can modify its own environment - this modifications will be inherited by child processes started later
a process can modify (at start time) the environment of its child processes
a process can never modify its parent's environment (*)
So when you start a Python script from a cmd.exe :
the script can change its own environment and those changes will be visible by all subsequent commands of the script and all its children
the script cannot change the environment for its parent cmd.exe nor for subsequent commands of that cmd.exe
If you need to execute other batch commands after changing the environment, you will have to start a new cmd.exe for the python script and have this new shell execute the other commands, or directly execute a .bat file (both via subprocessmodule).
setx is a completely different thing : it updates the default environmnent that is given to processes started from windows explorer (including cmd.exe). That environment is stored permanently in Windows registry, and every change to it is broadcasted to all active processes ... that monitors it. Any windows GUI application can process it (and explorer does - that's how every explorer window know immediatly what is the current default environment), but console applications normaly do not.
(*) well it used to be possible for .com executable in old MS/DOS system and was even documented. It should be possible on Windows recent system through WriteProcessMemory API call but is absolutely non documented (thanks to eryksun for noticing)
You can't change the value of a environment variable.
Allow me to clarify: environment variables represent the variables set on the environment of a process when that process starts.
From the point-of-view of the new process, its environment is unchanging. Changing a variable on the environment (the process' parent) will not change the value of the environment variable seen by the process. Changing a variable on the process will not make it's environment see the change.
So, what can you change?
Variables set on your process. This is achieved in python by changing os.environ, or using set on the shell. Any changes will be seen by your process and any children you make (os.system, subprocess, most commands on the shell).
Variables set by the system (what SetX does). These changes will be seen by any new process launched directly by the system (Explorer, in Windows) after you change them.
I have a requirement where 1 process sets a value as environment variable and I read that value in python using
os.environ
As per python doc:
This mapping is captured the first
time the os module is imported,
typically during Python startup as
part of processing site.py. Changes to
the environment made after this time
are not reflected in os.environ,
except for changes made by modifying
os.environ directly.
My problem is the process set/change the variable every time it calls the python script.
Please tell me a way to read the changed value.
Thanks,
I guess you can use os.getenv() to get the value of an environment variable any time, and this will reflect the most up-to-date state.
Update: note that there is no such thing as one "global" environment, at least not on Linux. Quoting Wikipedia:
In all Unix and Unix-like systems, each process has its own private set of environment variables. By default, when a process is created it inherits a duplicate environment of its parent process, except for explicit changes made by the parent when it creates the child.
Therefore, if you launch (fork) two processes from the same parent process (such as bash), and change an environment variable in one of the processes, the other process won't see it because it uses another copy of the environment of the parent process. Similarly, if you change the environment in the parent process after having launched the child processes, the child processes won't see the change because they have already created their private copy of the environment.
If your process sets/updates an environment variable and then calls the Python script, you would see the updated value in your Python script. But if these are parallel processes and the environment variable gets modified when the Python script is running then the updates to the environemnt variable is not seen in the Python script.