laketower 0.6.4__tar.gz → 0.6.5__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.

Potentially problematic release.


This version of laketower might be problematic. Click here for more details.

Files changed (74) hide show
  1. {laketower-0.6.4 → laketower-0.6.5}/.github/workflows/ci-cd.yml +6 -6
  2. {laketower-0.6.4 → laketower-0.6.5}/CHANGELOG.md +21 -1
  3. {laketower-0.6.4 → laketower-0.6.5}/PKG-INFO +14 -1
  4. {laketower-0.6.4 → laketower-0.6.5}/README.md +13 -0
  5. laketower-0.6.5/docs/static/queries_view.png +0 -0
  6. laketower-0.6.5/docs/static/tables_query.png +0 -0
  7. laketower-0.6.5/laketower/__about__.py +1 -0
  8. {laketower-0.6.4 → laketower-0.6.5}/laketower/cli.py +43 -4
  9. {laketower-0.6.4 → laketower-0.6.5}/laketower/config.py +10 -0
  10. laketower-0.6.5/laketower/static/datatables.bundle.js +27931 -0
  11. laketower-0.6.5/laketower/static/datatables.js +55 -0
  12. laketower-0.6.5/laketower/static/vendor/datatables.net-bs5/dataTables.bootstrap5.css +610 -0
  13. laketower-0.6.5/laketower/static/vendor/datatables.net-columncontrol-bs5/columnControl.bootstrap5.min.css +1 -0
  14. {laketower-0.6.4 → laketower-0.6.5}/laketower/tables.py +30 -2
  15. {laketower-0.6.4 → laketower-0.6.5}/laketower/templates/_base.html +14 -2
  16. {laketower-0.6.4 → laketower-0.6.5}/laketower/templates/queries/view.html +24 -2
  17. {laketower-0.6.4 → laketower-0.6.5}/laketower/templates/tables/query.html +20 -4
  18. {laketower-0.6.4 → laketower-0.6.5}/laketower/templates/tables/view.html +38 -38
  19. {laketower-0.6.4 → laketower-0.6.5}/laketower/web.py +38 -6
  20. {laketower-0.6.4 → laketower-0.6.5}/package-lock.json +142 -1
  21. {laketower-0.6.4 → laketower-0.6.5}/package.json +6 -1
  22. {laketower-0.6.4 → laketower-0.6.5}/pyproject.toml +5 -5
  23. {laketower-0.6.4 → laketower-0.6.5}/tasks.py +28 -8
  24. {laketower-0.6.4 → laketower-0.6.5}/tests/conftest.py +7 -1
  25. {laketower-0.6.4 → laketower-0.6.5}/tests/test_cli.py +67 -0
  26. {laketower-0.6.4 → laketower-0.6.5}/tests/test_config.py +5 -0
  27. {laketower-0.6.4 → laketower-0.6.5}/tests/test_tables.py +82 -0
  28. {laketower-0.6.4 → laketower-0.6.5}/tests/test_web.py +142 -4
  29. laketower-0.6.5/uv.lock +1795 -0
  30. laketower-0.6.4/docs/static/queries_view.png +0 -0
  31. laketower-0.6.4/docs/static/tables_query.png +0 -0
  32. laketower-0.6.4/laketower/__about__.py +0 -1
  33. laketower-0.6.4/uv.lock +0 -1782
  34. {laketower-0.6.4 → laketower-0.6.5}/.gitignore +0 -0
  35. {laketower-0.6.4 → laketower-0.6.5}/.python-version +0 -0
  36. {laketower-0.6.4 → laketower-0.6.5}/LICENSE +0 -0
  37. {laketower-0.6.4 → laketower-0.6.5}/demo/generate.py +0 -0
  38. {laketower-0.6.4 → laketower-0.6.5}/demo/laketower.yml +0 -0
  39. {laketower-0.6.4 → laketower-0.6.5}/demo/sample_table/_delta_log/00000000000000000000.json +0 -0
  40. {laketower-0.6.4 → laketower-0.6.5}/demo/sample_table/_delta_log/00000000000000000001.json +0 -0
  41. {laketower-0.6.4 → laketower-0.6.5}/demo/sample_table/_delta_log/00000000000000000002.json +0 -0
  42. {laketower-0.6.4 → laketower-0.6.5}/demo/sample_table/_delta_log/00000000000000000003.json +0 -0
  43. {laketower-0.6.4 → laketower-0.6.5}/demo/sample_table/part-00001-1a31a393-6db6-4d1a-bf4e-81ea061ff8cd-c000.snappy.parquet +0 -0
  44. {laketower-0.6.4 → laketower-0.6.5}/demo/sample_table/part-00001-5af77102-9207-4c89-aaf6-37e1f815ec26-c000.snappy.parquet +0 -0
  45. {laketower-0.6.4 → laketower-0.6.5}/demo/sample_table/part-00001-b11bab55-43d0-4d05-ae88-5b9481ae57db-c000.snappy.parquet +0 -0
  46. {laketower-0.6.4 → laketower-0.6.5}/demo/weather/_delta_log/00000000000000000000.json +0 -0
  47. {laketower-0.6.4 → laketower-0.6.5}/demo/weather/_delta_log/00000000000000000001.json +0 -0
  48. {laketower-0.6.4 → laketower-0.6.5}/demo/weather/_delta_log/00000000000000000002.json +0 -0
  49. {laketower-0.6.4 → laketower-0.6.5}/demo/weather/part-00001-2323b963-be56-44e0-8c10-e237e7e6d4b9-c000.snappy.parquet +0 -0
  50. {laketower-0.6.4 → laketower-0.6.5}/demo/weather/part-00001-6360cbf8-f8a9-475f-8729-6f20b4ca64a9-c000.snappy.parquet +0 -0
  51. {laketower-0.6.4 → laketower-0.6.5}/docs/static/tables_history.png +0 -0
  52. {laketower-0.6.4 → laketower-0.6.5}/docs/static/tables_import.png +0 -0
  53. {laketower-0.6.4 → laketower-0.6.5}/docs/static/tables_overview.png +0 -0
  54. {laketower-0.6.4 → laketower-0.6.5}/docs/static/tables_statistics.png +0 -0
  55. {laketower-0.6.4 → laketower-0.6.5}/docs/static/tables_view.png +0 -0
  56. {laketower-0.6.4 → laketower-0.6.5}/laketower/__init__.py +0 -0
  57. {laketower-0.6.4 → laketower-0.6.5}/laketower/__main__.py +0 -0
  58. {laketower-0.6.4 → laketower-0.6.5}/laketower/static/.gitkeep +0 -0
  59. {laketower-0.6.4 → laketower-0.6.5}/laketower/static/editor.bundle.js +0 -0
  60. {laketower-0.6.4 → laketower-0.6.5}/laketower/static/editor.js +0 -0
  61. {laketower-0.6.4 → laketower-0.6.5}/laketower/static/vendor/bootstrap/bootstrap.bundle.min.js +0 -0
  62. {laketower-0.6.4 → laketower-0.6.5}/laketower/static/vendor/bootstrap-icons/bootstrap-icons.min.css +0 -0
  63. {laketower-0.6.4 → laketower-0.6.5}/laketower/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
  64. {laketower-0.6.4 → laketower-0.6.5}/laketower/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
  65. {laketower-0.6.4 → laketower-0.6.5}/laketower/static/vendor/halfmoon/halfmoon.min.css +0 -0
  66. {laketower-0.6.4 → laketower-0.6.5}/laketower/static/vendor/halfmoon/halfmoon.modern.css +0 -0
  67. {laketower-0.6.4 → laketower-0.6.5}/laketower/templates/index.html +0 -0
  68. {laketower-0.6.4 → laketower-0.6.5}/laketower/templates/tables/_macros.html +0 -0
  69. {laketower-0.6.4 → laketower-0.6.5}/laketower/templates/tables/history.html +0 -0
  70. {laketower-0.6.4 → laketower-0.6.5}/laketower/templates/tables/import.html +0 -0
  71. {laketower-0.6.4 → laketower-0.6.5}/laketower/templates/tables/index.html +0 -0
  72. {laketower-0.6.4 → laketower-0.6.5}/laketower/templates/tables/statistics.html +0 -0
  73. {laketower-0.6.4 → laketower-0.6.5}/renovate.json +0 -0
  74. {laketower-0.6.4 → laketower-0.6.5}/tests/__init__.py +0 -0
