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 -
https://webapp2.readthedocs.io/en/latest/tutorials/quickstart.nogae.html describes how to use GAE apps outside of the GAE environment:
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.
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 inmain.py
to match thescript: main.app
config line in theapp.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:
splitting the app in multiple modules/services, each with their own
app.yaml
config file. For example: Can a default service/module in a Google App Engine app be a sibling of a non-default one in terms of folder structure?splitting a service/module into multiple "scripts" - primary entry points into the
app.yaml
file similar to yourmain.py
file, each with their ownapp
config` - which really are just mappers between routes and handlers. For example: App Engine throws 404 Not Found for any path but rootsplitting the handlers for one
app
mapper into multiple files, using webapp2's lazy loaded handler technique. Examples:In an extreme case a
main.py
file could contain just theapp
variable - that is really the only requirement.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: