laketower 0.6.2__py3-none-any.whl → 0.6.3__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 laketower might be problematic. Click here for more details.

laketower/__about__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.6.2"
1
+ __version__ = "0.6.3"
laketower/cli.py CHANGED
@@ -8,6 +8,7 @@ import rich.table
8
8
  import rich.text
9
9
  import rich.tree
10
10
  import uvicorn
11
+ import pyarrow.csv as pacsv
11
12
 
12
13
  from laketower.__about__ import __version__
13
14
  from laketower.config import load_yaml_config
@@ -135,11 +136,10 @@ def table_statistics(
135
136
  results = execute_query({table_name: table_dataset}, sql_query)
136
137
 
137
138
  out = rich.table.Table()
138
- for column in results.columns:
139
+ for column in results.column_names:
139
140
  out.add_column(column)
140
- for value_list in results.to_numpy().tolist():
141
- row = [str(x) for x in value_list]
142
- out.add_row(*row)
141
+ for row_dict in results.to_pylist():
142
+ out.add_row(*[str(row_dict[col]) for col in results.column_names])
143
143
  except Exception as e:
144
144
  out = rich.panel.Panel.fit(f"[red]{e}")
145
145
 
@@ -168,11 +168,10 @@ def view_table(
168
168
  results = execute_query({table_name: table_dataset}, sql_query)
169
169
 
170
170
  out = rich.table.Table()
171
- for column in results.columns:
171
+ for column in results.column_names:
172
172
  out.add_column(column)
173
- for value_list in results.to_numpy().tolist():
174
- row = [str(x) for x in value_list]
175
- out.add_row(*row)
173
+ for row_dict in results.to_pylist():
174
+ out.add_row(*[str(row_dict[col]) for col in results.column_names])
176
175
  except Exception as e:
177
176
  out = rich.panel.Panel.fit(f"[red]{e}")
178
177
 
@@ -198,15 +197,16 @@ def query_table(
198
197
  results = execute_query(tables_dataset, sql_query, sql_params=query_params)
199
198
 
200
199
  out = rich.table.Table()
201
- for column in results.columns:
200
+ for column in results.column_names:
202
201
  out.add_column(column)
203
- for value_list in results.values.tolist():
204
- row = [str(x) for x in value_list]
205
- out.add_row(*row)
202
+ for row_dict in results.to_pylist():
203
+ out.add_row(*[str(row_dict[col]) for col in results.column_names])
206
204
 
207
205
  if output_path is not None:
208
- results.to_csv(
209
- output_path, header=True, index=False, sep=",", encoding="utf-8"
206
+ pacsv.write_csv(
207
+ results,
208
+ output_path,
209
+ pacsv.WriteOptions(include_header=True, delimiter=","),
210
210
  )
211
211
  out = rich.text.Text(f"Query results written to: {output_path}")
212
212
  except ValueError as e:
@@ -271,11 +271,10 @@ def view_query(
271
271
  results = execute_query(tables_dataset, sql_query, sql_params=sql_params)
272
272
 
273
273
  out = rich.table.Table()
274
- for column in results.columns:
274
+ for column in results.column_names:
275
275
  out.add_column(column)
276
- for value_list in results.values.tolist():
277
- row = [str(x) for x in value_list]
278
- out.add_row(*row)
276
+ for row_dict in results.to_pylist():
277
+ out.add_row(*[str(row_dict[col]) for col in results.column_names])
279
278
  except ValueError as e:
280
279
  out = rich.panel.Panel.fit(f"[red]{e}")
281
280
 
laketower/tables.py CHANGED
@@ -4,8 +4,8 @@ from typing import Any, BinaryIO, Protocol, TextIO
4
4
 
5
5
  import deltalake
6
6
  import duckdb
7
- import pandas as pd
8
7
  import pyarrow as pa
8
+ import pyarrow.csv as csv
9
9
  import pyarrow.dataset as padataset
10
10
  import pydantic
11
11
  import sqlglot
@@ -61,7 +61,7 @@ class TableProtocol(Protocol): # pragma: no cover
61
61
  def history(self) -> TableHistory: ...
62
62
  def dataset(self, version: int | str | None = None) -> padataset.Dataset: ...
63
63
  def import_data(
64
- self, data: pd.DataFrame, mode: ImportModeEnum = ImportModeEnum.append
64
+ self, data: pa.Table, mode: ImportModeEnum = ImportModeEnum.append
65
65
  ) -> None: ...
66
66
 
67
67
 
@@ -202,7 +202,7 @@ class DeltaTable:
202
202
  return self._impl.to_pyarrow_dataset()
203
203
 
204
204
  def import_data(
205
- self, data: pd.DataFrame, mode: ImportModeEnum = ImportModeEnum.append
205
+ self, data: pa.Table, mode: ImportModeEnum = ImportModeEnum.append
206
206
  ) -> None:
207
207
  deltalake.write_deltalake(
208
208
  self.table_config.uri, data, mode=mode.value, schema_mode="merge"
@@ -274,7 +274,7 @@ def execute_query(
274
274
  tables_datasets: dict[str, padataset.Dataset],
275
275
  sql_query: str,
276
276
  sql_params: dict[str, str] = {},
277
- ) -> pd.DataFrame:
277
+ ) -> pa.Table:
278
278
  if not sql_query:
279
279
  raise ValueError("Error: Cannot execute empty SQL query")
280
280
 
@@ -289,7 +289,7 @@ def execute_query(
289
289
  view_name = f"{table_name}_view"
290
290
  conn.register(view_name, table_dataset)
291
291
  conn.execute(f'create view "{table_name}" as select * from "{view_name}"') # nosec B608
292
- return conn.execute(sql_query, parameters=sql_params).df()
292
+ return conn.execute(sql_query, parameters=sql_params).fetch_arrow_table()
293
293
  except duckdb.Error as e:
294
294
  raise ValueError(str(e)) from e
295
295
 
@@ -303,7 +303,11 @@ def import_file_to_table(
303
303
  encoding: str = "utf-8",
304
304
  ) -> int:
305
305
  file_format_handler = {
306
- ImportFileFormatEnum.csv: lambda f, d, e: pd.read_csv(f, sep=d, encoding=e)
306
+ ImportFileFormatEnum.csv: lambda f, d, e: csv.read_csv(
307
+ f,
308
+ read_options=csv.ReadOptions(encoding=e),
309
+ parse_options=csv.ParseOptions(delimiter=d),
310
+ )
307
311
  }
308
312
  table = load_table(table_config)
309
313
  df = file_format_handler[file_format](file_path, delimiter, encoding)
@@ -68,14 +68,12 @@
68
68
  </ul>
69
69
 
70
70
  <div class="mt-auto pt-3 border-top flex-shrink-0">
71
- <small class="text-muted px-3 d-block">
72
- <a href="https://github.com/datalpia/laketower/releases/tag/{{ app_metadata.app_version }}"
73
- target="_blank"
74
- class="text-muted text-decoration-none">
75
- v{{ app_metadata.app_version }}
76
- <i class="bi-box-arrow-up-right ms-1" style="font-size: 0.7em;"></i>
77
- </a>
78
- </small>
71
+ <div class="px-3 d-block">
72
+ <button type="button" class="btn btn-link btn-sm text-muted text-decoration-none p-0" data-bs-toggle="modal" data-bs-target="#creditsModal">
73
+ <i class="bi-info-circle me-1"></i>
74
+ About
75
+ </button>
76
+ </div>
79
77
  </div>
80
78
  </div>
81
79
  </nav>
@@ -99,6 +97,72 @@
99
97
  </div>
100
98
  </main>
101
99
 
100
+ <div class="modal fade" id="creditsModal" tabindex="-1" aria-labelledby="creditsModalLabel" aria-hidden="true">
101
+ <div class="modal-dialog modal-dialog-centered">
102
+ <div class="modal-content">
103
+ <div class="modal-header">
104
+ <h5 class="modal-title" id="creditsModalLabel">About</h5>
105
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
106
+ </div>
107
+ <div class="modal-body">
108
+ <div class="text-center mb-3">
109
+ <h4>{{ app_metadata.app_name }}</h4>
110
+ <p class="text-muted mb-0">Oversee your lakehouse</p>
111
+ </div>
112
+
113
+ <dl class="row mb-3">
114
+ <dt class="col-sm-4">Version</dt>
115
+ <dd class="col-sm-8">
116
+ <a href="https://github.com/datalpia/laketower/releases/tag/{{ app_metadata.app_version }}"
117
+ target="_blank"
118
+ class="text-decoration-none">
119
+ {{ app_metadata.app_version }}
120
+ <i class="bi-box-arrow-up-right ms-1" style="font-size: 0.8em;"></i>
121
+ </a>
122
+ </dd>
123
+
124
+ <dt class="col-sm-4">Repository</dt>
125
+ <dd class="col-sm-8">
126
+ <a href="https://github.com/datalpia/laketower"
127
+ target="_blank"
128
+ class="text-decoration-none">
129
+ github.com/datalpia/laketower
130
+ <i class="bi-box-arrow-up-right ms-1" style="font-size: 0.8em;"></i>
131
+ </a>
132
+ </dd>
133
+
134
+ <dt class="col-sm-4">Issue Tracker</dt>
135
+ <dd class="col-sm-8">
136
+ <a href="https://github.com/datalpia/laketower/issues"
137
+ target="_blank"
138
+ class="text-decoration-none">
139
+ Report an issue
140
+ <i class="bi-box-arrow-up-right ms-1" style="font-size: 0.8em;"></i>
141
+ </a>
142
+ </dd>
143
+
144
+ <dt class="col-sm-4">License</dt>
145
+ <dd class="col-sm-8">
146
+ <a href="https://github.com/datalpia/laketower/blob/main/LICENSE"
147
+ target="_blank"
148
+ class="text-decoration-none">
149
+ Apache License 2.0
150
+ <i class="bi-box-arrow-up-right ms-1" style="font-size: 0.8em;"></i>
151
+ </a>
152
+ </dd>
153
+ </dl>
154
+
155
+ <div class="text-center text-muted">
156
+ <small>Copyright © 2025 Romain Clement / Datalpia</small>
157
+ </div>
158
+ </div>
159
+ <div class="modal-footer">
160
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ </div>
165
+
102
166
  <script src="{{ url_for('static', path='/vendor/bootstrap/bootstrap.bundle.min.js') }}"></script>
103
167
  {% block extra_scripts %}{% endblock %}
104
168
  </body>
@@ -58,16 +58,16 @@
58
58
  <table class="table table-sm table-bordered table-striped table-hover">
59
59
  <thead>
60
60
  <tr>
61
- {% for column in query_results.columns %}
61
+ {% for column in query_results.column_names %}
62
62
  <th>{{ column }}</th>
63
63
  {% endfor %}
64
64
  </tr>
65
65
  </thead>
66
66
  <tbody class="table-group-divider">
67
- {% for row in query_results.to_numpy().tolist() %}
67
+ {% for row in query_results.to_pylist() %}
68
68
  <tr>
69
- {% for col in row %}
70
- <td>{{ col }}</td>
69
+ {% for column in query_results.column_names %}
70
+ <td>{{ row[column] }}</td>
71
71
  {% endfor %}
72
72
  </tr>
73
73
  {% endfor %}
@@ -47,16 +47,16 @@
47
47
  <table class="table table-sm table-bordered table-striped table-hover">
48
48
  <thead>
49
49
  <tr>
50
- {% for column in table_results.columns %}
50
+ {% for column in table_results.column_names %}
51
51
  <th>{{ column }}</th>
52
52
  {% endfor %}
53
53
  </tr>
54
54
  </thead>
55
55
  <tbody class="table-group-divider">
56
- {% for row in table_results.to_numpy().tolist() %}
56
+ {% for row in table_results.to_pylist() %}
57
57
  <tr>
58
- {% for col in row %}
59
- <td>{{ col }}</td>
58
+ {% for column in table_results.column_names %}
59
+ <td>{{ row[column] }}</td>
60
60
  {% endfor %}
61
61
  </tr>
62
62
  {% endfor %}
@@ -15,16 +15,16 @@
15
15
  <table class="table table-sm table-bordered table-striped table-hover">
16
16
  <thead>
17
17
  <tr>
18
- {% for column in table_results.columns %}
18
+ {% for column in table_results.column_names %}
19
19
  <th>{{ column }}</th>
20
20
  {% endfor %}
21
21
  </tr>
22
22
  </thead>
23
23
  <tbody class="table-group-divider">
24
- {% for row in table_results.to_numpy().tolist() %}
24
+ {% for row in table_results.to_pylist() %}
25
25
  <tr>
26
- {% for col in row %}
27
- <td>{{ col }}</td>
26
+ {% for column in table_results.column_names %}
27
+ <td>{{ row[column] }}</td>
28
28
  {% endfor %}
29
29
  </tr>
30
30
  {% endfor %}
@@ -15,7 +15,7 @@
15
15
  <table class="table table-sm table-bordered table-striped table-hover">
16
16
  <thead>
17
17
  <tr>
18
- {% for column in table_results.columns %}
18
+ {% for column in table_results.column_names %}
19
19
  <th>
20
20
  {{ column }}
21
21
  {% if column == request.query_params.sort_asc %}
@@ -44,10 +44,10 @@
44
44
  </tr>
45
45
  </thead>
46
46
  <tbody class="table-group-divider">
47
- {% for row in table_results.to_numpy().tolist() %}
47
+ {% for row in table_results.to_pylist() %}
48
48
  <tr>
49
- {% for col in row %}
50
- <td>{{ col }}</td>
49
+ {% for column in table_results.column_names %}
50
+ <td>{{ row[column] }}</td>
51
51
  {% endfor %}
52
52
  </tr>
53
53
  {% endfor %}
laketower/web.py CHANGED
@@ -1,3 +1,4 @@
1
+ import io
1
2
  import urllib.parse
2
3
  from dataclasses import dataclass
3
4
  from pathlib import Path
@@ -5,6 +6,7 @@ from typing import Annotated
5
6
 
6
7
  import bleach
7
8
  import markdown
9
+ import pyarrow.csv as pacsv
8
10
  import pydantic_settings
9
11
  from fastapi import APIRouter, FastAPI, File, Form, Query, Request, UploadFile
10
12
  from fastapi.responses import HTMLResponse, RedirectResponse, Response
@@ -119,10 +121,13 @@ def export_tables_query_csv(request: Request, sql: str) -> Response:
119
121
  tables_dataset = load_datasets(config.tables)
120
122
 
121
123
  results = execute_query(tables_dataset, sql)
122
- csv_content = results.to_csv(header=True, index=False, sep=",")
124
+ csv_content = io.BytesIO()
125
+ pacsv.write_csv(
126
+ results, csv_content, pacsv.WriteOptions(include_header=True, delimiter=",")
127
+ )
123
128
 
124
129
  return Response(
125
- content=csv_content,
130
+ content=csv_content.getvalue(),
126
131
  media_type="text/csv",
127
132
  headers={"Content-Disposition": "attachment; filename=query_results.csv"},
128
133
  )
@@ -415,7 +420,7 @@ def create_app() -> FastAPI:
415
420
  )
416
421
  app.include_router(router)
417
422
  app.state.app_metadata = AppMetadata(
418
- app_name="Laketower", app_version=__about__.__version__
423
+ app_name="🗼 Laketower", app_version=__about__.__version__
419
424
  )
420
425
  app.state.config = config
421
426
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: laketower
3
- Version: 0.6.2
3
+ Version: 0.6.3
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
@@ -28,7 +28,6 @@ Requires-Dist: duckdb
28
28
  Requires-Dist: fastapi
29
29
  Requires-Dist: jinja2!=3.1.5,>=3
30
30
  Requires-Dist: markdown
31
- Requires-Dist: pandas
32
31
  Requires-Dist: pyarrow!=19.0.0
33
32
  Requires-Dist: pydantic-settings>=2
34
33
  Requires-Dist: pydantic>=2
@@ -39,7 +38,7 @@ Requires-Dist: sqlglot
39
38
  Requires-Dist: uvicorn
40
39
  Description-Content-Type: text/markdown
41
40
 
42
- # Laketower
41
+ # 🗼 Laketower
43
42
 
44
43
  > Oversee your lakehouse
45
44
 
@@ -614,4 +613,4 @@ $ laketower -c demo/laketower.yml queries view daily_avg_temperature_params -p s
614
613
 
615
614
  Licensed under [Apache License 2.0](LICENSE)
616
615
 
617
- Copyright (c) 2025 - present Romain Clement
616
+ Copyright (c) 2025 - present Romain Clement / Datalpia
@@ -1,10 +1,10 @@
1
- laketower/__about__.py,sha256=jFlbxEJFS0G44LE-yXXVSwXACA1J_NyYDk5E20_2zpc,22
1
+ laketower/__about__.py,sha256=zYiFHqR7JwbvdK9dvKrh-RTNfUqjHUwC4CTcFAPVYLc,22
2
2
  laketower/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  laketower/__main__.py,sha256=czKxJKG8OfncnxWmpaOWx7b1JBwFnZNQi7wKSTncB4M,108
4
- laketower/cli.py,sha256=QmfgqpQQIL5pyrgmnCvLiNUNcAKW6a1vnS6qTUfYcBk,16554
4
+ laketower/cli.py,sha256=2oISOgHy1CpJLVwReVNUQSCHcrgV-dPSPmGdPKwqc1o,16624
5
5
  laketower/config.py,sha256=flRp9yb4DK4xQUJu5g6Mr_gSMtTyJ4_jBAb-08ROvqQ,3453
6
- laketower/tables.py,sha256=5SbN35LjNbxEYe_pybpb1m5joaFa1bcydtXQPSWJPUo,10732
7
- laketower/web.py,sha256=B77Zu2aUO3sLlaHoSSO8x8iV-BoVh2l5_Yg9bxu5l6A,13407
6
+ laketower/tables.py,sha256=AiWCMBCY3TnX1aeLwzE0rEUdTALpMHrDXy9Bz5fjv7c,10857
7
+ laketower/web.py,sha256=e_qGbMR80zZaAY1YwEpqe2bmNEDZw3YQf7OEWjrcIkM,13536
8
8
  laketower/static/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  laketower/static/editor.bundle.js,sha256=Wa1bS0xWwKDjHKPTdXd-PX41JAGkCjd0VLBCF-SyXWk,1159343
10
10
  laketower/static/editor.js,sha256=C8saQJH68X7CtdRyBmvrQxsDJ013YWoMfWF-6hzf_5s,15870
@@ -14,18 +14,18 @@ laketower/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff,sha256=9VUTt7
14
14
  laketower/static/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2,sha256=bHVxA2ShylYEJncW9tKJl7JjGf2weM8R4LQqtm_y6mE,134044
15
15
  laketower/static/vendor/halfmoon/halfmoon.min.css,sha256=RjeFzczeuZHCyS-Gvz-kleETzBF_o84ZRHukze_yv6o,369168
16
16
  laketower/static/vendor/halfmoon/halfmoon.modern.css,sha256=DD6elX-jPmbFYPsGvzodUv2-9FHkxHlVtQi0_RJVULs,10576
17
- laketower/templates/_base.html,sha256=2bVTAgdvZIop76oqzOGCkCf59KA14GBRWaIkEFcwZoI,3823
17
+ laketower/templates/_base.html,sha256=JB3n9ziRxFl1lv8TToXgm2EyihvIuaE4rZi4n9XwXv0,6342
18
18
  laketower/templates/index.html,sha256=dLF2Og0qgzBkvGyVRidRNzTv0u4o97ifOx1jVeig8Kg,59
19
- laketower/templates/queries/view.html,sha256=pOx03hTR1MNycs9YfD8yFLR3VCLFStyd6CAwqdsJEEc,2551
19
+ laketower/templates/queries/view.html,sha256=jrBItsibdcpo18qwwGVsasY41tSWAvz6ndaMwEkNqEg,2582
20
20
  laketower/templates/tables/_macros.html,sha256=sCI1TOFW0QA74oSXW87H6dNTudOs7n-FretnTPFcRh4,1174
21
21
  laketower/templates/tables/history.html,sha256=a5GBLXCiLlbWno5eR0XT5i_oMAghylUBBFOpr27NB3Q,1853
22
22
  laketower/templates/tables/import.html,sha256=bQZwRrv84tDBuf0AHJyc7L-PjW-XSoZhMHNDIo6TP4c,2604
23
23
  laketower/templates/tables/index.html,sha256=saNdQbJAjMJAzayTk4rA5Mmw_bCXvor2WpghVmoWSAI,2507
24
- laketower/templates/tables/query.html,sha256=GueaA2_JzqLWaopMGLoU8_axNZ1KtZKMt9Ku2ghHDCU,2602
25
- laketower/templates/tables/statistics.html,sha256=h6TiQtFwiRWvPqDphcRRF1rZ886FP00UbJuMHuW5l6U,1827
26
- laketower/templates/tables/view.html,sha256=ruiAX_S--wpodmgEbcQ-GT7BQzz-vzSCk4NpzlO3I80,3985
27
- laketower-0.6.2.dist-info/METADATA,sha256=Pff6860NGl-7R5k47D7yNvNIuH84QWBUT-n2PqRIK9U,27664
28
- laketower-0.6.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
- laketower-0.6.2.dist-info/entry_points.txt,sha256=sJpQgRwdeZhRBudNqBTqtHPCE-uLC9YgFXJY2CTEyCk,53
30
- laketower-0.6.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
31
- laketower-0.6.2.dist-info/RECORD,,
24
+ laketower/templates/tables/query.html,sha256=rtQtA011ScYO8WGnq_iRLgf3xUFVsSCCLpXzZJvP3ps,2633
25
+ laketower/templates/tables/statistics.html,sha256=yVsu-hNPSQ3-IX-4Z4bukLR4nERvy5PwWENfkW24xyg,1858
26
+ laketower/templates/tables/view.html,sha256=__Z-fDBHnud9iQcbYPZPGPQol2--a7TF4kT6K3001AE,4016
27
+ laketower-0.6.3.dist-info/METADATA,sha256=4AGC-yZWzQQqQOyGP7PY2ijQotAg6XSn1ApFUSEiljE,27658
28
+ laketower-0.6.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
+ laketower-0.6.3.dist-info/entry_points.txt,sha256=sJpQgRwdeZhRBudNqBTqtHPCE-uLC9YgFXJY2CTEyCk,53
30
+ laketower-0.6.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
31
+ laketower-0.6.3.dist-info/RECORD,,