1

Apologies in advance for how messy and meandering this question (and my code) may end up becoming. I have attempted to exhaust every single possible solution, without much luck, before deciding to post my problem here.

I am working on a Discord bot written in Discord.py, and having it hosted on Heroku. Everything is properly set-up on Heroku (i.e. requirements.txt, Procfile, etc). I have been getting errors from the Heroku log regarding a ModuleNotFoundError. This is weird because the code works perfectly fine on my desktop, laptop, and even RPi. Below is the log in question:

2020-08-13T16:27:38.629564+00:00 heroku[worker.1]: Starting process with command `python3 ./bot/exalted-sage.py`
2020-08-13T16:27:39.205170+00:00 heroku[worker.1]: State changed from starting to up
2020-08-13T16:27:42.106185+00:00 app[worker.1]: Traceback (most recent call last):
2020-08-13T16:27:42.106211+00:00 app[worker.1]: File "./bot/exalted-sage.py", line 20, in <module>
2020-08-13T16:27:42.106423+00:00 app[worker.1]: import dispatch
2020-08-13T16:27:42.106476+00:00 app[worker.1]: ModuleNotFoundError: No module named 'dispatch'
2020-08-13T16:27:42.222494+00:00 heroku[worker.1]: Process exited with status 1
2020-08-13T16:27:42.281076+00:00 heroku[worker.1]: State changed from up to crashed

So, I suspect it has to do with the way I'm doing things within my bot program. Here is the directory breakdown that I have. I'm only including that which I feel would be necessary in finding a solution to my problem:

bot/
    cogs/
        daily_alert.py
        dispatch.py
    data/
        dispatcher.json
    .env
    exalted-sage.py
    settings.py
Procfile
requirements.txt
runtime.txt

The bot file is exalted-sage.py. The way how I've been importing each Cog object is very weird, and it has been working for me so far, just not on Heroku. In exalted-sage.py, I have a init_cogs() function that takes a list of the cogs that I would like to load to the bot. This list is obtained from the dispatcher.json file stored within the data subdirectory. From there, I make use of the importlib module to import the cogs I want to load to the bot:

def init_cogs(bot, cog_list):
    """Add all the cogs in the given list of available cogs"""
    
    for cog in cog_list:
        importlib.import_module(cog)
        bot.add_cog(dispatch.dispatcher[cog]())

The dispatch object in question in the code above comes from an import statement I have near the top of the file where I've done this:

sys.path.insert(1, settings.COGS_PATH)
import dispatch

Anyone have any ideas or suggestions on what it could be? More importantly, is there a better way of doing anything that I have been doing so far? Any help would be greatly appreciated!

Here is the GitHub repo if anyone is interested at taking a better look at my code.

Solutions/Guides I have looked at to better understand my problem

Libraries I have attempted to use as an alternative

  • Rather than mess with system paths etc, why not just make it all a package and use relative imports? – bravosierra99 Aug 13 '20 at 19:33
  • So have the cogs directory be a package by creating a __init__.py file that exists in it? Would I be able to iteratively import each Cog this way within my init_cogs() function inside of exalted-sage.py? – Incapamentum Aug 14 '20 at 12:53
  • Yes, if it were me I would use the init file to pull all the classes up out of their modules to the top of the package (and possible store them in a cog_list) there. Then any time you add a cog module to the package just add it to the list. Then you essentially just import cog_list from cogs, and iterate over that calling add_cog – bravosierra99 Aug 14 '20 at 15:09
  • Thanks for your reply. Just so I can understand what you're suggesting: I already created an \_\_init\_\_.py within the `cogs` directory. Within that \_\_init\_\_.py file, I can just do an import statement in there to pull the classes I want to end up adding them to my bot? – Incapamentum Aug 15 '20 at 16:47
  • Yes... might have to play around with it if you are new to it. But yes, you are on the right track. – bravosierra99 Aug 15 '20 at 16:48
  • Hi @bravosierra99. I implemented what you suggested: created a \_\_init\_\_.py within the `cogs` subdirectory. Within that, I imported a cog through a `from .daily_alert import DailyAlertCog`. Also created a `cogs_list` in the file. Within `exalted-sage.py`, I imported the `cogs_list`. However, now I am receiving a TypeError whenever I add a cog from `cogs_list`, saying how it must derive from Cog, which it does inside of the module in question. I am considering attempting to typecast, but I'm not sure if that'll be wise... – Incapamentum Aug 15 '20 at 17:12
  • Update your question with the error. – bravosierra99 Aug 15 '20 at 17:47
  • 1
    Hi. I've found a workaround to my original issue, and everything is now back in working order on localhost. However, I am now encountering a new issue/error on Heroku that is no longer within the scope of the original question. I will post the workaround solution I have found. Thank you for all your help and suggestions @bravosierra99 – Incapamentum Aug 15 '20 at 18:43

1 Answers1

0

After trying out various suggestions and attempting to find alternatives, I ended up finding a workaround solution through the usage of loading the Cogs as extensions through the load_extensions() method. Here is where I got the idea to attempt loading extensions to my bot: discord.py rewrite: TypeError: cogs must derive from Cog

In a dispatcher.json file, I create a list of the Python modules containing Cog objects. I pass this list to my init_cogs() function where I then iterate over it to then load each Cog found within a cogs_list parameter.

I hope this solution/workaround helps others in a similar situation that I was.