rs-mock 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.
- rs_mock-0.1.0/PKG-INFO +49 -0
- rs_mock-0.1.0/README.md +38 -0
- rs_mock-0.1.0/pyproject.toml +30 -0
- rs_mock-0.1.0/src/rs_mock/__init__.py +5 -0
- rs_mock-0.1.0/src/rs_mock/mock.py +57 -0
- rs_mock-0.1.0/src/rs_mock/py.typed +0 -0
rs_mock-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: rs-mock
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Mock Redshift locally by transpiling Redshift SQL to DuckDB and running it.
|
|
5
|
+
Author: Tyler Riccio
|
|
6
|
+
Author-email: Tyler Riccio <tylerriccio8@gmail.com>
|
|
7
|
+
Requires-Dist: duckdb>=1.5.4
|
|
8
|
+
Requires-Dist: sqlglot>=30.12.0
|
|
9
|
+
Requires-Python: >=3.14
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# rs-mock
|
|
13
|
+
|
|
14
|
+
A super lightweight, in-process **Redshift mocker** for test suites.
|
|
15
|
+
|
|
16
|
+
rs-mock transpiles Redshift SQL to duckdb SQL with [sqlglot](https://github.com/tobymao/sqlglot)
|
|
17
|
+
and runs it against an in-memory [duckdb](https://duckdb.org/) database. No cluster,
|
|
18
|
+
no network, no Docker — just fast, in-process SQL.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from rs_mock import RedshiftMock
|
|
24
|
+
|
|
25
|
+
rs = RedshiftMock()
|
|
26
|
+
|
|
27
|
+
# DDL / DML — state persists across calls for the instance's lifetime
|
|
28
|
+
rs.execute("CREATE TABLE users (id INT, name VARCHAR)")
|
|
29
|
+
rs.execute("INSERT INTO users VALUES (1, 'alice'), (2, 'bob')")
|
|
30
|
+
|
|
31
|
+
# SELECTs, JOINs, CTEs — execute() returns the duckdb cursor
|
|
32
|
+
rows = rs.execute("SELECT id, name FROM users ORDER BY id").fetchall()
|
|
33
|
+
# [(1, 'alice'), (2, 'bob')]
|
|
34
|
+
|
|
35
|
+
# Need duckdb power features? Grab the cursor or the connection.
|
|
36
|
+
df = rs.execute("SELECT * FROM users").df()
|
|
37
|
+
rs.connection # the underlying duckdb connection
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Supported: regular selects, joins, CTEs, and DDL/DML. Redshift-specific syntax
|
|
41
|
+
(e.g. `GETDATE()`) is rewritten to its duckdb equivalent automatically.
|
|
42
|
+
|
|
43
|
+
## Development
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
make test # run tests
|
|
47
|
+
make lint # ruff + pyrefly
|
|
48
|
+
make prek # pre-commit hooks
|
|
49
|
+
```
|
rs_mock-0.1.0/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# rs-mock
|
|
2
|
+
|
|
3
|
+
A super lightweight, in-process **Redshift mocker** for test suites.
|
|
4
|
+
|
|
5
|
+
rs-mock transpiles Redshift SQL to duckdb SQL with [sqlglot](https://github.com/tobymao/sqlglot)
|
|
6
|
+
and runs it against an in-memory [duckdb](https://duckdb.org/) database. No cluster,
|
|
7
|
+
no network, no Docker — just fast, in-process SQL.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from rs_mock import RedshiftMock
|
|
13
|
+
|
|
14
|
+
rs = RedshiftMock()
|
|
15
|
+
|
|
16
|
+
# DDL / DML — state persists across calls for the instance's lifetime
|
|
17
|
+
rs.execute("CREATE TABLE users (id INT, name VARCHAR)")
|
|
18
|
+
rs.execute("INSERT INTO users VALUES (1, 'alice'), (2, 'bob')")
|
|
19
|
+
|
|
20
|
+
# SELECTs, JOINs, CTEs — execute() returns the duckdb cursor
|
|
21
|
+
rows = rs.execute("SELECT id, name FROM users ORDER BY id").fetchall()
|
|
22
|
+
# [(1, 'alice'), (2, 'bob')]
|
|
23
|
+
|
|
24
|
+
# Need duckdb power features? Grab the cursor or the connection.
|
|
25
|
+
df = rs.execute("SELECT * FROM users").df()
|
|
26
|
+
rs.connection # the underlying duckdb connection
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Supported: regular selects, joins, CTEs, and DDL/DML. Redshift-specific syntax
|
|
30
|
+
(e.g. `GETDATE()`) is rewritten to its duckdb equivalent automatically.
|
|
31
|
+
|
|
32
|
+
## Development
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
make test # run tests
|
|
36
|
+
make lint # ruff + pyrefly
|
|
37
|
+
make prek # pre-commit hooks
|
|
38
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "rs-mock"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Mock Redshift locally by transpiling Redshift SQL to DuckDB and running it."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Tyler Riccio", email = "tylerriccio8@gmail.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.14"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"duckdb>=1.5.4",
|
|
12
|
+
"sqlglot>=30.12.0",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
requires = ["uv_build>=0.11.18,<0.12.0"]
|
|
17
|
+
build-backend = "uv_build"
|
|
18
|
+
|
|
19
|
+
[dependency-groups]
|
|
20
|
+
dev = [
|
|
21
|
+
"hypothesis>=6.156.2",
|
|
22
|
+
"prek>=0.4.8",
|
|
23
|
+
"pyrefly>=1.1.1",
|
|
24
|
+
"pytest>=9.1.1",
|
|
25
|
+
"pytest-cov>=7.1.0",
|
|
26
|
+
"pytest-randomly>=4.1.0",
|
|
27
|
+
"pytest-testmap>=0.3.0",
|
|
28
|
+
"pytest-xdist>=3.8.0",
|
|
29
|
+
"ruff>=0.15.20",
|
|
30
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""The RedshiftMock: run Redshift SQL against an in-memory duckdb database."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import duckdb
|
|
6
|
+
import sqlglot
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UnimplementedPostgresFeature(Exception):
|
|
10
|
+
"""Raised when SQL uses a PostgreSQL construct that Redshift does not support.
|
|
11
|
+
|
|
12
|
+
Redshift excludes many PostgreSQL functions, data types, and features (see the
|
|
13
|
+
AWS "Unsupported PostgreSQL ..." docs). Since this mock stands in for Redshift,
|
|
14
|
+
such SQL must fail loudly here rather than silently succeed on duckdb.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RedshiftMock:
|
|
19
|
+
"""An in-process stand-in for Redshift.
|
|
20
|
+
|
|
21
|
+
Redshift SQL is transpiled to duckdb via sqlglot, then executed against a
|
|
22
|
+
persistent in-memory duckdb connection so state (tables, inserted rows)
|
|
23
|
+
survives across `execute` calls for the lifetime of the instance.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self) -> None:
|
|
27
|
+
self._conn = duckdb.connect(":memory:")
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def connection(self) -> duckdb.DuckDBPyConnection:
|
|
31
|
+
"""The underlying duckdb connection, for power users."""
|
|
32
|
+
return self._conn
|
|
33
|
+
|
|
34
|
+
def execute(self, sql: str) -> duckdb.DuckDBPyConnection:
|
|
35
|
+
"""Transpile Redshift `sql` to duckdb and execute it.
|
|
36
|
+
|
|
37
|
+
A single string may hold multiple statements; each is transpiled and
|
|
38
|
+
run in order. Returns the duckdb cursor of the final statement, so
|
|
39
|
+
callers can `.fetchall()`, `.df()`, `.arrow()`, etc.
|
|
40
|
+
"""
|
|
41
|
+
# `transpile` splits multi-statement input and parses with the redshift
|
|
42
|
+
# dialect; blank input transpiles to a single empty string, so filter
|
|
43
|
+
# those out. Executing nothing is a user error.
|
|
44
|
+
statements = [
|
|
45
|
+
s for s in sqlglot.transpile(sql, read="redshift", write="duckdb") if s
|
|
46
|
+
]
|
|
47
|
+
if not statements:
|
|
48
|
+
raise ValueError("no SQL statement to execute")
|
|
49
|
+
|
|
50
|
+
result = self._conn
|
|
51
|
+
for statement in statements:
|
|
52
|
+
result = self._conn.execute(statement)
|
|
53
|
+
return result
|
|
54
|
+
|
|
55
|
+
def close(self) -> None:
|
|
56
|
+
"""Close the underlying duckdb connection."""
|
|
57
|
+
self._conn.close()
|
|
File without changes
|