sql-glider 0.1.23__tar.gz → 0.1.24__tar.gz

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.
Files changed (163) hide show
  1. {sql_glider-0.1.23 → sql_glider-0.1.24}/CLAUDE.md +9 -0
  2. sql_glider-0.1.23/README.md → sql_glider-0.1.24/PKG-INFO +83 -1
  3. sql_glider-0.1.23/PKG-INFO → sql_glider-0.1.24/README.md +49 -33
  4. {sql_glider-0.1.23 → sql_glider-0.1.24}/docs/docs/graph-lineage.md +55 -1
  5. sql_glider-0.1.24/examples/plotly_viewer.py +109 -0
  6. {sql_glider-0.1.23 → sql_glider-0.1.24}/pyproject.toml +3 -0
  7. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/_version.py +2 -2
  8. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/cli.py +37 -13
  9. sql_glider-0.1.24/src/sqlglider/graph/diagram_formatters.py +658 -0
  10. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/graph/test_diagram_formatters.py +287 -1
  11. {sql_glider-0.1.23 → sql_glider-0.1.24}/uv.lock +146 -1
  12. sql_glider-0.1.23/src/sqlglider/graph/diagram_formatters.py +0 -330
  13. {sql_glider-0.1.23 → sql_glider-0.1.24}/.github/workflows/ci.yml +0 -0
  14. {sql_glider-0.1.23 → sql_glider-0.1.24}/.github/workflows/docs.yml +0 -0
  15. {sql_glider-0.1.23 → sql_glider-0.1.24}/.github/workflows/publish.yml +0 -0
  16. {sql_glider-0.1.23 → sql_glider-0.1.24}/.gitignore +0 -0
  17. {sql_glider-0.1.23 → sql_glider-0.1.24}/.python-version +0 -0
  18. {sql_glider-0.1.23 → sql_glider-0.1.24}/ARCHITECTURE.md +0 -0
  19. {sql_glider-0.1.23 → sql_glider-0.1.24}/LICENSE +0 -0
  20. {sql_glider-0.1.23 → sql_glider-0.1.24}/docs/.github/workflows/docs.yml +0 -0
  21. {sql_glider-0.1.23 → sql_glider-0.1.24}/docs/docs/catalogs.md +0 -0
  22. {sql_glider-0.1.23 → sql_glider-0.1.24}/docs/docs/index.md +0 -0
  23. {sql_glider-0.1.23 → sql_glider-0.1.24}/docs/docs/static/sqlglider-logo-transparent.png +0 -0
  24. {sql_glider-0.1.23 → sql_glider-0.1.24}/docs/docs/templating.md +0 -0
  25. {sql_glider-0.1.23 → sql_glider-0.1.24}/docs/zensical.toml +0 -0
  26. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2025-12-05-column-level-lineage.md +0 -0
  27. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2025-12-05-reverse-lineage.md +0 -0
  28. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2025-12-06-config-file-support.md +0 -0
  29. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2025-12-06-graph-lineage.md +0 -0
  30. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2025-12-06-unify-single-multi-query.md +0 -0
  31. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2025-12-07-sample-data-model.md +0 -0
  32. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2025-12-07-sql-templating.md +0 -0
  33. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2025-12-08-tables-command.md +0 -0
  34. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2025-12-09-graph-query-paths.md +0 -0
  35. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2025-12-13-dissect-command.md +0 -0
  36. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2025-12-14-tables-pull-command.md +0 -0
  37. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2026-01-25-fix-union-lineage-chain.md +0 -0
  38. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2026-01-26-file-scoped-schema-context.md +0 -0
  39. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2026-01-28-sparksql-table-extraction.md +0 -0
  40. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2026-01-29-no-star-flag.md +0 -0
  41. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2026-01-29-resolve-schema.md +0 -0
  42. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2026-01-29-schema-pruning-optimization.md +0 -0
  43. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2026-01-29-tables-scrape-command.md +0 -0
  44. {sql_glider-0.1.23 → sql_glider-0.1.24}/plans/2026-02-02-diagram-output-formats.md +0 -0
  45. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/README.md +0 -0
  46. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/business/expire_dim_customer.sql +0 -0
  47. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/business/load_fact_orders.sql +0 -0
  48. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/business/load_fact_payments.sql +0 -0
  49. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/business/merge_dim_customer.sql +0 -0
  50. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/business/merge_dim_product.sql +0 -0
  51. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/business/update_dim_customer_metrics.sql +0 -0
  52. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/complex/conditional_merge.sql +0 -0
  53. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/complex/cte_insert.sql +0 -0
  54. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/complex/multi_table_transform.sql +0 -0
  55. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/dim_customer.sql +0 -0
  56. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/dim_product.sql +0 -0
  57. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/fact_orders.sql +0 -0
  58. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/fact_payments.sql +0 -0
  59. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/raw_addresses.sql +0 -0
  60. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/raw_customers.sql +0 -0
  61. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/raw_order_items.sql +0 -0
  62. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/raw_orders.sql +0 -0
  63. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/raw_payments.sql +0 -0
  64. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/raw_products.sql +0 -0
  65. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/stg_customers.sql +0 -0
  66. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/stg_orders.sql +0 -0
  67. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/stg_payments.sql +0 -0
  68. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/ddl/stg_products.sql +0 -0
  69. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/incremental/incr_fact_orders.sql +0 -0
  70. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/incremental/incr_fact_payments.sql +0 -0
  71. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/incremental/incr_pres_sales_summary.sql +0 -0
  72. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/maintenance/delete_expired_customers.sql +0 -0
  73. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/maintenance/update_product_status.sql +0 -0
  74. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/presentation/load_pres_customer_360.sql +0 -0
  75. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/presentation/load_pres_customer_cohort.sql +0 -0
  76. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/presentation/load_pres_product_performance.sql +0 -0
  77. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/presentation/load_pres_sales_summary.sql +0 -0
  78. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/staging/load_stg_customers.sql +0 -0
  79. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/staging/load_stg_orders.sql +0 -0
  80. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/staging/load_stg_payments.sql +0 -0
  81. {sql_glider-0.1.23 → sql_glider-0.1.24}/sample_data_model/staging/load_stg_products.sql +0 -0
  82. {sql_glider-0.1.23 → sql_glider-0.1.24}/sqlglider.toml.example +0 -0
  83. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/__init__.py +0 -0
  84. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/catalog/__init__.py +0 -0
  85. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/catalog/base.py +0 -0
  86. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/catalog/databricks.py +0 -0
  87. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/catalog/registry.py +0 -0
  88. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/dissection/__init__.py +0 -0
  89. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/dissection/analyzer.py +0 -0
  90. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/dissection/formatters.py +0 -0
  91. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/dissection/models.py +0 -0
  92. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/global_models.py +0 -0
  93. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/graph/__init__.py +0 -0
  94. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/graph/builder.py +0 -0
  95. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/graph/formatters.py +0 -0
  96. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/graph/merge.py +0 -0
  97. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/graph/models.py +0 -0
  98. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/graph/query.py +0 -0
  99. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/graph/serialization.py +0 -0
  100. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/lineage/__init__.py +0 -0
  101. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/lineage/analyzer.py +0 -0
  102. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/lineage/formatters.py +0 -0
  103. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/schema/__init__.py +0 -0
  104. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/schema/extractor.py +0 -0
  105. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/templating/__init__.py +0 -0
  106. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/templating/base.py +0 -0
  107. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/templating/jinja.py +0 -0
  108. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/templating/registry.py +0 -0
  109. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/templating/variables.py +0 -0
  110. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/utils/__init__.py +0 -0
  111. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/utils/config.py +0 -0
  112. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/utils/file_utils.py +0 -0
  113. {sql_glider-0.1.23 → sql_glider-0.1.24}/src/sqlglider/utils/schema.py +0 -0
  114. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/__init__.py +0 -0
  115. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/multi_file_queries/analytics_pipeline.sql +0 -0
  116. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/multi_file_queries/analytics_pipeline_union_merge.sql +0 -0
  117. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/multi_file_queries/customers.sql +0 -0
  118. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/multi_file_queries/orders.sql +0 -0
  119. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/multi_file_queries/reports.sql +0 -0
  120. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/multi_file_queries/view_based_merge.sql +0 -0
  121. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/original_queries/test_cte.sql +0 -0
  122. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/original_queries/test_cte_query.sql +0 -0
  123. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/original_queries/test_cte_view_star.sql +0 -0
  124. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/original_queries/test_generated_column_query.sql +0 -0
  125. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/original_queries/test_multi.sql +0 -0
  126. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/original_queries/test_multi_query.sql +0 -0
  127. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/original_queries/test_single_query.sql +0 -0
  128. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/original_queries/test_subquery.sql +0 -0
  129. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/original_queries/test_tables.sql +0 -0
  130. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/original_queries/test_view.sql +0 -0
  131. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/original_queries/test_view_window_cte.sql +0 -0
  132. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/fixtures/sample_manifest.csv +0 -0
  133. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/__init__.py +0 -0
  134. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/catalog/__init__.py +0 -0
  135. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/catalog/test_base.py +0 -0
  136. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/catalog/test_databricks.py +0 -0
  137. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/catalog/test_registry.py +0 -0
  138. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/dissection/__init__.py +0 -0
  139. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/dissection/test_analyzer.py +0 -0
  140. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/dissection/test_formatters.py +0 -0
  141. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/dissection/test_models.py +0 -0
  142. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/graph/__init__.py +0 -0
  143. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/graph/test_builder.py +0 -0
  144. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/graph/test_formatters.py +0 -0
  145. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/graph/test_merge.py +0 -0
  146. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/graph/test_models.py +0 -0
  147. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/graph/test_query.py +0 -0
  148. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/graph/test_serialization.py +0 -0
  149. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/lineage/__init__.py +0 -0
  150. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/lineage/test_analyzer.py +0 -0
  151. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/lineage/test_formatters.py +0 -0
  152. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/schema/__init__.py +0 -0
  153. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/schema/test_extractor.py +0 -0
  154. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/templating/__init__.py +0 -0
  155. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/templating/test_base.py +0 -0
  156. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/templating/test_jinja.py +0 -0
  157. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/templating/test_registry.py +0 -0
  158. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/templating/test_variables.py +0 -0
  159. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/test_cli.py +0 -0
  160. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/utils/__init__.py +0 -0
  161. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/utils/test_config.py +0 -0
  162. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/utils/test_file_utils.py +0 -0
  163. {sql_glider-0.1.23 → sql_glider-0.1.24}/tests/sqlglider/utils/test_schema.py +0 -0
