Search code examples
pythondiscorddiscord.pybotsdotenv

NoneType when trying to read .env from another .py


Working enviromentns

  • Ubuntu 22.04.2 - Python 3.10.6

  • Windows 11 - Python 3.7.9

Projectstructur

project_folder/

├── main.py  
   ├── bot/
      └── bot.py
   └── logger/
      └── logger.py
├── .env
└── backup_arguments.txt

Relevant source code main.py

import discord
from discord.ext import commands
from dotenv import load_dotenv
import os
from bot.bot import Bot
from logger import logger

#Load environment variables
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')

intents = discord.Intents.all()

bot = Bot(command_prefix='!', intents=intents)

@bot.event
async def on_ready():
    logger.info(f'Logged in as {bot.user.name}')


@bot.event
async def on_message(message):
    await bot.process_commands(message)

bot.run(TOKEN)

Relevant sourcecode bot.py

import os
import subprocess
from discord.ext import commands
from utils import backup_utils
from logger import logger

#Load environment variables
print(os.getenv('TARGET_CHANNEL_ID'))
TARGET_CHANNEL_ID = int(os.getenv('TARGET_CHANNEL_ID'))
SCRIPT_URL = os.getenv('SCRIPT_URL')
BACKUP_ARGUMENTS_FILE = 'backup_arguments.txt'

.env File

DISCORD_TOKEN="token"
TARGET_CHANNEL_ID=12345
SCRIPT_URL="url/to/script"

The Error I get

None
Traceback (most recent call last):
  File ".\main.py", line 5, in <module>
    from bot.bot import Bot
  File "C:\Data\DiscordBotWatcher\watcher\bot\bot.py", line 9, in <module>
    TARGET_CHANNEL_ID = int(os.getenv('TARGET_CHANNEL_ID'))
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'

My Question

I have nearly tried everything I can, and I keep getting the same error message. I double and triple checked the .env file, but it seems alright. I tried to specify the path to the .env file, but that didn't make a change.

Outside that project running, it all runs when it's in one .py script. No errors at all. It seems like the Bot class is not able to read the .env data.

Thanks in advance


Solution

  • The problem is your code in bot.py is being evaluated the moment your import of the bot happens in main.py, before load_dotenv() is executed.

    import discord
    from discord.ext import commands
    from dotenv import load_dotenv
    import os
    from bot.bot import Bot  # <-- evaluation here triggers bot.py to be imported
    

    The evaluation happens up to this point. Then bot.py is imported, which causes the following to be evaluated immediately:

    #Load environment variables
    print(os.getenv('TARGET_CHANNEL_ID'))
    TARGET_CHANNEL_ID = int(os.getenv('TARGET_CHANNEL_ID'))
    

    Currently, load_dotenv() hasn't been called, so TARGET_CHANNEL_ID is not yet set.

    You would need to either perform the import of your Bot class after you call load_dotenv() or you would need to move this code in bot.py that utilizes the environment variable somewhere else, like within the Bot class's init method (or some other place), so the code isn't being evaluated until after your load_env() call, like when Bot is instantiated or when you first call some method of Bot.

    For example, this will work as you expect:

    main.py:

    #!/usr/bin/env python
    from dotenv import load_dotenv
    from bot import Bot
    
    load_dotenv()
    
    
    if __name__ == '__main__':
        bot = Bot()
        bot.run()
    

    bot.py:

    import os
    
    
    class Bot:
        def __init__(self):
            self.variable = os.getenv("MY_VARIABLE")
    
        def run(self):
            print(f"{self.variable}")