@@ -29,7 +29,7 @@ jobs:
29
29
  run: uv run inv qa
30
30
  - name: Upload coverage artifacts
31
31
  if: ${{ matrix.python-version == 3.13 }}
32
- uses: actions/upload-artifact@v4
32
+ uses: actions/upload-artifact@v5
33
33
  with:
34
34
  name: coverage
35
35
  path: .coverage
@@ -59,7 +59,7 @@ jobs:
59
59
  cache-dependency-glob: "uv.lock"
60
60
  - name: Install dependencies
61
61
  run: uv sync --all-extras --dev
62
- - uses: actions/download-artifact@v5
62
+ - uses: actions/download-artifact@v6
63
63
  with:
64
64
  name: coverage
65
65
  - name: Generate Markdown code coverage report
@@ -136,7 +136,7 @@ jobs:
136
136
  - name: Build package
137
137
  run: uv build
138
138
  - name: Upload package build artifacts
139
- uses: actions/upload-artifact@v4
139
+ uses: actions/upload-artifact@v5
140
140
  with:
141
141
  name: build
142
142
  path: dist
@@ -154,7 +154,7 @@ jobs:
154
154
  id-token: write
155
155
  steps:
156
156
  - name: Download package build artifacts
157
- uses: actions/download-artifact@v5
157
+ uses: actions/download-artifact@v6
158
158
  with:
