Learning Platform
Глоссарий Troubleshooting
Урок 02.03 · 24 мин
Продвинутый
dbt-coresourcerepository

Обзор репозитория dbt-core: Python-пакеты

Если предыдущие уроки рассказали «что делает» dbt-core, этот рассказывает где это лежит. После прочтения вы сможете открыть github.com/dbt-labs/dbt-core и за минуту понять, в какой файл идти за ответом на конкретный вопрос.

Версия — v1.11.5 (Q4 2025 stable). Структура медленно эволюционирует — между 1.7 и 1.11 был большой split на отдельные packages (dbt-adapters, dbt-common, dbt-semantic-interfaces), но основные модули в core/dbt/ стабильны.


Структура верхнего уровня

dbt-core/
├── core/dbt/                  ← главный пакет (исторически core/)
│   ├── cli/                   ← CLI entry point
│   ├── config/                ← RuntimeConfig, Project, Profile
│   ├── parser/                ← ManifestLoader и парсеры
│   ├── compilation.py         ← Linker, Compiler
│   ├── graph/                 ← networkx wrappers, queue, selector
│   ├── context/               ← Jinja contexts
│   ├── contracts/             ← dataclasses всех артефактов
│   ├── task/                  ← Task classes (Run/Test/Build/Snapshot)
│   ├── adapters/              ← legacy (теперь dbt-adapters package)
│   ├── events/                ← event system
│   ├── exceptions.py          ← все исключения dbt
│   ├── flags.py               ← global Flags
│   ├── tracking.py            ← telemetry (опционально)
│   └── version.py             ← dbt version constant
├── tests/                     ← test suite (~10000+ tests)
├── scripts/                   ← CI scripts
├── pyproject.toml             ← dependencies, build config
└── README.md

И параллельно — отдельные packages (с 1.8+ split):

dbt-adapters/                  ← отдельный repo
├── dbt/adapters/base/         ← BaseAdapter, BaseConnectionManager
├── dbt/adapters/sql/          ← SQLAdapter, SQLConnectionManager
└── dbt/adapters/contracts/    ← Credentials, AdapterResponse, ...

dbt-common/                    ← отдельный repo
└── dbt_common/                ← shared utilities (events, exceptions, helpers)

dbt-semantic-interfaces/       ← отдельный repo
└── dbt_semantic_interfaces/   ← MetricFlow contracts

В этом уроке фокус — на core/dbt/. Adapter-related packages разбираем в модулях 08-09, semantic-interfaces — в модуле 13.


core/dbt/cli — entry point

Что внутри:

  • main.py — Click-команды (run, test, build, compile, parse, deps, debug, …). ~600 строк.
  • flags.pyFlags dataclass. Mediator между CLI args и programmatic args.
  • option_types.py — кастомные Click типы (например ChoicesAllowingHyphens, YamlObject для --vars).
  • requires.py — Click decorators для preconditions (@requires.project, @requires.preflight).
  • resolvers.py — резолверы для default values из env vars и configs.
CLI module: data flow
Click command@cli.command('run') with @click.option(...) — declarative CLI args.
Flagsargparse.Namespace-like dataclass, normalized from CLI args and env vars.
requires decorators@requires.project — ensures dbt_project.yml present. @requires.preflight — RuntimeConfig built.
TaskRunTask(args=flags, config=runtime_config). Click delegates here.

Когда сюда лезть: вы добавляете новый CLI флаг или новую команду. Чтение main.py даёт паттерн.

Function decorators: что внутри @decorator

core/dbt/config — RuntimeConfig

Что внутри:

  • runtime.pyRuntimeConfig (главный класс).
  • project.pyProject (читает dbt_project.yml).
  • profile.pyProfile (читает profiles.yml).
  • renderer.py — Jinja renderer для конфигов (для env_var, var в YAML).
  • selectors.pySelectorConfig (selectors.yml для модуля 10).
  • utils.py — helpers.

