Импорт между модулями в Google App Engine

У меня есть проект Python Google App Engine со следующей структурой:

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

Файл user.py содержит класс User(ndb.Model).

Я пытаюсь получить доступ к классу User из register_user.py, чтобы создать и поместить нового пользователя в базу данных. Обычно я просто импортирую это так:

from ..models.user import User

Но эта ошибка возникает из-за того, что я пытаюсь импортировать что-то из моего корневого пакета, поэтому я предполагаю, что модели - это мой корневой пакет, и я не могу вернуться к пакету app?

Прямо сейчас я могу обойти это, импортировав вот так:

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

Хотя я думаю, что это немного беспорядочно. Итак, каков «правильный» способ импорта моего класса User?

Изменить: Полная трассировка стека:

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

(Имена пакетов здесь разные; я упростил их выше, чтобы сделать пример более общим)


person Leon Overweel    schedule 24.07.2016    source источник
comment
Опубликуйте полный текст получаемого вами обратного запроса.   -  person MattDMo    schedule 24.07.2016
comment
Я не уверен, что понимаю, но что будет, если from models.user import User   -  person joel goldstick    schedule 24.07.2016
comment
@joelgoldstick, который сделал свое дело!   -  person Leon Overweel    schedule 25.07.2016


Ответы (2)


Я думаю, что Дэн идет по правильному пути, однако нет необходимости продавать свой собственный код. Система поставщиков предназначена для управления сторонними зависимостями с помощью 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 файл в своем каталоге. В качестве примечания, если вы действительно используете каталог lib для стороннего кода, он не будет содержать __init__.py, поскольку это не пакет Python (просто каталог, содержащий пакеты Python). Тот факт, что это не пакет, является причиной того, что вы используете расширение поставщика, описанное Дэном, чтобы убедиться, что пакеты, которые он содержит, находятся на пути импорта.

По моему опыту, относительный импорт редко требуется и может привести к подобным проблемам, поэтому я бы просто избегал их.

Если вы все еще застряли, выложите всю файловую структуру вашего приложения, включая app.yaml содержимое и каждый из подкаталогов, включая то, содержат ли они __init__.py или нет.

person Bill Prin    schedule 24.07.2016
comment
Думаю, раньше я неправильно выполнял свой неродственный импорт, но теперь они работают! from models.user import User правильно импортирует из register_user.py. - person Leon Overweel; 25.07.2016

Я подошел к этому с помощью третьей версии GAE. Техника продажи партийных библиотек:

  • создано appengine_config.py:

содержание:

from google.appengine.ext import vendor

# Add any libraries installed in the "lib" folder.
vendor.add('lib')
  • создал /app/lib директорию
  • добавил пустой __init__.py файл в models каталог, чтобы сделать его пакетом
  • разместил / переместил / связывает директорию models внутри директории /app/lib

При этом на модели можно ссылаться, используя:

from models.user import User

Возможно интересно:

person Dan Cornilescu    schedule 24.07.2016
comment
Вендоринг предназначен для стороннего кода, не должно быть необходимости использовать расширение поставщика для вашего собственного кода (и на самом деле это в первую очередь нарушает его соглашения). - person Bill Prin; 25.07.2016
comment
@BillPrin Признаюсь, мне это было удобно, и я просто пошел на это. Что вы имеете в виду под нарушением конвенций - нарушение какой-либо политики Google или просто нарушение какой-то конвенции? - person Dan Cornilescu; 25.07.2016