LINUX.ORG.RU

Объясните тупому как «корректно» юзать grpc в питоне

 ,


0

2

Сабж. Суть - нигде нет нормального примера, как и в большинстве питонодоков. Разбор что и как все время идет на неком сферическом хелловорлде в вакууме, который запускается через if __name__ == "__main__" и живет 5 секунд. И по итогу нифига не понятно как, например, корректно использовать клиент во взаимодействии между серверами.

Пример: есть 3 сервиса (А, Б, В). Для простоты пусть А - гейт, а Б и В содержат бизнес-логику. У каждого сервиса есть несколько нод. Ноды внезапно могут упасть, затупить, пойти в отказ и т.д. Как правильно написать скажем ручку для фласка на А чтоб она ходила в Б и В?

Что значит «правильно»:

  • например дока говорит «канал нужно переиспользовать как можно дольше»; можно ли его скажем держать в статической переменной, или threadlocal, или когда вообще его нужно закрывать; также канал - это p2p или разные субканалы могут подключаться к разным конечным нодам (в случае если одна например приляжет)?
  • какие у канала есть опции и как их предполагается использовать? это про тот жирный жсон в котором например идет grpc.service_config. Предполагается ли что разработчик сервиса А должен знать что там происходит в Б или все-таки разработчик сервиса Б это должен как-то донести, например опциями?
  • как корректно сделать retry?
  • как правильно передать кастомную ошибку и какой код при этом юзать?
  • как правильно передавать хедеры и например отслеживать время исполнения запроса, особенно для stream-stream? как вообще предполагается делать distributed tracing?
  • как черт возьми корректно распространять протобуфную спеку?

Кто-нибудь знает где есть хоть какая-то адекватная документация по всем параметрам, практикам и прочему? желательно с примерами. А то тот же methodConfig и как его готовить приходится кусками собирать по исходникам и разным сайтам типа SO, при том часто оказывается что то что есть в доке, в спеке и в жизни - сильно разные вещи. Например тот же hedged call в доке есть, и даже структура под него есть, а реализации этого добра нема.

p.s. если что все написанное у меня реализовано, вопрос не в том «как сделать», а в том «как сделать правильно».

★★★★★

Надо ли сразу прямо так заморачиваться?

К слову говоря, по крайней мере, для Python, grpc не имеет никаких преимуществ перед REST, кроме строгой типизации. Даже по скорости, grpc в Python не обгоняет REST. При этом REST в разы проще и понятнее. Стоит ли тратить ресурсы при этом на grpc? Я для себя после единственного опыта решил, что не стоит, т.к. накладные расходы на разработку, тестирование, отладку, в разы выше.

как черт возьми корректно распространять протобуфную спеку?

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

emorozov
()
Ответ на: комментарий от emorozov

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

С каким из клиентов? Ваниль, protoplus, betterproto? Аналогично про северную часть

Надо ли сразу прямо так заморачиваться?

Не «сразу». Сразу как раз таки было херак-херак и в продакшен. А теперь разгребаю

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от upcFrost

С каким из клиентов? Ваниль, protoplus, betterproto? Аналогично про северную часть

Я думаю, спеку хранить в серверной части, как source of truth. Пользователям давать только пакет с клиентом. Выбрать клиент и использовать его (я использовал ваниль).

Альтернатива - git submodule или git subtree. Но меня почему-то все эти git sub* решения раздражают и утомляют.

На остальные вопросы у меня нет ответа, т.к. я не спец по grpc, использовал в продакшене только однажды, в невысоконагруженном проекте.

Сделал для себя вывод, что оно «не нужно». По крайней мере, не в Python.

emorozov
()
Последнее исправление: emorozov (всего исправлений: 1)

например дока говорит «канал нужно переиспользовать как можно дольше»; можно ли его скажем держать в статической переменной, или threadlocal, или когда вообще его нужно закрывать; также канал - это p2p или разные субканалы могут подключаться к разным конечным нодам (в случае если одна например приляжет)?

Ну это же GRPC. Если у тебя не стриминговый метод, то «канал» как бы и не существует пока ты не дергаешь какой-то метод. Если у тебя между двумя GRPC вызовами с клиента, пока клиент ничего не делает, упадет и поднимется обратно сервер, то клиент ничего и не заметит

