Skip to content

Commit e1bdc20

Browse files
authored
Stub interface (#2)
* add missing configs * copy over the sandbox, just to start somewhere * README-PYPI.md * head() works the same for both * remove cruft * checkpoint * xfail * make table_name explicit * map schema from polars to ibis * remove commented out code * slicing * slice syntax * eval() not needed * new exception * do not need "app" deps * add usage example in doctest * fix pyright coverage
1 parent cadaa88 commit e1bdc20

14 files changed

Lines changed: 285 additions & 27 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ celerybeat.pid
141141
.env
142142
.envrc
143143
.venv
144+
.venv-*
144145
env/
145146
venv/
146147
ENV/

.pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
filterwarnings =
44
# Treat warnings as errors,
55
error
6+
ignore:'json' serialization format of LazyFrame is deprecated:UserWarning
67

78
addopts = --doctest-glob '*.md' --doctest-modules
89

README-PYPI.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# polars-to-ibis
2+
3+
[![pypi](https://img.shields.io/pypi/v/polars_to_ibis)](https://pypi.org/project/polars_to_ibis/)
4+
5+
Convert Polars expressions to Ibis expressions
6+
7+
## Usage
8+
9+
**🚧 Under Construction! 🚧**
10+
11+
```python
12+
>>> import polars as pl
13+
>>> from polars_to_ibis import polars_to_ibis
14+
15+
>>> data = {
16+
... "ints": [1, 2, 3, 4],
17+
... }
18+
>>> polars_df = pl.DataFrame(data)
19+
>>> polars_lazy = polars_df.lazy().head(1)
20+
21+
>>> ibis_unbound_table = polars_to_ibis(polars_lazy, table_name="my_table")
22+
>>> ibis_unbound_table
23+
r0 := UnboundTable: my_table
24+
ints int64
25+
<BLANKLINE>
26+
Limit[r0, n=1]
27+
28+
```

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,27 @@ Convert Polars expressions to Ibis expressions
66

77
## Usage
88

9-
TODO
9+
**🚧 Under Construction! 🚧**
10+
11+
```python
12+
>>> import polars as pl
13+
>>> from polars_to_ibis import polars_to_ibis
14+
15+
>>> data = {
16+
... "ints": [1, 2, 3, 4],
17+
... }
18+
>>> polars_df = pl.DataFrame(data)
19+
>>> polars_lazy = polars_df.lazy().head(1)
20+
21+
>>> ibis_unbound_table = polars_to_ibis(polars_lazy, table_name="my_table")
22+
>>> ibis_unbound_table
23+
r0 := UnboundTable: my_table
24+
ints int64
25+
<BLANKLINE>
26+
Limit[r0, n=1]
27+
28+
```
29+
1030

1131
## Contributions
1232

polars_to_ibis/__init__.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,46 @@
11
"""Convert Polars expressions to Ibis expressions"""
22

3+
import json
34
from pathlib import Path
45

6+
import ibis
7+
import polars as pl
8+
59
__version__ = (Path(__file__).parent / "VERSION").read_text().strip()
10+
11+
12+
def polars_to_ibis(lf: pl.LazyFrame, table_name: str) -> ibis.Table:
13+
polars_json = lf.serialize(format="json")
14+
polars_plan = json.loads(polars_json)
15+
16+
polars_schema = lf.collect_schema()
17+
ibis_schema = _polars_schema_to_ibis(polars_schema)
18+
ibis_table = ibis.table(ibis_schema, name=table_name)
19+
20+
return _apply_polars_plan_to_ibis_table(polars_plan, ibis_table)
21+
22+
23+
class UnhandledPolarsException(Exception):
24+
pass
25+
26+
27+
def _apply_polars_plan_to_ibis_table(polars_plan: dict, table: ibis.Table):
28+
# TODO: More general!
29+
if "Slice" in polars_plan:
30+
slice_params = polars_plan["Slice"]
31+
return table.limit(slice_params["len"], offset=slice_params["offset"])
32+
raise UnhandledPolarsException("Unhandled polars plan")
33+
34+
35+
_type_map = {
36+
# TODO: Expand this!
37+
pl.Int64: "int64",
38+
pl.UInt32: "uint32",
39+
pl.Float64: "float64",
40+
pl.String: "string",
41+
pl.Boolean: "boolean",
42+
}
43+
44+
45+
def _polars_schema_to_ibis(schema: pl.Schema):
46+
return {k: _type_map[v] for k, v in schema.items()}

pyproject.toml

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,28 @@ build-backend = "flit_core.buildapi"
44

55
[project]
66
name = "polars_to_ibis"
7-
authors = [{name = "Chuck McCallum", email = "info@opendp.org"}]
8-
readme = "README.md"
9-
license = "MIT"
10-
license-files = ["LICENSE"]
7+
authors = [{name = "The OpenDP Project", email = "info@opendp.org"}]
8+
readme = "README-PYPI.md"
9+
license = {file = "LICENSE"}
10+
classifiers = [
11+
"Programming Language :: Python :: 3",
12+
"License :: OSI Approved :: MIT License",
13+
]
1114
dynamic = ["version", "description"]
1215
dependencies = [
13-
"ibis",
16+
"duckdb==1.3.1",
17+
"ibis-framework[duckdb,polars]==10.8.0",
1418
"polars",
1519
]
1620

1721
[project.urls]
1822
Home = "https://github.com/opendp/polars-to-ibis"
1923

20-
[project.optional-dependencies]
21-
app = [
22-
"ibis==3.3.0",
23-
"polars-runtime-32==1.34.0",
24-
"polars==1.34.0",
25-
]
24+
[options]
25+
python_requires = ">=3.10"
26+
27+
[tool.pyright]
28+
include = ["polars_to_ibis"]
29+
30+
[tool.isort]
31+
profile = "black"

requirements-dev.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ pyright
1616
pytest-cov
1717
pytest-xdist
1818

19-
# App dependencies:
19+
# Package dependencies:
2020
-r requirements.in

requirements-dev.txt

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#
2-
# This file is autogenerated by pip-compile with Python 3.13
2+
# This file is autogenerated by pip-compile with Python 3.10
33
# by the following command:
44
#
55
# pip-compile requirements-dev.in
66
#
7+
atpublic==6.0.2
8+
# via ibis-framework
79
attrs==25.4.0
810
# via flake8-bugbear
911
black==25.9.0
@@ -26,6 +28,12 @@ distlib==0.4.0
2628
# via virtualenv
2729
docutils==0.22.2
2830
# via flit
31+
duckdb==1.3.1
32+
# via
33+
# -r requirements.in
34+
# ibis-framework
35+
exceptiongroup==1.3.0
36+
# via pytest
2937
execnet==2.1.1
3038
# via pytest-xdist
3139
filelock==3.20.0
@@ -40,7 +48,7 @@ flit==3.12.0
4048
# via -r requirements-dev.in
4149
flit-core==3.12.0
4250
# via flit
43-
ibis==3.3.0
51+
ibis-framework[duckdb,polars]==10.8.0
4452
# via -r requirements.in
4553
identify==2.6.15
4654
# via pre-commit
@@ -50,19 +58,32 @@ iniconfig==2.1.0
5058
# via pytest
5159
isort==6.1.0
5260
# via -r requirements-dev.in
61+
markdown-it-py==4.0.0
62+
# via rich
5363
mccabe==0.7.0
5464
# via flake8
65+
mdurl==0.1.2
66+
# via markdown-it-py
5567
mypy-extensions==1.1.0
5668
# via black
5769
nodeenv==1.9.1
5870
# via
5971
# pre-commit
6072
# pyright
73+
numpy==2.2.6
74+
# via
75+
# ibis-framework
76+
# pandas
6177
packaging==25.0
6278
# via
6379
# black
6480
# build
81+
# ibis-framework
6582
# pytest
83+
pandas==2.3.3
84+
# via ibis-framework
85+
parsy==2.2
86+
# via ibis-framework
6687
pathspec==0.12.1
6788
# via black
6889
pip-tools==7.5.1
@@ -76,17 +97,25 @@ pluggy==1.6.0
7697
# pytest
7798
# pytest-cov
7899
polars==1.34.0
79-
# via -r requirements.in
100+
# via
101+
# -r requirements.in
102+
# ibis-framework
80103
polars-runtime-32==1.34.0
81104
# via polars
82105
pre-commit==4.3.0
83106
# via -r requirements-dev.in
107+
pyarrow==21.0.0
108+
# via ibis-framework
109+
pyarrow-hotfix==0.7
110+
# via ibis-framework
84111
pycodestyle==2.14.0
85112
# via flake8
86113
pyflakes==3.4.0
87114
# via flake8
88115
pygments==2.19.2
89-
# via pytest
116+
# via
117+
# pytest
118+
# rich
90119
pyproject-hooks==1.2.0
91120
# via
92121
# build
@@ -102,18 +131,48 @@ pytest-cov==7.0.0
102131
# via -r requirements-dev.in
103132
pytest-xdist==3.8.0
104133
# via -r requirements-dev.in
134+
python-dateutil==2.9.0.post0
135+
# via
136+
# ibis-framework
137+
# pandas
105138
pytokens==0.1.10
106139
# via black
140+
pytz==2025.2
141+
# via pandas
107142
pyyaml==6.0.3
108143
# via pre-commit
109144
requests==2.32.5
110145
# via flit
146+
rich==14.2.0
147+
# via ibis-framework
148+
six==1.17.0
149+
# via python-dateutil
150+
sqlglot==27.25.2
151+
# via ibis-framework
152+
tomli==2.3.0
153+
# via
154+
# black
155+
# build
156+
# coverage
157+
# pip-tools
158+
# pytest
111159
tomli-w==1.2.0
112160
# via flit
113161
tomlkit==0.13.3
114162
# via -r requirements-dev.in
163+
toolz==1.0.0
164+
# via ibis-framework
115165
typing-extensions==4.15.0
116-
# via pyright
166+
# via
167+
# black
168+
# exceptiongroup
169+
# ibis-framework
170+
# pyright
171+
# virtualenv
172+
tzdata==2025.2
173+
# via
174+
# ibis-framework
175+
# pandas
117176
urllib3==2.5.0
118177
# via requests
119178
virtualenv==20.34.0

requirements.in

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# After making changes here, run scripts/requirements.py.
22

33
polars
4-
ibis
4+
5+
# Ibis examples will not work unless duckdb is pinned
6+
ibis-framework[duckdb,polars]==10.8.0
7+
duckdb==1.3.1

requirements.txt

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,60 @@
11
#
2-
# This file is autogenerated by pip-compile with Python 3.13
2+
# This file is autogenerated by pip-compile with Python 3.10
33
# by the following command:
44
#
55
# pip-compile requirements.in
66
#
7-
ibis==3.3.0
7+
atpublic==6.0.2
8+
# via ibis-framework
9+
duckdb==1.3.1
10+
# via
11+
# -r requirements.in
12+
# ibis-framework
13+
ibis-framework[duckdb,polars]==10.8.0
814
# via -r requirements.in
15+
markdown-it-py==4.0.0
16+
# via rich
17+
mdurl==0.1.2
18+
# via markdown-it-py
19+
numpy==2.2.6
20+
# via
21+
# ibis-framework
22+
# pandas
23+
packaging==25.0
24+
# via ibis-framework
25+
pandas==2.3.3
26+
# via ibis-framework
27+
parsy==2.2
28+
# via ibis-framework
929
polars==1.34.0
10-
# via -r requirements.in
30+
# via
31+
# -r requirements.in
32+
# ibis-framework
1133
polars-runtime-32==1.34.0
1234
# via polars
35+
pyarrow==21.0.0
36+
# via ibis-framework
37+
pyarrow-hotfix==0.7
38+
# via ibis-framework
39+
pygments==2.19.2
40+
# via rich
41+
python-dateutil==2.9.0.post0
42+
# via
43+
# ibis-framework
44+
# pandas
45+
pytz==2025.2
46+
# via pandas
47+
rich==14.2.0
48+
# via ibis-framework
49+
six==1.17.0
50+
# via python-dateutil
51+
sqlglot==27.25.2
52+
# via ibis-framework
53+
toolz==1.0.0
54+
# via ibis-framework
55+
typing-extensions==4.15.0
56+
# via ibis-framework
57+
tzdata==2025.2
58+
# via
59+
# ibis-framework
60+
# pandas

0 commit comments

Comments
 (0)