@@ -276,17 +276,26 @@ uv run sqlglider graph query graph.json --upstream orders.total -f mermaid
276
276
  # Query with DOT (Graphviz) diagram output
277
277
  uv run sqlglider graph query graph.json --downstream customers.id -f dot
278
278
 
279
+ # Query with Plotly JSON output (for Dash/Plotly apps)
280
+ uv run sqlglider graph query graph.json --upstream orders.total -f plotly
281
+
279
282
  # Visualize entire graph as Mermaid diagram
280
283
  uv run sqlglider graph visualize graph.json
281
284
 
282
285
  # Visualize entire graph as DOT diagram
283
286
  uv run sqlglider graph visualize graph.json -f dot
284
287
 
288
+ # Visualize entire graph as Plotly JSON
289
+ uv run sqlglider graph visualize graph.json -f plotly
290
+
285
291
  # Save diagram to file
286
292
  uv run sqlglider graph visualize graph.json -o lineage.mmd
287
293
  uv run sqlglider graph visualize graph.json -f dot -o lineage.dot
294
+ uv run sqlglider graph visualize graph.json -f plotly -o lineage.json
288
295
  ```
289
296
 
297
+ **Note:** Plotly output requires the optional dependency: `pip install sql-glider[plotly]`
298
+
290
299
  ### SQL Templating
291
300
 
292
301
  SQL Glider supports Jinja2 templating for SQL files. This allows you to use variables, conditionals, and loops in your SQL before analysis.
@@ -1,3 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: sql-glider
3
+ Version: 0.1.24
4
+ Summary: SQL Utility Toolkit for better understanding, use, and governance of your queries in a native environment.
5
+ Project-URL: Homepage, https://github.com/rycowhi/sql-glider/
6
+ Project-URL: Repository, https://github.com/rycowhi/sql-glider/
7
+ Project-URL: Documentation, https://github.com/rycowhi/sql-glider/
8
+ Project-URL: Issues, https://github.com/rycowhi/sql-glider/issues
9
+ Author-email: Ryan Whitcomb <ryankwhitcomb@gmail.com>
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ Keywords: data-governance,data-lineage,lineage,sql,sqlglot
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: SQL
19
+ Classifier: Topic :: Database
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: jinja2>=3.0.0
24
+ Requires-Dist: pydantic>=2.0.0
25
+ Requires-Dist: rich>=13.0.0
26
+ Requires-Dist: rustworkx>=0.15.0
27
+ Requires-Dist: sqlglot[rs]>=25.0.0
28
+ Requires-Dist: typer>=0.9.0
29
+ Provides-Extra: databricks
30
+ Requires-Dist: databricks-sdk>=0.20.0; extra == 'databricks'
31
+ Provides-Extra: plotly
32
+ Requires-Dist: plotly>=5.0.0; extra == 'plotly'
33
+ Description-Content-Type: text/markdown
34
+
1
35
  # SQL Glider
2
36
 
3
37
  ![SQL Glider Logo](./docs/docs/static/sqlglider-logo-transparent.png)
@@ -388,12 +422,33 @@ uv run sqlglider graph query graph.json --upstream orders.customer_id -f mermaid
388
422
  # Query with DOT (Graphviz) diagram output
389
423
  uv run sqlglider graph query graph.json --downstream customers.id -f dot
390
424
 
425
+ # Query with Plotly JSON output (for interactive visualization)
426
+ uv run sqlglider graph query graph.json --upstream orders.customer_id -f plotly
427
+
391
428
  # Visualize entire graph as a diagram
392
429
  uv run sqlglider graph visualize graph.json # Mermaid (default)
393
430
  uv run sqlglider graph visualize graph.json -f dot # DOT/Graphviz
431
+ uv run sqlglider graph visualize graph.json -f plotly # Plotly JSON
394
432
  uv run sqlglider graph visualize graph.json -o lineage.mmd # Save to file
395
433
  ```