RuntimeConfig.from_args(args) — это canonical entry point. Внутри:

@classmethod
def from_args(cls, args) -> "RuntimeConfig":
    # 1. Locate dbt_project.yml
    project_root = get_project_root(args)
    project_renderer = ProfileRenderer(...)
    
    # 2. Load Project
    project = Project.from_project_root(project_root, project_renderer)
    
    # 3. Load Profile
    profile_renderer = ProfileRenderer(cli_vars)
    profile = Profile.from_args(args, project, profile_renderer)
    
    # 4. Combine
    return cls.from_parts(project, profile, args)

Когда сюда лезть: проблемы с target.name, var(), env_var(), schema_name override. Все они приходят из RuntimeConfig.


core/dbt/parser — ManifestLoader

Самый большой пакет — ~5000 строк Python. Что внутри:

  • manifest.pyManifestLoader (~800 строк, главный класс).
  • models.pyModelParser — парсит .sql модели.
  • macros.pyMacroParser — парсит .sql макросы.
  • schemas.pySchemaParser — парсит _models.yml, _sources.yml, _macros.yml.
  • generic_test_builders.py — builds тесты из YAML (tests: - unique).
  • snapshots.pySnapshotParser.
  • seeds.pySeedParser.
  • singular_test.py — singular tests (tests/my_test.sql).
  • sources.pySourceParser.
  • unit_tests.pyUnitTestParser (1.8+).
  • base.pyBaseParser, общая логика.
  • partial.py — partial parsing (msgpack).
  • search.py — file system search (find all .sql, all .yml).

ManifestLoader координирует все парсеры. Каждый парсер специализируется на одном типе ресурса.

# core/dbt/parser/manifest.py (упрощённо)
class ManifestLoader:
    def load(self) -> Manifest:
        for project in self.all_projects:
            ModelParser(project, ...).parse()       # adds to self.manifest.nodes
            MacroParser(project, ...).parse()       # adds to self.manifest.macros
            SchemaParser(project, ...).parse()      # patches existing nodes
            SnapshotParser(project, ...).parse()
            # ... остальные парсеры
        self._process_refs()
        self._process_sources()
        return self.manifest

Когда сюда лезть: модель не находится в Manifest, схема не подцепляется, ref не резолвится, partial parse инвалидируется неожиданно. Это всё parsing issues — parser/ module.


core/dbt/compilation.py + core/dbt/graph

compilation.py — относительно небольшой файл (~400 строк):

  • Linker — берёт Manifest, строит networkx DAG, проверяет cycles.
  • Compiler — компилирует одну ноду (Jinja -> SQL), используется в RunRunner.compile().

core/dbt/graph/ — wrappers вокруг networkx:

  • graph.pyGraph (тонкая обёртка над networkx.DiGraph).
  • queue.pyGraphQueue (thread-safe priority queue для execution).
  • selector.pyNodeSelector (--select, --exclude).
  • selector_spec.pySelectionSpec, SelectionCriteria.
  • selector_methods.pytag:, path:, package:, state:modified, result:error, …
  • cli.py — CLI args для selection.
NOTE

NodeSelector — это где вся магия dbt run --select tag:nightly state:modified+1 happens. SelectionCriteria — это компоненты вроде tag:nightly или +my_model+, SelectionSpec — это композиция через intersection/union/exclude. Senior, который понимает эту структуру, может добавить custom selector methods.

Когда сюда лезть: проблемы с DAG (cycles, missing deps), проблемы с selection (—select не находит, state comparison glitch’ит).


core/dbt/context — Jinja contexts

