5

I am trying to deploy my app on Heroku. I have to use Windows, and gunicorn won't work. I tried waitress, which keeps giving me a "module not callable" error whenever I try to load any page.

Note: I haven't deployed it on web so far, was trying heroku local before creating a publicly accessible one. It works on localhost when using PyCharm.

organization of files

/myapp
     requirements.txt
     Procfile
     /myapp
         /static
         /templates
         __init__.py

__init __.py:

# encoding=utf-8
import click

from myapp.application import create_app
from myapp.application import db, login_manager

app = create_app()

from myapp.config import SQLALCHEMY_TRACK_MODIFICATIONS
from myapp.models import User
from myapp.views import *

app.add_url_rule('/home', HomePage.endpoint, 
      view_func=HomePage.as_view(HomePage.endpoint), methods=['GET','POST'])
# pages are defined in views.py

#other code

if __name__ == '__main__':
    # set debug to false when moving to production
    app.run()

Procfile:

web: waitress-serve --port=5000 myapp:application

traceback:

\myapp>heroku local
[WARN] No ENV file found
14:58:51 web.1   |  ERROR:waitress:Exception when serving /home
14:58:51 web.1   |  Traceback (most recent call last):
14:58:51 web.1   |    File "c:\python34\lib\site-packages\waitress\channel.py",
line 338, in service
14:58:51 web.1   |      task.service()
14:58:51 web.1   |    File "c:\python34\lib\site-packages\waitress\task.py", lin
e 169, in service
14:58:51 web.1   |      self.execute()
14:58:51 web.1   |    File "c:\python34\lib\site-packages\waitress\task.py", lin
e 399, in execute
14:58:51 web.1   |      app_iter = self.channel.server.application(env, start_re
sponse)
14:58:51 web.1   |  TypeError: 'module' object is not callable
14:58:51 web.1   |  ERROR:waitress:Exception when serving /favicon.ico
14:58:51 web.1   |  Traceback (most recent call last):
14:58:51 web.1   |    File "c:\python34\lib\site-packages\waitress\channel.py",
line 338, in service
14:58:51 web.1   |      task.service()
14:58:51 web.1   |    File "c:\python34\lib\site-packages\waitress\task.py", lin
e 169, in service
14:58:51 web.1   |      self.execute()
14:58:51 web.1   |    File "c:\python34\lib\site-packages\waitress\task.py", lin
e 399, in execute
14:58:51 web.1   |      app_iter = self.channel.server.application(env, start_re
sponse)
14:58:51 web.1   |  TypeError: 'module' object is not callable

Any idea how to solve this?

ChrisGPT was on strike
  • 127,765
  • 105
  • 273
  • 257
hungryhippo
  • 83
  • 1
  • 7

2 Answers2

4

In your Procfile, try changing

web: waitress-serve --port=5000 myapp:application

to

web: waitress-serve --port=5000 myapp:app

The last argument to waitress-serve is MODULE:OBJECT, where OBJECT is the application object in MODULE. Here, you've named your application app:

app = create_app()

(You're not showing us all of your code, but it looks like myapp.application is in fact a module, not an object. You import create_app, db, and login_manager from it in your sample code.)

ChrisGPT was on strike
  • 127,765
  • 105
  • 273
  • 257
  • Am I right to say that the OBJECT can be a factory method also? – variable Nov 12 '19 at 19:36
  • 1
    @variable, "factory method" is a bit loaded, but [if this lines up](https://docs.pylonsproject.org/projects/waitress/en/latest/runner.html#invocation) you should add the `--call` flag: "A number of frameworks, _web.py_ being an example, have factory methods on their application objects that return usable WSGI functions when called. For cases like these, `waitress-serve` has the `--call` flag. Thus: `waitress-serve --call myapp.mymodule.app.wsgi_factory` would load the `myapp.mymodule` module, and call `app.wsgi_factory` to get a WSGI application function to be passed to `waitress.server`". – ChrisGPT was on strike Nov 12 '19 at 21:19
0

Waitress has it's own app, so you need to distinguish your app correctly. @Chris's answer shows you how to do so via Procfile. Here's another way, for those using Flask:

app/init.py:

from flask import Flask

app = Flask(__name__, template_folder="some path", static_folder="another path")

main.py

from waitress import serve
from app import app as my_app  # renamed to distinguish from waitress' 'app'

if __name__ == "__main__":
    serve(my_app, host="localhost", port=5005)

This allows you to keep your app named app in a routes file, since that is isolated from waitress

app/routes.py:

@app.route('/dothing1', methods=['POST', 'GET'])
def dothing1():
  pass
Tyler Dane
  • 951
  • 1
  • 14
  • 25