какие у канала есть опции и как их предполагается использовать? это про тот жирный жсон в котором например идет grpc.service_config. Предполагается ли что разработчик сервиса А должен знать что там происходит в Б или все-таки разработчик сервиса Б это должен как-то донести, например опциями?

Ну тут тебе надо спеку по GRPC читать, а не по питону

как корректно сделать retry?

Просто дважды дергаешь метод. Прост сервер надо писать так что бы он был готов к ретраям. Например использовать ключи идемпотентности там, где создаются какие-то новые объекты.

как черт возьми корректно распространять протобуфную спеку?

Через protoc перегоняешь .proto файлы в python. Там есть отдельная опция что бы сгенерить _stub.py файлы. Собсна эти стабы и есть готовая к импорту python библиотека со всеми спеками

Aswed ★★★★★
()
Ответ на: комментарий от Aswed

Если у тебя между двумя GRPC вызовами с клиента, пока клиент ничего не делает, упадет и поднимется обратно сервер, то клиент ничего и не заметит

А если сервер упадёт с одним ip и воскреснет с другим, а канал открыт по hostname?

Ну тут тебе надо спеку по GRPC читать, а не по питону

Читал. Запилил хедж. Почитал гитхаб, выпилил хедж потому что он не реализован. Такая себе спека. Плюс там весьма чётко сказано что многие опции implementation-specific

Просто дважды дергаешь метод

Вот ретрай таки есть в method config, только что закончил кодогенерацию для обертки. И кмк это гораздо более «прямой» метод чем велосипедостроение. Но тут увы, ключевое слово «кажется»

Например использовать ключи идемпотентности там, где создаются какие-то новые объекты.

Скорее там где не создаются (если ты про method options). Но ваниль их из коробки не может

Через protoc перегоняешь .proto файлы в python

Клиентов/генераторов больше одного. Сейчас генерим 4 разных (betterproto+grpclib, два protoplus с asyncio и без, ваниль), но это такое себе.

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от emorozov

Я думаю, спеку хранить в серверной части, как source of truth. Пользователям давать только пакет с клиентом. Выбрать клиент и использовать его (я использовал ваниль).

Примерно так и хочу сделать, кидать отдельным пакетом типа service_proto. Просто вроде как официально надо юзать server reflection и генерить код на клиенте, но чёт я хз, звучит велосипедно

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от zolden

Да я уж думал заюзать нейросеть когда надо будет архитектуру нового сервиса пилить, хотя бы в виде резиновой утки типа «а если так, а может вот так»

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от upcFrost

А если сервер упадёт с одним ip и воскреснет с другим, а канал открыт по hostname?

Эта проблема решается балансером, а не танцами с grpc.

Клиентов/генераторов больше одного. Сейчас генерим 4 разных (betterproto+grpclib, два protoplus с asyncio и без, ваниль), но это такое себе.

Каноничная версия это protoc от гугла. Мне больше нравится которая python -m grpc_tools.protoc

Aswed ★★★★★
()
Ответ на: комментарий от Aswed

Эта проблема решается балансером, а не танцами с grpc.

Балансер тоже может прилечь, его обычно тоже больше одной ноды. Суть - как себя поведёт сложенный например в статическую переменную канал если удалённый сервер отвалился (между запросами, не во время)? Переподключится, закроется, кинет ошибку?

grpc_tools.protoc

Это ж и есть ваниль, нет? С его убогим Value, oneof по строке и прочими радостями

upcFrost ★★★★★
() автор топика
Последнее исправление: upcFrost (всего исправлений: 1)
Ответ на: комментарий от ya-betmen

В смысле? Единственное, чего нет в REST - это стриминга, но он и нужен очень редко когда.

Зато геморроя grpc добавляет просто выше крыши. Например, автору сообщения на все вопросы так никто и не смог ответить.

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

Сам код gRPC и та дрянь, которую он генерирует - там поседеть можно от одного только взгляда.

emorozov
()
Ответ на: комментарий от upcFrost

Суть - как себя поведёт сложенный например в статическую переменную канал если удалённый сервер отвалился (между запросами, не во время)?

Если ты не по ip на сервер ходишь а по dns имени, то все нормально будет. Если по ip, то ты сам себе злой буратино.

Переподключится, закроется, кинет ошибку?

