Относительный импорт с помощью ProtoBuf: создание классов Python с помощью ProtoBuf дает ModuleNotFoundError - PullRequest
1 голос
/ 06 мая 2020

Поддерживает ли 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"
  }
}
...