396
434
 
435
+ > **Note:** Plotly output requires an optional dependency. Install with: `pip install sql-glider[plotly]`
436
+
437
+ The Plotly JSON output can be loaded into Plotly/Dash applications for interactive visualization:
438
+
439
+ ```python
440
+ import plotly.io as pio
441
+ from dash import Dash, dcc, html
442
+
443
+ # Load the JSON output
444
+ with open("lineage.json") as f:
445
+ fig = pio.from_json(f.read())
446
+
447
+ # Use in a Dash app
448
+ app = Dash(__name__)
449
+ app.layout = html.Div([dcc.Graph(figure=fig)])
450
+ ```
451
+
397
452
  **Example Upstream Query Output:**
398
453
  ```
399
454
  Sources for 'order_totals.total'
@@ -679,12 +734,24 @@ Arguments:
679
734
  Options:
680
735
  --upstream, -u Find source columns for this column [optional]
681
736
  --downstream, -d Find affected columns for this source [optional]
682
- --output-format, -f Output format: 'text', 'json', or 'csv' [default: text]
737
+ --output-format, -f Output format: 'text', 'json', 'csv', 'mermaid', 'mermaid-markdown', 'dot', or 'plotly' [default: text]
738
+ ```
739
+
740
+ ```
741
+ sqlglider graph visualize <graph_file> [OPTIONS]
742
+
743
+ Arguments:
744
+ graph_file Path to graph JSON file [required]
745
+
746
+ Options:
747
+ --output-format, -f Diagram format: 'mermaid', 'mermaid-markdown', 'dot', or 'plotly' [default: mermaid]
748
+ --output-file, -o Write diagram to file instead of stdout [optional]
683
749
  ```