Обычные методы, которые не стрим не держат подключения как такового. Там нечему переподключаться. Стрим же отвалится при любой проблеме. Стримы GRPC вообще ненадежные. Он может отвалиться молча, не кидая никакой ошибки. Как будто на сервере просто закончились события. Мне известны только два решения этой проблемы.

  1. стабильно переподключаться к стриму по таймауту
  2. слать с серевера каждые N-секунд ping событие. Если к клиенту через N секунд не пришло нового события - стрим отвалился, переподключайся
Aswed ★★★★★
()

можно ли его скажем держать в статической переменной, или threadlocal, или когда вообще его нужно закрывать

Канал это абстракция над физическим воплощением трубы для передачи даннах. По идее там внизу будут жить твои коннекты/реконнекты и т.д. Про то как и когда его подключать и отключать должно быть записано в спеке на либу которую ты используешь.

какие у канала есть опции и как их предполагается использовать?

Про все опции ты можешь прочитать в грпц-хидерс.х. Что из этого будет реализовано твоими клиентом и сервером читай в доках по твоим клиенту и серверу.

как корректно сделать retry?

Есть настроки для базовых ретраев по отсутствию коннекта и т.д. Можешь использовать их если хватает возможностей. Только помни что сами запросы оседают в памяти и могут вылезать из буфера.

как правильно передать кастомную ошибку и какой код при этом юзать?

Тут нет вменяемых кодов ошибок, это тебе не трифт. Передавай статусы.

как правильно передавать хедеры

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

как черт возьми корректно распространять протобуфную спеку?

Очевидно, что прото файликом.

Вообще гугл многие вещи оставляет на откуп реализаций и лучше читать доку по той либе, которую ты используешь.

ya-betmen ★★★★★
()
Ответ на: комментарий от ya-betmen

Вообще гугл многие вещи оставляет на откуп реализаций и лучше читать доку по той либе, которую ты используешь

дока к питонячьей grpc отвратна, это по сути api reference а не дока. Потому и вопрос как это все сделать корректно.

Цепляешь свой интерсептор запросов и докидываешь метаданные

оно для стримов в ванилле нормально работает? с открытием тредов с выходом из метода и прочими их выкрутасами

Тут нет вменяемых кодов ошибок, это тебе не трифт. Передавай статусы

сейчас так и делаю, но какой код таки юзать? unknown? потому что скажем есть возвращать какой-нибудь not found, то становится неочевидно что это not found для логики, а не для самого сервера (типа not implemented)

Про все опции ты можешь прочитать в грпц-хидерс.х. Что из этого будет реализовано твоими клиентом и сервером читай в доках по твоим клиенту и серверу

включая хедж которого по факту нет ни в одном клиенте. Плюс чет не нашел для питонячьей реализации списка, только обрывки информации в тестах, мейл-листах и на SO.

Про то как и когда его подключать и отключать должно быть записано в спеке на либу которую ты используешь

дык все это нытье здесь от того что не могу найти эту самую волшебную спеку где все написано

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от Aswed

Если ты не по ip на сервер ходишь а по dns имени, то все нормально будет

точно будет? то есть если я условно сделаю синглтон с открытым по fqdn каналом (или прямо со стабом) и дальше перезапущу все остальные сервисы включая балансер так чтоб они все получили новые ip-адреса (fqdn тот же), то оно не попытается закешить ответ dns-сервера а честно пойдет снова резолвить имя?

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от emorozov

Сам код gRPC и та дрянь, которую он генерирует - там поседеть можно от одного только взгляда

ваниль юзать не надо. protoplus неплох и весьма читабелен, betterproto вообще норм (только тормозной шопесец).

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от upcFrost

оно для стримов в ванилле нормально работает?

Да, не видел проблем. Хотя я не питонитс, чисто из опыта по сишной и джавовой говорю.

unknown?

Не хттп статуса, у тебя есть объект ответа, вот в нем и возвращай.

грпц-хидерс.х

Сорри в грпц_тайпс.х

дык все это нытье здесь от того что не могу найти эту самую волшебную спеку где все написано

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

ya-betmen ★★★★★
()
Последнее исправление: ya-betmen (всего исправлений: 1)
Ответ на: комментарий от ya-betmen

если у тебя либа такая что там хрен моржовый вместо доки

ванильная ж либа

Хотя я не питонитс, чисто из опыта по сишной и джавовой говорю

ну блин, так и я могу ассемблеристу рассказывать как легко поднять http-сервер и перекладывать жсон в базу

Попробуй по сявой либе доку почитать и поискать по коду

