Недостающий гид по 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