Это сердце Jinja-системы dbt. Что внутри:

  • base.pyBaseContext (минимальный, для конфигов и raw Jinja).
  • target.pyTargetContext (для profiles.yml rendering).
  • secret.pySecretContext (env_var, ограниченный).
  • configured.pyConfiguredContext (project + profile rendering).
  • docs.pyDocsContext (для {% docs %} blocks).
  • macros.pyMacroContext, MacroNamespace.
  • providers.pyProviderContext, ParseProvider, RuntimeProvider. Главный файл — ~1200 строк, читать обязательно.
  • context_config.pyContextConfig (config.get/require/set).
  • exceptions_jinja.py — Jinja-specific exceptions.

Иерархия context’ов — модуль 03 курса. Превью:

BaseContext
├── TargetContext (для profiles)
├── ConfiguredContext (project + profile)
│   ├── MacroResolvingContext (для tests, configs)
│   │   └── MacroContext
│   └── ProviderContext (для models, snapshots)
│       ├── ParseProvider (execute=False)
│       └── RuntimeProvider (execute=True)

Когда сюда лезть: «почему statement() не работает» (потому что execute=False), «почему ref() available в этом макросе но не в другом» (потому что разный context), «как добавить custom Jinja-функцию». Всё в context/providers.py.


core/dbt/contracts — dataclasses всех артефактов

Это API contracts dbt: что такое Manifest, RunResults, Catalog в виде Python dataclasses (с поддержкой сериализации в JSON).

Что внутри:

  • graph/manifest.pyManifest dataclass.
  • graph/nodes.pyManifestNode, ModelNode, SnapshotNode, TestNode, SeedNode, SourceDefinition, …
  • graph/model_config.pyModelConfig, NodeConfig, configs для разных типов.
  • graph/unparsed.py — pre-parse YAML schemas (для _models.yml).
  • graph/saved_queries.pySavedQuery (semantic layer).
  • graph/semantic_manifest.pySemanticManifest.
  • results.pyRunResult, RunResultsArtifact, BaseResult.
  • files.pyAnyFile, SchemaSourceFile, ParseFileType (для partial parsing).
  • project.pyProjectV1, ProjectFlags (как parsed dbt_project.yml).
  • selection.pySelectionSpec (selectors.yml).
  • relation.pyPath, RelationType.
  • util.py — JSONSerializable mixins.

Когда сюда лезть: вам нужно знать «что в JSON под этим ключом». Открываете соответствующий dataclass — там типы, описания, defaults.


core/dbt/task — Task classes

Что внутри:

  • runnable.pyGraphRunnableTask (база).
  • run.pyRunTask, RunRunner, ModelRunner.
  • compile.pyCompileTask, CompileRunner.
  • test.pyTestTask, TestRunner.
  • snapshot.pySnapshotTask, SnapshotRunner.
  • seed.pySeedTask, SeedRunner.
  • build.pyBuildTask (combo run + test).
  • docs/generate.pyGenerateTask (генерация catalog.json).
  • docs/serve.pyServeTask.
  • debug.pyDebugTask (dbt debug).
  • deps.pyDepsTask (dbt deps).
  • clean.py, init.py, list.py, parse.py, retry.py, run_operation.py, show.py — остальные команды.
  • base.pyBaseTask, ConfiguredTask.
  • printer.py — пользовательский вывод (event handlers для terminal).

Каждая dbt <command> имеет соответствующий *Task класс. Если хотите понять «что делает dbt clean» — открываете clean.py, читаете ~30 строк.


core/dbt/events — event system

dbt излучает события через свою event-инфраструктуру. Это structured logging + observability.

Что внутри:

  • types.py — все event-типы (NodeStart, NodeFinished, RunStart, …).
  • event_handler.py — handlers для terminal output.
  • logging.py — Python logging integration.
  • adapter_endpoint.py — events от адаптеров.
  • base_types.pyBaseEvent, EventLevel, EventMsg.

Когда dbt run выполняется, каждая нода излучает события:

  • NodeStart — начало execution
  • LogStartLine — стартовая строка вывода
  • NodeFinished — завершение с RunResult