я этим и занимаюсь. И меня это уже подзадолбало

Не хттп статуса, у тебя есть объект ответа, вот в нем и возвращай

ты имеешь ввиду мой собственный кастомный объект ответа? ну, с одной стороны да, с другой - есть rpc state, и у него помимо кода есть скажем details, куда вполне можно впихнуть причину почему именно все пропало. Пока самый прозрачный путь из тех что вижу, без пихания поля error везде и всюду, с рабочем ретраем и вообще с использованием внутренних механик grpc-клиента - объявить енум ошибок, пихать в state его номер в виде details и возможно еще что-то в trailing metadata, и потом парсить это на принимающей стороне. Плюсы - нет разницы между разбором ошибок в бизнес-логике и ошибок на уровне сети, в любом случае ожидаешь RpcError, в любом случае парсишь, дальше уже решаешь. Сложнее продолбать что-то

upcFrost ★★★★★
() автор топика
Последнее исправление: upcFrost (всего исправлений: 1)
Ответ на: комментарий от upcFrost

Пока самый прозрачный путь

Нет он совсем не прозрачный. Если ты отдаешь кому-то твой протофайлик то эту твою магию транспортного уровня там никто не найдёт. Получается у тебя кусок бизнес логики мало того что вынесен в неадекватное место так ещё и нигде не задокументирован.

ya-betmen ★★★★★
()
Ответ на: комментарий от ya-betmen

Отдельное поле имхо тоже не панацея. Хз как в сишке/жабе, а в пистоне это будет означать как минимум что ошибки сети, пойманные ошибки логики и не пойманные ошибки логики надо парсить по-разному.

  • Если ты не поймал экспешн и не подсунул кастомную ошибку - он рвёт контекст и вызывает исключение на клиенте, которое нужно ловить
  • Если ты его поймал - контекст не рвётся, исключения нет
  • Если твой код идеален и все исключения на сервере пойманы или отсутствуют - ошибки сети один фиг надо ловить

В сухом остатке у тебя try-catch и на сервере, и на клиенте, при том на сервере он еще и имеет «общий» clause что не очень круто. Учитывая что код/спек в целом для внутреннего пользования имхо проще таки задокументировать поведение и ловить эксепшн один раз

Алсо, раз уж ты ссылался на доку: https://pkg.go.dev/google.golang.org/grpc/status

gRPC service handlers should return an error created by this package, and gRPC clients should expect a corresponding error to be returned from the RPC call.

Именно handlers и clients, а не protocol implementations. Так что это не магия а вполне таки официальное поведение (только что нашёл этот кусок если честно)

upcFrost ★★★★★
() автор топика
Последнее исправление: upcFrost (всего исправлений: 1)
Ответ на: комментарий от upcFrost

Ты на гошную доку ссылаешься. Там язык дизайнили левой пяткойимеет специфичный дизайн, поэтому там вместо разделения на ошибки и коды возврата есть только коды возврата (панику нельзя перехватить). Если они начинают нормально всё обрабатывать, то у них получается жуткая портянка (наследования то нет поэтому каждый код надо процессить индивадуально) вот и извращаются чтобы такого не было.

Если твой метод умеет в бизнес-ошибки и это важно знать на клиенте они должны быть в ИДЛ, иначе на кой тебе вообще грпц.

ya-betmen ★★★★★
()
Ответ на: комментарий от ya-betmen

Если твой метод умеет в бизнес-ошибки и это важно знать на клиенте они должны быть в ИДЛ, иначе на кой тебе вообще грпц.

grpc у нас для server-server. Конечный клиент (веб, ведро/иос, плагины к офису и прочее) ходит либо через rest либо через gql. Ошибка обычно возникает в сервиса с бизнес-логикой, откуда её надо прокинуть на гейт, обработать/залогировать и отдать клиенту. Ошибки могут маппится не 1:1, например ошибка авторизации может вернуть общий 403, хотя внутри до гейта может дойти что-то более конкретное

И кстати: https://chromium.googlesource.com/external/github.com/grpc/grpc/+/HEAD/examples/python/errors/README.md

Grpc-status это не только Go, питон тоже там же.

upcFrost ★★★★★
() автор топика
Последнее исправление: upcFrost (всего исправлений: 1)
Ответ на: комментарий от upcFrost

