TGViewer
Находки в опенсорсе Находки в опенсорсе @opensource_findings · 12.6K subscribers
Post #899 11.6K
PEP765: больше никакой грязи в finally

Ссылка на PEP: https://peps.python.org/pep-0765
Одна из самых сломанных частей питона была пофикшена в 3.14

В чем проблема?

Ранее такой код вел себя крайне странно:


>>> def some():
... try:
... return 1
... finally:
... return 2

>>> some()
2


Так как из функции может быть только один return, а код в finally всегда выполняется – то получаем, что получаем.

Или такой код:


>>> def other():
... try:
... 1 / 0
... finally:
... return 2

>>> other()
2


Тут вообще жесть. У нас подавляется исключение без except 🫠
Аналогично можно делать не только с return, но и с break / continue в циклах:


>>> def cycle():
... for i in range(2):
... try:
... i / 0
... finally:
... print(i)
... continue
... return 2

>>> cycle()
# prints: 0
# prints: 1
# returns: 2


Такой код позволял создавать ошибки на ровном месте.

Как исправили?

Теперь такой код генерирует SyntaxWarning в 3.14:


>>> def some():
... try:
... return 1
... finally:
... return 2
<python-input-14>:5: SyntaxWarning: 'return' in a 'finally' block


Скоро – будет SyntaxError, в будущих версиях.
Но, WPS, например, запрещал такое делать уже очень давно.

Как работает?

Сам патч очень маленький и простой:

1. Добавляет список текущего контекста _Py_c_array_t cf_finally; в ast_opt.c

Храним внутри структуры вида:


typedef struct {
bool in_finally; // мы в `finally`?
bool in_funcdef; // мы в `def` или `async def`?
bool in_loop; // мы в `for`, `async for` или `while`?
} ControlFlowInFinallyContext;


2. При обходе дерева, добавляет нужные данные в текущий контекст: находимся ли мы в finally, функции, цикле
3. Если находим return, break или continue, то выполняем проверку синтаксиса; вот код для return:


static int
before_return(_PyASTOptimizeState *state, stmt_ty node_)
{
if (state->cf_finally_used > 0) {
ControlFlowInFinallyContext *ctx = get_cf_finally_top(state);
// если нашли `return` в `finally`, но не во вложенной функции,
// то показываем warning пользователю:
if (ctx->in_finally && ! ctx->in_funcdef) {
if (!control_flow_in_finally_warning("return", node_, state)) {
return 0;
}
}
}
return 1;
}


4. Внутри control_flow_in_finally_warning используем специальное АПИ для SyntaxWarning:


static int
control_flow_in_finally_warning(const char *kw, stmt_ty n, _PyASTOptimizeState *state)
{
PyObject *msg = PyUnicode_FromFormat("'%s' in a 'finally' block", kw);
if (msg == NULL) {
return 0;
}
int ret = _PyErr_EmitSyntaxWarning(msg, state->filename, n->lineno,
n->col_offset + 1, n->end_lineno,
n->end_col_offset + 1);
Py_DECREF(msg);
return ret < 0 ? 0 : 1;
}


Готово!

Обсуждение: вы знали про такую проблему? Стреляли так себе в ногу?

P.S. Пока писал пост, нашел багу в питоне и важную опечатку :)

| Поддержать | YouTube | GitHub | Чат |
  • 👍 143
  • ❤ 37
  • 🔥 27
  • 🤡 7
  • 🤯 5
  • 🎉 2
  • 👏 1
  • 😁 1
More from @opensource_findings
  1. Sep 25, 2026Монадические выражения в Python. Наконец-то! Продолжаем разговор про интересные PEPы. Пого…
  2. Sep 15, 2026PEPы в Python окончательно вышли из-под контроля Давайте посмотрим, что происходит с ПЕПам…
  3. Sep 10, 2026Большая бесплатная конференция в Нижнем Новгороде 17 октября Регистрация: https://itgorky.…
  4. Sep 1, 2026Post #985
  5. Aug 24, 2026JIT могут удалить из CPython! PEP-836: https://peps.python.org/pep-0836 JIT в CPython имее…
  6. Aug 18, 2026Как AWS у опенсорсера пакеты отжимал Нерегулярная рубрика "посмотрите, что творится!". Дан…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →