Комментарии 7
НЛО прилетело и опубликовало эту надпись здесь

Кстати, согласен. Полезная фича, хоть и спрятанная и неочевидная.


Во фреймворке Kopf сначала выполняется код фреймворка, потом пользовательские функции или обработчики, и потом опять код фреймворка для некоторых функций, вызываемых пользователями из обработчиков.


Совершенно бесполезно и бессмысленно просить пользователей передавать какие-либо аргументы "насквозь", даже если это будет один объект-контекст: всё равно забудут; да и будет некрасиво.


И тут контекстные переменные приходят на помощь: во фреймворке перед входом в пользовательские код устанавливаем значения, а в вызываемых пользователями функциях восстанавливаем значения. И так эти значения проходят "сквозь" пользовательский код, не требуя никаких действий со стороны пользователя.


Для примера, передача текущего обрабатываемого случая/объекта:



Или подобное — вызов вторичных обработчиков из главного обработчика, когда нужно знать хотя бы идентификатор главного:



Но есть нюансы, конечно. Нельзя вызвать одну задачу (например, login()), которая заполнит эти переменные, а потом другую, которая их использует (например, doit()). Значения переменных уходят вглубь стека и в порождённые задачи, но никак не в родительские или в одноуровневые родственные задачи. Это местами немного раздражает, так как приходится делать обёртку вокруг этих login-doit, которая сама и хранит состояние.


Но, несмотря на полезность для некоторых видов задач, эти контекстные переменные — просто хитрый вид глобальных переменных. Глобальные переменные чреваты тем, что в коде сложно проследить откуда приходят значения переменных — если они приходят не через аргументы функций по стеку вызовов. И эти контекстные переменные открывают портал в ад (в плане отладки). Вопрос лишь в том, когда (не "если") в проекте появится тот человек, который начнёт ими злоупотреблять.

Совершенно бесполезно и бессмысленно просить пользователей передавать какие-либо аргументы "насквозь", даже если это будет один объект-контекст: всё равно забудут; да и будет некрасиво.

А почему?

Почему забудут? Ну, почему бы и не забыть?


Почему некрасиво? Потому что в Python-based DSL их предметной области — K8s-оператор чего-нибудь — вносится сущность, которая не даёт им никакой пользы и выгоды, и не относится к предметной области, которой они оперируют, ни даже к предметной области K8s-операторов как таковых. Leaked abstraction во плоти.


И, кстати, потому и забудут: сущность, не относящаяся к предметной области, — первый кандидат на забывание. Чем больше правил, требований и соглашений вводится, тем хуже они соблюдаются (фраза верна в любом контексте с людьми).

contextvars — это не фича, которую нужно брать в каждый проект. Однако она способна сделать код значительно проще и чище, если правильно проектировать архитектуру сервиса.


Вы о чём?
В PEP чётко сказано — если у вас используется async и вам нужен threading.local(), то вам надо вместо него использовать ContextVar!

О какой полезности идёт речь? Это конкретный инструмент для работы с асинхронным кодом.
Про генераторы вы самое интересное почему-то не написали, в PEP четко сказано в чём с ними проблема, их могут возобновить в любом месте — непонятен их контекст.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.