Я не уверен, что grpc либа вообще умеет самостоятельно кешировать ответ dns сервера. Впрочем возми, да проверь. Ты же все равно не в pet-project это делаешь, а в какой-то конторе с собственными балансерами. Собсна запусти апп в тестовом окружении и передеплой тестовый балансер.

Aswed ★★★★★
()
Последнее исправление: Aswed (всего исправлений: 1)

Quack-quack! Дока - хеллоуворлдное говно, да. Чтоб разобраться «канал - это p2p или разные субканалы могут подключаться к разным конечным нодам» - надо лезть в исходники C++ реализации (если юзается питонячая дефолтная). Можно довериться интуиции что gRPC это HTTP2, и target это URL (но про ньюансы всё равно придется закапываться глубоко в исходники и настройки системы/окружения).

В целом есть простой ответ на вопрос «Как правильно». Правильно - так, как правильно в твоем случае, и иди разбирайся чо там внутри.

держать в статической переменной, или threadlocal

в общем случае - лучше нет, но раз у тебя flask - то пофиг, оберни инициализацию/teardown как принято во flask

какие у канала есть опции и как их предполагается использовать?

в каких-то местах доки вообще есть ссылки на .h файлы которые уже сто раз были переписаны и того на что дока ссылается там давно нет (но можно пойти в исходники v1.0.0 и посмотреть, может оно хотя бы тогда там было)

выход - смотреть реализацию в C++ (и попробовать поискать константы где-то рядом с python/cython-кодом, но не факт что эти опции там вообще есть)

как корректно сделать retry?

практически целиком зависит от логики твоего приложения, если всё идемпотентно - можно добавить сахара завернув в удобное молодежное tenacity

как правильно передать кастомную ошибку и какой код при этом юзать?

тут вроде более менее, есть пример https://github.com/grpc/grpc/blob/master/examples/python/errors/server.py

код юзать тот который тебе надо, в этом месте правильно так, как документировано у вас во внутренней доке

distributed tracing

grpc-opentracing само вроде должно всё менеджить, или нет?

как черт возьми корректно распространять протобуфную спеку?

git submodule, генерить в целевой репе при обновлении сабмодуля

ei-grad ★★★★★
()
Последнее исправление: ei-grad (всего исправлений: 4)
Ответ на: комментарий от Aswed

Я не уверен, что grpc либа вообще умеет самостоятельно кешировать ответ dns сервера.

Да легко, смотря что там внизу отвечает за http и вообще за резолв.

Собсна запусти апп в тестовом окружении и передеплой тестовый балансер.

Вот чую так и придётся. Просто времени и так нет нифига

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от ei-grad

Правильно - так, как правильно в твоем случае

«Как в моем случае» работает в целом норм, но всё-таки хочется понимать как разрабы grpc предполагали его использовать.

Суть - взять тот же ретрай. Я блин не первый кто это делает, даже не в первом миллионе. Сначала сделали велосипед, потом почитал спеку, заюзал опции, ещё почитал, сделал сахар вокруг idempotence чтоб списки методов генерить, чуть ещё поправил - по итогу кода меньше, а работает лучше.

Аналогично ошибки - можно в теле ответа, но тогда ловить исключения в трех местах как я выше написал. Если через Grpc-status - в одном, меньше кода, проще уследить.

Вот теперь от тебя узнал про grpc-opentracing. Просто вся эта хрень раскидана по сотням доков и модулей, и зачастую просто не знаешь есть ли стандартное решение для на вид откровенно стандартной проблемы.

в общем случае - лучше нет, но раз у тебя flask

Не фласк, вернее не только фласк, это просто пример. У меня есть и aiohttp, и фласк, и даже джанга в паре мест. И если «в общем случае нет» - что тогда значит «держать открытым как можно дольше»? Для меня это обычно значит singleton, но тут как ты сам сказал «возможны нюансы», оттуда и вопрос

git submodule, генерить в целевой репе при обновлении сабмодуля

Тоже вариант в целом

upcFrost ★★★★★
() автор топика
Последнее исправление: upcFrost (всего исправлений: 1)
Ответ на: комментарий от ya-betmen

Да ну, держать автогенеренные классы в гите - обсосство какое-то

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

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от ya-betmen

А в чём проблема?