159
159
  name: build
160
160
  path: dist/
@@ -180,7 +180,7 @@ jobs:
180
180
  id-token: write
181
181
  steps:
182
182
  - name: Download package build artifacts
183
- uses: actions/download-artifact@v5
183
+ uses: actions/download-artifact@v6
184
184
  with:
185
185
  name: build
186
186
  path: dist/
@@ -204,7 +204,7 @@ jobs:
204
204
  steps:
205
205
  - uses: actions/checkout@v5
206
206
  - name: Download package build artifacts
207
- uses: actions/download-artifact@v5
207
+ uses: actions/download-artifact@v6
208
208
  with:
209
209
  name: build
210
210
  path: dist/
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.5] - 2025-11-11
11
+ Patch release with minor enhancements (interactive SQL query results, enforced
12
+ row limits for large tables, query execution time indicator) and many minor fixes.
13
+
14
+ ### Added
15
+ - new `settings` section in YAML configuration
16
+ - setting `max_query_rows` to limit sql query results
17
+ - setting `web.hide_tables` to hide the tables section for the web application
18
+ - query execution time indicator
19
+
20
+ ### Changed
21
+ - web: use `datatables.js` for interactive query results
22
+
23
+ ### Fixed
24
+ - web: table view hide column button link regression
25
+ - web: exclude actions from table data view horizontal scrolling
26
+ - web: handle sql query parsing errors
27
+ - web: do not highlight multiple menu items sharing the same path prefix
28
+
10
29
  ## [0.6.4] - 2025-10-20
11
30
  ### Fixed
12
31
  - add missing `tzdata` dependency from previous `pandas` dependency removal
@@ -152,7 +171,8 @@ Initial release of `laketower`.
152
171
  - View a given table with simple query builder
153
172
  - Query all registered tables with DuckDB SQL dialect
154
173
 
155
- [Unreleased]: https://github.com/datalpia/laketower/compare/0.6.4...HEAD
174
+ [Unreleased]: https://github.com/datalpia/laketower/compare/0.6.5...HEAD
175
+ [0.6.5]: https://github.com/datalpia/laketower/compare/0.6.4...0.6.5
156
176
  [0.6.4]: https://github.com/datalpia/laketower/compare/0.6.3...0.6.4
157
177
  [0.6.3]: https://github.com/datalpia/laketower/compare/0.6.2...0.6.3
158
178
  [0.6.2]: https://github.com/datalpia/laketower/compare/0.6.1...0.6.2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: laketower
3
- Version: 0.6.4
3
+ Version: 0.6.5
4
4
  Summary: Oversee your lakehouse
5
5
  Project-URL: Repository, https://github.com/datalpia/laketower
6
6
  Project-URL: Issues, https://github.com/datalpia/laketower/issues
@@ -92,6 +92,11 @@ Laketower configuration is based on a static YAML configuration file allowing to
92
92
  Format:
93
93
 
