csvql-query 2.6.2__py3-none-win_amd64.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.
csvql/__init__.py ADDED
@@ -0,0 +1,146 @@
1
+ """
2
+ csvql — fast CSV querying from Python.
3
+
4
+ Usage::
5
+
6
+ import csvql
7
+
8
+ # Returns a list of dicts
9
+ rows = csvql.query("SELECT category, COUNT(*) FROM 'sales.csv' GROUP BY category")
10
+ # [{"category": "Electronics", "COUNT(*)": "42"}, ...]
11
+
12
+ # Returns raw CSV string (header + rows)
13
+ csv_text = csvql.query_csv("SELECT * FROM 'data.csv' WHERE amount > 100")
14
+
15
+ # Optional pandas integration (pandas not required)
16
+ df = csvql.query_df("SELECT region, SUM(revenue) FROM 'data.csv' GROUP BY region")
17
+ """
18
+
19
+ import ctypes
20
+ import csv
21
+ import io
22
+ import json
23
+ from typing import Any
24
+
25
+ from ._loader import load
26
+
27
+
28
+ def query(sql: str) -> list[dict[str, Any]]:
29
+ """Run a SQL query and return results as a list of dicts.
30
+
31
+ Args:
32
+ sql: SQL string, e.g. ``"SELECT * FROM 'data.csv' WHERE age > 30"``
33
+
34
+ Returns:
35
+ List of row dicts, e.g. ``[{"name": "Alice", "age": "31"}, ...]``
36
+ All values are strings (CSV has no type information).
37
+
38
+ Raises:
39
+ RuntimeError: if the query fails (bad SQL, file not found, etc.)
40
+ """
41
+ lib = load()
42
+ out = ctypes.c_void_p(None)
43
+ rc = lib.csvql_query_json(sql.encode(), ctypes.byref(out))
44
+ addr = out.value
45
+ if rc != 0:
46
+ raw = ctypes.string_at(addr) if addr else b"unknown error"
47
+ if addr:
48
+ lib.csvql_free(addr)
49
+ raise RuntimeError(raw.decode(errors="replace"))
50
+ if not addr:
51
+ return []
52
+ raw = ctypes.string_at(addr)
53
+ lib.csvql_free(addr)
54
+ return json.loads(raw.decode())
55
+
56
+
57
+ def query_csv(sql: str) -> str:
58
+ """Run a SQL query and return results as a CSV string (header + rows).
59
+
60
+ Args:
61
+ sql: SQL string.
62
+
63
+ Returns:
64
+ CSV text with a header row, e.g. ``"name,age\\nAlice,31\\n"``
65
+
66
+ Raises:
67
+ RuntimeError: if the query fails.
68
+ """
69
+ lib = load()
70
+ out = ctypes.c_void_p(None)
71
+ rc = lib.csvql_query_csv(sql.encode(), ctypes.byref(out))
72
+ addr = out.value
73
+ if rc != 0:
74
+ raw = ctypes.string_at(addr) if addr else b"unknown error"
75
+ if addr:
76
+ lib.csvql_free(addr)
77
+ raise RuntimeError(raw.decode(errors="replace"))
78
+ if not addr:
79
+ return ""
80
+ raw = ctypes.string_at(addr)
81
+ lib.csvql_free(addr)
82
+ return raw.decode()
83
+
84
+
85
+ def query_df(sql: str):
86
+ """Run a SQL query and return a pandas DataFrame.
87
+
88
+ Requires pandas to be installed. All columns will be ``object`` dtype
89
+ (strings) — cast as needed after calling this function.
90
+
91
+ Args:
92
+ sql: SQL string.
93
+
94
+ Returns:
95
+ ``pandas.DataFrame``
96
+
97
+ Raises:
98
+ ImportError: if pandas is not installed.
99
+ RuntimeError: if the query fails.
100
+ """
101
+ try:
102
+ import pandas as pd
103
+ except ImportError as e:
104
+ raise ImportError("pandas is required for query_df(). Install it with: pip install pandas") from e
105
+
106
+ csv_text = query_csv(sql)
107
+ return pd.read_csv(io.StringIO(csv_text))
108
+
109
+
110
+ def query_tuples(sql: str) -> tuple[list[str], list[tuple[str, ...]]]:
111
+ """Run a SQL query and return (headers, rows) as plain tuples.
112
+
113
+ Useful when you want array-of-arrays access (``row[0]``) instead of
114
+ dicts, or when memory overhead of dicts matters.
115
+
116
+ Args:
117
+ sql: SQL string.
118
+
119
+ Returns:
120
+ A ``(headers, rows)`` tuple where ``headers`` is a list of column
121
+ name strings and ``rows`` is a list of string tuples — one per row.
122
+
123
+ Raises:
124
+ RuntimeError: if the query fails.
125
+
126
+ Example::
127
+
128
+ headers, rows = csvql.query_tuples("SELECT name, age FROM 'data.csv'")
129
+ # headers: ['name', 'age']
130
+ # rows: [('Alice', '30'), ('Bob', '25'), ...]
131
+ for row in rows:
132
+ print(row[0], row[1])
133
+ """
134
+ csv_text = query_csv(sql)
135
+ if not csv_text:
136
+ return [], []
137
+ reader = csv.reader(io.StringIO(csv_text))
138
+ all_rows = list(reader)
139
+ if not all_rows:
140
+ return [], []
141
+ headers = all_rows[0]
142
+ data = [tuple(r) for r in all_rows[1:] if r]
143
+ return headers, data
144
+
145
+
146
+ __all__ = ["query", "query_csv", "query_df", "query_tuples"]
csvql/_loader.py ADDED
@@ -0,0 +1,90 @@
1
+ """
2
+ Locates and loads libcsvql (.dll on Windows, .dylib on macOS, .so elsewhere).
3
+
4
+ Search order:
5
+ 1. Same directory as this file (installed wheel — lib bundled alongside .py)
6
+ 2. zig-out/ relative to the repo root (development build)
7
+ 3. Directories listed in CSVQL_LIB_PATH environment variable
8
+ """
9
+
10
+ import ctypes
11
+ import os
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ _lib_cache: ctypes.CDLL | None = None
16
+
17
+
18
+ def _lib_name() -> str:
19
+ if sys.platform == "win32":
20
+ # Zig names the Windows shared library csvql.dll — no "lib" prefix.
21
+ return "csvql.dll"
22
+ if sys.platform == "darwin":
23
+ return "libcsvql.dylib"
24
+ return "libcsvql.so"
25
+
26
+
27
+ def _candidate_dirs() -> list[Path]:
28
+ dirs: list[Path] = []
29
+
30
+ # 1. Next to this .py file (wheel install)
31
+ dirs.append(Path(__file__).parent)
32
+
33
+ # 2. zig-out/ — walk up from this file looking for build.zig.
34
+ # Zig installs a .so/.dylib into zig-out/lib, but puts a Windows DLL
35
+ # in zig-out/bin alongside the executables (only the import library
36
+ # csvql.lib lands in lib/). Check both so a development build is
37
+ # found on every platform.
38
+ here = Path(__file__).resolve()
39
+ for parent in here.parents:
40
+ if (parent / "build.zig").exists():
41
+ dirs.append(parent / "zig-out" / "lib")
42
+ dirs.append(parent / "zig-out" / "bin")
43
+ break
44
+
45
+ # 3. CSVQL_LIB_PATH env override
46
+ env_path = os.environ.get("CSVQL_LIB_PATH")
47
+ if env_path:
48
+ dirs.append(Path(env_path))
49
+
50
+ return dirs
51
+
52
+
53
+ def load() -> ctypes.CDLL:
54
+ global _lib_cache
55
+ if _lib_cache is not None:
56
+ return _lib_cache
57
+
58
+ name = _lib_name()
59
+ for d in _candidate_dirs():
60
+ candidate = d / name
61
+ if candidate.exists():
62
+ lib = ctypes.CDLL(str(candidate))
63
+ _setup_signatures(lib)
64
+ _lib_cache = lib
65
+ return lib
66
+
67
+ searched = "\n ".join(str(d / name) for d in _candidate_dirs())
68
+ raise FileNotFoundError(
69
+ f"Could not find {name}. Searched:\n {searched}\n"
70
+ "Run `zig build lib -Doptimize=ReleaseFast` to build it, "
71
+ "or set CSVQL_LIB_PATH to its directory."
72
+ )
73
+
74
+
75
+ def _setup_signatures(lib: ctypes.CDLL) -> None:
76
+ """Declare argument and return types for all exported functions."""
77
+ lib.csvql_query_json.argtypes = [
78
+ ctypes.c_char_p,
79
+ ctypes.POINTER(ctypes.c_void_p),
80
+ ]
81
+ lib.csvql_query_json.restype = ctypes.c_int
82
+
83
+ lib.csvql_query_csv.argtypes = [
84
+ ctypes.c_char_p,
85
+ ctypes.POINTER(ctypes.c_void_p),
86
+ ]
87
+ lib.csvql_query_csv.restype = ctypes.c_int
88
+
89
+ lib.csvql_free.argtypes = [ctypes.c_void_p]
90
+ lib.csvql_free.restype = None
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: csvql-query
3
+ Version: 2.6.2
4
+ Summary: Fast CSV querying from Python — powered by a Zig/SIMD engine
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/melihbirim/csvql
7
+ Project-URL: Repository, https://github.com/melihbirim/csvql
8
+ Keywords: csv,query,sql,fast,simd,agent,ai,mcp
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Database
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Provides-Extra: pandas
23
+ Requires-Dist: pandas>=1.5; extra == "pandas"
24
+
25
+ # csvql-query
26
+
27
+ [![CI](https://github.com/melihbirim/csvql/actions/workflows/ci.yml/badge.svg)](https://github.com/melihbirim/csvql/actions/workflows/ci.yml)
28
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/melihbirim/csvql/blob/main/LICENSE.md)
29
+ [![PyPI](https://img.shields.io/pypi/v/csvql-query)](https://pypi.org/project/csvql-query/)
30
+
31
+ **Query CSV files with SQL from Python — powered by a Zig/SIMD engine.**
32
+
33
+ Zero-copy mmap reads + SIMD parsing happen before Python ever sees the data.
34
+ Faster than DuckDB on typical workloads, no dependencies required.
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install csvql-query
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ ```python
45
+ import csvql
46
+
47
+ # Returns a list of dicts (like csv.DictReader, but with SQL)
48
+ rows = csvql.query("SELECT name, salary FROM 'employees.csv' WHERE salary > 100000 ORDER BY salary DESC")
49
+ # [{'name': 'Alice', 'salary': '185000'}, ...]
50
+
51
+ # Raw CSV string
52
+ csv_str = csvql.query_csv("SELECT * FROM 'data.csv' LIMIT 10")
53
+
54
+ # pandas DataFrame (pandas must be installed)
55
+ df = csvql.query_df("SELECT category, COUNT(*) as n FROM 'sales.csv' GROUP BY category")
56
+
57
+ # (headers, rows) tuples — no dependencies
58
+ headers, rows = csvql.query_tuples("SELECT name, age FROM 'users.csv' WHERE age > 25")
59
+ ```
60
+
61
+ ## API
62
+
63
+ | Function | Returns | Description |
64
+ |---|---|---|
65
+ | `query(sql)` | `list[dict]` | Execute SQL, get list of dicts |
66
+ | `query_csv(sql)` | `str` | Execute SQL, get raw CSV string |
67
+ | `query_df(sql)` | `DataFrame` | Execute SQL, get pandas DataFrame |
68
+ | `query_tuples(sql)` | `(list[str], list[tuple])` | Execute SQL, get (headers, rows) |
69
+
70
+ ## SQL Support
71
+
72
+ The SQL path is embedded in the query string (same as the CLI):
73
+
74
+ ```python
75
+ # Filtering, ordering, limiting
76
+ csvql.query("SELECT name, city FROM 'data.csv' WHERE age > 30 ORDER BY name LIMIT 5")
77
+
78
+ # Aggregation
79
+ csvql.query("SELECT department, AVG(salary) FROM 'emp.csv' GROUP BY department")
80
+
81
+ # Unix pipes — use '-' as the filename
82
+ import subprocess, sys
83
+ # or just pass stdin data via the engine directly
84
+ ```
85
+
86
+ Full SQL reference: [SIMPLE_QUERY_LANGUAGE.md](https://github.com/melihbirim/csvql/blob/main/SIMPLE_QUERY_LANGUAGE.md)
87
+
88
+ ## Performance
89
+
90
+ - mmap + SIMD parsing — data is never copied into Python memory
91
+ - Parallel chunk processing on multi-core machines
92
+ - Typically 5–9x faster than DuckDB on 1M-row CSVs
93
+
94
+ ## Requirements
95
+
96
+ - Python ≥ 3.10
97
+ - macOS (x86_64 / arm64) or Linux (x86_64)
98
+ - `pandas` optional — only needed for `query_df()`
99
+
100
+ ## Links
101
+
102
+ - [GitHub](https://github.com/melihbirim/csvql)
103
+ - [CLI Installation](https://github.com/melihbirim/csvql#installation)
104
+ - [SQL Reference](https://github.com/melihbirim/csvql/blob/main/SIMPLE_QUERY_LANGUAGE.md)
@@ -0,0 +1,6 @@
1
+ csvql/__init__.py,sha256=kHEihvCYuuJb-0RQIWi_4wp-5UVFi3dPMvO9LPR7Pck,4177
2
+ csvql/_loader.py,sha256=pWlOb6xEevlRNYqtQv8loQJpqtxhAM1nWBoZ2TCHgkE,2776
3
+ csvql_query-2.6.2.dist-info/METADATA,sha256=Z3IA185CWmYbBEWZx3XJ5K3dEiPxNyFqqtdqvcfla5Y,3770
4
+ csvql_query-2.6.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
5
+ csvql_query-2.6.2.dist-info/top_level.txt,sha256=iVE80iZxyPJ-SsWu0-vcYeu_8yAKnru0E7PaX6ZeoMM,6
6
+ csvql_query-2.6.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ csvql