如何在GAE (Google App Engine)中使用Python main()函数



我想在GAE代码中使用main()函数
(注意:下面的代码只是一个更大的程序的最小演示,因此需要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')
app = webapp2.WSGIApplication([
    ('/get',    GetHandler),
    ('/set',    SetHandler),
], debug=True)

我的app.yaml是:

runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /.*
  script: main.app

然而,我无法弄清楚如何实现main()函数,并且仍然有app的行为,因为它在顶部的代码。即:

# 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()

给出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 -

编辑1

尝试:

# 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()

结果:

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 -

GAE应用程序不是为独立应用程序设计的,main()功能对它们来说没有多大意义。

基本上GAE应用程序实际上只是处理程序代码和规则/配置的集合,旨在扩展和自定义通用GAE infra/sandbox代码的行为,以便它表现您的应用程序。您可以从您的回溯中看到-其他代码正在调用您的处理程序代码(并且在到达您的代码之前的堆栈可以深得多)。

在您的特殊情况下,app变量必须main.py中的全局变量,以匹配app.yaml配置文件中的script: main.app配置行。这就是回溯的目的。

对于大型应用程序的代码组织,还有其他方法:

  • 将应用分成多个模块/服务,每个模块/服务都有自己的app.yaml配置文件。例如:谷歌应用引擎应用中的默认服务/模块可以在文件夹结构方面成为非默认服务/模块的兄弟姐妹吗?

  • 将一个服务/模块拆分为多个"脚本"——app.yaml文件的主要入口点类似于你的main.py文件,每个都有自己的app配置——它们实际上只是路由和处理程序之间的映射器。例如:应用引擎抛出404 Not Found For any path but root

  • 使用webapp2的延迟加载处理程序技术,将一个app映射器的处理程序拆分为多个文件。例子:

    • App Engine:几个大脚本还是很多小脚本?
    • 是什么决定了动态实例的启动时间,如果代码相同,它可以在周之间变化吗

在极端情况下,main.py文件可能只包含 app变量——这是唯一的要求。

似乎解决方案很简单(它一直躲避我,因为它隐藏在明显的视线):__name__主要而不是__main__!

简而言之,下面的代码按照预期使用了main():
# 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()

https://webapp2.readthedocs.io/en/latest/tutorials/quickstart.nogae.html描述了如何在GAE环境之外使用GAE应用程序:

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

我这样做是为了调试我的应用程序使用Pycharm IDE而不是从dev_appserver命令窗口;它工作得很好。我比较了运行在8080端口上的dev_appserver和运行在8070端口上的调试器的结果。

最新更新