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 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
+ ```
@@ -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,5 @@
1
+ """rs-mock: a lightweight in-process Redshift mocker built on duckdb + sqlglot."""
2
+
3
+ from rs_mock.mock import RedshiftMock, UnimplementedPostgresFeature
4
+
5
+ __all__ = ["RedshiftMock", "UnimplementedPostgresFeature"]
@@ -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