在 Google App Engine 中跨模块导入

Import across modules in Google App Engine

我有一个 Python Google App Engine 项目,结构如下:

app/
    handlers/
        register_user.py
    models/
        user.py

user.py 文件包含 class User(ndb.Model).

我正在尝试从 register_user.py 访问 User class 以创建新用户并将其放入数据库中。通常,我会像这样导入它:

from ..models.user import User

但是这个错误是因为我试图从我的根包上方导入一些东西 - 所以我猜 models 是我的根包,我无法返回到 app 包?

现在,我可以像这样导入来解决这个问题:

import importlib
User = importlib.import_module('models.user').User

不过我觉得这有点乱。那么 "right" 导入我的用户 class 的方法是什么?

编辑: 完整堆栈跟踪:

Attempted relative import beyond toplevel package (/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py:1552)
Traceback (most recent call last):
  File "/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py", line 1535, in __call__
    rv = self.handle_exception(request, response, e)
  File "/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py", line 1529, in __call__
    rv = self.router.dispatch(request, response)
  File "/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py", line 1278, in default_dispatcher
    return route.handler_adapter(request, response)
  File "/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py", line 1102, in __call__
    return handler.dispatch()
  File "/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py", line 572, in dispatch
    return self.handle_exception(e, self.app.debug)
  File "/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py", line 570, in dispatch
    return method(*args, **kwargs)
  File "/base/data/home/apps/s~polly-chat/1.394430414829237783/main.py", line 48, in post
    receive_message(messaging_event)
  File "/base/data/home/apps/s~polly-chat/1.394430414829237783/messaging/handler.py", line 39, in receive_message
    intent_picker.respond_to_postback(messaging_event)
  File "/base/data/home/apps/s~polly-chat/1.394430414829237783/messaging/intent_picker.py", line 71, in respond_to_postback
    intent = importlib.import_module('intents.register_user')
  File "/base/data/home/runtimes/python27/python27_dist/lib/python2.7/importlib/__init__.py", line 37, in import_module
    __import__(name)
  File "/base/data/home/apps/s~polly-chat/1.394430414829237783/intents/register_user.py", line 1, in <module>
    from ..models import messenger_user
ValueError: Attempted relative import beyond toplevel package

(这里的包名不同;我在上面简化了它们以使示例更通用)

我采用的方法是使用 the GAE 3rd party lib vendoring technique:

  • 已创建 appengine_config.py:

内容:

from google.appengine.ext import vendor

# Add any libraries installed in the "lib" folder.
vendor.add('lib')
  • 创建了 /app/lib 目录
  • models 目录中添加了一个空的 __init__.py 文件以使其成为一个包
  • placed/moved/symlinked /app/lib 目录中的 models 目录

有了这个,可以使用以下方式引用模型:

from models.user import User

可能感兴趣:

  • Define common files for GAE projects

我认为 Dan 走在正确的道路上,但是没有必要出售您自己的代码。 vendoring 系统是通过 pip 管理第三方依赖项,对于您的用例来说应该是完全不必要的,并且出售您自己的代码会违反约定。

根据您告诉我们的内容,您应该能够导入您的代码

from models import user

如果这不起作用,您应该找出原因,但您绝对不需要供应商扩展或 importlib 来解决它。

您的基础模块是您的基础 WSGI 应用程序所在的位置,这将由您的 app.yaml 路由到的位置定义。通常,您的 app.yaml 将包含如下内容:

- url: .*  # This regex directs all routes to main.app
  script: main.app

在这种情况下,在与 app.yaml 相同的目录中有一个 main.py 包含一个 app WSGI 应用程序。在某些其他情况下,script 可能是 application.main.app,在这种情况下,app 变量位于 application/main.py 中,然后应用程序目录将是基本目录。

每个包含模块的 Python 包都应该在其目录中包含一个 __init__.py 文件,正如 Dan 提到的那样。作为旁注,如果您确实为第三方代码使用 lib 目录,它将不包含 __init__.py 因为它不是 Python 包(只是一个包含 Python 包)。它不是包的事实是您使用 Dan 描述的供应商扩展来确保它包含的包在导入路径上的原因。

根据我的经验,很少需要相对导入,并且可能会让您遇到此类问题,所以我会避免它们。

如果您仍然遇到困难,请布置您应用程序的整个文件结构,包括 app.yaml 内容和每个子目录,包括它们是否包含 __init__.py