версии генераторов, необходимость вечно гонять генерацию самому, понимание что где меняется на уровне кода а не только на уровне спеки, вся вот эта фигня. Не, конечно можно поспорить что при грамотном тестировании, правильном управлении зависимостями, четком цикле разработки и прочем все это не проблема. Но имхо намного проще и понятнее когда это все лежит уже сгенеренное в репе.

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от upcFrost

необходимость вечно гонять генерацию самому

Питон не умеет автоматом прогонять автогенерацию перед запуском или подтягивать автогенеренный модуль по зависимостям. Сорри я просто не в курсе как там сборка организована.

Я обычно больше беспокоюсь, что апдейт файлика с апи будет продолбан.

ya-betmen ★★★★★
()
Ответ на: комментарий от ya-betmen

Я обычно больше беспокоюсь, что апдейт файлика с апи будет продолбан.

В плане сама схема? Так код же из неё генерится, как ты её продолбаешь. Не закоммитишь типа?

Питон не умеет автоматом прогонять автогенерацию перед запуском или подтягивать автогенеренный модуль по зависимостям

Ну, в целом да но нет. То есть такая возможность в целом есть но я не видел чтоб ей кто-то шибко пользовался кроме буквально пары проектов, тем более на каждый запуск. Обычно просто дёргают нужные команды в CI и всё. Можно в ide настроить, но это опять же не built-in solution. Такого как в жабе точно нет, даже близко

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от ya-betmen

Ну если схему обновили, но код не перегенерили

В нашем случае так (пока) быть не может поскольку схема обновляется только в случае изменений в коде, отдельного этапа «обновить api» у нас (пока) нет.

upcFrost ★★★★★
() автор топика
Ответ на: комментарий от upcFrost

Сначала сделали велосипед, потом почитал спеку, заюзал опции

А проверил что оно работает вообще? Исходники просмотрел? Я эту всю балалайку вокруг service config чот стремаюсь использовать. Оно нужно когда ты - гугл, но ЧСХ у гугла внутри свои велосипеды интегрированные со внутренней инфраструктурой, сильно сомневаюсь что этот конкретный костыль там может быть в употреблении.

ei-grad ★★★★★
()
Ответ на: комментарий от upcFrost

И если «в общем случае нет» - что тогда значит «держать открытым как можно дольше»? Для меня это обычно значит singleton, но тут как ты сам сказал «возможны нюансы»

Инициализировать при старте приложения, передавать явно туда где он используется. Можно в какой-нибудь «контекст» положить, который передается везде. Или dependency injection какой-нибудь накостылять. Суть - избавиться от глобальной переменной которой нельзя универсально управлять, и сделать явным выбор конкретного объекта. Singleton тоже имеет право на жизнь, но надо понимать что это просто порождающий паттерн, а не паттерн «давайте всё сложим в глобальные переменные».

ei-grad ★★★★★
()
Ответ на: комментарий от ya-betmen

Я обычно больше беспокоюсь, что апдейт файлика с апи будет продолбан.

Можно настроить CI который проверяет что сгенерированный код в гите соответствует .proto подключенного сабмодуля.

Можно и «по-питонячему» сделать, собирать в .proto-репе полноценные пакеты для *_pb2 и класть в приватный PyPI, и доку актуальную на внутренний HTTP портал выкладывать. Так тоже все будет работать, если каждому клиенту не будет вдруг нужен свой кастомный генератор.

Повторюсь, что простой ответ на вопрос «Как правильно» - это «Правильно - так, как правильно в конкретном случае», не для каждой проблемы можно найти универсальное решение (и не всегда нужно его искать).

(но решение со скриптом который обновляет .proto через git submodules и после этого не забывает обновить сгенеренный код, в моей практике показало себя наименьшим из зол)

ei-grad ★★★★★
()
Ответ на: комментарий от ei-grad

А проверил что оно работает вообще? Исходники просмотрел?

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

Инициализировать при старте приложения, передавать явно туда где он используется

Ну погоди, вон выше регистрант сказал что это плохо (не указав правда как сделать хорошо).

Повторюсь, что простой ответ на вопрос «Как правильно» - это «Правильно - так, как правильно в конкретном случае», не для каждой проблемы можно найти универсальное решение (и не всегда нужно его искать).

Кмк желательно всё-таки знать что типового решения нет в рамках конкретной либы. Иначе это хождение по одним и тем же граблям и вероятность получить ещё больше граблей если идти против дизайна либы (как в случае с ошибками). проще уж не пользоваться

upcFrost ★★★★★
() автор топика