Эти события подхватываются printer.py для terminal output и могут быть подписаны программно (dbtRunner.callbacks=[my_handler]). Модуль 06 курса покажет, как использовать events для метрик в Dagster.


Сравнение: что в Python пакетах, а что в Jinja

Полезное наблюдение для senior:

LayerWhereЧто делает
CLIPython (cli/)Парсит флаги, делегирует в task
ConfigPython (config/)dbt_project.yml + profiles.yml
ParserPython (parser/)Читает .sql, .yml, строит Manifest
LinkerPython (compilation.py, graph/)DAG из Manifest
TaskPython (task/)Orchestrator nodes execution
RunnerPython (task/)Compile + execute одной ноды
MaterializationJinja (include/global_project/macros/materializations/)SQL templates для table/view/incremental
Adapter macrosJinja (include/global_project/macros/)adapter.get_columns, list_relations, …
AdapterPython (dbt-adapters/, dbt-duckdb/)execute(sql), connection management

Materialization-логика — это Jinja, не Python. Это сознательное дизайн-решение: dbt позволяет users override materializations через свои packages, и Jinja удобнее для SQL-генерации. Python-часть — только инфраструктура. Модуль 07 курса — глубокий разбор.

include/global_project/ — это «builtin package» dbt-core, содержащий все builtin macros, materializations, generic tests. Его можно открыть в репозитории и читать как обычный dbt-package — это и есть source of truth для table/view/incremental/snapshot.

View — материализация по умолчанию (dbt I) Определение macros: синтаксис, аргументы, return (dbt I)

Попробуй сам

  1. Откройте core/dbt/ в IDE с структурой. Скроллите дерево, опознавайте каждый модуль по этому уроку.
  2. git grep -n "class ProviderContext" core/dbt/ — найдите определение. Откройте файл.
  3. git grep -rn "materialization" include/global_project/macros/materializations/ — посмотрите как написаны builtin materializations.
  4. find core/dbt -name "*.py" | xargs wc -l | sort -nr | head -20 — топ-20 самых больших файлов dbt-core. Это даёт ощущение «где наибольший concentrated complexity».
  5. Откройте один из больших файлов (например, context/providers.py или parser/manifest.py). Прочитайте class definitions, не пытаясь понять детали. Цель — карта.

Проверка знанийKnowledge check
У вас есть симптом: на больших проектах dbt parse занимает 90 секунд. Хочется ускорить или хотя бы понять почему. В какие модули dbt-core имеет смысл лезть first?
ОтветAnswer
Главное — core/dbt/parser/ — это где живёт ManifestLoader. Конкретно: (1) parser/manifest.py — главный класс, посмотрите load() метод, какие parsers вызываются последовательно. (2) parser/partial.py — partial parsing logic, hash computation, msgpack serialization. Может быть проблема инвалидации. (3) parser/search.py — file system traversal, может быть медленно если много вложенных директорий. (4) parser/schemas.py — YAML parsing, на больших проектах с тысячами _models.yml файлов может стать боттлнеком. (5) Профилирование — поставить cProfile вокруг dbtRunner.invoke(['parse']), посмотреть top функции по времени. Часто на больших проектах боттлнек — это Jinja-парсинг через jinja2 + проверка SQL через tree-sitter (модуль 02). Менее очевидные места: (6) core/dbt/contracts/graph/nodes.py — если у вас тысячи нод, dataclass instantiation становится заметным. (7) Если используете dbt Fusion (модуль 12) — parsing вообще на Rust, до 30x быстрее. Бывает что parse медленный из-за того что используется много macros через packages.yml — каждый макрос парсится. Module 10 курса даёт detailed tuning, но first step — это grep по parser/ модулю + cProfile.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Senior хочет понять, как именно partial parsing инвалидируется при изменении env var. В какой модуль dbt-core источник идти?

Закончили урок?

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

Войдите чтобы оценить урок

Прогресс модуля
0 из 4