tabulynx 0.1.0__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.
@@ -0,0 +1,190 @@
1
+ name: Publish Python Package
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - development
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: publish-development
14
+ cancel-in-progress: false
15
+
16
+ jobs:
17
+ prepare:
18
+ name: Prepare Release
19
+ runs-on: ubuntu-latest
20
+ container:
21
+ image: ghcr.io/astral-sh/uv:python3.12-bookworm
22
+ outputs:
23
+ should_release: ${{ steps.version.outputs.should_release }}
24
+ version: ${{ steps.version.outputs.version }}
25
+
26
+ steps:
27
+ - name: Checkout
28
+ uses: actions/checkout@v5
29
+ with:
30
+ fetch-depth: 0
31
+
32
+ - name: Detect version bump
33
+ id: version
34
+ shell: bash
35
+ run: |
36
+ current_version="$(python -c 'import tomllib; from pathlib import Path; data = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8")); print(data["project"]["version"])')"
37
+ echo "version=$current_version" >> "$GITHUB_OUTPUT"
38
+
39
+ previous_ref="${{ github.event.before }}"
40
+ if [ -n "$previous_ref" ] && git cat-file -e "${previous_ref}:pyproject.toml" 2>/dev/null; then
41
+ previous_version="$(git show "${previous_ref}:pyproject.toml" | python -c 'import sys, tomllib; data = tomllib.loads(sys.stdin.read()); print(data["project"]["version"])')"
42
+ else
43
+ previous_version=""
44
+ fi
45
+
46
+ if [ "$current_version" = "$previous_version" ]; then
47
+ echo "No version bump detected. Skipping release."
48
+ echo "should_release=false" >> "$GITHUB_OUTPUT"
49
+ exit 0
50
+ fi
51
+
52
+ if git rev-parse "refs/tags/v${current_version}" >/dev/null 2>&1; then
53
+ echo "Tag v${current_version} already exists." >&2
54
+ exit 1
55
+ fi
56
+
57
+ echo "should_release=true" >> "$GITHUB_OUTPUT"
58
+
59
+ test_build:
60
+ name: Test & Build
61
+ needs: prepare
62
+ if: needs.prepare.outputs.should_release == 'true'
63
+ runs-on: ubuntu-latest
64
+ container:
65
+ image: ghcr.io/astral-sh/uv:python3.12-bookworm
66
+
67
+ steps:
68
+ - name: Checkout
69
+ uses: actions/checkout@v5
70
+
71
+ - name: Create venv
72
+ run: uv venv
73
+
74
+ - name: Install project + dev deps
75
+ run: uv sync --group dev
76
+
77
+ - name: Run tests
78
+ run: uv run pytest -q
79
+
80
+ - name: Build package
81
+ run: uv build
82
+
83
+ - name: Upload dist artifacts
84
+ uses: actions/upload-artifact@v4
85
+ with:
86
+ name: dist
87
+ path: dist/*
88
+ if-no-files-found: error
89
+
90
+ publish:
91
+ name: Publish to PyPI
92
+ needs:
93
+ - prepare
94
+ - test_build
95
+ if: needs.prepare.outputs.should_release == 'true'
96
+ runs-on: ubuntu-latest
97
+ container:
98
+ image: ghcr.io/astral-sh/uv:python3.12-bookworm
99
+
100
+ steps:
101
+ - name: Download dist artifacts
102
+ uses: actions/download-artifact@v4
103
+ with:
104
+ name: dist
105
+ path: dist
106
+
107
+ - name: Publish to PyPI
108
+ run: uv publish dist/* --token ${{ secrets.PYPI_API_TOKEN }}
109
+
110
+ tag_release:
111
+ name: Tag Release
112
+ needs:
113
+ - prepare
114
+ - publish
115
+ if: needs.prepare.outputs.should_release == 'true'
116
+ runs-on: ubuntu-latest
117
+ permissions:
118
+ contents: write
119
+
120
+ steps:
121
+ - name: Checkout
122
+ uses: actions/checkout@v5
123
+ with:
124
+ fetch-depth: 0
125
+
126
+ - name: Create release tag
127
+ shell: bash
128
+ run: |
129
+ version="${{ needs.prepare.outputs.version }}"
130
+ git config user.name "github-actions[bot]"
131
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
132
+ git tag "v${version}" "${GITHUB_SHA}"
133
+ git push origin "v${version}"
134
+
135
+ github_release:
136
+ name: Create GitHub Release
137
+ needs:
138
+ - prepare
139
+ - tag_release
140
+ if: needs.prepare.outputs.should_release == 'true'
141
+ runs-on: ubuntu-latest
142
+ permissions:
143
+ contents: write
144
+
145
+ steps:
146
+ - name: Checkout
147
+ uses: actions/checkout@v5
148
+ with:
149
+ fetch-depth: 0
150
+
151
+ - name: Build release notes
152
+ shell: bash
153
+ run: |
154
+ version="${{ needs.prepare.outputs.version }}"
155
+ previous_tag="$(git tag --sort=-version:refname | grep -v "^v${version}$" | head -n 1)"
156
+ version_notes="release-notes/v${version}.md"
157
+
158
+ if [ -f "$version_notes" ]; then
159
+ cp "$version_notes" release-notes.md
160
+ else
161
+ {
162
+ echo "## Release"
163
+ echo
164
+ echo "This release is published to PyPI."
165
+ echo
166
+ echo "Install with:"
167
+ echo
168
+ echo "\`\`\`bash"
169
+ echo "pip install -U tabulynx"
170
+ echo "\`\`\`"
171
+ } > release-notes.md
172
+ fi
173
+
174
+ if [ -n "$previous_tag" ]; then
175
+ {
176
+ echo
177
+ echo "## Full Changelog"
178
+ echo
179
+ echo "https://github.com/${{ github.repository }}/compare/${previous_tag}...v${version}"
180
+ } >> release-notes.md
181
+ fi
182
+
183
+ - name: Create GitHub Release
184
+ env:
185
+ GH_TOKEN: ${{ github.token }}
186
+ shell: bash
187
+ run: |
188
+ gh release create "v${{ needs.prepare.outputs.version }}" \
189
+ --notes-file release-notes.md \
190
+ --title "v${{ needs.prepare.outputs.version }}"
@@ -0,0 +1,13 @@
1
+ .venv/
2
+ .pytest_cache/
3
+ .uv-cache/
4
+ .idea/
5
+ __pycache__/
6
+ *.pyc
7
+ *.pyo
8
+ *.pyd
9
+ *.egg-info/
10
+ build/
11
+ dist/
12
+ .coverage
13
+ htmlcov/
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,314 @@
1
+ Metadata-Version: 2.4
2
+ Name: tabulynx
3
+ Version: 0.1.0
4
+ Summary: Desktop spreadsheet-style viewer for CSV, XLSX, and Parquet files
5
+ Project-URL: Homepage, https://github.com/Jeferson-Peter/tabulynx
6
+ Project-URL: Repository, https://github.com/Jeferson-Peter/tabulynx
7
+ Project-URL: Issues, https://github.com/Jeferson-Peter/tabulynx/issues
8
+ Keywords: csv,desktop,parquet,polars,qt,spreadsheet,xlsx
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Win32 (MS Windows)
11
+ Classifier: Environment :: X11 Applications :: Qt
12
+ Classifier: Intended Audience :: End Users/Desktop
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Office/Business
18
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: fastexcel>=0.11.5
21
+ Requires-Dist: openpyxl>=3.1.5
22
+ Requires-Dist: polars>=1.12.0
23
+ Requires-Dist: pyside6>=6.8.0
24
+ Description-Content-Type: text/markdown
25
+
26
+ # TabuLynx
27
+
28
+ TabuLynx is a local desktop spreadsheet-style viewer for tabular files. It is meant for opening, exploring, filtering, sorting, querying, and exporting local datasets without starting a web server or using a full spreadsheet suite.
29
+
30
+ It supports:
31
+
32
+ - CSV
33
+ - XLSX
34
+ - Parquet
35
+
36
+ The core is built on `polars`, and the desktop interface is built with `PySide6`.
37
+
38
+ ## Features
39
+
40
+ - Native desktop table viewer for local tabular files
41
+ - Data-type aware filters for text, numbers, booleans, dates, and datetimes
42
+ - Multi-filter and multi-sort support
43
+ - Global search across columns
44
+ - Column visibility controls
45
+ - Explicit pagination with configurable page size
46
+ - Row details side panel
47
+ - Column statistics side panel
48
+ - Dataset summary side panel
49
+ - Export of the current view to CSV or Parquet
50
+ - SQL queries against the current view using Polars SQL
51
+ - Temporary SQL result tabs
52
+ - SQL examples, SQL clear action, and local SQL history
53
+ - Query presets
54
+ - Multiple files in tabs
55
+ - Recent files and pinned files
56
+ - Last-session restore
57
+ - Query history per tab with back/forward navigation
58
+ - Context menu actions on cells and headers
59
+ - Optimized XLSX browsing through cached Parquet conversion
60
+ - Keyboard shortcuts for common actions
61
+
62
+ ## Quick Start
63
+
64
+ Install dependencies:
65
+
66
+ ```bash
67
+ uv sync
68
+ ```
69
+
70
+ Run the desktop app:
71
+
72
+ ```bash
73
+ uv run tabulynx
74
+ ```
75
+
76
+ Open a file directly:
77
+
78
+ ```bash
79
+ uv run tabulynx open ./your-file.csv
80
+ ```
81
+
82
+ You can also open:
83
+
84
+ ```bash
85
+ uv run tabulynx open ./your-file.xlsx
86
+ uv run tabulynx open ./your-file.parquet
87
+ ```
88
+
89
+ ## Basic Workflow
90
+
91
+ 1. Open a `csv`, `xlsx`, or `parquet` file.
92
+ 2. Use the global search box to quickly reduce the visible rows.
93
+ 3. Add filters and sorts from the query area.
94
+ 4. Use the `Columns` menu to show or hide columns.
95
+ 5. Select a row to inspect full row values in the side panel.
96
+ 6. Click a column header to view column statistics.
97
+ 7. Use `Export` to save the current view as CSV or Parquet.
98
+
99
+ The current view means the result after applying search, filters, sorts, and visible columns.
100
+
101
+ ## SQL Support
102
+
103
+ The SQL panel uses Polars SQL and runs against the current tab's current view. The available table name is always `current`.
104
+
105
+ Example:
106
+
107
+ ```sql
108
+ SELECT customer, revenue
109
+ FROM current
110
+ WHERE revenue > 100
111
+ ORDER BY revenue DESC
112
+ LIMIT 50
113
+ ```
114
+
115
+ Supported today:
116
+
117
+ - Querying the current tab as `current`
118
+ - `SELECT`, `WHERE`, `ORDER BY`, `LIMIT`, and common Polars SQL expressions supported by Polars
119
+ - Aggregations and grouping supported by Polars SQL
120
+ - SQL over the current view, so active search, filters, sorts, and visible columns are applied before the SQL query runs
121
+ - Opening SQL results as temporary result tabs
122
+ - Quick SQL examples
123
+ - Local SQL history
124
+
125
+ Not supported yet:
126
+
127
+ - Cross-tab SQL queries or joins between multiple open files
128
+ - Persistent SQL result tabs across app restarts
129
+ - SQL editing of source files
130
+ - Database features such as indexes, transactions, stored procedures, or user-defined SQL functions
131
+ - Full DuckDB/PostgreSQL-style SQL compatibility
132
+
133
+ ## XLSX Handling
134
+
135
+ Excel files are handled differently from CSV and Parquet.
136
+
137
+ For `xlsx`, TabuLynx:
138
+
139
+ - Discovers sheet names up front
140
+ - Lets you switch sheets in the desktop UI
141
+ - Opens workbooks with a read-only ingestion path
142
+ - Converts the selected sheet into cached Parquet
143
+ - Uses the cached Parquet file for faster browsing, filtering, sorting, SQL, and pagination
144
+
145
+ This keeps the UI experience consistent while avoiding repeated direct Excel reads during exploration.
146
+
147
+ Important notes:
148
+
149
+ - The first open of a large sheet may take longer while the cache is created.
150
+ - The cache is invalidated when the source file metadata changes.
151
+ - Excel formulas are read as stored values when available.
152
+ - TabuLynx does not recalculate Excel formulas.
153
+ - TabuLynx does not preserve Excel formatting, formulas, merged cells, charts, or styles during export.
154
+
155
+ ## Keyboard Shortcuts
156
+
157
+ - `Ctrl+O`: open file
158
+ - `Ctrl+F`: focus global search
159
+ - `Ctrl+Enter`: run SQL in the current tab
160
+ - `Esc`: clear filters in the current tab
161
+ - Double-click a column header divider: autosize the column
162
+
163
+ ## Context Menus
164
+
165
+ Right-click a table cell for quick actions such as:
166
+
167
+ - Copy cell
168
+ - Copy row
169
+ - Filter by selected value
170
+ - Search/filter by selected text
171
+ - Hide the clicked column
172
+
173
+ Right-click a column header for quick column actions such as:
174
+
175
+ - Search in that column
176
+ - Clear filters for that column
177
+ - Sort ascending or descending
178
+ - Hide the column
179
+
180
+ ## Presets, Recent Files, And Session State
181
+
182
+ TabuLynx stores local app state under `~/.tabulynx/`.
183
+
184
+ Current local state includes:
185
+
186
+ - Query presets
187
+ - SQL history
188
+ - Recent files
189
+ - Pinned files
190
+ - Last-session restore data
191
+ - XLSX Parquet cache
192
+
193
+ The app state is local to your machine and is not synced or uploaded by TabuLynx.
194
+
195
+ ## Library Usage
196
+
197
+ You can also use the core library directly:
198
+
199
+ ```python
200
+ from tabulynx import open_dataset
201
+
202
+ dataset = open_dataset("your-file.xlsx", sheet="Sheet1")
203
+
204
+ print(dataset.columns())
205
+ print(dataset.row_count())
206
+ print(dataset.page(0, 100))
207
+ ```
208
+
209
+ Apply filters and sorts:
210
+
211
+ ```python
212
+ from tabulynx.core import ColumnFilter, SortRule, open_dataset
213
+
214
+ dataset = open_dataset("your-file.parquet")
215
+ dataset.add_filter(ColumnFilter(column="revenue", operator="gte", value="100"))
216
+ dataset.set_sorts([SortRule(column="revenue", descending=True)])
217
+
218
+ page = dataset.page(offset=0, limit=50)
219
+ print(page.rows)
220
+ ```
221
+
222
+ Run SQL from Python:
223
+
224
+ ```python
225
+ dataset = open_dataset("your-file.csv")
226
+
227
+ result = dataset.sql_query(
228
+ """
229
+ SELECT customer, SUM(revenue) AS total_revenue
230
+ FROM current
231
+ GROUP BY customer
232
+ ORDER BY total_revenue DESC
233
+ """
234
+ )
235
+
236
+ print(result)
237
+ ```
238
+
239
+ ## Sample Data
240
+
241
+ Sample data for local testing lives in:
242
+
243
+ - `sample_data/filters_demo.csv`
244
+ - `sample_data/filters_demo.parquet`
245
+
246
+ Regenerate the sample files:
247
+
248
+ ```bash
249
+ uv run python scripts/create_sample_data.py
250
+ ```
251
+
252
+ Generate a larger sample:
253
+
254
+ ```bash
255
+ uv run python scripts/create_sample_data.py --rows 20000
256
+ ```
257
+
258
+ ## Run Tests
259
+
260
+ Install development dependencies:
261
+
262
+ ```bash
263
+ uv sync --group dev
264
+ ```
265
+
266
+ Run the test suite:
267
+
268
+ ```bash
269
+ uv run pytest -q
270
+ ```
271
+
272
+ ## Project Structure
273
+
274
+ ```text
275
+ src/tabulynx/
276
+ __init__.py
277
+ __main__.py
278
+ cli.py
279
+ core.py
280
+ qt.py
281
+ viewer.py
282
+
283
+ sample_data/
284
+ filters_demo.csv
285
+ filters_demo.parquet
286
+
287
+ scripts/
288
+ create_sample_data.py
289
+
290
+ tests/
291
+ test_core.py
292
+ ```
293
+
294
+ ## Current Limitations
295
+
296
+ - TabuLynx is a viewer and exploration tool, not a full spreadsheet editor.
297
+ - It does not currently edit cells or save changes back into the original source file.
298
+ - XLSX support uses cached Parquet conversion per selected sheet; very large or irregular workbooks may take time on first open.
299
+ - Excel formulas are not recalculated by TabuLynx.
300
+ - Excel formatting, styles, merged cells, charts, and workbook-specific layout features are not preserved.
301
+ - SQL currently runs only against the current tab's current view as the table `current`.
302
+ - SQL result tabs are temporary and are not restored in the next session.
303
+ - Cross-tab SQL joins are not supported yet.
304
+ - Extremely large datasets may still depend on available memory and local disk cache.
305
+ - The UI is focused on desktop use and is not intended for browser or mobile workflows.
306
+
307
+ ## Roadmap Ideas
308
+
309
+ - Cross-tab SQL queries
310
+ - Better SQL editor experience
311
+ - More test coverage for UI state and Excel edge cases
312
+ - More robust XLSX type inference for irregular workbooks
313
+ - Optional app packaging with PyInstaller or Nuitka
314
+ - Screenshots and release packaging documentation