94
94
  ```yaml
95
+ settings:
96
+ max_query_rows: 1000
97
+ web:
98
+ hide_tables: false
99
+
95
100
  tables:
96
101
  - name: <table_name>
97
102
  uri: <local path to table>
@@ -531,6 +536,8 @@ $ laketower -c demo/laketower.yml tables query "select date_trunc('day', time) a
531
536
  │ 2025-02-11 00:00:00+01:00 │ 4.833333373069763 │
532
537
  │ 2025-02-10 00:00:00+01:00 │ 2.1083333243926368 │
533
538
  └───────────────────────────┴────────────────────┘
539
+ 3 rows returned
540
+ Execution time: 33.72ms
534
541
  ```
535
542
 
536
543
  Use named parameters within a giving query (note: escape `$` prefixes properly!):
@@ -545,6 +552,8 @@ $ laketower -c demo/laketower.yml tables query "select date_trunc('day', time) a
545
552
  │ 2025-01-30 00:00:00+01:00 │ 8.900000015894571 │
546
553
  │ 2025-01-29 00:00:00+01:00 │ 7.770833313465118 │
547
554
  └───────────────────────────┴────────────────────┘
555
+ 4 rows returned
556
+ Execution time: 30.59ms
548
557
  ```
549
558
 
550
559
  Export query results to CSV:
@@ -592,6 +601,8 @@ $ laketower -c demo/laketower.yml queries view daily_avg_temperature
592
601
  │ 2025-02-11 00:00:00+01:00 │ 5.0 │
593
602
  │ 2025-02-12 00:00:00+01:00 │ 5.0 │
594
603
  └───────────────────────────┴─────────────────┘
604
+ 18 rows returned
605
+ Execution time: 39.52ms
595
606
  ```
596
607
 
597
608
  Executing a predefined query with parameters (here `start_date` and `end_date`):
@@ -608,6 +619,8 @@ $ laketower -c demo/laketower.yml queries view daily_avg_temperature_params -p s
608
619
  │ 2025-02-04 00:00:00+01:00 │ 3.0 │
609
620
  │ 2025-02-05 00:00:00+01:00 │ 3.0 │
610
621
  └───────────────────────────┴─────────────────┘
622
+ 6 rows returned
623
+ Execution time: 32.08ms
611
624
  ```
612
625
 
613
626
  ## License
@@ -51,6 +51,11 @@ Laketower configuration is based on a static YAML configuration file allowing to
51
51
  Format:
52
52
 
53
53
  ```yaml
54
+ settings:
55
+ max_query_rows: 1000
56
+ web:
57
+ hide_tables: false
58
+
54
59
  tables:
55
60
  - name: <table_name>
56
61
  uri: <local path to table>
@@ -490,6 +495,8 @@ $ laketower -c demo/laketower.yml tables query "select date_trunc('day', time) a
490
495
  │ 2025-02-11 00:00:00+01:00 │ 4.833333373069763 │
491
496
  │ 2025-02-10 00:00:00+01:00 │ 2.1083333243926368 │
492
497
  └───────────────────────────┴────────────────────┘
498
+ 3 rows returned
499
+ Execution time: 33.72ms
493
500
  ```
494
501
 
495
502
  Use named parameters within a giving query (note: escape `$` prefixes properly!):
@@ -504,6 +511,8 @@ $ laketower -c demo/laketower.yml tables query "select date_trunc('day', time) a
504
511
  │ 2025-01-30 00:00:00+01:00 │ 8.900000015894571 │
505
512
  │ 2025-01-29 00:00:00+01:00 │ 7.770833313465118 │
506
513
  └───────────────────────────┴────────────────────┘
514
+ 4 rows returned
515
+ Execution time: 30.59ms
507
516
  ```
508
517
 
509
518
  Export query results to CSV:
@@ -551,6 +560,8 @@ $ laketower -c demo/laketower.yml queries view daily_avg_temperature
551
560
  │ 2025-02-11 00:00:00+01:00 │ 5.0 │
552
561
  │ 2025-02-12 00:00:00+01:00 │ 5.0 │
553
562
  └───────────────────────────┴─────────────────┘
563
+ 18 rows returned
564
+ Execution time: 39.52ms
554
565
  ```
555
566
 
556
567
  Executing a predefined query with parameters (here `start_date` and `end_date`):
@@ -567,6 +578,8 @@ $ laketower -c demo/laketower.yml queries view daily_avg_temperature_params -p s
567
578
  │ 2025-02-04 00:00:00+01:00 │ 3.0 │
568
579
  │ 2025-02-05 00:00:00+01:00 │ 3.0 │
569
580
  └───────────────────────────┴─────────────────┘
581
+ 6 rows returned
582
+ Execution time: 32.08ms
570
583
  ```
