Search code examples
pythonpython-3.xdotenv

Do we need to run load_dotenv() in every module?


I have a .env defined with the following content:

env=loc

I have three python module that make use of this variable.

├── __init__.py
├── cli.py
|── settings.py
├── commands
│   ├── __init__.py
│   └── output.py

settings.py:

from dotenv import load_dotenv

load_dotenv()

if not os.getenv("env"):
    raise TypeError("'env' variable not found in .env file")

output.py:

import os

def output():
  return getenv("env")

cli.py:

import settings
from commands.output import output
import os

CURR_ENV = getenv("env") 
print(CURR_ENV)
print(output())

Output:

loc 
None

Why is the output from output.py not loc? The environment variables were loaded when load_dotenv() was run for the first time.

Do I have to run load_dotenv() every time I need to access the environment variables?


Solution

  • This answer more or less repeats what's mentioned in the comments and adds a demo example.

    Once load_dotenv() is called, environment variables will be visible in the process it's called in (and in any child process) from that point.

    Suppose you have a project organized as follows.

    ├── commands
    │   ├── __init__.py
    │   └── output.py
    ├── __init__.py
    ├── .env
    ├── main.py
    |── settings.py
    

    settings.py:

    from os import getenv
    
    print("From settings.py:", getenv("env"))
    

    output.py:

    from os import getenv
    
    def output():
      print("From output.py:", getenv("env"))
    
    output()
    

    main.py:

    from os import getenv
    from dotenv import load_dotenv
    import settings                     # <-- settings don't see .env yet
    from commands.output import output  # <-- output don't see .env yet
    load_dotenv()                       # <-- because load_dotenv is called here
    
    print("From main.py:", getenv("env"))
    output()            # <--- the function call is made after load_dotenv(), so .env is visible
    

    So if you run main.py in the CLI, you'll get the following output.

    PS path\to\module> python .\main.py
    From settings.py: None
    From output.py: None
    From main.py: loc
    From output.py: loc
    

    To make .env visible everywhere, move load_dotenv logic before any of the imports in main.py or make a call to load_dotenv inside the first import (in this example, settings.py) instead of main.py because every line in settings.py will be read once it's imported so every import and function call in it will be read in main.py anyway.


    In the above example of executing main.py, .env became visible when calling the output() function inside main.py because it was called after the call to load_dotenv. That means, if you want to execute output.py by itself, you'll need to import load_dotenv and make a call to it inside output.py in order for it to see the environment variables. This becomes somewhat useful if you have a test/ directory in your module and want to execute some file from there.