Недостающий гид по debug() в dbt
Примечание редактора — этот пост предполагает средний уровень знаний Jinja и разработки макросов в dbt. Для введения в Jinja в dbt ознакомьтесь с документацией и бесплатным курсом Jinja, Macros, Packages.
Jinja приносит много возможностей в dbt, позволяя нам использовать ref(), source(), условный код и макросы. Но, хотя Jinja приносит гибкость, она также добавляет сложность, и, как часто бывает с кодом, вещи могут работать не так, как ожидалось.
Макрос debug() в dbt — отличный инструмент для тех, кто пишет много кода на Jinja, но может быть сложно понять, как его использовать и какие преимущества он приносит.
Давайте погрузимся в последний случай, когда я использовал debug() и как он помог мне решить ошибки в моем коде.
Jinja в dbt
Работая над функцией для пакета dbt_project_evaluator, мои запуски dbt начали постоянно завершаться сбоем, предоставляя мне следующее сообщение:
16:49:26 Database error while running on-run-end
16:49:26 Encountered an error:
Runtime Error
Parser Error:
И всё!?!?
Так как моя конфигурация on-run-end в dbt_project.yml была следующей, я, по крайней мере, мог определить, что проблема была в моем макросе print_dbt_project_evaluator_issues:
on-run-end: "{{ dbt_project_evaluator.print_dbt_project_evaluator_issues() }}"
Но, кроме этого понимания, не было упоминания о конкретной строке или неудачном макросе — поэтому первым шагом было попытаться понять, какая часть моего кода вызывает ошибку. У меня было два варианта:
- Написать кучу операторов
print("Here")илиlog("there", info=true)в моих макросах и посмотреть, какие из них будут напечатаны, а какие нет. - Использовать команду
debug(), чтобы как найти, где мой код не работает, так и посмотреть на мои переменные во время выполнения кода.
Как вы могли догадаться, это руководство о варианте №2.
Введение в debug() в Jinja
debug() — это команда, доступная в dbt, используемая для установки точек останова в вашем коде Jinja. Эти точки останова останавливают выполнение вашего кода и предоставляют возможность исследовать переменные и выполнять следующую часть вашего кода шаг за шагом.
Как использовать
Прежде всего, debug() недоступен в dbt Cloud, так как он не предоставляет полный доступ к терминалу, поэтому вам придется установить и использовать dbt-core локально.
Затем, чтобы войти в режим отладки, вам нужно:
- Написать
{{ debug() }}в вашем коде — там, где вы хотите начать отладку — и - установить переменную окружения
DBT_MACRO_DEBUGGINGв любое значение. Это можно сделать для всей сессии оболочки, введяexport DBT_MACRO_DEBUGGING=1в командной строке, или для каждой команды, добавив переменную окружения перед всей командой, например,DBT_MACRO_DEBUGGING=1 dbt build. Без этой переменной командаdebug()не будет оценена, и вы не войдете в режим отладки.
Вернемся к нашей первоначальной проблеме, давайте используем debug, чтобы определить, где в нашем коде есть ошибки
Если вы поместите {{ debug() }} в одну или несколько секций вашего кода и в режиме отладки нажмете c, отладчик остановится на каждой из ваших точек останова, позволяя вам найти, какая часть кода не работает.
В моем случае,
{% set my_results = run_query(sql_statement) %}
{{ debug() }}
не удалось войти в режим отладки, но
{{ debug() }}
{% set my_results = run_query(sql_statement) %}
вошел в режим отладки, сообщив мне, что что-то не так с выполнением моего фактического запроса.
Теперь, когда мы нашли, где проблема, может ли debug() помочь нам ее исправить? Давайте посмотрим на различные команды, доступные в отладчике.
Использование полной мощности отладки Jinja
команды отладки
С кодом в режиме отладки мы получаем полностью функциональный интерактивный отладчик Python, показывающий нам эту информацию: ipdb> (технически, ipdb означает IPython отладчик).
Первая команда, которую мы можем ввести, это h, чтобы получить список помощи и доступных команд:
Documented commands (type help <topic>):
========================================
EOF clear display l pfile return tbreak where
a commands down list pinfo retval u
alias condition enable ll pinfo2 run unalias
args cont exit longlist pp rv undisplay
b context h n psource s unt
break continue help next q skip_hidden until
bt d ignore p quit skip_predicates up
c debug j pdef r source w
cl disable jump pdoc restart step whatis
Miscellaneous help topics:
==========================
exec pdb
Undocumented commands:
======================
interact
Это руководство не будет описывать все команды ipdb, доступные нам, существует множество онлайн-руководств по этой теме, но мы сосредоточимся на самых полезных в большинстве случаев отладки Jinja:
a: Список текущих параметров для функций, в которых вы находитесь.c: Продолжить выполнение кода до следующей точки останова или до конца программы, если других точек останова нет.pиpp: Печать и красивая печать данных.pчасто будет печатать данные в одной строке, обернутой на несколько строк.ppнапечатает ту же информацию, но добавит новые строки, чтобы было легче быстро взглянуть на переменную;ppособенно полезен для печати списков и словарей.
Использование интерактивной подска зки для решения нашей проблемы
Находясь в ipdb, вы также можете ввести некоторый код на Python, чтобы исследовать вашу программу и текущее значение ваших переменных. Например, ввод locals().keys() или p locals().keys() возвращает список текущих локальных переменных (ввод просто locals() печатает как имена переменных, так и их значения, что, скорее всего, полностью заполнит ваш терминал).
ipdb в Jinja не вернет список переменных с точно такими же именами, как в вашем коде, но вы увидите переменные с очень похожими именами, с просто префиксом, как l_1_<my_variable> или l_2_<my_variable> в зависимости от циклов в вашем коде Jinja.
В моем случае отладчик возвращает следующий (сокращенный) список:
dict_keys(['l_1_schema_project_evaluator', 'l_1_db_project_evaluator', 't_2', ..., 'l_1_results', ..., 'l_2_graph', ..., 'l_2_sql_statement', 'environment', 'missing', 'resolve', 't_1', 'undefined'])
Советую искать переменные с похожими именами на переменные, которые я либо сам определил, либо прочитал из своего кода. Здесь я вижу l_2_sql_statement как часть моего списка переменных и могу также напечатать его значение в своем терминале, введя p l_2_sql_statement.
Ввод p l_2_sql_statement вернул следующее в мой терминал:
`'\n select * from duck.main.model.dbt_project_evaluator.fct_documentation_coverage\n '`
Мы можем сразу увидеть, что есть проблема в SQL, сгенерированном в рамках моего макроса, так как я пытаюсь прочитать из duck.main.model.dbt_project_evaluator.fct_documentation_coverage (конкатенируя базу данных, схему и уникальный идентификатор модели) вместо duck.main.fct_documentation_coverage (конкатенируя базу данных, схему и имя таблицы модели). Мы нашли проблему.
Чтобы исправить это, мы можем воспользоваться возможностью изменять переменные в режиме отладки. Сначала мы можем присвоить новое значение переменной, введя l_2_sql_statement = '\n select * from duck.main.fct_documentation_coverage\n ', а затем ввести c в отладчике, чтобы позволить макросу выполняться до завершения или достижения новой точки останова. В моем случае оператор сработал после того, как я изменил l_2_sql_statement, и я могу вернуться к логике в своем коде, чтобы увидеть, почему его значение не такое, как я ожидал.
Использование отладчика для анализа переменных Jinja в dbt
Отладчик также можно использовать для исследования встроенных переменных и функций Jinja, доступных в dbt.
В моем коде я также смотрел на объект результатов, доступный в контексте on-run-end. Мы можем фактически увидеть его в предыдущем списке, названном l_1_results.
В отладчик е, если я введу type(l_1_results), программа скажет мне, что это list. Затем я могу выполнить type(l_1_results[0]), и dbt теперь скажет мне, что тип переменной — это dbt.contracts.results.RunResult.
Мой последний шаг для анализа объекта результатов — ввести pp l_1_results[0].to_dict(), и CLI затем вернет красиво отформатированную версию всех полей и значений, доступных в первом элементе моего объекта results.
Заключительные мысли
Надеюсь, это краткое руководство дало вам представление о том, как debug() может помочь вам более эффективно разрабатывать код Jinja и исследовать потенциальные ошибки. И не стесняйтесь переходить в #advice-dbt-for-power-users в сообществе dbt в Slack, если вы хотите обсудить более подробно отладку!

Comments