1

I'd like to use a main() function in my GAE code
(note: the code below is just a minimal demonstration for a much larger program, hence the need for a main()).

If I use the following code, it performs as expected:

import webapp2

class GetHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('in GET')

class SetHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('in SET')

app = webapp2.WSGIApplication([
    ('/get',    GetHandler),
    ('/set',    SetHandler),
], debug=True)

where my app.yaml is:

runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /.*
  script: main.app

However, I cannot figure out how to implement a main() function, and still have app act as it does in the code at the top. Namely, the following:

# with main()
import webapp2

class GetHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('in GET')

class SetHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('in SET')

def main():
    app = webapp2.WSGIApplication([
        ('/get',    GetHandler),
        ('/set',    SetHandler),
    ], debug=True)

if __name__ == '__main__':
    main()

gives the following error for http://localhost:8080/get:

$ dev_appserver.py .
INFO     2016-10-17 11:29:30,962 devappserver2.py:769] Skipping SDK update check.
INFO     2016-10-17 11:29:31,059 api_server.py:205] Starting API server at: http://localhost:45865
INFO     2016-10-17 11:29:31,069 dispatcher.py:197] Starting module "default" running at: http://localhost:8080
INFO     2016-10-17 11:29:31,073 admin_server.py:116] Starting admin server at: http://localhost:8000
ERROR    2016-10-17 11:29:37,461 wsgi.py:263] 
Traceback (most recent call last):
  File "/home/.../sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 240, in Handle
    handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
  File "/home/.../sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 302, in _LoadHandler
    raise err
ImportError: <module 'main' from '/home/.../main.pyc'> has no attribute app
INFO     2016-10-17 11:29:37,496 module.py:788] default: "GET /get HTTP/1.1" 500 -

Edit 1

Trying:

# with main()
import webapp2

app = webapp2.RequestHandler()

class GetHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('in GET')

class SetHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('in SET')

def main():
    global app
    app = webapp2.WSGIApplication([
        ('/get',    GetHandler),
        ('/set',    SetHandler),
    ], debug=True)
    return app

if __name__ == '__main__':
    app = main()

Results in:

INFO     2016-10-17 12:30:34,751 module.py:402] [default] Detected file changes:
  /home/openstack/googleAppEngine/fastsimon/task2/task2_with_main/main.py
ERROR    2016-10-17 12:30:42,328 wsgi.py:279] 
Traceback (most recent call last):
  File "/home/openstack/googleAppEngine/google-cloud-sdk/platform/google_appengine/google/appengine/runtime/wsgi.py", line 267, in Handle
    result = handler(dict(self._environ), self._StartResponse)
TypeError: 'RequestHandler' object is not callable
INFO     2016-10-17 12:30:42,335 module.py:788] default: "GET /get HTTP/1.1" 500 -
boardrider
  • 5,882
  • 7
  • 49
  • 86
  • 2
    Why do you want to do this? What's the point? – Daniel Roseman Oct 17 '16 at 11:51
  • As I mentioned (1st para), I have many handlers that need to be implemented, and it's much more orderly in my app to keep them in a separate function. Is my request to utilise a `main()` in GAE an impossibility, @Daniel? – boardrider Oct 17 '16 at 12:34
  • This article explains why: http://blog.notdot.net/2011/10/Migrating-to-Python-2-7-part-1-Threadsafe – voscausa Oct 17 '16 at 15:43
  • @boardrideryou can implement your app across function and files, I still don't understand why you want to use `main()` – marcadian Oct 17 '16 at 19:23

3 Answers3

2

GAE apps are not designed to be standalone apps, a main() function doesn't make a lot of sense for them.

Basically GAE apps are really just collections of handler code and rules/configurations designed to extend and customize the behaviour of the generic GAE infra/sandbox code so that it behaves your app. You can see that from your backtrace - other code is invoking your handler code (and the stack before reaching your code can be a lot deeper than that).

In your particular case the app variable must be a global in main.py to match the script: main.app config line in the app.yaml config file. This is what the traceback is about.

As for organizing the code for huge apps, there are other ways of doing it:

In an extreme case a main.py file could contain just the app variable - that is really the only requirement.

Community
  • 1
  • 1
Dan Cornilescu
  • 39,470
  • 12
  • 57
  • 97
0

Seems that the solution was quite simple (it kept eluding me because it hid in plain sight): __name__ is main and not __main__!

In short, the following code utilises a main() as expected:

# with main()
import webapp2

class GetHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('in GET')

class SetHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('in SET')

def main():
    global app
    app = webapp2.WSGIApplication([
        ('/get',    GetHandler),
        ('/set',    SetHandler),
    ], debug=True)

# Note that it's 'main' and not '__main__'
if __name__ == 'main':
    main()
boardrider
  • 5,882
  • 7
  • 49
  • 86
0

https://webapp2.readthedocs.io/en/latest/tutorials/quickstart.nogae.html describes how to use GAE apps outside of the GAE environment:

from myapp import app

def main():
    from paste import httpserver
    httpserver.serve(app, host='localhost', port='8070')

if __name__ == '__main__':
    main()

I did that in order to debug my app using the Pycharm IDE instead of from the dev_appserver command window; it works fine. I compare results with dev_appserver running on port 8080 and the debugger running on 8070.

Dave
  • 3,834
  • 2
  • 29
  • 44