faceberg 0.1.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.
- faceberg/__init__.py +15 -0
- faceberg/bridge.py +586 -0
- faceberg/catalog.py +1491 -0
- faceberg/cli.py +483 -0
- faceberg/config.py +208 -0
- faceberg/convert.py +813 -0
- faceberg/pretty.py +224 -0
- faceberg/server.py +439 -0
- faceberg/shell.py +83 -0
- faceberg/spaces/Dockerfile +10 -0
- faceberg/spaces/README.md +85 -0
- faceberg/spaces/landing.html +799 -0
- faceberg/tests/__init__.py +0 -0
- faceberg/tests/conftest.py +229 -0
- faceberg/tests/test_bridge.py +825 -0
- faceberg/tests/test_catalog.py +1347 -0
- faceberg/tests/test_catalog_duckdb.py +341 -0
- faceberg/tests/test_catalog_pandas.py +290 -0
- faceberg/tests/test_cli.py +62 -0
- faceberg/tests/test_config.py +367 -0
- faceberg/tests/test_convert.py +422 -0
- faceberg/tests/test_pretty.py +366 -0
- faceberg/tests/test_server.py +343 -0
- faceberg/tests/test_server_playwright.py +524 -0
- faceberg-0.1.0.dist-info/METADATA +175 -0
- faceberg-0.1.0.dist-info/RECORD +29 -0
- faceberg-0.1.0.dist-info/WHEEL +4 -0
- faceberg-0.1.0.dist-info/entry_points.txt +2 -0
- faceberg-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.tree import Tree
|
|
6
|
+
|
|
7
|
+
from faceberg import config as cfg
|
|
8
|
+
from faceberg.pretty import TableState, node, progress_bars, progress_tree, tree
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_table_state_creation():
|
|
12
|
+
"""Test TableState dataclass creation with defaults."""
|
|
13
|
+
state = TableState()
|
|
14
|
+
|
|
15
|
+
assert state.kind == "pending"
|
|
16
|
+
assert state.progress is None
|
|
17
|
+
assert state.error is None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_table_state_with_all_fields():
|
|
21
|
+
"""Test TableState with all fields specified."""
|
|
22
|
+
state = TableState(kind="in_progress", progress=50, error="test error")
|
|
23
|
+
|
|
24
|
+
assert state.kind == "in_progress"
|
|
25
|
+
assert state.progress == 50
|
|
26
|
+
assert state.error == "test error"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_table_state_icon_property():
|
|
30
|
+
"""Test that TableState.icon returns correct icons for different states."""
|
|
31
|
+
assert TableState(kind="pending").icon == "⏳"
|
|
32
|
+
assert TableState(kind="in_progress").icon == "▶️"
|
|
33
|
+
assert TableState(kind="complete").icon == "✓"
|
|
34
|
+
assert TableState(kind="up_to_date").icon == "✓"
|
|
35
|
+
assert TableState(kind="needs_update").icon == "↻"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_table_state_color_property():
|
|
39
|
+
"""Test that TableState.color returns correct colors for different states."""
|
|
40
|
+
assert TableState(kind="pending").color == "dim white"
|
|
41
|
+
assert TableState(kind="in_progress").color == "yellow"
|
|
42
|
+
assert TableState(kind="complete").color == "green"
|
|
43
|
+
assert TableState(kind="up_to_date").color == "dim green"
|
|
44
|
+
assert TableState(kind="needs_update").color == "blue"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_tree_empty_config():
|
|
48
|
+
"""Test tree building with empty config."""
|
|
49
|
+
config = cfg.Config()
|
|
50
|
+
result = tree(config)
|
|
51
|
+
|
|
52
|
+
assert isinstance(result, Tree)
|
|
53
|
+
assert result.hide_root is True
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_tree_with_namespace():
|
|
57
|
+
"""Test tree building with namespace."""
|
|
58
|
+
config = cfg.Config()
|
|
59
|
+
config["default"] = cfg.Namespace()
|
|
60
|
+
|
|
61
|
+
result = tree(config)
|
|
62
|
+
|
|
63
|
+
assert isinstance(result, Tree)
|
|
64
|
+
# Should have one child (the namespace)
|
|
65
|
+
assert len(result.children) == 1
|
|
66
|
+
label_str = str(result.children[0].label)
|
|
67
|
+
assert "default" in label_str
|
|
68
|
+
assert "cyan" in label_str
|
|
69
|
+
assert "namespace" in label_str
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_tree_with_table():
|
|
73
|
+
"""Test tree building with table node."""
|
|
74
|
+
config = cfg.Config()
|
|
75
|
+
config["default"] = cfg.Namespace()
|
|
76
|
+
config["default"]["my_table"] = cfg.Table(uri="")
|
|
77
|
+
|
|
78
|
+
result = tree(config)
|
|
79
|
+
|
|
80
|
+
assert isinstance(result, Tree)
|
|
81
|
+
# Navigate to the table node
|
|
82
|
+
namespace_node = result.children[0]
|
|
83
|
+
assert len(namespace_node.children) == 1
|
|
84
|
+
table_label = str(namespace_node.children[0].label)
|
|
85
|
+
assert "my_table" in table_label
|
|
86
|
+
assert "green" in table_label
|
|
87
|
+
assert "table" in table_label
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_tree_with_dataset():
|
|
91
|
+
"""Test tree building with dataset node."""
|
|
92
|
+
config = cfg.Config()
|
|
93
|
+
config["default"] = cfg.Namespace()
|
|
94
|
+
config["default"]["imdb"] = cfg.Dataset(repo="stanfordnlp/imdb", config="plain_text")
|
|
95
|
+
|
|
96
|
+
result = tree(config)
|
|
97
|
+
|
|
98
|
+
assert isinstance(result, Tree)
|
|
99
|
+
namespace_node = result.children[0]
|
|
100
|
+
dataset_label = str(namespace_node.children[0].label)
|
|
101
|
+
assert "imdb" in dataset_label
|
|
102
|
+
assert "stanfordnlp/imdb" in dataset_label
|
|
103
|
+
assert "yellow" in dataset_label
|
|
104
|
+
assert "dataset" in dataset_label
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_tree_with_view():
|
|
108
|
+
"""Test tree building with view node."""
|
|
109
|
+
config = cfg.Config()
|
|
110
|
+
config["default"] = cfg.Namespace()
|
|
111
|
+
config["default"]["my_view"] = cfg.View(query="SELECT * FROM table WHERE condition = true")
|
|
112
|
+
|
|
113
|
+
result = tree(config)
|
|
114
|
+
|
|
115
|
+
assert isinstance(result, Tree)
|
|
116
|
+
namespace_node = result.children[0]
|
|
117
|
+
view_label = str(namespace_node.children[0].label)
|
|
118
|
+
assert "my_view" in view_label
|
|
119
|
+
assert "SELECT" in view_label
|
|
120
|
+
assert "blue" in view_label
|
|
121
|
+
assert "view" in view_label
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_tree_with_states():
|
|
125
|
+
"""Test tree building with state tracking."""
|
|
126
|
+
config = cfg.Config()
|
|
127
|
+
config["default"] = cfg.Namespace()
|
|
128
|
+
config["default"]["test_table"] = cfg.Table(uri="")
|
|
129
|
+
|
|
130
|
+
states = {("default", "test_table"): TableState(kind="in_progress", progress=50)}
|
|
131
|
+
|
|
132
|
+
result = tree(config, states)
|
|
133
|
+
|
|
134
|
+
assert isinstance(result, Tree)
|
|
135
|
+
namespace_node = result.children[0]
|
|
136
|
+
table_label = str(namespace_node.children[0].label)
|
|
137
|
+
assert "▶️" in table_label # In progress icon
|
|
138
|
+
assert "yellow" in table_label # In progress color
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_tree_with_error_state():
|
|
142
|
+
"""Test tree building with error state."""
|
|
143
|
+
config = cfg.Config()
|
|
144
|
+
config["default"] = cfg.Namespace()
|
|
145
|
+
config["default"]["failed_table"] = cfg.Table(uri="")
|
|
146
|
+
|
|
147
|
+
states = {("default", "failed_table"): TableState(kind="complete", error="Connection timeout")}
|
|
148
|
+
|
|
149
|
+
result = tree(config, states)
|
|
150
|
+
|
|
151
|
+
assert isinstance(result, Tree)
|
|
152
|
+
namespace_node = result.children[0]
|
|
153
|
+
table_node = namespace_node.children[0]
|
|
154
|
+
# Error should be added as a child node
|
|
155
|
+
assert len(table_node.children) == 1
|
|
156
|
+
error_label = str(table_node.children[0].label)
|
|
157
|
+
assert "Connection timeout" in error_label
|
|
158
|
+
assert "red" in error_label
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def test_node_namespace():
|
|
162
|
+
"""Test node function with namespace."""
|
|
163
|
+
ns = cfg.Namespace()
|
|
164
|
+
ns["child_table"] = cfg.Table(uri="")
|
|
165
|
+
parent = Tree("root")
|
|
166
|
+
states = {}
|
|
167
|
+
|
|
168
|
+
node(ns, ("default",), parent, states)
|
|
169
|
+
|
|
170
|
+
assert len(parent.children) == 1
|
|
171
|
+
namespace_label = str(parent.children[0].label)
|
|
172
|
+
assert "default" in namespace_label
|
|
173
|
+
assert "cyan" in namespace_label
|
|
174
|
+
# Should have one child (the table)
|
|
175
|
+
assert len(parent.children[0].children) == 1
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_node_table():
|
|
179
|
+
"""Test node function with table."""
|
|
180
|
+
table = cfg.Table(uri="")
|
|
181
|
+
parent = Tree("root")
|
|
182
|
+
states = {}
|
|
183
|
+
|
|
184
|
+
node(table, ("default", "my_table"), parent, states)
|
|
185
|
+
|
|
186
|
+
assert len(parent.children) == 1
|
|
187
|
+
table_label = str(parent.children[0].label)
|
|
188
|
+
assert "my_table" in table_label
|
|
189
|
+
assert "green" in table_label
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def test_node_dataset():
|
|
193
|
+
"""Test node function with dataset."""
|
|
194
|
+
dataset = cfg.Dataset(repo="org/repo", config="default")
|
|
195
|
+
parent = Tree("root")
|
|
196
|
+
states = {}
|
|
197
|
+
|
|
198
|
+
node(dataset, ("default", "my_dataset"), parent, states)
|
|
199
|
+
|
|
200
|
+
assert len(parent.children) == 1
|
|
201
|
+
dataset_label = str(parent.children[0].label)
|
|
202
|
+
assert "my_dataset" in dataset_label
|
|
203
|
+
assert "org/repo" in dataset_label
|
|
204
|
+
assert "yellow" in dataset_label
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def test_node_view():
|
|
208
|
+
"""Test node function with view."""
|
|
209
|
+
view = cfg.View(query="SELECT * FROM table")
|
|
210
|
+
parent = Tree("root")
|
|
211
|
+
states = {}
|
|
212
|
+
|
|
213
|
+
node(view, ("default", "my_view"), parent, states)
|
|
214
|
+
|
|
215
|
+
assert len(parent.children) == 1
|
|
216
|
+
view_label = str(parent.children[0].label)
|
|
217
|
+
assert "my_view" in view_label
|
|
218
|
+
assert "SELECT" in view_label
|
|
219
|
+
assert "blue" in view_label
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def test_node_view_long_query():
|
|
223
|
+
"""Test node function with view that has long query."""
|
|
224
|
+
long_query = "SELECT * FROM table WHERE condition = true AND another_condition = false"
|
|
225
|
+
view = cfg.View(query=long_query)
|
|
226
|
+
parent = Tree("root")
|
|
227
|
+
states = {}
|
|
228
|
+
|
|
229
|
+
node(view, ("default", "my_view"), parent, states)
|
|
230
|
+
|
|
231
|
+
assert len(parent.children) == 1
|
|
232
|
+
view_label = str(parent.children[0].label)
|
|
233
|
+
# Should truncate query to 30 chars + "..."
|
|
234
|
+
assert "..." in view_label
|
|
235
|
+
assert len(long_query) > 30 # Verify our test query is actually long
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def test_progress_tree_context_manager():
|
|
239
|
+
"""Test progress_tree context manager."""
|
|
240
|
+
config = cfg.Config()
|
|
241
|
+
config["default"] = cfg.Namespace()
|
|
242
|
+
config["default"]["test_table"] = cfg.Table(uri="")
|
|
243
|
+
|
|
244
|
+
string_io = io.StringIO()
|
|
245
|
+
console = Console(file=string_io, force_terminal=True, width=80)
|
|
246
|
+
|
|
247
|
+
with progress_tree(config, console) as updater:
|
|
248
|
+
# Update state
|
|
249
|
+
updater(("default", "test_table"), "in_progress", percent=50)
|
|
250
|
+
time.sleep(0.1) # Give time for live display to render
|
|
251
|
+
|
|
252
|
+
updater(("default", "test_table"), "complete")
|
|
253
|
+
time.sleep(0.1)
|
|
254
|
+
|
|
255
|
+
# Verify output was generated
|
|
256
|
+
output = string_io.getvalue()
|
|
257
|
+
assert len(output) > 0
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def test_progress_tree_updater_with_error():
|
|
261
|
+
"""Test progress_tree updater with error."""
|
|
262
|
+
config = cfg.Config()
|
|
263
|
+
config["default"] = cfg.Namespace()
|
|
264
|
+
config["default"]["test_table"] = cfg.Table(uri="")
|
|
265
|
+
|
|
266
|
+
string_io = io.StringIO()
|
|
267
|
+
console = Console(file=string_io, force_terminal=True, width=80)
|
|
268
|
+
|
|
269
|
+
with progress_tree(config, console) as updater:
|
|
270
|
+
updater(("default", "test_table"), "complete", error="Test error")
|
|
271
|
+
time.sleep(0.1)
|
|
272
|
+
|
|
273
|
+
output = string_io.getvalue()
|
|
274
|
+
assert len(output) > 0
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def test_progress_bars_context_manager():
|
|
278
|
+
"""Test progress_bars context manager."""
|
|
279
|
+
config = cfg.Config()
|
|
280
|
+
config["default"] = cfg.Namespace()
|
|
281
|
+
config["default"]["dataset1"] = cfg.Dataset(repo="org/repo1", config="default")
|
|
282
|
+
config["default"]["dataset2"] = cfg.Dataset(repo="org/repo2", config="default")
|
|
283
|
+
|
|
284
|
+
identifiers = [("default", "dataset1"), ("default", "dataset2")]
|
|
285
|
+
|
|
286
|
+
string_io = io.StringIO()
|
|
287
|
+
console = Console(file=string_io, force_terminal=True, width=80)
|
|
288
|
+
|
|
289
|
+
with progress_bars(config, console, identifiers) as updater:
|
|
290
|
+
# Update progress for first dataset
|
|
291
|
+
updater(("default", "dataset1"), "in_progress", percent=30, stage="Processing")
|
|
292
|
+
time.sleep(0.1)
|
|
293
|
+
|
|
294
|
+
# Update progress for second dataset
|
|
295
|
+
updater(("default", "dataset2"), "in_progress", percent=60, stage="Almost done")
|
|
296
|
+
time.sleep(0.1)
|
|
297
|
+
|
|
298
|
+
# Complete both
|
|
299
|
+
updater(("default", "dataset1"), "complete")
|
|
300
|
+
updater(("default", "dataset2"), "complete")
|
|
301
|
+
time.sleep(0.1)
|
|
302
|
+
|
|
303
|
+
output = string_io.getvalue()
|
|
304
|
+
assert len(output) > 0
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def test_progress_bars_updater_stages():
|
|
308
|
+
"""Test progress_bars updater with different stages."""
|
|
309
|
+
config = cfg.Config()
|
|
310
|
+
config["default"] = cfg.Namespace()
|
|
311
|
+
config["default"]["test"] = cfg.Dataset(repo="org/repo", config="default")
|
|
312
|
+
|
|
313
|
+
identifiers = [("default", "test")]
|
|
314
|
+
|
|
315
|
+
string_io = io.StringIO()
|
|
316
|
+
console = Console(file=string_io, force_terminal=True, width=80)
|
|
317
|
+
|
|
318
|
+
with progress_bars(config, console, identifiers) as updater:
|
|
319
|
+
updater(("default", "test"), "pending")
|
|
320
|
+
time.sleep(0.05)
|
|
321
|
+
|
|
322
|
+
updater(("default", "test"), "in_progress", percent=50, stage="Downloading")
|
|
323
|
+
time.sleep(0.05)
|
|
324
|
+
|
|
325
|
+
updater(("default", "test"), "complete", percent=100)
|
|
326
|
+
time.sleep(0.05)
|
|
327
|
+
|
|
328
|
+
output = string_io.getvalue()
|
|
329
|
+
assert len(output) > 0
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def test_nested_namespaces():
|
|
333
|
+
"""Test tree building with nested namespaces."""
|
|
334
|
+
config = cfg.Config()
|
|
335
|
+
config["level1"] = cfg.Namespace()
|
|
336
|
+
config["level1"]["level2"] = cfg.Namespace()
|
|
337
|
+
config["level1"]["level2"]["table"] = cfg.Table(uri="")
|
|
338
|
+
|
|
339
|
+
result = tree(config)
|
|
340
|
+
|
|
341
|
+
assert isinstance(result, Tree)
|
|
342
|
+
# Navigate through the hierarchy
|
|
343
|
+
level1_node = result.children[0]
|
|
344
|
+
assert "level1" in str(level1_node.label)
|
|
345
|
+
|
|
346
|
+
level2_node = level1_node.children[0]
|
|
347
|
+
assert "level2" in str(level2_node.label)
|
|
348
|
+
|
|
349
|
+
table_node = level2_node.children[0]
|
|
350
|
+
assert "table" in str(table_node.label)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def test_multiple_tables_in_namespace():
|
|
354
|
+
"""Test tree building with multiple tables in a namespace."""
|
|
355
|
+
config = cfg.Config()
|
|
356
|
+
config["default"] = cfg.Namespace()
|
|
357
|
+
config["default"]["table1"] = cfg.Table(uri="")
|
|
358
|
+
config["default"]["table2"] = cfg.Table(uri="")
|
|
359
|
+
config["default"]["table3"] = cfg.Table(uri="")
|
|
360
|
+
|
|
361
|
+
result = tree(config)
|
|
362
|
+
|
|
363
|
+
assert isinstance(result, Tree)
|
|
364
|
+
namespace_node = result.children[0]
|
|
365
|
+
# Should have three children (the tables)
|
|
366
|
+
assert len(namespace_node.children) == 3
|