metaxy 0.0.0__py3-none-any.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.
Potentially problematic release.
This version of metaxy might be problematic. Click here for more details.
- metaxy/__init__.py +61 -0
- metaxy/_testing.py +542 -0
- metaxy/_utils.py +16 -0
- metaxy/_version.py +1 -0
- metaxy/cli/app.py +76 -0
- metaxy/cli/context.py +71 -0
- metaxy/cli/graph.py +576 -0
- metaxy/cli/graph_diff.py +290 -0
- metaxy/cli/list.py +42 -0
- metaxy/cli/metadata.py +271 -0
- metaxy/cli/migrations.py +862 -0
- metaxy/cli/push.py +55 -0
- metaxy/config.py +450 -0
- metaxy/data_versioning/__init__.py +24 -0
- metaxy/data_versioning/calculators/__init__.py +13 -0
- metaxy/data_versioning/calculators/base.py +97 -0
- metaxy/data_versioning/calculators/duckdb.py +186 -0
- metaxy/data_versioning/calculators/ibis.py +225 -0
- metaxy/data_versioning/calculators/polars.py +135 -0
- metaxy/data_versioning/diff/__init__.py +15 -0
- metaxy/data_versioning/diff/base.py +150 -0
- metaxy/data_versioning/diff/narwhals.py +108 -0
- metaxy/data_versioning/hash_algorithms.py +19 -0
- metaxy/data_versioning/joiners/__init__.py +9 -0
- metaxy/data_versioning/joiners/base.py +70 -0
- metaxy/data_versioning/joiners/narwhals.py +235 -0
- metaxy/entrypoints.py +309 -0
- metaxy/ext/__init__.py +1 -0
- metaxy/ext/alembic.py +326 -0
- metaxy/ext/sqlmodel.py +172 -0
- metaxy/ext/sqlmodel_system_tables.py +139 -0
- metaxy/graph/__init__.py +21 -0
- metaxy/graph/diff/__init__.py +21 -0
- metaxy/graph/diff/diff_models.py +399 -0
- metaxy/graph/diff/differ.py +740 -0
- metaxy/graph/diff/models.py +418 -0
- metaxy/graph/diff/rendering/__init__.py +18 -0
- metaxy/graph/diff/rendering/base.py +274 -0
- metaxy/graph/diff/rendering/cards.py +188 -0
- metaxy/graph/diff/rendering/formatter.py +805 -0
- metaxy/graph/diff/rendering/graphviz.py +246 -0
- metaxy/graph/diff/rendering/mermaid.py +320 -0
- metaxy/graph/diff/rendering/rich.py +165 -0
- metaxy/graph/diff/rendering/theme.py +48 -0
- metaxy/graph/diff/traversal.py +247 -0
- metaxy/graph/utils.py +58 -0
- metaxy/metadata_store/__init__.py +31 -0
- metaxy/metadata_store/_protocols.py +38 -0
- metaxy/metadata_store/base.py +1676 -0
- metaxy/metadata_store/clickhouse.py +161 -0
- metaxy/metadata_store/duckdb.py +167 -0
- metaxy/metadata_store/exceptions.py +43 -0
- metaxy/metadata_store/ibis.py +451 -0
- metaxy/metadata_store/memory.py +228 -0
- metaxy/metadata_store/sqlite.py +187 -0
- metaxy/metadata_store/system_tables.py +257 -0
- metaxy/migrations/__init__.py +34 -0
- metaxy/migrations/detector.py +153 -0
- metaxy/migrations/executor.py +208 -0
- metaxy/migrations/loader.py +260 -0
- metaxy/migrations/models.py +718 -0
- metaxy/migrations/ops.py +390 -0
- metaxy/models/__init__.py +0 -0
- metaxy/models/bases.py +6 -0
- metaxy/models/constants.py +24 -0
- metaxy/models/feature.py +665 -0
- metaxy/models/feature_spec.py +105 -0
- metaxy/models/field.py +25 -0
- metaxy/models/plan.py +155 -0
- metaxy/models/types.py +157 -0
- metaxy/py.typed +0 -0
- metaxy-0.0.0.dist-info/METADATA +247 -0
- metaxy-0.0.0.dist-info/RECORD +75 -0
- metaxy-0.0.0.dist-info/WHEEL +4 -0
- metaxy-0.0.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Cards renderer using Rich panels for graph visualization.
|
|
2
|
+
|
|
3
|
+
Requires rich library to be installed.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from metaxy.graph.diff.models import NodeStatus
|
|
7
|
+
from metaxy.graph.diff.rendering.base import BaseRenderer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CardsRenderer(BaseRenderer):
|
|
11
|
+
"""Renders graph as cards with edges for terminal display.
|
|
12
|
+
|
|
13
|
+
Uses Rich panels to show features as cards/boxes with dependency information.
|
|
14
|
+
Supports both normal and diff rendering via node status.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def render(self) -> str:
|
|
18
|
+
"""Render graph as cards.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Rendered cards as string with ANSI color codes
|
|
22
|
+
"""
|
|
23
|
+
from rich.columns import Columns
|
|
24
|
+
from rich.console import Console, Group
|
|
25
|
+
from rich.text import Text
|
|
26
|
+
|
|
27
|
+
console = Console()
|
|
28
|
+
|
|
29
|
+
# Get filtered graph data based on config
|
|
30
|
+
filtered_graph = self._get_filtered_graph_data()
|
|
31
|
+
|
|
32
|
+
# Build feature panels in topological order
|
|
33
|
+
from metaxy.graph.diff.traversal import GraphWalker
|
|
34
|
+
|
|
35
|
+
walker = GraphWalker(filtered_graph)
|
|
36
|
+
feature_panels = []
|
|
37
|
+
|
|
38
|
+
for node in walker.topological_sort():
|
|
39
|
+
panel = self._build_feature_panel(node)
|
|
40
|
+
feature_panels.append(panel)
|
|
41
|
+
|
|
42
|
+
# Build edges representation
|
|
43
|
+
edges_text = Text()
|
|
44
|
+
if self.config.show_snapshot_version:
|
|
45
|
+
snapshot_version = self._format_hash(filtered_graph.snapshot_version)
|
|
46
|
+
edges_text.append(
|
|
47
|
+
f"📊 Graph (snapshot: {snapshot_version})\n\n", style="bold"
|
|
48
|
+
)
|
|
49
|
+
else:
|
|
50
|
+
edges_text.append("📊 Graph\n\n", style="bold")
|
|
51
|
+
|
|
52
|
+
# Show dependency edges
|
|
53
|
+
edges_text.append("Dependencies:\n", style="bold cyan")
|
|
54
|
+
for node in walker.topological_sort():
|
|
55
|
+
if node.dependencies:
|
|
56
|
+
source_label = self._format_feature_key(node.key)
|
|
57
|
+
source_color = self._get_status_color(node.status)
|
|
58
|
+
for dep_key in node.dependencies:
|
|
59
|
+
dep_node = filtered_graph.get_node(dep_key)
|
|
60
|
+
target_label = self._format_feature_key(dep_key)
|
|
61
|
+
target_color = (
|
|
62
|
+
self._get_status_color(dep_node.status)
|
|
63
|
+
if dep_node
|
|
64
|
+
else source_color
|
|
65
|
+
)
|
|
66
|
+
edges_text.append(f" {target_label} ", style=target_color)
|
|
67
|
+
edges_text.append("→", style="yellow bold")
|
|
68
|
+
edges_text.append(f" {source_label}\n", style=source_color)
|
|
69
|
+
|
|
70
|
+
# Combine everything
|
|
71
|
+
output_group = Group(
|
|
72
|
+
edges_text,
|
|
73
|
+
Text("\nFeatures:", style="bold"),
|
|
74
|
+
Columns(feature_panels, equal=True, expand=True),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Render to string
|
|
78
|
+
with console.capture() as capture:
|
|
79
|
+
console.print(output_group)
|
|
80
|
+
return capture.get()
|
|
81
|
+
|
|
82
|
+
def _build_feature_panel(self, node):
|
|
83
|
+
"""Build a Rich Panel for a feature.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
node: GraphNode
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Rich Panel with feature information
|
|
90
|
+
"""
|
|
91
|
+
from rich.panel import Panel
|
|
92
|
+
from rich.text import Text
|
|
93
|
+
|
|
94
|
+
content = Text()
|
|
95
|
+
|
|
96
|
+
# Get status color
|
|
97
|
+
status_color = self._get_status_color(node.status)
|
|
98
|
+
border_color = status_color
|
|
99
|
+
|
|
100
|
+
# Feature name
|
|
101
|
+
content.append(self._format_feature_key(node.key), style=f"bold {status_color}")
|
|
102
|
+
|
|
103
|
+
# Add status badge for diff mode
|
|
104
|
+
if node.status != NodeStatus.NORMAL:
|
|
105
|
+
status_badge = self._get_status_badge(node.status)
|
|
106
|
+
content.append(f" {status_badge}")
|
|
107
|
+
|
|
108
|
+
content.append("\n")
|
|
109
|
+
|
|
110
|
+
# Versions
|
|
111
|
+
if self.config.show_feature_versions:
|
|
112
|
+
if node.status == NodeStatus.CHANGED and node.old_version is not None:
|
|
113
|
+
# Show version transition for changed nodes
|
|
114
|
+
old_v = self._format_hash(node.old_version)
|
|
115
|
+
new_v = self._format_hash(node.version)
|
|
116
|
+
content.append(f"v: {old_v} → {new_v}", style="yellow")
|
|
117
|
+
else:
|
|
118
|
+
version = self._format_hash(node.version)
|
|
119
|
+
content.append(f"v: {version}", style="yellow")
|
|
120
|
+
content.append("\n")
|
|
121
|
+
|
|
122
|
+
if self.config.show_code_versions and node.code_version is not None:
|
|
123
|
+
content.append(f"cv: {node.code_version}", style="dim")
|
|
124
|
+
content.append("\n")
|
|
125
|
+
|
|
126
|
+
# Fields
|
|
127
|
+
if self.config.show_fields and node.fields:
|
|
128
|
+
content.append("\nFields:\n", style="bold green")
|
|
129
|
+
for field_node in node.fields:
|
|
130
|
+
field_text = self._format_field_info(field_node)
|
|
131
|
+
content.append(f" • {field_text}\n")
|
|
132
|
+
|
|
133
|
+
return Panel(content, border_style=border_color, padding=(0, 1))
|
|
134
|
+
|
|
135
|
+
def _format_field_info(self, field_node) -> str:
|
|
136
|
+
"""Format field information as a string.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
field_node: FieldNode
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Formatted field string
|
|
143
|
+
"""
|
|
144
|
+
parts = [self._format_field_key(field_node.key)]
|
|
145
|
+
|
|
146
|
+
# Show version info
|
|
147
|
+
if self.config.show_field_versions:
|
|
148
|
+
if (
|
|
149
|
+
field_node.status == NodeStatus.CHANGED
|
|
150
|
+
and field_node.old_version is not None
|
|
151
|
+
):
|
|
152
|
+
# Show version transition for changed fields
|
|
153
|
+
old_v = self._format_hash(field_node.old_version)
|
|
154
|
+
new_v = self._format_hash(field_node.version)
|
|
155
|
+
parts.append(f"(v: {old_v} → {new_v})")
|
|
156
|
+
else:
|
|
157
|
+
version = self._format_hash(field_node.version)
|
|
158
|
+
parts.append(f"(v: {version})")
|
|
159
|
+
|
|
160
|
+
if self.config.show_code_versions and field_node.code_version is not None:
|
|
161
|
+
parts.append(f"(cv: {field_node.code_version})")
|
|
162
|
+
|
|
163
|
+
# Add status badge for diff mode
|
|
164
|
+
if field_node.status != NodeStatus.NORMAL:
|
|
165
|
+
status_badge = self._get_status_badge(field_node.status)
|
|
166
|
+
parts.append(status_badge)
|
|
167
|
+
|
|
168
|
+
return " ".join(parts)
|
|
169
|
+
|
|
170
|
+
def _get_status_badge(self, status: NodeStatus) -> str:
|
|
171
|
+
"""Get status badge text.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
status: Node status
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Status badge string
|
|
178
|
+
"""
|
|
179
|
+
if status == NodeStatus.ADDED:
|
|
180
|
+
return "[+]"
|
|
181
|
+
elif status == NodeStatus.REMOVED:
|
|
182
|
+
return "[-]"
|
|
183
|
+
elif status == NodeStatus.CHANGED:
|
|
184
|
+
return "[~]"
|
|
185
|
+
elif status == NodeStatus.UNCHANGED:
|
|
186
|
+
return ""
|
|
187
|
+
else:
|
|
188
|
+
return ""
|