sql-glider 0.1.26__tar.gz → 0.1.27__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.
- {sql_glider-0.1.26 → sql_glider-0.1.27}/PKG-INFO +2 -1
- {sql_glider-0.1.26 → sql_glider-0.1.27}/docs/docs/graph-lineage.md +14 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/pyproject.toml +1 -1
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/_version.py +2 -2
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/graph/diagram_formatters.py +88 -1
- {sql_glider-0.1.26 → sql_glider-0.1.27}/uv.lock +8 -0
- sql_glider-0.1.26/graph.json +0 -5319
- sql_glider-0.1.26/lineage.json +0 -0
- sql_glider-0.1.26/mm.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/.github/workflows/ci.yml +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/.github/workflows/docs.yml +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/.github/workflows/publish.yml +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/.gitignore +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/.python-version +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/ARCHITECTURE.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/CLAUDE.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/LICENSE +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/README.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/docs/.github/workflows/docs.yml +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/docs/docs/catalogs.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/docs/docs/index.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/docs/docs/static/plotly-dash-example.png +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/docs/docs/static/sqlglider-logo-transparent.png +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/docs/docs/templating.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/docs/zensical.toml +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/examples/plotly_viewer.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2025-12-05-column-level-lineage.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2025-12-05-reverse-lineage.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2025-12-06-config-file-support.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2025-12-06-graph-lineage.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2025-12-06-unify-single-multi-query.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2025-12-07-sample-data-model.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2025-12-07-sql-templating.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2025-12-08-tables-command.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2025-12-09-graph-query-paths.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2025-12-13-dissect-command.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2025-12-14-tables-pull-command.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2026-01-25-fix-union-lineage-chain.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2026-01-26-file-scoped-schema-context.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2026-01-28-sparksql-table-extraction.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2026-01-29-no-star-flag.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2026-01-29-resolve-schema.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2026-01-29-schema-pruning-optimization.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2026-01-29-tables-scrape-command.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/plans/2026-02-02-diagram-output-formats.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/README.md +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/business/expire_dim_customer.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/business/load_fact_orders.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/business/load_fact_payments.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/business/merge_dim_customer.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/business/merge_dim_product.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/business/update_dim_customer_metrics.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/complex/conditional_merge.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/complex/cte_insert.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/complex/multi_table_transform.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/dim_customer.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/dim_product.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/fact_orders.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/fact_payments.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/raw_addresses.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/raw_customers.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/raw_order_items.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/raw_orders.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/raw_payments.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/raw_products.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/stg_customers.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/stg_orders.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/stg_payments.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/ddl/stg_products.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/incremental/incr_fact_orders.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/incremental/incr_fact_payments.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/incremental/incr_pres_sales_summary.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/maintenance/delete_expired_customers.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/maintenance/update_product_status.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/presentation/load_pres_customer_360.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/presentation/load_pres_customer_cohort.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/presentation/load_pres_product_performance.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/presentation/load_pres_sales_summary.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/staging/load_stg_customers.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/staging/load_stg_orders.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/staging/load_stg_payments.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sample_data_model/staging/load_stg_products.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/sqlglider.toml.example +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/catalog/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/catalog/base.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/catalog/databricks.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/catalog/registry.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/cli.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/dissection/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/dissection/analyzer.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/dissection/formatters.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/dissection/models.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/global_models.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/graph/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/graph/builder.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/graph/formatters.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/graph/merge.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/graph/models.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/graph/query.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/graph/serialization.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/lineage/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/lineage/analyzer.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/lineage/formatters.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/schema/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/schema/extractor.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/templating/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/templating/base.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/templating/jinja.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/templating/registry.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/templating/variables.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/utils/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/utils/config.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/utils/file_utils.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/src/sqlglider/utils/schema.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/multi_file_queries/analytics_pipeline.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/multi_file_queries/analytics_pipeline_union_merge.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/multi_file_queries/customers.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/multi_file_queries/orders.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/multi_file_queries/reports.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/multi_file_queries/view_based_merge.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/original_queries/test_cte.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/original_queries/test_cte_query.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/original_queries/test_cte_view_star.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/original_queries/test_generated_column_query.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/original_queries/test_multi.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/original_queries/test_multi_query.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/original_queries/test_single_query.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/original_queries/test_subquery.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/original_queries/test_tables.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/original_queries/test_view.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/original_queries/test_view_window_cte.sql +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/fixtures/sample_manifest.csv +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/catalog/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/catalog/test_base.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/catalog/test_databricks.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/catalog/test_registry.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/dissection/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/dissection/test_analyzer.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/dissection/test_formatters.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/dissection/test_models.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/graph/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/graph/test_builder.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/graph/test_diagram_formatters.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/graph/test_formatters.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/graph/test_merge.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/graph/test_models.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/graph/test_query.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/graph/test_serialization.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/lineage/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/lineage/test_analyzer.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/lineage/test_formatters.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/schema/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/schema/test_extractor.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/templating/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/templating/test_base.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/templating/test_jinja.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/templating/test_registry.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/templating/test_variables.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/test_cli.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/utils/__init__.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/utils/test_config.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/utils/test_file_utils.py +0 -0
- {sql_glider-0.1.26 → sql_glider-0.1.27}/tests/sqlglider/utils/test_schema.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sql-glider
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.27
|
|
4
4
|
Summary: SQL Utility Toolkit for better understanding, use, and governance of your queries in a native environment.
|
|
5
5
|
Project-URL: Homepage, https://github.com/rycowhi/sql-glider/
|
|
6
6
|
Project-URL: Repository, https://github.com/rycowhi/sql-glider/
|
|
@@ -30,6 +30,7 @@ Provides-Extra: databricks
|
|
|
30
30
|
Requires-Dist: databricks-sdk>=0.20.0; extra == 'databricks'
|
|
31
31
|
Provides-Extra: plotly
|
|
32
32
|
Requires-Dist: plotly>=5.0.0; extra == 'plotly'
|
|
33
|
+
Requires-Dist: pygraphviz>=1.11; extra == 'plotly'
|
|
33
34
|
Description-Content-Type: text/markdown
|
|
34
35
|
|
|
35
36
|
# SQL Glider
|
|
@@ -329,6 +329,20 @@ The `plotly` format outputs a JSON figure specification that can be loaded into
|
|
|
329
329
|
pip install sql-glider[plotly]
|
|
330
330
|
```
|
|
331
331
|
|
|
332
|
+
!!! tip "Better Layout with Graphviz"
|
|
333
|
+
|
|
334
|
+
For complex graphs with many nodes and edges, installing [Graphviz](https://graphviz.org/download/) on your system enables an optimized layout algorithm that minimizes edge crossings. Without Graphviz, a simpler layout is used that may result in overlapping edges.
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
# Install Graphviz first (system package)
|
|
338
|
+
# Windows: choco install graphviz
|
|
339
|
+
# macOS: brew install graphviz
|
|
340
|
+
# Linux: apt install graphviz graphviz-dev
|
|
341
|
+
|
|
342
|
+
# Then install sql-glider with plotly extra
|
|
343
|
+
pip install sql-glider[plotly]
|
|
344
|
+
```
|
|
345
|
+
|
|
332
346
|
**Example usage with Dash:**
|
|
333
347
|
|
|
334
348
|
```python
|
|
@@ -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.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
31
|
+
__version__ = version = '0.1.27'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 1, 27)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -364,7 +364,11 @@ def _compute_layered_layout(
|
|
|
364
364
|
x_spacing: float = 250.0,
|
|
365
365
|
y_spacing: float = 100.0,
|
|
366
366
|
) -> dict[str, tuple[float, float]]:
|
|
367
|
-
"""Compute layered layout positions for nodes using
|
|
367
|
+
"""Compute layered layout positions for nodes using Graphviz's dot algorithm.
|
|
368
|
+
|
|
369
|
+
Uses Graphviz's dot layout engine (Sugiyama algorithm) which minimizes edge
|
|
370
|
+
crossings by using the barycenter heuristic to optimize node ordering within
|
|
371
|
+
each layer.
|
|
368
372
|
|
|
369
373
|
Positions nodes in layers from left to right based on their dependencies.
|
|
370
374
|
Nodes with no incoming edges are placed in layer 0, their dependents in
|
|
@@ -382,6 +386,89 @@ def _compute_layered_layout(
|
|
|
382
386
|
if not nodes:
|
|
383
387
|
return {}
|
|
384
388
|
|
|
389
|
+
try:
|
|
390
|
+
import pygraphviz # noqa: F401 # type: ignore[import-not-found]
|
|
391
|
+
|
|
392
|
+
return _compute_graphviz_layout(nodes, edges, x_spacing, y_spacing)
|
|
393
|
+
except ImportError:
|
|
394
|
+
# Fallback to simple layered layout if pygraphviz not available
|
|
395
|
+
return _compute_simple_layered_layout(nodes, edges, x_spacing, y_spacing)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def _compute_graphviz_layout(
|
|
399
|
+
nodes: list[str],
|
|
400
|
+
edges: list[tuple[str, str]],
|
|
401
|
+
x_spacing: float = 250.0,
|
|
402
|
+
y_spacing: float = 100.0,
|
|
403
|
+
) -> dict[str, tuple[float, float]]:
|
|
404
|
+
"""Compute layout using Graphviz's dot algorithm with crossing minimization.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
nodes: List of node identifiers
|
|
408
|
+
edges: List of (source, target) edge tuples
|
|
409
|
+
x_spacing: Horizontal spacing between layers
|
|
410
|
+
y_spacing: Vertical spacing between nodes in the same layer
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
Dictionary mapping node identifiers to (x, y) positions
|
|
414
|
+
"""
|
|
415
|
+
import pygraphviz as pgv # type: ignore[import-not-found]
|
|
416
|
+
|
|
417
|
+
# Create directed graph
|
|
418
|
+
g = pgv.AGraph(directed=True, rankdir="LR")
|
|
419
|
+
|
|
420
|
+
# Set graph attributes for spacing
|
|
421
|
+
g.graph_attr["ranksep"] = str(x_spacing / 72.0) # Convert pixels to inches
|
|
422
|
+
g.graph_attr["nodesep"] = str(y_spacing / 72.0)
|
|
423
|
+
|
|
424
|
+
# Add nodes
|
|
425
|
+
for node in nodes:
|
|
426
|
+
g.add_node(node)
|
|
427
|
+
|
|
428
|
+
# Add edges (only for nodes that exist in our node list)
|
|
429
|
+
node_set = set(nodes)
|
|
430
|
+
for src, tgt in edges:
|
|
431
|
+
if src in node_set and tgt in node_set:
|
|
432
|
+
g.add_edge(src, tgt)
|
|
433
|
+
|
|
434
|
+
# Compute layout using dot algorithm
|
|
435
|
+
g.layout(prog="dot")
|
|
436
|
+
|
|
437
|
+
# Extract positions
|
|
438
|
+
positions: dict[str, tuple[float, float]] = {}
|
|
439
|
+
for node in nodes:
|
|
440
|
+
n = g.get_node(node)
|
|
441
|
+
# Position is returned as "x,y" string in points (1/72 inch)
|
|
442
|
+
pos_str = n.attr.get("pos", "0,0")
|
|
443
|
+
if pos_str:
|
|
444
|
+
x_str, y_str = pos_str.split(",")
|
|
445
|
+
# Convert from points to our coordinate system
|
|
446
|
+
x = float(x_str)
|
|
447
|
+
y = float(y_str)
|
|
448
|
+
positions[node] = (x, y)
|
|
449
|
+
|
|
450
|
+
return positions
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def _compute_simple_layered_layout(
|
|
454
|
+
nodes: list[str],
|
|
455
|
+
edges: list[tuple[str, str]],
|
|
456
|
+
x_spacing: float = 250.0,
|
|
457
|
+
y_spacing: float = 100.0,
|
|
458
|
+
) -> dict[str, tuple[float, float]]:
|
|
459
|
+
"""Fallback simple layered layout using topological ordering.
|
|
460
|
+
|
|
461
|
+
Used when pygraphviz is not available. Does not minimize edge crossings.
|
|
462
|
+
|
|
463
|
+
Args:
|
|
464
|
+
nodes: List of node identifiers
|
|
465
|
+
edges: List of (source, target) edge tuples
|
|
466
|
+
x_spacing: Horizontal spacing between layers
|
|
467
|
+
y_spacing: Vertical spacing between nodes in the same layer
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
Dictionary mapping node identifiers to (x, y) positions
|
|
471
|
+
"""
|
|
385
472
|
# Build adjacency structures
|
|
386
473
|
incoming: dict[str, set[str]] = defaultdict(set)
|
|
387
474
|
outgoing: dict[str, set[str]] = defaultdict(set)
|
|
@@ -814,6 +814,12 @@ wheels = [
|
|
|
814
814
|
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
|
815
815
|
]
|
|
816
816
|
|
|
817
|
+
[[package]]
|
|
818
|
+
name = "pygraphviz"
|
|
819
|
+
version = "1.14"
|
|
820
|
+
source = { registry = "https://pypi.org/simple/" }
|
|
821
|
+
sdist = { url = "https://files.pythonhosted.org/packages/66/ca/823d5c74a73d6b8b08e1f5aea12468ef334f0732c65cbb18df2a7f285c87/pygraphviz-1.14.tar.gz", hash = "sha256:c10df02377f4e39b00ae17c862f4ee7e5767317f1c6b2dfd04cea6acc7fc2bea", size = 106003, upload-time = "2024-09-29T18:31:12.471Z" }
|
|
822
|
+
|
|
817
823
|
[[package]]
|
|
818
824
|
name = "pymdown-extensions"
|
|
819
825
|
version = "10.20.1"
|
|
@@ -1161,6 +1167,7 @@ databricks = [
|
|
|
1161
1167
|
]
|
|
1162
1168
|
plotly = [
|
|
1163
1169
|
{ name = "plotly" },
|
|
1170
|
+
{ name = "pygraphviz" },
|
|
1164
1171
|
]
|
|
1165
1172
|
|
|
1166
1173
|
[package.dev-dependencies]
|
|
@@ -1185,6 +1192,7 @@ requires-dist = [
|
|
|
1185
1192
|
{ name = "jinja2", specifier = ">=3.0.0" },
|
|
1186
1193
|
{ name = "plotly", marker = "extra == 'plotly'", specifier = ">=5.0.0" },
|
|
1187
1194
|
{ name = "pydantic", specifier = ">=2.0.0" },
|
|
1195
|
+
{ name = "pygraphviz", marker = "extra == 'plotly'", specifier = ">=1.11" },
|
|
1188
1196
|
{ name = "rich", specifier = ">=13.0.0" },
|
|
1189
1197
|
{ name = "rustworkx", specifier = ">=0.15.0" },
|
|
1190
1198
|
{ name = "sqlglot", extras = ["rs"], specifier = ">=25.0.0" },
|