dvt-core 0.58.6__cp311-cp311-macosx_10_9_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dbt/__init__.py +7 -0
- dbt/_pydantic_shim.py +26 -0
- dbt/artifacts/__init__.py +0 -0
- dbt/artifacts/exceptions/__init__.py +1 -0
- dbt/artifacts/exceptions/schemas.py +31 -0
- dbt/artifacts/resources/__init__.py +116 -0
- dbt/artifacts/resources/base.py +67 -0
- dbt/artifacts/resources/types.py +93 -0
- dbt/artifacts/resources/v1/analysis.py +10 -0
- dbt/artifacts/resources/v1/catalog.py +23 -0
- dbt/artifacts/resources/v1/components.py +274 -0
- dbt/artifacts/resources/v1/config.py +277 -0
- dbt/artifacts/resources/v1/documentation.py +11 -0
- dbt/artifacts/resources/v1/exposure.py +51 -0
- dbt/artifacts/resources/v1/function.py +52 -0
- dbt/artifacts/resources/v1/generic_test.py +31 -0
- dbt/artifacts/resources/v1/group.py +21 -0
- dbt/artifacts/resources/v1/hook.py +11 -0
- dbt/artifacts/resources/v1/macro.py +29 -0
- dbt/artifacts/resources/v1/metric.py +172 -0
- dbt/artifacts/resources/v1/model.py +145 -0
- dbt/artifacts/resources/v1/owner.py +10 -0
- dbt/artifacts/resources/v1/saved_query.py +111 -0
- dbt/artifacts/resources/v1/seed.py +41 -0
- dbt/artifacts/resources/v1/semantic_layer_components.py +72 -0
- dbt/artifacts/resources/v1/semantic_model.py +314 -0
- dbt/artifacts/resources/v1/singular_test.py +14 -0
- dbt/artifacts/resources/v1/snapshot.py +91 -0
- dbt/artifacts/resources/v1/source_definition.py +84 -0
- dbt/artifacts/resources/v1/sql_operation.py +10 -0
- dbt/artifacts/resources/v1/unit_test_definition.py +77 -0
- dbt/artifacts/schemas/__init__.py +0 -0
- dbt/artifacts/schemas/base.py +191 -0
- dbt/artifacts/schemas/batch_results.py +24 -0
- dbt/artifacts/schemas/catalog/__init__.py +11 -0
- dbt/artifacts/schemas/catalog/v1/__init__.py +0 -0
- dbt/artifacts/schemas/catalog/v1/catalog.py +59 -0
- dbt/artifacts/schemas/freshness/__init__.py +1 -0
- dbt/artifacts/schemas/freshness/v3/__init__.py +0 -0
- dbt/artifacts/schemas/freshness/v3/freshness.py +158 -0
- dbt/artifacts/schemas/manifest/__init__.py +2 -0
- dbt/artifacts/schemas/manifest/v12/__init__.py +0 -0
- dbt/artifacts/schemas/manifest/v12/manifest.py +211 -0
- dbt/artifacts/schemas/results.py +147 -0
- dbt/artifacts/schemas/run/__init__.py +2 -0
- dbt/artifacts/schemas/run/v5/__init__.py +0 -0
- dbt/artifacts/schemas/run/v5/run.py +184 -0
- dbt/artifacts/schemas/upgrades/__init__.py +4 -0
- dbt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
- dbt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
- dbt/artifacts/utils/validation.py +153 -0
- dbt/cli/__init__.py +1 -0
- dbt/cli/context.py +17 -0
- dbt/cli/exceptions.py +57 -0
- dbt/cli/flags.py +560 -0
- dbt/cli/main.py +2403 -0
- dbt/cli/option_types.py +121 -0
- dbt/cli/options.py +80 -0
- dbt/cli/params.py +844 -0
- dbt/cli/requires.py +490 -0
- dbt/cli/resolvers.py +50 -0
- dbt/cli/types.py +40 -0
- dbt/clients/__init__.py +0 -0
- dbt/clients/checked_load.py +83 -0
- dbt/clients/git.py +164 -0
- dbt/clients/jinja.py +206 -0
- dbt/clients/jinja_static.py +245 -0
- dbt/clients/registry.py +192 -0
- dbt/clients/yaml_helper.py +68 -0
- dbt/compilation.py +876 -0
- dbt/compute/__init__.py +14 -0
- dbt/compute/engines/__init__.py +12 -0
- dbt/compute/engines/spark_engine.cpython-311-darwin.so +0 -0
- dbt/compute/engines/spark_engine.py +642 -0
- dbt/compute/federated_executor.cpython-311-darwin.so +0 -0
- dbt/compute/federated_executor.py +1080 -0
- dbt/compute/filter_pushdown.cpython-311-darwin.so +0 -0
- dbt/compute/filter_pushdown.py +273 -0
- dbt/compute/jar_provisioning.cpython-311-darwin.so +0 -0
- dbt/compute/jar_provisioning.py +255 -0
- dbt/compute/java_compat.cpython-311-darwin.so +0 -0
- dbt/compute/java_compat.py +689 -0
- dbt/compute/jdbc_utils.cpython-311-darwin.so +0 -0
- dbt/compute/jdbc_utils.py +678 -0
- dbt/compute/metadata/__init__.py +40 -0
- dbt/compute/metadata/adapters_registry.cpython-311-darwin.so +0 -0
- dbt/compute/metadata/adapters_registry.py +370 -0
- dbt/compute/metadata/registry.cpython-311-darwin.so +0 -0
- dbt/compute/metadata/registry.py +674 -0
- dbt/compute/metadata/store.cpython-311-darwin.so +0 -0
- dbt/compute/metadata/store.py +1499 -0
- dbt/compute/smart_selector.cpython-311-darwin.so +0 -0
- dbt/compute/smart_selector.py +377 -0
- dbt/compute/strategies/__init__.py +55 -0
- dbt/compute/strategies/base.cpython-311-darwin.so +0 -0
- dbt/compute/strategies/base.py +165 -0
- dbt/compute/strategies/dataproc.cpython-311-darwin.so +0 -0
- dbt/compute/strategies/dataproc.py +207 -0
- dbt/compute/strategies/emr.cpython-311-darwin.so +0 -0
- dbt/compute/strategies/emr.py +203 -0
- dbt/compute/strategies/local.cpython-311-darwin.so +0 -0
- dbt/compute/strategies/local.py +443 -0
- dbt/compute/strategies/standalone.cpython-311-darwin.so +0 -0
- dbt/compute/strategies/standalone.py +262 -0
- dbt/config/__init__.py +4 -0
- dbt/config/catalogs.py +94 -0
- dbt/config/compute.cpython-311-darwin.so +0 -0
- dbt/config/compute.py +513 -0
- dbt/config/dvt_profile.cpython-311-darwin.so +0 -0
- dbt/config/dvt_profile.py +342 -0
- dbt/config/profile.py +422 -0
- dbt/config/project.py +873 -0
- dbt/config/project_utils.py +28 -0
- dbt/config/renderer.py +231 -0
- dbt/config/runtime.py +553 -0
- dbt/config/selectors.py +208 -0
- dbt/config/utils.py +77 -0
- dbt/constants.py +28 -0
- dbt/context/__init__.py +0 -0
- dbt/context/base.py +745 -0
- dbt/context/configured.py +135 -0
- dbt/context/context_config.py +382 -0
- dbt/context/docs.py +82 -0
- dbt/context/exceptions_jinja.py +178 -0
- dbt/context/macro_resolver.py +195 -0
- dbt/context/macros.py +171 -0
- dbt/context/manifest.py +72 -0
- dbt/context/providers.py +2249 -0
- dbt/context/query_header.py +13 -0
- dbt/context/secret.py +58 -0
- dbt/context/target.py +74 -0
- dbt/contracts/__init__.py +0 -0
- dbt/contracts/files.py +413 -0
- dbt/contracts/graph/__init__.py +0 -0
- dbt/contracts/graph/manifest.py +1904 -0
- dbt/contracts/graph/metrics.py +97 -0
- dbt/contracts/graph/model_config.py +70 -0
- dbt/contracts/graph/node_args.py +42 -0
- dbt/contracts/graph/nodes.py +1806 -0
- dbt/contracts/graph/semantic_manifest.py +232 -0
- dbt/contracts/graph/unparsed.py +811 -0
- dbt/contracts/project.py +417 -0
- dbt/contracts/results.py +53 -0
- dbt/contracts/selection.py +23 -0
- dbt/contracts/sql.py +85 -0
- dbt/contracts/state.py +68 -0
- dbt/contracts/util.py +46 -0
- dbt/deprecations.py +348 -0
- dbt/deps/__init__.py +0 -0
- dbt/deps/base.py +152 -0
- dbt/deps/git.py +195 -0
- dbt/deps/local.py +79 -0
- dbt/deps/registry.py +130 -0
- dbt/deps/resolver.py +149 -0
- dbt/deps/tarball.py +120 -0
- dbt/docs/source/_ext/dbt_click.py +119 -0
- dbt/docs/source/conf.py +32 -0
- dbt/env_vars.py +64 -0
- dbt/event_time/event_time.py +40 -0
- dbt/event_time/sample_window.py +60 -0
- dbt/events/__init__.py +15 -0
- dbt/events/base_types.py +36 -0
- dbt/events/core_types_pb2.py +2 -0
- dbt/events/logging.py +108 -0
- dbt/events/types.py +2516 -0
- dbt/exceptions.py +1486 -0
- dbt/flags.py +89 -0
- dbt/graph/__init__.py +11 -0
- dbt/graph/cli.py +249 -0
- dbt/graph/graph.py +172 -0
- dbt/graph/queue.py +214 -0
- dbt/graph/selector.py +374 -0
- dbt/graph/selector_methods.py +975 -0
- dbt/graph/selector_spec.py +222 -0
- dbt/graph/thread_pool.py +18 -0
- dbt/hooks.py +21 -0
- dbt/include/README.md +49 -0
- dbt/include/__init__.py +3 -0
- dbt/include/data/adapters_registry.duckdb +0 -0
- dbt/include/data/build_registry.py +242 -0
- dbt/include/data/csv/adapter_queries.csv +33 -0
- dbt/include/data/csv/syntax_rules.csv +9 -0
- dbt/include/data/csv/type_mappings_bigquery.csv +28 -0
- dbt/include/data/csv/type_mappings_databricks.csv +30 -0
- dbt/include/data/csv/type_mappings_mysql.csv +40 -0
- dbt/include/data/csv/type_mappings_oracle.csv +30 -0
- dbt/include/data/csv/type_mappings_postgres.csv +56 -0
- dbt/include/data/csv/type_mappings_redshift.csv +33 -0
- dbt/include/data/csv/type_mappings_snowflake.csv +38 -0
- dbt/include/data/csv/type_mappings_sqlserver.csv +35 -0
- dbt/include/starter_project/.gitignore +4 -0
- dbt/include/starter_project/README.md +15 -0
- dbt/include/starter_project/__init__.py +3 -0
- dbt/include/starter_project/analyses/.gitkeep +0 -0
- dbt/include/starter_project/dbt_project.yml +36 -0
- dbt/include/starter_project/macros/.gitkeep +0 -0
- dbt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
- dbt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
- dbt/include/starter_project/models/example/schema.yml +21 -0
- dbt/include/starter_project/seeds/.gitkeep +0 -0
- dbt/include/starter_project/snapshots/.gitkeep +0 -0
- dbt/include/starter_project/tests/.gitkeep +0 -0
- dbt/internal_deprecations.py +26 -0
- dbt/jsonschemas/__init__.py +3 -0
- dbt/jsonschemas/jsonschemas.py +309 -0
- dbt/jsonschemas/project/0.0.110.json +4717 -0
- dbt/jsonschemas/project/0.0.85.json +2015 -0
- dbt/jsonschemas/resources/0.0.110.json +2636 -0
- dbt/jsonschemas/resources/0.0.85.json +2536 -0
- dbt/jsonschemas/resources/latest.json +6773 -0
- dbt/links.py +4 -0
- dbt/materializations/__init__.py +0 -0
- dbt/materializations/incremental/__init__.py +0 -0
- dbt/materializations/incremental/microbatch.py +236 -0
- dbt/mp_context.py +8 -0
- dbt/node_types.py +37 -0
- dbt/parser/__init__.py +23 -0
- dbt/parser/analysis.py +21 -0
- dbt/parser/base.py +548 -0
- dbt/parser/common.py +266 -0
- dbt/parser/docs.py +52 -0
- dbt/parser/fixtures.py +51 -0
- dbt/parser/functions.py +30 -0
- dbt/parser/generic_test.py +100 -0
- dbt/parser/generic_test_builders.py +333 -0
- dbt/parser/hooks.py +118 -0
- dbt/parser/macros.py +137 -0
- dbt/parser/manifest.py +2204 -0
- dbt/parser/models.py +573 -0
- dbt/parser/partial.py +1178 -0
- dbt/parser/read_files.py +445 -0
- dbt/parser/schema_generic_tests.py +422 -0
- dbt/parser/schema_renderer.py +111 -0
- dbt/parser/schema_yaml_readers.py +935 -0
- dbt/parser/schemas.py +1466 -0
- dbt/parser/search.py +149 -0
- dbt/parser/seeds.py +28 -0
- dbt/parser/singular_test.py +20 -0
- dbt/parser/snapshots.py +44 -0
- dbt/parser/sources.py +558 -0
- dbt/parser/sql.py +62 -0
- dbt/parser/unit_tests.py +621 -0
- dbt/plugins/__init__.py +20 -0
- dbt/plugins/contracts.py +9 -0
- dbt/plugins/exceptions.py +2 -0
- dbt/plugins/manager.py +163 -0
- dbt/plugins/manifest.py +21 -0
- dbt/profiler.py +20 -0
- dbt/py.typed +1 -0
- dbt/query_analyzer.cpython-311-darwin.so +0 -0
- dbt/query_analyzer.py +410 -0
- dbt/runners/__init__.py +2 -0
- dbt/runners/exposure_runner.py +7 -0
- dbt/runners/no_op_runner.py +45 -0
- dbt/runners/saved_query_runner.py +7 -0
- dbt/selected_resources.py +8 -0
- dbt/task/__init__.py +0 -0
- dbt/task/base.py +503 -0
- dbt/task/build.py +197 -0
- dbt/task/clean.py +56 -0
- dbt/task/clone.py +161 -0
- dbt/task/compile.py +150 -0
- dbt/task/compute.cpython-311-darwin.so +0 -0
- dbt/task/compute.py +458 -0
- dbt/task/debug.py +505 -0
- dbt/task/deps.py +280 -0
- dbt/task/docs/__init__.py +3 -0
- dbt/task/docs/api/__init__.py +23 -0
- dbt/task/docs/api/catalog.cpython-311-darwin.so +0 -0
- dbt/task/docs/api/catalog.py +204 -0
- dbt/task/docs/api/lineage.cpython-311-darwin.so +0 -0
- dbt/task/docs/api/lineage.py +234 -0
- dbt/task/docs/api/profile.cpython-311-darwin.so +0 -0
- dbt/task/docs/api/profile.py +204 -0
- dbt/task/docs/api/spark.cpython-311-darwin.so +0 -0
- dbt/task/docs/api/spark.py +186 -0
- dbt/task/docs/generate.py +947 -0
- dbt/task/docs/index.html +250 -0
- dbt/task/docs/serve.cpython-311-darwin.so +0 -0
- dbt/task/docs/serve.py +174 -0
- dbt/task/dvt_output.py +362 -0
- dbt/task/dvt_run.py +204 -0
- dbt/task/freshness.py +322 -0
- dbt/task/function.py +121 -0
- dbt/task/group_lookup.py +46 -0
- dbt/task/init.cpython-311-darwin.so +0 -0
- dbt/task/init.py +604 -0
- dbt/task/java.cpython-311-darwin.so +0 -0
- dbt/task/java.py +316 -0
- dbt/task/list.py +236 -0
- dbt/task/metadata.cpython-311-darwin.so +0 -0
- dbt/task/metadata.py +804 -0
- dbt/task/printer.py +175 -0
- dbt/task/profile.cpython-311-darwin.so +0 -0
- dbt/task/profile.py +1307 -0
- dbt/task/profile_serve.py +615 -0
- dbt/task/retract.py +438 -0
- dbt/task/retry.py +175 -0
- dbt/task/run.py +1387 -0
- dbt/task/run_operation.py +141 -0
- dbt/task/runnable.py +758 -0
- dbt/task/seed.py +103 -0
- dbt/task/show.py +149 -0
- dbt/task/snapshot.py +56 -0
- dbt/task/spark.cpython-311-darwin.so +0 -0
- dbt/task/spark.py +414 -0
- dbt/task/sql.py +110 -0
- dbt/task/target_sync.cpython-311-darwin.so +0 -0
- dbt/task/target_sync.py +766 -0
- dbt/task/test.py +464 -0
- dbt/tests/fixtures/__init__.py +1 -0
- dbt/tests/fixtures/project.py +620 -0
- dbt/tests/util.py +651 -0
- dbt/tracking.py +529 -0
- dbt/utils/__init__.py +3 -0
- dbt/utils/artifact_upload.py +151 -0
- dbt/utils/utils.py +408 -0
- dbt/version.py +270 -0
- dvt_cli/__init__.py +72 -0
- dvt_core-0.58.6.dist-info/METADATA +288 -0
- dvt_core-0.58.6.dist-info/RECORD +324 -0
- dvt_core-0.58.6.dist-info/WHEEL +5 -0
- dvt_core-0.58.6.dist-info/entry_points.txt +2 -0
- dvt_core-0.58.6.dist-info/top_level.txt +2 -0
|
Binary file
|
dbt/task/docs/serve.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DVT Docs Serve Task
|
|
3
|
+
|
|
4
|
+
v0.56.0: Enhanced with FastAPI backend and 3-tab web UI.
|
|
5
|
+
|
|
6
|
+
Serves documentation with:
|
|
7
|
+
- Traditional static HTML docs (backward compatible)
|
|
8
|
+
- REST API for catalog, profiling, lineage, and Spark status
|
|
9
|
+
- 3-tab web UI: Catalog, Profiling, Spark Monitor
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import shutil
|
|
14
|
+
import webbrowser
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
import click
|
|
19
|
+
|
|
20
|
+
from dbt.task.base import ConfiguredTask
|
|
21
|
+
from dbt.task.docs import DOCS_INDEX_FILE_PATH
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ServeTask(ConfiguredTask):
|
|
25
|
+
"""
|
|
26
|
+
Serve documentation with optional enhanced API.
|
|
27
|
+
|
|
28
|
+
When FastAPI is available, serves:
|
|
29
|
+
- /api/catalog/* - Catalog nodes and search
|
|
30
|
+
- /api/profile/* - Profile results and alerts
|
|
31
|
+
- /api/lineage/* - Lineage graph and traversal
|
|
32
|
+
- /api/spark/* - Spark status (local only)
|
|
33
|
+
- /* - Static documentation files
|
|
34
|
+
|
|
35
|
+
Falls back to simple HTTP server when FastAPI is not installed.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def run(self):
|
|
39
|
+
port = self.args.port
|
|
40
|
+
host = self.args.host
|
|
41
|
+
project_root = Path(self.config.project_root)
|
|
42
|
+
target_path = Path(self.config.project_target_path)
|
|
43
|
+
|
|
44
|
+
# Check if FastAPI is available for enhanced serving
|
|
45
|
+
if self._fastapi_available():
|
|
46
|
+
self._run_fastapi_server(host, port, project_root, target_path)
|
|
47
|
+
else:
|
|
48
|
+
self._run_simple_server(host, port, target_path)
|
|
49
|
+
|
|
50
|
+
def _fastapi_available(self) -> bool:
|
|
51
|
+
"""Check if FastAPI and uvicorn are installed."""
|
|
52
|
+
try:
|
|
53
|
+
import fastapi # noqa: F401
|
|
54
|
+
import uvicorn # noqa: F401
|
|
55
|
+
return True
|
|
56
|
+
except ImportError:
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
def _run_fastapi_server(
|
|
60
|
+
self,
|
|
61
|
+
host: str,
|
|
62
|
+
port: int,
|
|
63
|
+
project_root: Path,
|
|
64
|
+
target_path: Path,
|
|
65
|
+
):
|
|
66
|
+
"""Run enhanced FastAPI server with REST API."""
|
|
67
|
+
from fastapi import FastAPI
|
|
68
|
+
from fastapi.staticfiles import StaticFiles
|
|
69
|
+
from fastapi.responses import RedirectResponse
|
|
70
|
+
import uvicorn
|
|
71
|
+
|
|
72
|
+
# Import API routers
|
|
73
|
+
from dbt.task.docs.api import (
|
|
74
|
+
catalog_router,
|
|
75
|
+
profile_router,
|
|
76
|
+
lineage_router,
|
|
77
|
+
spark_router,
|
|
78
|
+
)
|
|
79
|
+
from dbt.task.docs.api import catalog, profile, lineage, spark
|
|
80
|
+
|
|
81
|
+
# Initialize metadata store ONCE at startup
|
|
82
|
+
try:
|
|
83
|
+
from dbt.compute.metadata import ProjectMetadataStore
|
|
84
|
+
store = ProjectMetadataStore(project_root)
|
|
85
|
+
store.initialize() # Only called once here
|
|
86
|
+
click.echo(f" Metadata store initialized: {project_root}/.dvt/metadata_store.duckdb")
|
|
87
|
+
except Exception as e:
|
|
88
|
+
click.echo(f" Warning: Could not initialize metadata store: {e}")
|
|
89
|
+
|
|
90
|
+
# Set project root for all API modules (store will skip re-initialization)
|
|
91
|
+
catalog.set_project_root(project_root)
|
|
92
|
+
profile.set_project_root(project_root)
|
|
93
|
+
lineage.set_project_root(project_root)
|
|
94
|
+
spark.set_project_root(project_root)
|
|
95
|
+
|
|
96
|
+
# Create FastAPI app
|
|
97
|
+
app = FastAPI(
|
|
98
|
+
title="DVT Documentation",
|
|
99
|
+
description="DVT catalog, profiling, and lineage API",
|
|
100
|
+
version="0.56.0",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Include API routers
|
|
104
|
+
app.include_router(catalog_router)
|
|
105
|
+
app.include_router(profile_router)
|
|
106
|
+
app.include_router(lineage_router)
|
|
107
|
+
app.include_router(spark_router)
|
|
108
|
+
|
|
109
|
+
# Prepare static files directory
|
|
110
|
+
os.chdir(target_path)
|
|
111
|
+
shutil.copyfile(DOCS_INDEX_FILE_PATH, "index.html")
|
|
112
|
+
|
|
113
|
+
# Root redirect to index.html
|
|
114
|
+
@app.get("/")
|
|
115
|
+
async def root():
|
|
116
|
+
return RedirectResponse(url="/index.html")
|
|
117
|
+
|
|
118
|
+
# Mount static files (must be last to not override API routes)
|
|
119
|
+
app.mount("/", StaticFiles(directory=str(target_path), html=True), name="static")
|
|
120
|
+
|
|
121
|
+
# Open browser if requested
|
|
122
|
+
if self.args.browser:
|
|
123
|
+
webbrowser.open_new_tab(f"http://localhost:{port}")
|
|
124
|
+
|
|
125
|
+
# Print server info
|
|
126
|
+
click.echo("")
|
|
127
|
+
click.echo("╔════════════════════════════════════════════════════════════════╗")
|
|
128
|
+
click.echo("║ DVT Documentation Server (Enhanced) ║")
|
|
129
|
+
click.echo("╠════════════════════════════════════════════════════════════════╣")
|
|
130
|
+
click.echo(f"║ Serving at: http://{host}:{port}".ljust(66) + "║")
|
|
131
|
+
click.echo("║ ║")
|
|
132
|
+
click.echo("║ Web UI: ║")
|
|
133
|
+
click.echo(f"║ • Documentation: http://localhost:{port}/".ljust(66) + "║")
|
|
134
|
+
click.echo(f"║ • API Docs: http://localhost:{port}/docs".ljust(66) + "║")
|
|
135
|
+
click.echo("║ ║")
|
|
136
|
+
click.echo("║ API Endpoints: ║")
|
|
137
|
+
click.echo("║ • /api/catalog/* - Catalog nodes and search ║")
|
|
138
|
+
click.echo("║ • /api/profile/* - Profile results and alerts ║")
|
|
139
|
+
click.echo("║ • /api/lineage/* - Lineage graph and traversal ║")
|
|
140
|
+
click.echo("║ • /api/spark/* - Spark status (local only) ║")
|
|
141
|
+
click.echo("╠════════════════════════════════════════════════════════════════╣")
|
|
142
|
+
click.echo("║ Press Ctrl+C to stop ║")
|
|
143
|
+
click.echo("╚════════════════════════════════════════════════════════════════╝")
|
|
144
|
+
click.echo("")
|
|
145
|
+
|
|
146
|
+
# Run server
|
|
147
|
+
uvicorn.run(app, host=host, port=port, log_level="warning")
|
|
148
|
+
|
|
149
|
+
def _run_simple_server(self, host: str, port: int, target_path: Path):
|
|
150
|
+
"""Run simple HTTP server (fallback when FastAPI not installed)."""
|
|
151
|
+
import socketserver
|
|
152
|
+
from http.server import SimpleHTTPRequestHandler
|
|
153
|
+
|
|
154
|
+
os.chdir(target_path)
|
|
155
|
+
shutil.copyfile(DOCS_INDEX_FILE_PATH, "index.html")
|
|
156
|
+
|
|
157
|
+
if self.args.browser:
|
|
158
|
+
webbrowser.open_new_tab(f"http://localhost:{port}")
|
|
159
|
+
|
|
160
|
+
click.echo("")
|
|
161
|
+
click.echo("╔════════════════════════════════════════════════════════════════╗")
|
|
162
|
+
click.echo("║ DVT Documentation Server (Basic) ║")
|
|
163
|
+
click.echo("╠════════════════════════════════════════════════════════════════╣")
|
|
164
|
+
click.echo(f"║ Serving at: http://{host}:{port}".ljust(66) + "║")
|
|
165
|
+
click.echo("║ ║")
|
|
166
|
+
click.echo("║ Note: Install fastapi and uvicorn for enhanced features: ║")
|
|
167
|
+
click.echo("║ pip install fastapi uvicorn ║")
|
|
168
|
+
click.echo("╠════════════════════════════════════════════════════════════════╣")
|
|
169
|
+
click.echo("║ Press Ctrl+C to stop ║")
|
|
170
|
+
click.echo("╚════════════════════════════════════════════════════════════════╝")
|
|
171
|
+
click.echo("")
|
|
172
|
+
|
|
173
|
+
with socketserver.TCPServer((host, port), SimpleHTTPRequestHandler) as httpd:
|
|
174
|
+
httpd.serve_forever()
|
dbt/task/dvt_output.py
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# DVT Rich Output Helpers
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# Beautiful CLI output using Rich library for DVT commands.
|
|
5
|
+
#
|
|
6
|
+
# DVT v0.58.0: Unified output styling for all DVT commands
|
|
7
|
+
# DVT v0.58.1: Enhanced for dvt run integration
|
|
8
|
+
# =============================================================================
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import time
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from typing import Any, Dict, List, Optional, Callable
|
|
16
|
+
|
|
17
|
+
# Try to import Rich - graceful fallback if not available
|
|
18
|
+
try:
|
|
19
|
+
from rich.console import Console
|
|
20
|
+
from rich.progress import (
|
|
21
|
+
Progress,
|
|
22
|
+
TextColumn,
|
|
23
|
+
BarColumn,
|
|
24
|
+
MofNCompleteColumn,
|
|
25
|
+
TimeElapsedColumn,
|
|
26
|
+
SpinnerColumn,
|
|
27
|
+
TaskProgressColumn,
|
|
28
|
+
)
|
|
29
|
+
from rich.table import Table
|
|
30
|
+
from rich.panel import Panel
|
|
31
|
+
from rich.live import Live
|
|
32
|
+
from rich import box
|
|
33
|
+
HAS_RICH = True
|
|
34
|
+
except ImportError:
|
|
35
|
+
HAS_RICH = False
|
|
36
|
+
Console = None
|
|
37
|
+
Progress = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class DVTRunStats:
|
|
42
|
+
"""Statistics for a DVT run execution."""
|
|
43
|
+
total: int = 0
|
|
44
|
+
passed: int = 0
|
|
45
|
+
failed: int = 0
|
|
46
|
+
skipped: int = 0
|
|
47
|
+
warned: int = 0
|
|
48
|
+
errored: int = 0
|
|
49
|
+
start_time: float = field(default_factory=time.time)
|
|
50
|
+
end_time: float = 0.0
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def duration_seconds(self) -> float:
|
|
54
|
+
return self.end_time - self.start_time if self.end_time else 0.0
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def success_rate(self) -> float:
|
|
58
|
+
if self.total == 0:
|
|
59
|
+
return 100.0
|
|
60
|
+
return (self.passed / self.total) * 100
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class DVTProgressDisplay:
|
|
64
|
+
"""
|
|
65
|
+
Rich progress display for DVT commands.
|
|
66
|
+
|
|
67
|
+
Provides a beautiful CLI UI with:
|
|
68
|
+
- Header panel with command info
|
|
69
|
+
- Progress bar with spinner and current task
|
|
70
|
+
- Per-model result lines
|
|
71
|
+
- Summary table at the end
|
|
72
|
+
|
|
73
|
+
DVT v0.58.1: Enhanced for dvt run integration with per-model progress.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def __init__(self, title: str = "DVT Run", subtitle: str = ""):
|
|
77
|
+
self.title = title
|
|
78
|
+
self.subtitle = subtitle
|
|
79
|
+
self._console = None
|
|
80
|
+
self._progress = None
|
|
81
|
+
self._task_id = None
|
|
82
|
+
self._use_rich = HAS_RICH
|
|
83
|
+
self.stats = DVTRunStats()
|
|
84
|
+
self._model_results: List[Dict[str, Any]] = []
|
|
85
|
+
|
|
86
|
+
if self._use_rich:
|
|
87
|
+
self._console = Console()
|
|
88
|
+
|
|
89
|
+
def print_header(self, mode: str = "", target: str = "", compute: str = ""):
|
|
90
|
+
"""Print a beautiful header panel."""
|
|
91
|
+
if not self._use_rich:
|
|
92
|
+
print(f"\n{'=' * 60}")
|
|
93
|
+
print(f" {self.title}")
|
|
94
|
+
if self.subtitle:
|
|
95
|
+
print(f" {self.subtitle}")
|
|
96
|
+
if target:
|
|
97
|
+
print(f" Target: {target}")
|
|
98
|
+
if compute:
|
|
99
|
+
print(f" Compute: {compute}")
|
|
100
|
+
print(f"{'=' * 60}\n")
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
# Build info line
|
|
104
|
+
info_parts = []
|
|
105
|
+
if target:
|
|
106
|
+
info_parts.append(f"[bold cyan]Target:[/bold cyan] [yellow]{target}[/yellow]")
|
|
107
|
+
if compute:
|
|
108
|
+
info_parts.append(f"[bold cyan]Compute:[/bold cyan] [yellow]{compute}[/yellow]")
|
|
109
|
+
if mode:
|
|
110
|
+
info_parts.append(f"[bold cyan]Mode:[/bold cyan] [yellow]{mode}[/yellow]")
|
|
111
|
+
|
|
112
|
+
info_line = " | ".join(info_parts) if info_parts else ""
|
|
113
|
+
|
|
114
|
+
header_panel = Panel(
|
|
115
|
+
info_line or "[dim]Processing models...[/dim]",
|
|
116
|
+
title=f"[bold magenta]{self.title}[/bold magenta]",
|
|
117
|
+
subtitle=f"[dim]{self.subtitle}[/dim]" if self.subtitle else None,
|
|
118
|
+
border_style="magenta",
|
|
119
|
+
box=box.DOUBLE,
|
|
120
|
+
)
|
|
121
|
+
self._console.print()
|
|
122
|
+
self._console.print(header_panel)
|
|
123
|
+
self._console.print()
|
|
124
|
+
|
|
125
|
+
def start_progress(self, total: int, description: str = "Running models..."):
|
|
126
|
+
"""Start the progress bar."""
|
|
127
|
+
self.stats.total = total
|
|
128
|
+
self.stats.start_time = time.time()
|
|
129
|
+
|
|
130
|
+
if not self._use_rich:
|
|
131
|
+
print(f"Starting: {description} ({total} total)")
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
self._progress = Progress(
|
|
135
|
+
SpinnerColumn(),
|
|
136
|
+
TextColumn("[bold blue]{task.description}"),
|
|
137
|
+
BarColumn(bar_width=40),
|
|
138
|
+
TaskProgressColumn(),
|
|
139
|
+
MofNCompleteColumn(),
|
|
140
|
+
TimeElapsedColumn(),
|
|
141
|
+
console=self._console,
|
|
142
|
+
)
|
|
143
|
+
self._progress.__enter__()
|
|
144
|
+
self._task_id = self._progress.add_task(f"[cyan]{description}", total=total)
|
|
145
|
+
|
|
146
|
+
def update_current(self, model_name: str, status: str = "running"):
|
|
147
|
+
"""Update the current model being processed."""
|
|
148
|
+
if not self._use_rich or not self._progress:
|
|
149
|
+
if status == "running":
|
|
150
|
+
print(f" [{self.stats.passed + self.stats.errored + 1}/{self.stats.total}] {model_name}...")
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
# Color based on status
|
|
154
|
+
status_colors = {
|
|
155
|
+
"running": "cyan",
|
|
156
|
+
"success": "green",
|
|
157
|
+
"error": "red",
|
|
158
|
+
"skip": "yellow",
|
|
159
|
+
"warn": "yellow",
|
|
160
|
+
}
|
|
161
|
+
color = status_colors.get(status, "white")
|
|
162
|
+
|
|
163
|
+
self._progress.update(
|
|
164
|
+
self._task_id,
|
|
165
|
+
description=f"[{color}]{status.upper()}[/{color}] [bold]{model_name}[/bold]"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
def advance(self, status: str = "success"):
|
|
169
|
+
"""Advance the progress bar and update stats."""
|
|
170
|
+
if status == "success" or status == "pass":
|
|
171
|
+
self.stats.passed += 1
|
|
172
|
+
elif status == "error" or status == "fail":
|
|
173
|
+
self.stats.errored += 1
|
|
174
|
+
elif status == "skip":
|
|
175
|
+
self.stats.skipped += 1
|
|
176
|
+
elif status == "warn":
|
|
177
|
+
self.stats.warned += 1
|
|
178
|
+
else:
|
|
179
|
+
self.stats.passed += 1
|
|
180
|
+
|
|
181
|
+
if self._use_rich and self._progress:
|
|
182
|
+
self._progress.advance(self._task_id)
|
|
183
|
+
|
|
184
|
+
def stop_progress(self):
|
|
185
|
+
"""Stop the progress bar."""
|
|
186
|
+
self.stats.end_time = time.time()
|
|
187
|
+
if self._use_rich and self._progress:
|
|
188
|
+
self._progress.__exit__(None, None, None)
|
|
189
|
+
self._progress = None
|
|
190
|
+
|
|
191
|
+
def print_model_result(
|
|
192
|
+
self,
|
|
193
|
+
model_name: str,
|
|
194
|
+
status: str,
|
|
195
|
+
duration_ms: float = 0,
|
|
196
|
+
message: str = "",
|
|
197
|
+
materialization: str = "",
|
|
198
|
+
execution_path: str = "",
|
|
199
|
+
):
|
|
200
|
+
"""Print a single model result line.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
model_name: Name of the model
|
|
204
|
+
status: Execution status (success, error, skip, warn)
|
|
205
|
+
duration_ms: Execution time in milliseconds
|
|
206
|
+
message: Additional message (e.g., row count, error)
|
|
207
|
+
materialization: Materialization type (table, view, incremental)
|
|
208
|
+
execution_path: DVT execution path (PUSHDOWN or FEDERATION)
|
|
209
|
+
"""
|
|
210
|
+
# Store result for summary
|
|
211
|
+
self._model_results.append({
|
|
212
|
+
"name": model_name,
|
|
213
|
+
"status": status,
|
|
214
|
+
"duration_ms": duration_ms,
|
|
215
|
+
"message": message,
|
|
216
|
+
"materialization": materialization,
|
|
217
|
+
"execution_path": execution_path,
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
# Determine execution path indicator
|
|
221
|
+
path_str = ""
|
|
222
|
+
if execution_path:
|
|
223
|
+
path_str = f"[{execution_path}]"
|
|
224
|
+
|
|
225
|
+
if not self._use_rich:
|
|
226
|
+
status_symbol = "OK" if status in ("success", "pass") else "FAIL" if status in ("error", "fail") else "SKIP"
|
|
227
|
+
mat_str = f" [{materialization}]" if materialization else ""
|
|
228
|
+
path_display = f" {path_str}" if path_str else ""
|
|
229
|
+
msg_display = f" {message}" if message else ""
|
|
230
|
+
print(f" {status_symbol} {model_name}{path_display}{mat_str} ({duration_ms:.0f}ms){msg_display}")
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
# Rich formatted output
|
|
234
|
+
status_styles = {
|
|
235
|
+
"success": ("[green]OK[/green]", "green"),
|
|
236
|
+
"pass": ("[green]OK[/green]", "green"),
|
|
237
|
+
"error": ("[red]FAIL[/red]", "red"),
|
|
238
|
+
"fail": ("[red]FAIL[/red]", "red"),
|
|
239
|
+
"skip": ("[yellow]SKIP[/yellow]", "yellow"),
|
|
240
|
+
"warn": ("[yellow]WARN[/yellow]", "yellow"),
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
# Execution path styling
|
|
244
|
+
path_styles = {
|
|
245
|
+
"PUSHDOWN": "[bold blue]PUSHDOWN[/bold blue]",
|
|
246
|
+
"FEDERATION": "[bold magenta]FEDERATION[/bold magenta]",
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
symbol, color = status_styles.get(status, ("[white]--[/white]", "white"))
|
|
250
|
+
mat_str = f" [dim][{materialization}][/dim]" if materialization else ""
|
|
251
|
+
|
|
252
|
+
# Build execution path display
|
|
253
|
+
path_display = ""
|
|
254
|
+
if execution_path:
|
|
255
|
+
styled_path = path_styles.get(execution_path, f"[dim]{execution_path}[/dim]")
|
|
256
|
+
path_display = f" [{styled_path}]"
|
|
257
|
+
|
|
258
|
+
# Build message display (e.g., row count, error details)
|
|
259
|
+
msg_str = ""
|
|
260
|
+
if message and execution_path != message: # Avoid duplicating execution_path
|
|
261
|
+
msg_str = f" [dim]{message}[/dim]"
|
|
262
|
+
|
|
263
|
+
self._console.print(
|
|
264
|
+
f" {symbol} [{color}]{model_name}[/{color}]{path_display}{mat_str} "
|
|
265
|
+
f"[dim]({duration_ms:.0f}ms)[/dim]{msg_str}"
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
def print_summary(self, additional_info: Dict[str, Any] = None):
|
|
269
|
+
"""Print a beautiful summary panel."""
|
|
270
|
+
duration = self.stats.duration_seconds
|
|
271
|
+
|
|
272
|
+
if not self._use_rich:
|
|
273
|
+
print(f"\n{'=' * 60}")
|
|
274
|
+
print(f" SUMMARY")
|
|
275
|
+
print(f" Passed: {self.stats.passed}/{self.stats.total}")
|
|
276
|
+
if self.stats.errored:
|
|
277
|
+
print(f" Failed: {self.stats.errored}")
|
|
278
|
+
if self.stats.skipped:
|
|
279
|
+
print(f" Skipped: {self.stats.skipped}")
|
|
280
|
+
print(f" Duration: {duration:.2f}s")
|
|
281
|
+
print(f"{'=' * 60}\n")
|
|
282
|
+
return
|
|
283
|
+
|
|
284
|
+
# Create summary panel
|
|
285
|
+
self._console.print()
|
|
286
|
+
|
|
287
|
+
# Status indicator
|
|
288
|
+
if self.stats.errored > 0:
|
|
289
|
+
status_icon = "[bold red]FAILED[/bold red]"
|
|
290
|
+
status_text = f"[bold red]{self.stats.errored} of {self.stats.total} models failed[/bold red]"
|
|
291
|
+
border_color = "red"
|
|
292
|
+
elif self.stats.warned > 0:
|
|
293
|
+
status_icon = "[bold yellow]WARNING[/bold yellow]"
|
|
294
|
+
status_text = f"[bold yellow]{self.stats.warned} warnings[/bold yellow]"
|
|
295
|
+
border_color = "yellow"
|
|
296
|
+
else:
|
|
297
|
+
status_icon = "[bold green]SUCCESS[/bold green]"
|
|
298
|
+
status_text = f"[bold green]All {self.stats.passed} models completed successfully[/bold green]"
|
|
299
|
+
border_color = "green"
|
|
300
|
+
|
|
301
|
+
# Build summary content
|
|
302
|
+
lines = [
|
|
303
|
+
f"{status_text}",
|
|
304
|
+
"",
|
|
305
|
+
f"[bold]Passed:[/bold] {self.stats.passed}",
|
|
306
|
+
]
|
|
307
|
+
if self.stats.errored:
|
|
308
|
+
lines.append(f"[bold red]Failed:[/bold red] {self.stats.errored}")
|
|
309
|
+
if self.stats.skipped:
|
|
310
|
+
lines.append(f"[bold yellow]Skipped:[/bold yellow] {self.stats.skipped}")
|
|
311
|
+
if self.stats.warned:
|
|
312
|
+
lines.append(f"[bold yellow]Warned:[/bold yellow] {self.stats.warned}")
|
|
313
|
+
lines.append("")
|
|
314
|
+
lines.append(f"[dim]Duration: {duration:.2f}s[/dim]")
|
|
315
|
+
|
|
316
|
+
if additional_info:
|
|
317
|
+
lines.append("")
|
|
318
|
+
for key, value in additional_info.items():
|
|
319
|
+
lines.append(f"[dim]{key}: {value}[/dim]")
|
|
320
|
+
|
|
321
|
+
summary_content = "\n".join(lines)
|
|
322
|
+
|
|
323
|
+
summary_panel = Panel(
|
|
324
|
+
summary_content,
|
|
325
|
+
title=f"[bold]{self.title} Complete[/bold]",
|
|
326
|
+
border_style=border_color,
|
|
327
|
+
box=box.ROUNDED,
|
|
328
|
+
)
|
|
329
|
+
self._console.print(summary_panel)
|
|
330
|
+
self._console.print()
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def create_progress_callback(display: DVTProgressDisplay) -> Callable:
|
|
334
|
+
"""Create a callback function for use with dbt's event system."""
|
|
335
|
+
def callback(event):
|
|
336
|
+
# This would integrate with dbt's event system
|
|
337
|
+
# For now, it's a placeholder
|
|
338
|
+
pass
|
|
339
|
+
return callback
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def create_dvt_run_display(target: str = "", compute: str = "") -> DVTProgressDisplay:
|
|
343
|
+
"""
|
|
344
|
+
Factory function to create a DVT run display.
|
|
345
|
+
|
|
346
|
+
Usage:
|
|
347
|
+
display = create_dvt_run_display(target="postgres", compute="spark-local")
|
|
348
|
+
display.print_header()
|
|
349
|
+
display.start_progress(total=10)
|
|
350
|
+
for model in models:
|
|
351
|
+
display.update_current(model.name, "running")
|
|
352
|
+
# ... execute model ...
|
|
353
|
+
display.print_model_result(model.name, "success", duration_ms=123, materialization="table")
|
|
354
|
+
display.advance("success")
|
|
355
|
+
display.stop_progress()
|
|
356
|
+
display.print_summary()
|
|
357
|
+
"""
|
|
358
|
+
display = DVTProgressDisplay(
|
|
359
|
+
title="DVT Run",
|
|
360
|
+
subtitle="Executing models with DVT compute rules",
|
|
361
|
+
)
|
|
362
|
+
return display
|