Why doesn't os.environ.get() return a variable created by sourcing a .env file, but echo can find it?

Question:

Here’s one of those rare questions that gets asked a lot on SO, but there’s no answer. I’ll ask it again a little differently.

I’m trying to set an environment variable using a file and then read the variable in python. That should be pretty basic.

However, check this out:

$ cat .env.test
NAME=Bob
$ cat x.py
import os
print(os.environ.get("NAME"))
$ (source .env.test;  echo $NAME) 
Bob
$ (source .env.test;  python3 x.py)
None

Isn’t that strange? Why doesn’t it work?

Is it because I didn’t write export in the .env file? If I add export, it does work. But why? Isn’t this what source is supposed to do?

$ cat .env.test
export NAME=Bob
$ cat x.py
import os
print(os.environ.get("NAME"))
$ (source .env.test;  echo $NAME) 
Bob
$ (source .env.test;  python3 x.py)
Bob

I think the community could use an explanation about how sub-shells work, how source works and where python is looking for the environment variables, to answer this once and for all. Can you do that?

Related Questions

Asked By: 101010

||

Answers:

var=value does not define an environment variable unless the option set -a is defined. By default, it only defines a shell variable.

Non-exported shell variables do not survive an exec() boundary — that is to say, when you start a new executable, only environment variables survive.


To export all variables sourced in from a file, enable the set -a shell option before sourcing it, as described in How to set environment variables from .env file:

$ (set -a; source .env.test; exec python3 x.py)

In the above case we don’t need to turn off set -a because it’s scoped to a subshell that exits when the ) is hit — or even before, because we can use exec to tell the subshell to replace itself with Python without forking again.

For a more general case, when this is used inside a script that’s going to continue to run for a while, it would instead look like:

set -a            # enable auto-export
source .env.test  # source file content
set +a            # disable auto-export
Answered By: Charles Duffy
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.