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.

Files changed (75) hide show
  1. metaxy/__init__.py +61 -0
  2. metaxy/_testing.py +542 -0
  3. metaxy/_utils.py +16 -0
  4. metaxy/_version.py +1 -0
  5. metaxy/cli/app.py +76 -0
  6. metaxy/cli/context.py +71 -0
  7. metaxy/cli/graph.py +576 -0
  8. metaxy/cli/graph_diff.py +290 -0
  9. metaxy/cli/list.py +42 -0
  10. metaxy/cli/metadata.py +271 -0
  11. metaxy/cli/migrations.py +862 -0
  12. metaxy/cli/push.py +55 -0
  13. metaxy/config.py +450 -0
  14. metaxy/data_versioning/__init__.py +24 -0
  15. metaxy/data_versioning/calculators/__init__.py +13 -0
  16. metaxy/data_versioning/calculators/base.py +97 -0
  17. metaxy/data_versioning/calculators/duckdb.py +186 -0
  18. metaxy/data_versioning/calculators/ibis.py +225 -0
  19. metaxy/data_versioning/calculators/polars.py +135 -0
  20. metaxy/data_versioning/diff/__init__.py +15 -0
  21. metaxy/data_versioning/diff/base.py +150 -0
  22. metaxy/data_versioning/diff/narwhals.py +108 -0
  23. metaxy/data_versioning/hash_algorithms.py +19 -0
  24. metaxy/data_versioning/joiners/__init__.py +9 -0
  25. metaxy/data_versioning/joiners/base.py +70 -0
  26. metaxy/data_versioning/joiners/narwhals.py +235 -0
  27. metaxy/entrypoints.py +309 -0
  28. metaxy/ext/__init__.py +1 -0
  29. metaxy/ext/alembic.py +326 -0
  30. metaxy/ext/sqlmodel.py +172 -0
  31. metaxy/ext/sqlmodel_system_tables.py +139 -0
  32. metaxy/graph/__init__.py +21 -0
  33. metaxy/graph/diff/__init__.py +21 -0
  34. metaxy/graph/diff/diff_models.py +399 -0
  35. metaxy/graph/diff/differ.py +740 -0
  36. metaxy/graph/diff/models.py +418 -0
  37. metaxy/graph/diff/rendering/__init__.py +18 -0
  38. metaxy/graph/diff/rendering/base.py +274 -0
  39. metaxy/graph/diff/rendering/cards.py +188 -0
  40. metaxy/graph/diff/rendering/formatter.py +805 -0
  41. metaxy/graph/diff/rendering/graphviz.py +246 -0
  42. metaxy/graph/diff/rendering/mermaid.py +320 -0
  43. metaxy/graph/diff/rendering/rich.py +165 -0
  44. metaxy/graph/diff/rendering/theme.py +48 -0
  45. metaxy/graph/diff/traversal.py +247 -0
  46. metaxy/graph/utils.py +58 -0
  47. metaxy/metadata_store/__init__.py +31 -0
  48. metaxy/metadata_store/_protocols.py +38 -0
  49. metaxy/metadata_store/base.py +1676 -0
  50. metaxy/metadata_store/clickhouse.py +161 -0
  51. metaxy/metadata_store/duckdb.py +167 -0
  52. metaxy/metadata_store/exceptions.py +43 -0
  53. metaxy/metadata_store/ibis.py +451 -0
  54. metaxy/metadata_store/memory.py +228 -0
  55. metaxy/metadata_store/sqlite.py +187 -0
  56. metaxy/metadata_store/system_tables.py +257 -0
  57. metaxy/migrations/__init__.py +34 -0
  58. metaxy/migrations/detector.py +153 -0
  59. metaxy/migrations/executor.py +208 -0
  60. metaxy/migrations/loader.py +260 -0
  61. metaxy/migrations/models.py +718 -0
  62. metaxy/migrations/ops.py +390 -0
  63. metaxy/models/__init__.py +0 -0
  64. metaxy/models/bases.py +6 -0
  65. metaxy/models/constants.py +24 -0
  66. metaxy/models/feature.py +665 -0
  67. metaxy/models/feature_spec.py +105 -0
  68. metaxy/models/field.py +25 -0
  69. metaxy/models/plan.py +155 -0
  70. metaxy/models/types.py +157 -0
  71. metaxy/py.typed +0 -0
  72. metaxy-0.0.0.dist-info/METADATA +247 -0
  73. metaxy-0.0.0.dist-info/RECORD +75 -0
  74. metaxy-0.0.0.dist-info/WHEEL +4 -0
  75. 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 ""