pytrilogy 0.3.138__cp311-cp311-manylinux_2_17_x86_64.manylinux2014_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.
- LICENSE.md +19 -0
- _preql_import_resolver/__init__.py +5 -0
- _preql_import_resolver/_preql_import_resolver.cpython-311-x86_64-linux-gnu.so +0 -0
- pytrilogy-0.3.138.dist-info/METADATA +525 -0
- pytrilogy-0.3.138.dist-info/RECORD +182 -0
- pytrilogy-0.3.138.dist-info/WHEEL +5 -0
- pytrilogy-0.3.138.dist-info/entry_points.txt +2 -0
- pytrilogy-0.3.138.dist-info/licenses/LICENSE.md +19 -0
- trilogy/__init__.py +9 -0
- trilogy/ai/README.md +10 -0
- trilogy/ai/__init__.py +19 -0
- trilogy/ai/constants.py +92 -0
- trilogy/ai/conversation.py +107 -0
- trilogy/ai/enums.py +7 -0
- trilogy/ai/execute.py +50 -0
- trilogy/ai/models.py +34 -0
- trilogy/ai/prompts.py +87 -0
- trilogy/ai/providers/__init__.py +0 -0
- trilogy/ai/providers/anthropic.py +106 -0
- trilogy/ai/providers/base.py +24 -0
- trilogy/ai/providers/google.py +146 -0
- trilogy/ai/providers/openai.py +89 -0
- trilogy/ai/providers/utils.py +68 -0
- trilogy/authoring/README.md +3 -0
- trilogy/authoring/__init__.py +143 -0
- trilogy/constants.py +113 -0
- trilogy/core/README.md +52 -0
- trilogy/core/__init__.py +0 -0
- trilogy/core/constants.py +6 -0
- trilogy/core/enums.py +443 -0
- trilogy/core/env_processor.py +120 -0
- trilogy/core/environment_helpers.py +320 -0
- trilogy/core/ergonomics.py +193 -0
- trilogy/core/exceptions.py +123 -0
- trilogy/core/functions.py +1227 -0
- trilogy/core/graph_models.py +139 -0
- trilogy/core/internal.py +85 -0
- trilogy/core/models/__init__.py +0 -0
- trilogy/core/models/author.py +2672 -0
- trilogy/core/models/build.py +2521 -0
- trilogy/core/models/build_environment.py +180 -0
- trilogy/core/models/core.py +494 -0
- trilogy/core/models/datasource.py +322 -0
- trilogy/core/models/environment.py +748 -0
- trilogy/core/models/execute.py +1177 -0
- trilogy/core/optimization.py +251 -0
- trilogy/core/optimizations/__init__.py +12 -0
- trilogy/core/optimizations/base_optimization.py +17 -0
- trilogy/core/optimizations/hide_unused_concept.py +47 -0
- trilogy/core/optimizations/inline_datasource.py +102 -0
- trilogy/core/optimizations/predicate_pushdown.py +245 -0
- trilogy/core/processing/README.md +94 -0
- trilogy/core/processing/READMEv2.md +121 -0
- trilogy/core/processing/VIRTUAL_UNNEST.md +30 -0
- trilogy/core/processing/__init__.py +0 -0
- trilogy/core/processing/concept_strategies_v3.py +508 -0
- trilogy/core/processing/constants.py +15 -0
- trilogy/core/processing/discovery_node_factory.py +451 -0
- trilogy/core/processing/discovery_utility.py +517 -0
- trilogy/core/processing/discovery_validation.py +167 -0
- trilogy/core/processing/graph_utils.py +43 -0
- trilogy/core/processing/node_generators/README.md +9 -0
- trilogy/core/processing/node_generators/__init__.py +31 -0
- trilogy/core/processing/node_generators/basic_node.py +160 -0
- trilogy/core/processing/node_generators/common.py +268 -0
- trilogy/core/processing/node_generators/constant_node.py +38 -0
- trilogy/core/processing/node_generators/filter_node.py +315 -0
- trilogy/core/processing/node_generators/group_node.py +213 -0
- trilogy/core/processing/node_generators/group_to_node.py +117 -0
- trilogy/core/processing/node_generators/multiselect_node.py +205 -0
- trilogy/core/processing/node_generators/node_merge_node.py +653 -0
- trilogy/core/processing/node_generators/recursive_node.py +88 -0
- trilogy/core/processing/node_generators/rowset_node.py +165 -0
- trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +261 -0
- trilogy/core/processing/node_generators/select_merge_node.py +748 -0
- trilogy/core/processing/node_generators/select_node.py +95 -0
- trilogy/core/processing/node_generators/synonym_node.py +98 -0
- trilogy/core/processing/node_generators/union_node.py +91 -0
- trilogy/core/processing/node_generators/unnest_node.py +182 -0
- trilogy/core/processing/node_generators/window_node.py +201 -0
- trilogy/core/processing/nodes/README.md +28 -0
- trilogy/core/processing/nodes/__init__.py +179 -0
- trilogy/core/processing/nodes/base_node.py +519 -0
- trilogy/core/processing/nodes/filter_node.py +75 -0
- trilogy/core/processing/nodes/group_node.py +194 -0
- trilogy/core/processing/nodes/merge_node.py +420 -0
- trilogy/core/processing/nodes/recursive_node.py +46 -0
- trilogy/core/processing/nodes/select_node_v2.py +242 -0
- trilogy/core/processing/nodes/union_node.py +53 -0
- trilogy/core/processing/nodes/unnest_node.py +62 -0
- trilogy/core/processing/nodes/window_node.py +56 -0
- trilogy/core/processing/utility.py +823 -0
- trilogy/core/query_processor.py +596 -0
- trilogy/core/statements/README.md +35 -0
- trilogy/core/statements/__init__.py +0 -0
- trilogy/core/statements/author.py +536 -0
- trilogy/core/statements/build.py +0 -0
- trilogy/core/statements/common.py +20 -0
- trilogy/core/statements/execute.py +155 -0
- trilogy/core/table_processor.py +66 -0
- trilogy/core/utility.py +8 -0
- trilogy/core/validation/README.md +46 -0
- trilogy/core/validation/__init__.py +0 -0
- trilogy/core/validation/common.py +161 -0
- trilogy/core/validation/concept.py +146 -0
- trilogy/core/validation/datasource.py +227 -0
- trilogy/core/validation/environment.py +73 -0
- trilogy/core/validation/fix.py +106 -0
- trilogy/dialect/__init__.py +32 -0
- trilogy/dialect/base.py +1359 -0
- trilogy/dialect/bigquery.py +256 -0
- trilogy/dialect/common.py +147 -0
- trilogy/dialect/config.py +144 -0
- trilogy/dialect/dataframe.py +50 -0
- trilogy/dialect/duckdb.py +177 -0
- trilogy/dialect/enums.py +147 -0
- trilogy/dialect/metadata.py +173 -0
- trilogy/dialect/mock.py +190 -0
- trilogy/dialect/postgres.py +91 -0
- trilogy/dialect/presto.py +104 -0
- trilogy/dialect/results.py +89 -0
- trilogy/dialect/snowflake.py +90 -0
- trilogy/dialect/sql_server.py +92 -0
- trilogy/engine.py +48 -0
- trilogy/execution/config.py +75 -0
- trilogy/executor.py +568 -0
- trilogy/hooks/__init__.py +4 -0
- trilogy/hooks/base_hook.py +40 -0
- trilogy/hooks/graph_hook.py +139 -0
- trilogy/hooks/query_debugger.py +166 -0
- trilogy/metadata/__init__.py +0 -0
- trilogy/parser.py +10 -0
- trilogy/parsing/README.md +21 -0
- trilogy/parsing/__init__.py +0 -0
- trilogy/parsing/common.py +1069 -0
- trilogy/parsing/config.py +5 -0
- trilogy/parsing/exceptions.py +8 -0
- trilogy/parsing/helpers.py +1 -0
- trilogy/parsing/parse_engine.py +2813 -0
- trilogy/parsing/render.py +750 -0
- trilogy/parsing/trilogy.lark +540 -0
- trilogy/py.typed +0 -0
- trilogy/render.py +42 -0
- trilogy/scripts/README.md +7 -0
- trilogy/scripts/__init__.py +0 -0
- trilogy/scripts/dependency/Cargo.lock +617 -0
- trilogy/scripts/dependency/Cargo.toml +39 -0
- trilogy/scripts/dependency/README.md +131 -0
- trilogy/scripts/dependency/build.sh +25 -0
- trilogy/scripts/dependency/src/directory_resolver.rs +162 -0
- trilogy/scripts/dependency/src/lib.rs +16 -0
- trilogy/scripts/dependency/src/main.rs +770 -0
- trilogy/scripts/dependency/src/parser.rs +435 -0
- trilogy/scripts/dependency/src/preql.pest +208 -0
- trilogy/scripts/dependency/src/python_bindings.rs +289 -0
- trilogy/scripts/dependency/src/resolver.rs +716 -0
- trilogy/scripts/dependency/tests/base.preql +3 -0
- trilogy/scripts/dependency/tests/cli_integration.rs +377 -0
- trilogy/scripts/dependency/tests/customer.preql +6 -0
- trilogy/scripts/dependency/tests/main.preql +9 -0
- trilogy/scripts/dependency/tests/orders.preql +7 -0
- trilogy/scripts/dependency/tests/test_data/base.preql +9 -0
- trilogy/scripts/dependency/tests/test_data/consumer.preql +1 -0
- trilogy/scripts/dependency.py +323 -0
- trilogy/scripts/display.py +460 -0
- trilogy/scripts/environment.py +46 -0
- trilogy/scripts/parallel_execution.py +483 -0
- trilogy/scripts/single_execution.py +131 -0
- trilogy/scripts/trilogy.py +772 -0
- trilogy/std/__init__.py +0 -0
- trilogy/std/color.preql +3 -0
- trilogy/std/date.preql +13 -0
- trilogy/std/display.preql +18 -0
- trilogy/std/geography.preql +22 -0
- trilogy/std/metric.preql +15 -0
- trilogy/std/money.preql +67 -0
- trilogy/std/net.preql +14 -0
- trilogy/std/ranking.preql +7 -0
- trilogy/std/report.preql +5 -0
- trilogy/std/semantic.preql +6 -0
- trilogy/utility.py +34 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
use pyo3::prelude::*;
|
|
2
|
+
use pyo3::types::{PyDict, PyList};
|
|
3
|
+
use std::path::PathBuf;
|
|
4
|
+
|
|
5
|
+
use crate::parser::parse_file;
|
|
6
|
+
use crate::resolver::ImportResolver;
|
|
7
|
+
|
|
8
|
+
/// Python module for PreQL import resolution
|
|
9
|
+
#[pymodule]
|
|
10
|
+
fn _preql_import_resolver(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
11
|
+
m.add_class::<PyImportResolver>()?;
|
|
12
|
+
m.add_function(wrap_pyfunction!(parse_preql_file, m)?)?;
|
|
13
|
+
Ok(())
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/// Parse a PreQL file and return imports, datasources, and persists
|
|
17
|
+
#[pyfunction]
|
|
18
|
+
fn parse_preql_file(py: Python<'_>, content: &str) -> PyResult<PyObject> {
|
|
19
|
+
let parsed = parse_file(content)
|
|
20
|
+
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("Parse error: {}", e)))?;
|
|
21
|
+
|
|
22
|
+
let result = PyDict::new_bound(py);
|
|
23
|
+
|
|
24
|
+
// Add imports
|
|
25
|
+
let imports = PyList::empty_bound(py);
|
|
26
|
+
for import in parsed.imports {
|
|
27
|
+
let import_dict = PyDict::new_bound(py);
|
|
28
|
+
import_dict.set_item("raw_path", import.raw_path)?;
|
|
29
|
+
import_dict.set_item("alias", import.alias)?;
|
|
30
|
+
import_dict.set_item("is_stdlib", import.is_stdlib)?;
|
|
31
|
+
import_dict.set_item("parent_dirs", import.parent_dirs)?;
|
|
32
|
+
imports.append(import_dict)?;
|
|
33
|
+
}
|
|
34
|
+
result.set_item("imports", imports)?;
|
|
35
|
+
|
|
36
|
+
// Add datasources
|
|
37
|
+
let datasources = PyList::empty_bound(py);
|
|
38
|
+
for ds in parsed.datasources {
|
|
39
|
+
let ds_dict = PyDict::new_bound(py);
|
|
40
|
+
ds_dict.set_item("name", ds.name)?;
|
|
41
|
+
datasources.append(ds_dict)?;
|
|
42
|
+
}
|
|
43
|
+
result.set_item("datasources", datasources)?;
|
|
44
|
+
|
|
45
|
+
// Add persists
|
|
46
|
+
let persists = PyList::empty_bound(py);
|
|
47
|
+
for persist in parsed.persists {
|
|
48
|
+
let persist_dict = PyDict::new_bound(py);
|
|
49
|
+
persist_dict.set_item("mode", persist.mode.to_string())?;
|
|
50
|
+
persist_dict.set_item("target_datasource", persist.target_datasource)?;
|
|
51
|
+
persists.append(persist_dict)?;
|
|
52
|
+
}
|
|
53
|
+
result.set_item("persists", persists)?;
|
|
54
|
+
|
|
55
|
+
Ok(result.into())
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Python wrapper for ImportResolver
|
|
59
|
+
#[pyclass]
|
|
60
|
+
struct PyImportResolver {
|
|
61
|
+
resolver: ImportResolver,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#[pymethods]
|
|
65
|
+
impl PyImportResolver {
|
|
66
|
+
#[new]
|
|
67
|
+
fn new() -> Self {
|
|
68
|
+
PyImportResolver {
|
|
69
|
+
resolver: ImportResolver::new(),
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Resolve dependencies for a file and return the dependency graph
|
|
74
|
+
fn resolve(&mut self, py: Python<'_>, path: &str) -> PyResult<PyObject> {
|
|
75
|
+
let path_buf = PathBuf::from(path);
|
|
76
|
+
|
|
77
|
+
let graph = self
|
|
78
|
+
.resolver
|
|
79
|
+
.resolve(&path_buf)
|
|
80
|
+
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("Resolve error: {}", e)))?;
|
|
81
|
+
|
|
82
|
+
let result = pyo3::types::PyDict::new_bound(py);
|
|
83
|
+
|
|
84
|
+
// Add root
|
|
85
|
+
result.set_item("root", graph.root.to_string_lossy().to_string())?;
|
|
86
|
+
|
|
87
|
+
// Add order (list of file paths in execution order)
|
|
88
|
+
let order = PyList::empty_bound(py);
|
|
89
|
+
for file_path in &graph.order {
|
|
90
|
+
order.append(file_path.to_string_lossy().to_string())?;
|
|
91
|
+
}
|
|
92
|
+
result.set_item("order", order)?;
|
|
93
|
+
|
|
94
|
+
// Add files (detailed info about each file)
|
|
95
|
+
let files = pyo3::types::PyDict::new_bound(py);
|
|
96
|
+
for (path, node) in &graph.files {
|
|
97
|
+
let node_dict = pyo3::types::PyDict::new_bound(py);
|
|
98
|
+
|
|
99
|
+
node_dict.set_item("path", node.path.to_string_lossy().to_string())?;
|
|
100
|
+
node_dict.set_item("relative_path", node.relative_path.to_string_lossy().to_string())?;
|
|
101
|
+
|
|
102
|
+
// imports
|
|
103
|
+
let imports = PyList::empty_bound(py);
|
|
104
|
+
for import in &node.imports {
|
|
105
|
+
let import_dict = pyo3::types::PyDict::new_bound(py);
|
|
106
|
+
import_dict.set_item("raw_path", &import.raw_path)?;
|
|
107
|
+
import_dict.set_item("alias", &import.alias)?;
|
|
108
|
+
import_dict.set_item("is_stdlib", import.is_stdlib)?;
|
|
109
|
+
if let Some(resolved_path) = &import.resolved_path {
|
|
110
|
+
import_dict.set_item("resolved_path", resolved_path.to_string_lossy().to_string())?;
|
|
111
|
+
}
|
|
112
|
+
imports.append(import_dict)?;
|
|
113
|
+
}
|
|
114
|
+
node_dict.set_item("imports", imports)?;
|
|
115
|
+
|
|
116
|
+
// datasources
|
|
117
|
+
let datasources = PyList::empty_bound(py);
|
|
118
|
+
for ds in &node.datasources {
|
|
119
|
+
let ds_dict = pyo3::types::PyDict::new_bound(py);
|
|
120
|
+
ds_dict.set_item("name", &ds.name)?;
|
|
121
|
+
datasources.append(ds_dict)?;
|
|
122
|
+
}
|
|
123
|
+
node_dict.set_item("datasources", datasources)?;
|
|
124
|
+
|
|
125
|
+
// persists
|
|
126
|
+
let persists = PyList::empty_bound(py);
|
|
127
|
+
for persist in &node.persists {
|
|
128
|
+
let persist_dict = pyo3::types::PyDict::new_bound(py);
|
|
129
|
+
persist_dict.set_item("mode", &persist.mode)?;
|
|
130
|
+
persist_dict.set_item("target_datasource", &persist.target_datasource)?;
|
|
131
|
+
persists.append(persist_dict)?;
|
|
132
|
+
}
|
|
133
|
+
node_dict.set_item("persists", persists)?;
|
|
134
|
+
|
|
135
|
+
// dependency lists
|
|
136
|
+
let import_dependencies = PyList::empty_bound(py);
|
|
137
|
+
for dep in &node.import_dependencies {
|
|
138
|
+
import_dependencies.append(dep.to_string_lossy().to_string())?;
|
|
139
|
+
}
|
|
140
|
+
node_dict.set_item("import_dependencies", import_dependencies)?;
|
|
141
|
+
|
|
142
|
+
let updates_datasources = PyList::empty_bound(py);
|
|
143
|
+
for ds in &node.updates_datasources {
|
|
144
|
+
updates_datasources.append(ds)?;
|
|
145
|
+
}
|
|
146
|
+
node_dict.set_item("updates_datasources", updates_datasources)?;
|
|
147
|
+
|
|
148
|
+
let declares_datasources = PyList::empty_bound(py);
|
|
149
|
+
for ds in &node.declares_datasources {
|
|
150
|
+
declares_datasources.append(ds)?;
|
|
151
|
+
}
|
|
152
|
+
node_dict.set_item("declares_datasources", declares_datasources)?;
|
|
153
|
+
|
|
154
|
+
let depends_on_datasources = PyList::empty_bound(py);
|
|
155
|
+
for ds in &node.depends_on_datasources {
|
|
156
|
+
depends_on_datasources.append(ds)?;
|
|
157
|
+
}
|
|
158
|
+
node_dict.set_item("depends_on_datasources", depends_on_datasources)?;
|
|
159
|
+
|
|
160
|
+
files.set_item(path.to_string_lossy().to_string(), node_dict)?;
|
|
161
|
+
}
|
|
162
|
+
result.set_item("files", files)?;
|
|
163
|
+
|
|
164
|
+
// Add datasource_declarations
|
|
165
|
+
let declarations = pyo3::types::PyDict::new_bound(py);
|
|
166
|
+
for (ds_name, declaring_path) in &graph.datasource_declarations {
|
|
167
|
+
declarations.set_item(ds_name, declaring_path.to_string_lossy().to_string())?;
|
|
168
|
+
}
|
|
169
|
+
result.set_item("datasource_declarations", declarations)?;
|
|
170
|
+
|
|
171
|
+
// Add datasource_updaters
|
|
172
|
+
let updaters = pyo3::types::PyDict::new_bound(py);
|
|
173
|
+
for (ds_name, updater_paths) in &graph.datasource_updaters {
|
|
174
|
+
let paths_list = PyList::empty_bound(py);
|
|
175
|
+
for updater_path in updater_paths {
|
|
176
|
+
paths_list.append(updater_path.to_string_lossy().to_string())?;
|
|
177
|
+
}
|
|
178
|
+
updaters.set_item(ds_name, paths_list)?;
|
|
179
|
+
}
|
|
180
|
+
result.set_item("datasource_updaters", updaters)?;
|
|
181
|
+
|
|
182
|
+
// Add warnings
|
|
183
|
+
let warnings = PyList::empty_bound(py);
|
|
184
|
+
for warning in &graph.warnings {
|
|
185
|
+
warnings.append(warning)?;
|
|
186
|
+
}
|
|
187
|
+
result.set_item("warnings", warnings)?;
|
|
188
|
+
|
|
189
|
+
Ok(result.into())
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Get just the dependency order for a file
|
|
193
|
+
fn resolve_order(&mut self, path: &str) -> PyResult<Vec<String>> {
|
|
194
|
+
let path_buf = PathBuf::from(path);
|
|
195
|
+
|
|
196
|
+
let graph = self
|
|
197
|
+
.resolver
|
|
198
|
+
.resolve(&path_buf)
|
|
199
|
+
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("Resolve error: {}", e)))?;
|
|
200
|
+
|
|
201
|
+
Ok(graph
|
|
202
|
+
.order
|
|
203
|
+
.iter()
|
|
204
|
+
.map(|p| p.to_string_lossy().to_string())
|
|
205
|
+
.collect())
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/// Resolve dependencies for all files in a directory
|
|
209
|
+
fn resolve_directory(&mut self, py: Python<'_>, dir_path: &str, _recursive: bool) -> PyResult<PyObject> {
|
|
210
|
+
use crate::directory_resolver::{process_directory_with_imports, build_edges, EdgeReason};
|
|
211
|
+
use std::fs;
|
|
212
|
+
|
|
213
|
+
let dir = PathBuf::from(dir_path);
|
|
214
|
+
if !dir.is_dir() {
|
|
215
|
+
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
|
|
216
|
+
format!("{} is not a directory", dir_path)
|
|
217
|
+
));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Collect all .preql files in the top-level directory
|
|
221
|
+
let mut initial_files = Vec::new();
|
|
222
|
+
let read_dir = fs::read_dir(&dir)
|
|
223
|
+
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("Failed to read directory: {}", e)))?;
|
|
224
|
+
|
|
225
|
+
for entry in read_dir {
|
|
226
|
+
let entry = entry.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(format!("Failed to read entry: {}", e)))?;
|
|
227
|
+
let path = entry.path();
|
|
228
|
+
if path.is_file() && path.extension().map_or(false, |ext| ext == "preql") {
|
|
229
|
+
initial_files.push(path);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if initial_files.is_empty() {
|
|
234
|
+
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
|
|
235
|
+
format!("No .preql files found in {}", dir_path)
|
|
236
|
+
));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Process directory with transitive import discovery
|
|
240
|
+
let graph = process_directory_with_imports(initial_files)
|
|
241
|
+
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e))?;
|
|
242
|
+
|
|
243
|
+
// Build edges
|
|
244
|
+
let edges = build_edges(&graph);
|
|
245
|
+
|
|
246
|
+
// Convert to Python objects
|
|
247
|
+
let result = PyDict::new_bound(py);
|
|
248
|
+
let edges_list = PyList::empty_bound(py);
|
|
249
|
+
let files_list = PyList::empty_bound(py);
|
|
250
|
+
let warnings_list = PyList::empty_bound(py);
|
|
251
|
+
|
|
252
|
+
// Add files
|
|
253
|
+
for file_info in graph.files.values() {
|
|
254
|
+
files_list.append(file_info.path.to_string_lossy().to_string())?;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Add edges
|
|
258
|
+
for edge in edges {
|
|
259
|
+
let edge_dict = PyDict::new_bound(py);
|
|
260
|
+
edge_dict.set_item("from", edge.from.to_string_lossy().to_string())?;
|
|
261
|
+
edge_dict.set_item("to", edge.to.to_string_lossy().to_string())?;
|
|
262
|
+
|
|
263
|
+
let reason_dict = PyDict::new_bound(py);
|
|
264
|
+
match edge.reason {
|
|
265
|
+
EdgeReason::Import => {
|
|
266
|
+
reason_dict.set_item("type", "import")?;
|
|
267
|
+
}
|
|
268
|
+
EdgeReason::PersistBeforeDeclare { datasource } => {
|
|
269
|
+
reason_dict.set_item("type", "persist_before_declare")?;
|
|
270
|
+
reason_dict.set_item("datasource", datasource)?;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
edge_dict.set_item("reason", reason_dict)?;
|
|
274
|
+
edges_list.append(edge_dict)?;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Add warnings
|
|
278
|
+
for warning in graph.warnings {
|
|
279
|
+
warnings_list.append(warning)?;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
result.set_item("directory", dir.to_string_lossy().to_string())?;
|
|
283
|
+
result.set_item("files", files_list)?;
|
|
284
|
+
result.set_item("edges", edges_list)?;
|
|
285
|
+
result.set_item("warnings", warnings_list)?;
|
|
286
|
+
|
|
287
|
+
Ok(result.into())
|
|
288
|
+
}
|
|
289
|
+
}
|