300

I want to check my environment for the existence of a variable, say "FOO", in Python. For this purpose, I am using the os standard library. After reading the library's documentation, I have figured out 2 ways to achieve my goal:

Method 1:

if "FOO" in os.environ: pass 

Method 2:

if os.getenv("FOO") is not None: pass 

I would like to know which method, if either, is a good/preferred conditional and why.

8
  • 1
    It is primarily opinion based. Both serve the same purpose. I will prefer method 1 as it is cleaner Commented Nov 19, 2016 at 21:02
  • 1
    I can't say there's anything in it. Pick one (flip a coin?) and reevaluate later if it turns out not to work. Frankly I think you've spent more time typing this question than you'd save either way! Commented Nov 19, 2016 at 21:03
  • 1
    @Ayoub: I think you forgot to see question "What is a good practice to check if an environmental variable exists or not in Python?" Commented Nov 19, 2016 at 21:05
  • 1
    @jonrsharpe Thank you for your comment. I asked this question in order to know the community's convention. As the Zen says: There should be one-- and preferably only one --obvious way to do it.. I wanted to know that one way here. Commented Nov 19, 2016 at 21:11
  • 2
    Opinion based. Method 1 syntax serves better, since you ask if foo is in the env vars, not if seeking for foo results in None values. Commented Nov 19, 2016 at 21:11

7 Answers 7

322

Use the first; it directly tries to check if something is defined in environ. Though the second form works equally well, it's lacking semantically since you get a value back if it exists and only use it for a comparison.

You're trying to see if something is present in environ, why would you get just to compare it and then toss it away?

That's exactly what getenv does:

Get an environment variable, return None if it doesn't exist. The optional second argument can specify an alternate default.

(this also means your check could just be if getenv("FOO"))

you don't want to get it, you want to check for it's existence.

Either way, getenv is just a wrapper around environ.get but you don't see people checking for membership in mappings with:

from os import environ if environ.get('Foo') is not None: 

To summarize, use:

if "FOO" in os.environ: pass 

if you just want to check for existence, while, use getenv("FOO") if you actually want to do something with the value you might get.

Sign up to request clarification or add additional context in comments.

4 Comments

Conversely: Why would you check it's present, if you're not going to use it? Perhaps he's only asking about the error-checking aspect, but later his program uses it. (This is very common). It would be most cohesive to capture the value at the same time the check is made. There is a less-likely case, that OP wants to check before calling a subprogram that needs it, but Occam's razor suggests this is the less likely case.
I did it like that: foo = os.environ.get("FOO"); if foo is not None: do_something(foo)
@ThomasW My case is exactly your "less-likely" case, of needing to check whether the user has remembered to source a third party script before trying to build for Emscripten. In fact, that's the first thing all our build scripts do for all platforms.
"your check could just be if getenv("FOO")" This is wrong, isn't it? You can export an empty environment variable export FOO="". And getenv("FOO") then returns an empty string which is a falsy value.
73

There is a case for either solution, depending on what you want to do conditional on the existence of the environment variable.

Case 1

When you want to take different actions purely based on the existence of the environment variable, without caring for its value, the first solution is the best practice. It succinctly describes what you test for: is 'FOO' in the list of environment variables.

if 'KITTEN_ALLERGY' in os.environ: buy_puppy() else: buy_kitten() 

Case 2

When you want to set a default value if the value is not defined in the environment variables the second solution is actually useful, though not in the form you wrote it:

server = os.getenv('MY_CAT_STREAMS', 'youtube.com') 

or perhaps

server = os.environ.get('MY_CAT_STREAMS', 'youtube.com') 

Note that if you have several options for your application you might want to look into ChainMap, which allows to merge multiple dicts based on keys. There is an example of this in the ChainMap documentation:

[...] combined = ChainMap(command_line_args, os.environ, defaults) 

As pointed out by @Levon, this might return unexpected results if the variable is set but empty. You should therefore also check the returned value. For example like:

combined = ChainMap(command_line_args, os.environ) server = combined.get("MY_CAT_STREAMS") or defaults["MY_CAT_STREAMS"] 

1 Comment

And this is how to improve understanding with examples, kids!
34

To be on the safe side use

os.getenv('FOO') or 'bar' 

A corner case with the above answers is when the environment variable is set but is empty

For this special case you get

print(os.getenv('FOO', 'bar')) # prints new line - though you expected `bar` 

or

if "FOO" in os.environ: print("FOO is here") # prints FOO is here - however its not 

To avoid this just use or

os.getenv('FOO') or 'bar' 

Then you get

print(os.getenv('FOO') or 'bar') # bar 

When do we have empty environment variables?

You forgot to set the value in the .env file

# .env FOO= 

or exported as

$ export FOO= 

or forgot to set it in settings.py

# settings.py os.environ['FOO'] = '' 

Update: if in doubt, check out these one-liners

>>> import os; os.environ['FOO'] = ''; print(os.getenv('FOO', 'bar')) $ FOO= python -c "import os; print(os.getenv('FOO', 'bar'))" 

4 Comments

The signature for getenv is def getenv(key, default=None): so if so that should allow a default vs the or syntax at the start
@Chris McKee try this >>> import os; os.environ['FOO'] = ''; print(os.getenv('FOO', 'bar'))
Misleading method signature 😜
@Chris McKee, another example $ FOO= python -c "import os; print(os.getenv('FOO', 'bar'))"
12

In case you want to check if multiple env variables are not set, you can do the following:

import os MANDATORY_ENV_VARS = ["FOO", "BAR"] for var in MANDATORY_ENV_VARS: if var not in os.environ: raise EnvironmentError("Failed because {} is not set.".format(var)) 

Comments

10

I'd recommend the following solution.

It prints the env vars you didn't include, which lets you add them all at once. If you go for the for loop, you're going to have to rerun the program to see each missing var.

from os import environ REQUIRED_ENV_VARS = {"A", "B", "C", "D"} diff = REQUIRED_ENV_VARS.difference(environ) if len(diff) > 0: raise EnvironmentError(f'Failed because {diff} are not set') 

3 Comments

You don't need to check the length of the diff, just using if diff: does the trick (and perhaps change diff to missing_variables for a more descriptive name). Also in python 3.8 and up you could use the walrus operator to compress it some more: if missing_variables := REQUIRED_ENV_VARS.difference(environ):.
Neat trick! Note the var type dict instead of list.
@MarkHu It is actually a set type. print(type({'a', 'b', 'c'})) yields <class 'set'> as output.
5

Considering that you're using an environment variable named Foo which could change based on different environments, I would suggest to get its value and INFO (or DEBUG) log it even if you use its value (e.g. boolean) just as a toggle or decision-making criterion. It could help you when something goes wrong or just for sanity check later.

#python=3.8 import os import logging foo = os.getenv("FOO") if foo: logging.info(f"Proceeding since environment variable FOO exists {foo=}") pass else: # proper action/logging based on the context pass 

I would advise against hardcoding alternatives in the code itself without proper logging. Using foo = os.getenv("FOO", "BAR") could silently change the behavior of your code.

Also, it is best to have all your constants either in a config file or as environment variables and don't hardcode anything in the source code itself. Defaults and alternatives could go inside the config file and environment-changing constants should be env vars so that you could change them during the deployments easily.

Comments

-3

Through docker I used:

docker exec <container_name> bash -c 'echo "$DB_PASSWORD"' 

Change DB_PASSWORD and <container_name> for your var and you will see exact value.

1 Comment

This do not really answer the question and it is very late

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.