684
750
 
685
751
  **Notes:**
686
752
  - `--upstream` and `--downstream` are mutually exclusive. Use one or the other.
687
753
  - Graph queries are case-insensitive for column matching.
754
+ - Plotly output requires optional dependency: `pip install sql-glider[plotly]`
688
755
 
689
756
  ## Output Formats
690
757
 
@@ -889,6 +956,21 @@ UV_PUBLISH_TOKEN=pypi-...
889
956
  - **pydantic:** Data validation and serialization
890
957
  - **rustworkx:** High-performance graph library for cross-file lineage analysis
891
958
 
959
+ ### Optional Dependencies
960
+
961
+ Install optional features with extras:
962
+
963
+ ```bash
964
+ # Databricks catalog integration
965
+ pip install sql-glider[databricks]
966
+
967
+ # Plotly interactive visualization
968
+ pip install sql-glider[plotly]
969
+
970
+ # Install multiple extras
971
+ pip install sql-glider[databricks,plotly]
972
+ ```
973
+
892
974
  ## References
893
975
 
894
976
  - [SQLGlot Documentation](https://sqlglot.com/)
@@ -1,35 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: sql-glider
3
- Version: 0.1.23
4
- Summary: SQL Utility Toolkit for better understanding, use, and governance of your queries in a native environment.
5
- Project-URL: Homepage, https://github.com/rycowhi/sql-glider/
6
- Project-URL: Repository, https://github.com/rycowhi/sql-glider/
7
- Project-URL: Documentation, https://github.com/rycowhi/sql-glider/
8
- Project-URL: Issues, https://github.com/rycowhi/sql-glider/issues
9
- Author-email: Ryan Whitcomb <ryankwhitcomb@gmail.com>
10
- License-Expression: Apache-2.0
11
- License-File: LICENSE
12
- Keywords: data-governance,data-lineage,lineage,sql,sqlglot
13
- Classifier: Development Status :: 3 - Alpha
14
- Classifier: Intended Audience :: Developers
15
- Classifier: License :: OSI Approved :: Apache Software License
16
- Classifier: Operating System :: OS Independent
17
- Classifier: Programming Language :: Python :: 3 :: Only
18
- Classifier: Programming Language :: SQL
19
- Classifier: Topic :: Database
20
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
- Classifier: Typing :: Typed
22
- Requires-Python: >=3.11
23
- Requires-Dist: jinja2>=3.0.0
24
- Requires-Dist: pydantic>=2.0.0
25
- Requires-Dist: rich>=13.0.0
26
- Requires-Dist: rustworkx>=0.15.0
27
- Requires-Dist: sqlglot[rs]>=25.0.0
28
- Requires-Dist: typer>=0.9.0
29
- Provides-Extra: databricks
30
- Requires-Dist: databricks-sdk>=0.20.0; extra == 'databricks'
31
- Description-Content-Type: text/markdown
32
-
33
1
  # SQL Glider
34
2
 
35
3
  ![SQL Glider Logo](./docs/docs/static/sqlglider-logo-transparent.png)
@@ -420,12 +388,33 @@ uv run sqlglider graph query graph.json --upstream orders.customer_id -f mermaid
420
388
  # Query with DOT (Graphviz) diagram output
421
389
  uv run sqlglider graph query graph.json --downstream customers.id -f dot
422
390
 
391
+ # Query with Plotly JSON output (for interactive visualization)
392
+ uv run sqlglider graph query graph.json --upstream orders.customer_id -f plotly
393
+
423
394
  # Visualize entire graph as a diagram
424
395
  uv run sqlglider graph visualize graph.json # Mermaid (default)
425
396
  uv run sqlglider graph visualize graph.json -f dot # DOT/Graphviz
397
+ uv run sqlglider graph visualize graph.json -f plotly # Plotly JSON
426
398
  uv run sqlglider graph visualize graph.json -o lineage.mmd # Save to file
427
399
  ```
428
400
 
401
+ > **Note:** Plotly output requires an optional dependency. Install with: `pip install sql-glider[plotly]`
402
+
403
+ The Plotly JSON output can be loaded into Plotly/Dash applications for interactive visualization:
404
+
405
+ ```python
406
+ import plotly.io as pio
407
+ from dash import Dash, dcc, html
408
+
409
+ # Load the JSON output
410
+ with open("lineage.json") as f:
411
+ fig = pio.from_json(f.read())
412
+
413
+ # Use in a Dash app
414
+ app = Dash(__name__)
415
+ app.layout = html.Div([dcc.Graph(figure=fig)])
416
+ ```
417
+
429
418
  **Example Upstream Query Output:**
430
419
  ```
431
420
  Sources for 'order_totals.total'
@@ -711,12 +700,24 @@ Arguments:
711
700
  Options:
712
701
  --upstream, -u Find source columns for this column [optional]
713
702
  --downstream, -d Find affected columns for this source [optional]
714
- --output-format, -f Output format: 'text', 'json', or 'csv' [default: text]
703
+ --output-format, -f Output format: 'text', 'json', 'csv', 'mermaid', 'mermaid-markdown', 'dot', or 'plotly' [default: text]
704
+ ```
705
+
706
+ ```
707
+ sqlglider graph visualize <graph_file> [OPTIONS]
708
+
709
+ Arguments:
710
+ graph_file Path to graph JSON file [required]
711
+
712
+ Options:
713
+ --output-format, -f Diagram format: 'mermaid', 'mermaid-markdown', 'dot', or 'plotly' [default: mermaid]
714
+ --output-file, -o Write diagram to file instead of stdout [optional]
715
715
  ```
716
716
 
717
717
  **Notes:**
718
718
  - `--upstream` and `--downstream` are mutually exclusive. Use one or the other.
719
719
  - Graph queries are case-insensitive for column matching.
720
+ - Plotly output requires optional dependency: `pip install sql-glider[plotly]`
720
721
 
721
722
  ## Output Formats
722
723
 
@@ -921,6 +922,21 @@ UV_PUBLISH_TOKEN=pypi-...
921
922
  - **pydantic:** Data validation and serialization
922
923
  - **rustworkx:** High-performance graph library for cross-file lineage analysis
923
924
 
925
+ ### Optional Dependencies
926
+
927
+ Install optional features with extras:
928
+
929
+ ```bash
930
+ # Databricks catalog integration
931
+ pip install sql-glider[databricks]
932
+
933
+ # Plotly interactive visualization
934
+ pip install sql-glider[plotly]
935
+
936
+ # Install multiple extras
937
+ pip install sql-glider[databricks,plotly]
938
+ ```
939
+
924
940
  ## References
925
941
 
926
942
  - [SQLGlot Documentation](https://sqlglot.com/)
@@ -221,7 +221,7 @@ Each entry in `columns` tells you:
221
221
 
222
222
  ## Visualizing Lineage
223
223
 
224
- SQL Glider can generate diagrams from lineage graphs in [Mermaid](https://mermaid.js.org/) and [DOT (Graphviz)](https://graphviz.org/doc/info/lang.html) formats. These are text-based diagram languages that render in many tools — Mermaid works natively in GitHub Markdown, GitLab, Notion, and more; DOT can be rendered with Graphviz into SVG, PNG, or PDF.
224
+ SQL Glider can generate diagrams from lineage graphs in [Mermaid](https://mermaid.js.org/), [DOT (Graphviz)](https://graphviz.org/doc/info/lang.html), and [Plotly](https://plotly.com/python/) formats. Mermaid and DOT are text-based diagram languages that render in many tools — Mermaid works natively in GitHub Markdown, GitLab, Notion, and more; DOT can be rendered with Graphviz into SVG, PNG, or PDF. Plotly outputs JSON that can be loaded into interactive Plotly/Dash applications.
225
225
 
226
226
  ### Visualize an Entire Graph
227
227
 
@@ -234,9 +234,13 @@ sqlglider graph visualize graph.json
234
234
  # DOT (Graphviz)
235
235
  sqlglider graph visualize graph.json -f dot
236
236
 
237
+ # Plotly JSON (for interactive visualization)
238
+ sqlglider graph visualize graph.json -f plotly
239
+
237
240
  # Save to file
238
241
  sqlglider graph visualize graph.json -o lineage.mmd
239
242
  sqlglider graph visualize graph.json -f dot -o lineage.dot
243
+ sqlglider graph visualize graph.json -f plotly -o lineage.json
240
244
  ```
241
245
 
242
246
  ### Diagram Output from Queries
@@ -249,6 +253,9 @@ sqlglider graph query graph.json --upstream total_spent -f mermaid
249
253
 
250
254
  # DOT diagram of downstream impact
251
255
  sqlglider graph query graph.json --downstream orders.order_total -f dot
256
+
257
+ # Plotly JSON for interactive exploration
258
+ sqlglider graph query graph.json --upstream total_spent -f plotly
252
259
  ```
253
260
 
254
261
  Query diagrams include color-coded nodes and a legend:
@@ -308,6 +315,47 @@ sqlglider graph visualize graph.json -f mermaid-markdown -o lineage.md
308
315
 
309
316
  This produces output with the `` ```mermaid `` fence included, so the diagram renders automatically when viewed in GitHub, GitLab, or any markdown tool with Mermaid support.
310
317
 
318
+ ### Plotly Interactive Visualization
319
+
320
+ The `plotly` format outputs a JSON figure specification that can be loaded into [Plotly](https://plotly.com/python/) or [Dash](https://dash.plotly.com/) applications for interactive visualization with zooming, panning, and hover details.
321
+
322
+ !!! warning "Optional Dependency"
323
+
324
+ Plotly output requires an optional dependency. Install it with:
325
+
326
+ ```bash
327
+ pip install sql-glider[plotly]
328
+ ```
329
+
330
+ **Example usage with Dash:**
331
+
332
+ ```python
333
+ import plotly.io as pio
334
+ from dash import Dash, dcc, html
335
+
336
+ # Load the JSON output from sqlglider
337
+ with open("lineage.json") as f:
338
+ fig = pio.from_json(f.read())
339
+
340
+ # Create an interactive Dash app
341
+ app = Dash(__name__)
342
+ app.layout = html.Div([
343
+ html.H1("Lineage Graph"),
344
+ dcc.Graph(figure=fig, style={"height": "80vh"})
345
+ ])
346
+
347
+ if __name__ == "__main__":
348
+ app.run(debug=True)
349
+ ```
350
+
351
+ The Plotly output uses the same color scheme as Mermaid and DOT diagrams:
352
+
353
+ | Color | Meaning |
354
+ |--------|---------|
355
+ | Amber | The queried column |
356
+ | Teal | Root node (no upstream dependencies) |
357
+ | Violet | Leaf node (no downstream consumers) |
358
+
311
359
  ### Rendering Diagrams
312
360
 
313
361
  **Mermaid:**
@@ -322,6 +370,12 @@ This produces output with the `` ```mermaid `` fence included, so the diagram re
322
370
  - Use `-Tsvg` for scalable vector output
323
371
  - Many IDEs have Graphviz preview extensions
324
372
 
373
+ **Plotly:**
374
+
375
+ - Load the JSON into any Plotly-compatible environment (Python, Dash, Jupyter notebooks)
376
+ - SQL Glider includes a viewer script: `python examples/plotly_viewer.py lineage.json`
377
+ - Export to static images with `fig.write_image("lineage.png")`
378
+
325
379
  ## Building Graphs from Multiple Sources
326
380
 
327
381
  ### Explicit File List
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env python3
2
+ """Simple Dash app to view SQL Glider Plotly lineage output.
3
+
4
+ Usage:
5
+ # graph visualize has -o flag for file output
6
+ uv run sqlglider graph visualize graph.json -f plotly -o lineage.json
7
+ uv run python examples/plotly_viewer.py lineage.json
8
+
9
+ # graph query needs shell redirect - use PowerShell on Windows for proper encoding
10
+ uv run sqlglider graph query graph.json --upstream orders.total -f plotly | Out-File -Encoding utf8 lineage.json
11
+ uv run python examples/plotly_viewer.py lineage.json
12
+
13
+ # On Unix/macOS:
14
+ sqlglider graph query graph.json --upstream orders.total -f plotly > lineage.json
15
+ python examples/plotly_viewer.py lineage.json
16
+ """
17
+
18
+ import json
19
+ import sys
20
+ from pathlib import Path
21
+
22
+ try:
23
+ import dash
24
+ from dash import dcc, html
25
+ except ImportError:
26
+ print("Error: Dash is required. Install with: pip install dash", file=sys.stderr)
27
+ sys.exit(1)
28
+
29
+
30
+ def load_figure(source: str | Path | None = None) -> dict:
31
+ """Load Plotly figure from file or stdin."""
32
+ if source is None or source == "-":
33
+ # Read from stdin
34
+ if sys.stdin.isatty():
35
+ print("Usage: python plotly_viewer.py <lineage.json>", file=sys.stderr)
36
+ print(" or: sqlglider graph visualize graph.json -f plotly | python plotly_viewer.py", file=sys.stderr)
37
+ sys.exit(1)
38
+ content = sys.stdin.read()
39
+ if not content.strip():
40
+ print("Error: No input received from stdin", file=sys.stderr)
41
+ print("Note: On Windows, piping may not work reliably.", file=sys.stderr)
42
+ print("Try: sqlglider graph query ... -f plotly -o output.json", file=sys.stderr)
43
+ print("Then: python plotly_viewer.py output.json", file=sys.stderr)
44
+ sys.exit(1)
45
+ else:
46
+ path = Path(source)
47
+ if not path.exists():
48
+ print(f"Error: File not found: {path}", file=sys.stderr)
49
+ sys.exit(1)
50
+ # Try multiple encodings - Windows redirect can create UTF-16 files
51
+ for encoding in ["utf-8", "utf-16", "utf-8-sig"]:
52
+ try:
53
+ content = path.read_text(encoding=encoding)
54
+ break
55
+ except UnicodeDecodeError:
56
+ continue
57
+ else:
58
+ print(f"Error: Could not decode file with UTF-8 or UTF-16 encoding", file=sys.stderr)
59
+ sys.exit(1)
60
+
61
+ try:
62
+ return json.loads(content)
63
+ except json.JSONDecodeError as e:
64
+ print(f"Error: Invalid JSON: {e}", file=sys.stderr)
65
+ if len(content) < 200:
66
+ print(f"Content received: {content!r}", file=sys.stderr)
67
+ else:
68
+ print(f"Content starts with: {content[:200]!r}...", file=sys.stderr)
69
+ sys.exit(1)
70
+
71
+
72
+ def create_app(figure: dict) -> dash.Dash:
73
+ """Create Dash app with the lineage graph."""
74
+ app = dash.Dash(__name__)
75
+
76
+ title = figure.get("layout", {}).get("title", {}).get("text", "Lineage Graph")
77
+
78
+ app.layout = html.Div(
79
+ [
80
+ html.H1(title, style={"textAlign": "center", "fontFamily": "sans-serif"}),
81
+ dcc.Graph(
82
+ id="lineage-graph",
83
+ figure=figure,
84
+ style={"height": "85vh"},
85
+ config={
86
+ "displayModeBar": True,
87
+ "scrollZoom": True,
88
+ "modeBarButtonsToAdd": ["select2d", "lasso2d"],
89
+ },
90
+ ),
91
+ ],
92
+ style={"padding": "20px"},
93
+ )
94
+
95
+ return app
96
+
97
+
98
+ def main():
99
+ source = sys.argv[1] if len(sys.argv) > 1 else None
100
+ figure = load_figure(source)
101
+ app = create_app(figure)
102
+
103
+ print("Starting Dash server at http://127.0.0.1:8050")
104
+ print("Press Ctrl+C to stop")
105
+ app.run(debug=True, host="127.0.0.1", port=8050)
106
+
107
+
108
+ if __name__ == "__main__":
109
+ main()
@@ -35,6 +35,7 @@ dependencies = [
35
35
 
36
36
  [project.optional-dependencies]
37
37
  databricks = ["databricks-sdk>=0.20.0"]
38
+ plotly = ["plotly>=5.0.0"]
38
39
 
39
40
  [project.urls]
40
41
  Homepage = "https://github.com/rycowhi/sql-glider/"
@@ -67,7 +68,9 @@ package = true
67
68
  [dependency-groups]
68
69
  dev = [
69
70
  "basedpyright>=1.36.0",
71
+ "dash>=3.4.0",
70
72
  "databricks-sdk>=0.20.0", # For testing catalog integration
73
+ "plotly>=6.5.2",
71
74
  "pytest>=9.0.1",
72
75
  "pytest-cov>=7.0.0",
73
76
  "pytest-mock>=3.15.1",
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.1.23'
32
- __version_tuple__ = version_tuple = (0, 1, 23)
31
+ __version__ = version = '0.1.24'
32
+ __version_tuple__ = version_tuple = (0, 1, 24)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -1642,7 +1642,7 @@ def graph_query(
1642
1642
  "text",
1643
1643
  "--output-format",
1644
1644
  "-f",
1645
- help="Output format: 'text', 'json', 'csv', 'mermaid', 'mermaid-markdown', or 'dot'",
1645
+ help="Output format: 'text', 'json', 'csv', 'mermaid', 'mermaid-markdown', 'dot', or 'plotly'",
1646
1646
  ),
1647
1647
  ) -> None:
1648
1648
  """
@@ -1661,6 +1661,9 @@ def graph_query(
1661
1661
 
1662
1662
  # CSV output
1663
1663
  sqlglider graph query graph.json --downstream orders.order_id -f csv
1664
+
1665
+ # Plotly JSON output (for Dash/Plotly visualization)
1666
+ sqlglider graph query graph.json --upstream orders.total -f plotly
1664
1667
  """
1665
1668
  from sqlglider.graph.query import GraphQuerier
1666
1669
 
@@ -1685,10 +1688,11 @@ def graph_query(
1685
1688
  "mermaid",
1686
1689
  "mermaid-markdown",
1687
1690
  "dot",
1691
+ "plotly",
1688
1692
  ]:
1689
1693
  err_console.print(
1690
1694
  f"[red]Error:[/red] Invalid output format '{output_format}'. "
1691
- "Use 'text', 'json', 'csv', 'mermaid', 'mermaid-markdown', or 'dot'."
1695
+ "Use 'text', 'json', 'csv', 'mermaid', 'mermaid-markdown', 'dot', or 'plotly'."
1692
1696
  )
1693
1697
  raise typer.Exit(1)
1694
1698
 
@@ -1720,6 +1724,14 @@ def graph_query(
1720
1724
  from sqlglider.graph.diagram_formatters import DotFormatter
1721
1725
 
1722
1726
  print(DotFormatter.format_query_result(result))
1727
+ elif output_format == "plotly":
1728
+ from sqlglider.graph.diagram_formatters import PlotlyFormatter
1729
+
1730
+ print(PlotlyFormatter.format_query_result(result))
1731
+
1732
+ except ImportError as e:
1733
+ err_console.print(f"[red]Error:[/red] {e}")
1734
+ raise typer.Exit(1)
1723
1735
 
1724
1736
  except FileNotFoundError as e:
1725
1737
  err_console.print(f"[red]Error:[/red] {e}")
@@ -1825,7 +1837,7 @@ def graph_visualize(
1825
1837
  "mermaid",
1826
1838
  "--output-format",
1827
1839
  "-f",
1828
- help="Diagram format: 'mermaid', 'mermaid-markdown', or 'dot'",
1840
+ help="Diagram format: 'mermaid', 'mermaid-markdown', 'dot', or 'plotly'",
1829
1841
  ),
1830
1842
  output_file: Optional[Path] = typer.Option(
1831
1843
  None,
@@ -1837,8 +1849,8 @@ def graph_visualize(
1837
1849
  """
1838
1850
  Visualize the entire lineage graph as a diagram.
1839
1851
 
1840
- Generates Mermaid or DOT (Graphviz) diagrams showing all nodes and edges
1841
- in the graph for visualization tools.
1852
+ Generates Mermaid, DOT (Graphviz), or Plotly JSON diagrams showing all
1853
+ nodes and edges in the graph for visualization tools.
1842
1854
 
1843
1855
  Examples:
1844
1856
 
@@ -1853,18 +1865,16 @@ def graph_visualize(
1853
1865
 
1854
1866
  # Render DOT to PNG with Graphviz
1855
1867
  sqlglider graph visualize graph.json -f dot -o lineage.dot
1868
+
1869
+ # Generate Plotly JSON for Dash/Plotly apps
1870
+ sqlglider graph visualize graph.json -f plotly -o lineage.json
1856
1871
  """
1857
- from sqlglider.graph.diagram_formatters import (
1858
- DotFormatter,
1859
- MermaidFormatter,
1860
- MermaidMarkdownFormatter,
1861
- )
1862
1872
  from sqlglider.graph.serialization import load_graph
1863
1873
 
1864
- if output_format not in ["mermaid", "mermaid-markdown", "dot"]:
1874
+ if output_format not in ["mermaid", "mermaid-markdown", "dot", "plotly"]:
1865
1875
  err_console.print(
1866
1876
  f"[red]Error:[/red] Invalid output format '{output_format}'. "
1867
- "Use 'mermaid', 'mermaid-markdown', or 'dot'."
1877
+ "Use 'mermaid', 'mermaid-markdown', 'dot', or 'plotly'."
1868
1878
  )
1869
1879
  raise typer.Exit(1)
1870
1880
 
@@ -1872,11 +1882,21 @@ def graph_visualize(
1872
1882
  graph = load_graph(graph_file)
1873
1883
 
1874
1884
  if output_format == "mermaid":
1885
+ from sqlglider.graph.diagram_formatters import MermaidFormatter
1886
+
1875
1887
  diagram = MermaidFormatter.format_full_graph(graph)
1876
1888
  elif output_format == "mermaid-markdown":
1889
+ from sqlglider.graph.diagram_formatters import MermaidMarkdownFormatter
1890
+
1877
1891
  diagram = MermaidMarkdownFormatter.format_full_graph(graph)
1878
- else:
1892
+ elif output_format == "dot":
1893
+ from sqlglider.graph.diagram_formatters import DotFormatter
1894
+
1879
1895
  diagram = DotFormatter.format_full_graph(graph)
1896
+ else: # plotly
1897
+ from sqlglider.graph.diagram_formatters import PlotlyFormatter
1898
+
1899
+ diagram = PlotlyFormatter.format_full_graph(graph)
1880
1900
 
1881
1901
  if output_file:
1882
1902
  output_file.write_text(diagram, encoding="utf-8")
@@ -1884,6 +1904,10 @@ def graph_visualize(
1884
1904
  else:
1885
1905
  print(diagram)
1886
1906
 
1907
+ except ImportError as e:
1908
+ err_console.print(f"[red]Error:[/red] {e}")
1909
+ raise typer.Exit(1)
1910
+
1887
1911
  except FileNotFoundError as e:
1888
1912
  err_console.print(f"[red]Error:[/red] {e}")
1889
1913
  raise typer.Exit(1)