571
584
 
572
585
  ## License
@@ -0,0 +1 @@
1
+ __version__ = "0.6.5"
@@ -1,9 +1,11 @@
1
1
  import argparse
2
2
  import os
3
+ import time
3
4
  from pathlib import Path
4
5
 
5
6
  import rich.jupyter
6
7
  import rich.panel
8
+ import rich.style
7
9
  import rich.table
8
10
  import rich.text
9
11
  import rich.tree
@@ -20,6 +22,7 @@ from laketower.tables import (
20
22
  generate_table_query,
21
23
  generate_table_statistics_query,
22
24
  import_file_to_table,
25
+ limit_query,
23
26
  load_datasets,
24
27
  load_table,
25
28
  )
@@ -194,9 +197,27 @@ def query_table(
194
197
  query_params = {
195
198
  name: sql_params_dict.get(name) or "" for name in query_param_names
196
199
  }
197
- results = execute_query(tables_dataset, sql_query, sql_params=query_params)
200
+ limited_sql_query = limit_query(sql_query, config.settings.max_query_rows + 1)
198
201
 
199
- out = rich.table.Table()
202
+ start_time = time.perf_counter()
203
+ results = execute_query(
204
+ tables_dataset, limited_sql_query, sql_params=query_params
205
+ )
206
+ execution_time_ms = (time.perf_counter() - start_time) * 1000
207
+
208
+ truncated = results.num_rows > config.settings.max_query_rows
209
+ results = results.slice(
210
+ 0, min(results.num_rows, config.settings.max_query_rows)
211
+ )
212
+
213
+ out = rich.table.Table(
214
+ caption=(
215
+ f"{results.num_rows} rows returned{' (truncated)' if truncated else ''}"
216
+ f"\nExecution time: {execution_time_ms:.2f}ms"
217
+ ),
218
+ caption_justify="left",
219
+ caption_style=rich.style.Style(dim=True),
220
+ )
200
221
  for column in results.column_names:
201
222
  out.add_column(column)
202
223
  for row_dict in results.to_pylist():
@@ -268,9 +289,27 @@ def view_query(
268
289
  name: query_params_dict.get(name) or default_parameters.get(name) or ""
269
290
  for name in sql_param_names
270
291
  }
271
- results = execute_query(tables_dataset, sql_query, sql_params=sql_params)
292
+ limited_sql_query = limit_query(sql_query, config.settings.max_query_rows + 1)
272
293
 
273
- out = rich.table.Table()
294
+ start_time = time.perf_counter()
295
+ results = execute_query(
296
+ tables_dataset, limited_sql_query, sql_params=sql_params
297
+ )
298
+ execution_time_ms = (time.perf_counter() - start_time) * 1000
299
+
300
+ truncated = results.num_rows > config.settings.max_query_rows
301
+ results = results.slice(
302
+ 0, min(results.num_rows, config.settings.max_query_rows)
303
+ )
304
+
305
+ out = rich.table.Table(
306
+ caption=(
307
+ f"{results.num_rows} rows returned{' (truncated)' if truncated else ''}"
308
+ f"\nExecution time: {execution_time_ms:.2f}ms"
309
+ ),
310
+ caption_justify="left",
311
+ caption_style=rich.style.Style(dim=True),
312
+ )
274
313
  for column in results.column_names:
275
314
  out.add_column(column)
276
315
  for row_dict in results.to_pylist():
@@ -89,6 +89,15 @@ class ConfigTableConnection(pydantic.BaseModel):
89
89
  return self
90
90
 
91
91
 
92
+ class ConfigSettingsWeb(pydantic.BaseModel):
93
+ hide_tables: bool = False
94
+
95
+
96
+ class ConfigSettings(pydantic.BaseModel):
97
+ max_query_rows: int = 1_000
98
+ web: ConfigSettingsWeb = ConfigSettingsWeb()
99
+
100
+
92
101
  class ConfigTable(pydantic.BaseModel):
93
102
  name: str
94
103
  uri: str
@@ -109,6 +118,7 @@ class ConfigQuery(pydantic.BaseModel):
109
118
 
110
119
 
111
120
  class Config(pydantic.BaseModel):
121
+ settings: ConfigSettings = ConfigSettings()
112
122
  tables: list[ConfigTable] = []
113
123
  queries: list[ConfigQuery] = []
114
124