This post talks about how I refactored a Flask application and subsequently ran into a gotcha with Google App Engine's defaults, and how to resolve it.
Backstory: I previously wrote about creating a Slack app in Python. At the time, the app was pretty small, so all of my logic went into a
main.py. However, that's not good development practice, and the file became hard to read at 500 lines long. I also wanted to add in things like config files, as the list of things to configure was growing longer. So I decided it was time to restructure the entire application and abide more by conventions.
I followed the Flask docs on structuring larger applications. I created separated different kinds of logic, like route handling and error handling, into different files. My end result looked something like this:
/myapp setup.py test.py /myapp __init__.py routes.py app_logic.py config.py /errors handlers.py exceptions.py
Unfortunately, when I actually deployed it to GAE, it blew up. Specifically, the GAE logs had the following error:
"Traceback (most recent call last): File "/env/lib/python3.7/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker worker.init_process() File "/env/lib/python3.7/site-packages/gunicorn/workers/gthread.py", line 104, in init_process super(ThreadWorker, self).init_process() File "/env/lib/python3.7/site-packages/gunicorn/workers/base.py", line 129, in init_process self.load_wsgi() File "/env/lib/python3.7/site-packages/gunicorn/workers/base.py", line 138, in load_wsgi self.wsgi = self.app.wsgi() File "/env/lib/python3.7/site-packages/gunicorn/app/base.py", line 67, in wsgi self.callable = self.load() File "/env/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 52, in load return self.load_wsgiapp() File "/env/lib/python3.7/site-packages/gunicorn/app/wsgiapp.py", line 41, in load_wsgiapp return util.import_app(self.app_uri) File "/env/lib/python3.7/site-packages/gunicorn/util.py", line 350, in import_app __import__(module) ModuleNotFoundError: No module named 'main'"
Turns out that according to Google's docs, GAE looks for a
main.py in the application directory root containing a variable called
app, which is the application. Since I switched to putting the
myapp/__init__.py, GAE couldn't find this anymore.
The suggested solution is to add a custom
entrypoint to your
app.yaml such as
entrypoint: gunicorn -b :$PORT myapp:app. However, that didn't work for me. So what I did was to simply conform to what GAE wanted, and added a dummy
main.py which imports my application as
app. The file only contains the following:
from myapp import app
And now my folder structure looks like this:
/myapp main.py test.py setup.py /myapp __init__.py routes.py app_logic.py config.py /errors handlers.py exceptions.py
Maybe not the most elegant solution, but it got my app working on GAE.