Поддерживает ли protobuf относительный импорт для python?
Мне не удалось создать сценарий сборки protobuf, который поддерживает это. При создании классов python из моих .proto-файлов я могу импортировать модули python только в том случае, если я запускаю python из той же папки, где были созданы сгенерированные .py-файлы.
Я построил следующий MVP. В идеале мне нужна структура, в которой сгенерированный код python помещается в отдельную папку (например, ./generated
), которую я затем могу перенести в другие проекты. Я опубликовал подходы, которые у меня есть, но я надеюсь, что кто-то более опытный сможет указать мне на лучшее решение.
Общая информация:
- Python 3.6.8
- protobuf 3.11.3
Структура папки:
.
|--- schemas
|---- main_class.proto
|---- sub
|----sub_class.proto
|--- generated
Попытка 1: относительный импорт
main_class.proto:
syntax = "proto3";
import public "sub/sub_class.proto";
message MainClass {
repeated SubClass subclass = 1;
}
sub_class.proto:
syntax = "proto3";
message LogMessage {
enum Status {
STATUS_ERROR = 0;
STATUS_OK = 1;
}
Status status = 1;
string timestamp = 2;
}
message SubClass {
string name = 1;
repeated LogMessage log = 2;
}
Proto c команда:
Из папки root:
protoc -I=schemas --python_out=generated main_class.proto sub/sub_class.proto
Это помещает файлы python в папку ./generated
.
Что работает, а что нет
Используя описанный выше подход, я могу запустить python в папке ./generated
и импортировать, используя
import main_class_pb2 as MC_proto
.
Однако, когда я запускаю python из папки .
root (или любой другой папки, если на то пошло), импорт с использованием
import generated.main_class_pb2 as MC_proto
дает ошибку ModuleNotFoundError: No module named 'sub'
. На основе этого сообщения , я вручную изменил сгенерированный main_class_pb2.py
-файл следующим образом:
# Original
# from sub import sub_class_pb2 as sub_dot_sub__class__pb2
# from sub.sub_class_pb2 import *
# Fix
from .sub import sub_class_pb2 as sub_dot_sub__class__pb2
from .sub.sub_class_pb2 import *
Добавив .
в начале оператора импорта, я теперь возможность импортировать модуль из папки root, используя import generated.main_class_pb2 as MC_proto
. Однако очень непрактично редактировать сгенерированные файлы каждый раз вручную, поэтому мне не нравится этот подход.
Попытка 2: Абсолютный импорт
Второй подход состоял в том, чтобы попробовать абсолютный импорт . Если бы я знал, где будет находиться папка моего проекта root, я мог бы переместить .proto-файлы туда, где я хотел бы, чтобы были python -классы, и сгенерировать их там. В этом примере я использовал ту же структуру папок, что и раньше, но без папки ./generated
. Мне также пришлось изменить папку root для команды proto c, что потребовало от меня изменения оператора импорта в файле main_class.proto
следующим образом:
main_class.proto:
syntax = "proto3";
// Old
//import public "sub/sub_class.proto";
// New
import public "schemas/sub/sub_class.proto";
message MainClass {
repeated SubClass subclass = 1;
}
Proto c команда
protoc -I=. --python_out=. schemas/main_class.proto schemas/sub/sub_class.proto
Что работает, а что нет
Предполагается, что моя папка root также является папкой моего проекта root, этот подход теперь позволяет мне запустить python в папке root и импортировать модуль, используя
import schemas.main_class_pb2
Однако это означает, что мои .proto-файлы должны быть расположен в тех же папках, что и мои python -файлы в этом проекте, что кажется довольно беспорядочным. Это также означает, что вы должны сгенерировать python -файлы из той же папки root, что и проект, что не всегда возможно. Файлы .proto могут использоваться для создания общего интерфейса для двух совершенно разных приложений, и необходимость поддерживать два немного разных проекта protobuf, похоже, лишает смысла использование protobuf. * 1091 *
Пример python code
Я предоставляю пример кода python, который можно использовать для проверки того, что импорт работает и классы работают должным образом. Этот пример взят из попытки 1 и предполагает, что python запускается из папки ./generated
import main_class_pb2 as MC_proto
sub1, sub2 = (MC_proto.SubClass(name='sub1'),
MC_proto.SubClass(name='sub2'))
sub1.log.append(MC_proto.LogMessage(status=1, timestamp='2020-01-01'))
sub1.log.append(MC_proto.LogMessage(status=0, timestamp='2020-01-01'))
sub2.log.append(MC_proto.LogMessage(status=1, timestamp='2020-01-02'))
main = MC_proto.MainClass(subclass=[sub1, sub2])
main
Out[]:
subclass {
name: "sub1"
log {
status: STATUS_OK
timestamp: "2020-01-01"
}
log {
timestamp: "2020-01-01"
}
}
subclass {
name: "sub2"
log {
status: STATUS_OK
timestamp: "2020-01-02"
}
}