narwhals-datafusion 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,30 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.so
4
+ .venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .ruff_cache/
9
+ .pytest_cache/
10
+ .hypothesis/
11
+ tmp/
12
+
13
+ # Rust (extra-functions-ffi)
14
+ target/
15
+
16
+ # IDEs
17
+ .vscode/
18
+ .idea/
19
+
20
+ # IPython / Jupyter
21
+ .ipynb_checkpoints/
22
+ profile_default/
23
+ ipython_config.py
24
+ *.ipynb
25
+
26
+ # Claude Code
27
+ .claude/
28
+
29
+ # Local-only docs (kept out of git and any publishing)
30
+ docs/
@@ -0,0 +1,3 @@
1
+ [submodule "narwhals"]
2
+ path = narwhals
3
+ url = https://github.com/narwhals-dev/narwhals
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 to present, Dmitrii Bugakov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.5
2
+ Name: narwhals-datafusion
3
+ Version: 0.1.0
4
+ Summary: Apache DataFusion backend for Narwhals, via the Narwhals plugin system
5
+ Project-URL: Repository, https://github.com/s5dsn-eqee/narwhals-datafusion
6
+ License-Expression: MIT
7
+ License-File: LICENSE.md
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: datafusion-extra-functions-ffi<0.2,>=0.1
15
+ Requires-Dist: datafusion<55,>=54
16
+ Requires-Dist: narwhals<2.26,>=2.25
17
+ Requires-Dist: pyarrow>=14.0.0
18
+ Description-Content-Type: text/markdown
19
+
20
+ # narwhals-datafusion
21
+
22
+ [![CI](https://github.com/s5dsn-eqee/narwhals-datafusion/actions/workflows/ci.yml/badge.svg)](https://github.com/s5dsn-eqee/narwhals-datafusion/actions/workflows/ci.yml)
23
+ [![PyPI version](https://badge.fury.io/py/narwhals-datafusion.svg)](https://badge.fury.io/py/narwhals-datafusion)
24
+ [![Downloads](https://static.pepy.tech/badge/narwhals-datafusion/month)](https://pepy.tech/project/narwhals-datafusion)
25
+ [![Trusted publishing](https://img.shields.io/badge/Trusted_publishing-Provides_attestations-bright_green)](https://peps.python.org/pep-0740/)
26
+ [![PYPI - Types](https://img.shields.io/pypi/types/narwhals-datafusion)](https://pypi.org/project/narwhals-datafusion)
27
+
28
+ [Apache DataFusion](https://datafusion.apache.org/python/) backend for
29
+ [Narwhals](https://github.com/narwhals-dev/narwhals), implemented as an
30
+ out-of-tree plugin via the `narwhals.plugins` entry-point system.
31
+
32
+ ## Usage
33
+
34
+ ```python
35
+ import narwhals as nw
36
+ import pyarrow as pa
37
+ from datafusion import SessionContext
38
+
39
+ ctx = SessionContext()
40
+ df = ctx.from_arrow(pa.table({"a": [1, 2, 3], "b": ["x", "y", "x"]}))
41
+
42
+ lf = nw.from_native(df) # -> nw.LazyFrame, dispatched to this plugin
43
+ result = (
44
+ lf.group_by("b")
45
+ .agg(nw.col("a").sum())
46
+ .sort("b")
47
+ .collect(backend="pyarrow")
48
+ )
49
+ ```
50
+
51
+ Everything stays lazy until `.collect()`: narwhals expressions are translated to
52
+ `datafusion.Expr`, frame verbs to `datafusion.DataFrame` methods, and the plan
53
+ executes in DataFusion's Rust engine. Dtypes are pyarrow end-to-end.
54
+
55
+ ## Architecture
56
+
57
+ The backend sits on narwhals' shared SQL layer (`narwhals._sql`), the same
58
+ abstraction DuckDB, Ibis, and Spark use. It targets `narwhals==2.25` internals
59
+ (`narwhals._sql`, `narwhals._compliant`), vendored as the `narwhals/` git
60
+ submodule pinned to that release. The plugin provides:
61
+
62
+ | Module | Class |
63
+ |---|---|
64
+ | `dataframe.py` | `DataFusionLazyFrame` — frame verbs over `datafusion.DataFrame` |
65
+ | `expr.py` | `DataFusionExpr` — the six `SQLExpr` hooks + backend specifics |
66
+ | `namespace.py` | `DataFusionNamespace` — the four `SQLNamespace` primitives, IO, horizontal fns |
67
+ | `group_by.py`, `selectors.py`, `expr_str/dt/list/struct.py` | supporting surface |
68
+ | `utils.py` | function-name remapping, window/sort builders, dtype bridge (delegates to `narwhals._arrow`) |
69
+
70
+ ## `mode`, `skew`, `kurtosis` via `datafusion-extra-functions`
71
+
72
+ DataFusion core deliberately keeps its function library lean, so aggregates
73
+ like `mode`/`skewness`/`kurtosis` live in the contrib
74
+ [`datafusion-extra-functions`](https://github.com/datafusion-contrib/datafusion-extra-functions)
75
+ crate (Rust-only, no wheel on PyPI). The
76
+ [`datafusion-extra-functions-ffi`](https://github.com/s5dsn-eqee/datafusion-extra-functions-ffi)
77
+ package — a required dependency, installed as a prebuilt wheel — exposes its
78
+ aggregate UDFs to datafusion-python via the `__datafusion_aggregate_udf__`
79
+ PyCapsule protocol; they back `Expr.mode`, `Expr.skew`, and `Expr.kurtosis`.
80
+ Its FFI ABI is tied to the `datafusion` major it was compiled against
81
+ (currently 54), which the wheel's own dependency pin enforces.
82
+
83
+ ## API coverage
84
+
85
+ Status of the narwhals public API on this backend (`narwhals==2.25`,
86
+ `datafusion==54`). ⚠️ entries work with the caveat in parentheses; details in
87
+ [Known limitations](#known-limitations-as-of-datafusion-54).
88
+
89
+ | Namespace | ✅ Supported | ⚠️ Partial | ❌ Not supported |
90
+ |---|---|---|---|
91
+ | `Expr` | `abs` `alias` `all` `any` `any_value` `ceil` `clip` `cos` `count` `cum_count` `cum_max` `cum_min` `cum_sum` `diff` `exp` `fill_nan` `first` `floor` `is_between` `is_close` `is_duplicated` `is_finite` `is_first_distinct` `is_in` `is_last_distinct` `is_nan` `is_null` `is_unique` `kurtosis` `last` `len` `log` `max` `mean` `median` `min` `null_count` `over` `pipe` `rank` `rolling_mean` `rolling_std` `rolling_sum` `rolling_var` `round` `shift` `sin` `skew` `sqrt` `std` `sum` `var` | `cast` (no `Enum`) · `fill_null` (no `strategy` + `limit`) · `mode` (`keep="any"` only) · `n_unique` (not over windows) · `replace_strict` (explicit `default` required) | `cum_prod` `quantile` |
92
+ | `Expr.str` | `contains` `ends_with` `head` `len_chars` `pad_end` `pad_start` `replace_all` `slice` `split` `starts_with` `strip_chars` `strip_chars_end` `strip_chars_start` `tail` `to_lowercase` `to_uppercase` `zfill` | `to_date`/`to_datetime` (explicit `format` required) · `to_time` (`"HH:MM:SS"`-style only) · `to_titlecase` (no word breaks on digits) | `replace` (use `replace_all`) |
93
+ | `Expr.dt` | `convert_time_zone` `date` `day` `hour` `microsecond` `millisecond` `minute` `month` `nanosecond` `ordinal_day` `second` `to_string` `truncate` `weekday` `year` | `replace_time_zone` (`None`/`"UTC"` only) | `offset_by` `timestamp` `total_microseconds` `total_milliseconds` `total_minutes` `total_nanoseconds` `total_seconds` |
94
+ | `Expr.list` | `contains` `get` `len` `max` `min` `sort` | `unique` (`maintain_order=False` only) | `mean` `median` `sum` |
95
+ | `Expr.struct` | `field` | | |
96
+ | `LazyFrame` | `collect` `collect_schema` `drop` `drop_nulls` `filter` `group_by` `head` `join` `rename` `select` `sort` `top_k` `unique` `unpivot` `with_columns` `with_row_index` | `explode` (single column) · `sink_parquet` (file path only) | `join_asof` |
97
+
98
+ Not listed: methods narwhals itself doesn't support on *any* lazy/SQL backend
99
+ (`Expr.filter`, `Expr.drop_nulls`, `Expr.unique`, `Expr.map_batches`,
100
+ `Expr.ewm_mean`, `LazyFrame.tail`, `LazyFrame.gather_every`).
101
+
102
+ ## Known limitations (as of datafusion 54)
103
+
104
+ - `join_asof`, exact `quantile`,
105
+ `cum_prod`, `list.sum/mean/median`, `dt.total_*`, `dt.offset_by`,
106
+ `dt.timestamp`, `str.replace`, `Enum` casts — no engine support;
107
+ raise `NotImplementedError`.
108
+ - `n_unique().over(...)` raises: DataFusion silently ignores `DISTINCT` inside
109
+ window aggregates, which would return wrong results. (The same engine quirk
110
+ drops `ORDER BY`/`IGNORE NULLS` declared inside window aggregates — this
111
+ backend moves those modifiers onto the window itself.)
112
+ - `fill_null(strategy=..., limit=n)` raises: bounded window frames with
113
+ `first_value`/`last_value` need `retract_batch`, unimplemented engine-side.
114
+ - `replace_time_zone` supports `None` (strip) and `"UTC"` only; use
115
+ `convert_time_zone` for instant-preserving conversions.
116
+ - `str.to_datetime`/`to_date` require an explicit `format`;
117
+ `str.to_time` parses `"HH:MM:SS"`-style strings via Arrow's cast, ignoring
118
+ custom formats.
119
+ - `replace_strict` requires an explicit `default`.
120
+ - `str.to_titlecase` uses `initcap`, which doesn't break words on digits.
121
+ - No row-order guarantees except after `sort` (standard for SQL engines);
122
+ backward `fill_null` may reorder rows.
123
+
124
+ ## Development
125
+
126
+ ```sh
127
+ git submodule update --init # narwhals, pinned to the targeted release
128
+ uv sync --group tests
129
+ ```
130
+
131
+ ## Running narwhals' own test suite against this backend
132
+
133
+ The narwhals repo is vendored as a git submodule pinned to the targeted
134
+ release:
135
+
136
+ ```sh
137
+ git submodule update --init
138
+ uv run --group tests python run_tests.py # known failures deselected
139
+ ```
140
+
141
+ For the full, unfiltered run:
142
+
143
+ ```sh
144
+ uv run --group tests pytest narwhals/tests -c narwhals/pyproject.toml \
145
+ -p narwhals_datafusion.testing -p env --use-external-constructor
146
+ ```
147
+
148
+ Regenerate the deselect list after fixing tests or bumping the submodule with
149
+ `uv run --group tests python update_run_tests.py`.
@@ -0,0 +1,130 @@
1
+ # narwhals-datafusion
2
+
3
+ [![CI](https://github.com/s5dsn-eqee/narwhals-datafusion/actions/workflows/ci.yml/badge.svg)](https://github.com/s5dsn-eqee/narwhals-datafusion/actions/workflows/ci.yml)
4
+ [![PyPI version](https://badge.fury.io/py/narwhals-datafusion.svg)](https://badge.fury.io/py/narwhals-datafusion)
5
+ [![Downloads](https://static.pepy.tech/badge/narwhals-datafusion/month)](https://pepy.tech/project/narwhals-datafusion)
6
+ [![Trusted publishing](https://img.shields.io/badge/Trusted_publishing-Provides_attestations-bright_green)](https://peps.python.org/pep-0740/)
7
+ [![PYPI - Types](https://img.shields.io/pypi/types/narwhals-datafusion)](https://pypi.org/project/narwhals-datafusion)
8
+
9
+ [Apache DataFusion](https://datafusion.apache.org/python/) backend for
10
+ [Narwhals](https://github.com/narwhals-dev/narwhals), implemented as an
11
+ out-of-tree plugin via the `narwhals.plugins` entry-point system.
12
+
13
+ ## Usage
14
+
15
+ ```python
16
+ import narwhals as nw
17
+ import pyarrow as pa
18
+ from datafusion import SessionContext
19
+
20
+ ctx = SessionContext()
21
+ df = ctx.from_arrow(pa.table({"a": [1, 2, 3], "b": ["x", "y", "x"]}))
22
+
23
+ lf = nw.from_native(df) # -> nw.LazyFrame, dispatched to this plugin
24
+ result = (
25
+ lf.group_by("b")
26
+ .agg(nw.col("a").sum())
27
+ .sort("b")
28
+ .collect(backend="pyarrow")
29
+ )
30
+ ```
31
+
32
+ Everything stays lazy until `.collect()`: narwhals expressions are translated to
33
+ `datafusion.Expr`, frame verbs to `datafusion.DataFrame` methods, and the plan
34
+ executes in DataFusion's Rust engine. Dtypes are pyarrow end-to-end.
35
+
36
+ ## Architecture
37
+
38
+ The backend sits on narwhals' shared SQL layer (`narwhals._sql`), the same
39
+ abstraction DuckDB, Ibis, and Spark use. It targets `narwhals==2.25` internals
40
+ (`narwhals._sql`, `narwhals._compliant`), vendored as the `narwhals/` git
41
+ submodule pinned to that release. The plugin provides:
42
+
43
+ | Module | Class |
44
+ |---|---|
45
+ | `dataframe.py` | `DataFusionLazyFrame` — frame verbs over `datafusion.DataFrame` |
46
+ | `expr.py` | `DataFusionExpr` — the six `SQLExpr` hooks + backend specifics |
47
+ | `namespace.py` | `DataFusionNamespace` — the four `SQLNamespace` primitives, IO, horizontal fns |
48
+ | `group_by.py`, `selectors.py`, `expr_str/dt/list/struct.py` | supporting surface |
49
+ | `utils.py` | function-name remapping, window/sort builders, dtype bridge (delegates to `narwhals._arrow`) |
50
+
51
+ ## `mode`, `skew`, `kurtosis` via `datafusion-extra-functions`
52
+
53
+ DataFusion core deliberately keeps its function library lean, so aggregates
54
+ like `mode`/`skewness`/`kurtosis` live in the contrib
55
+ [`datafusion-extra-functions`](https://github.com/datafusion-contrib/datafusion-extra-functions)
56
+ crate (Rust-only, no wheel on PyPI). The
57
+ [`datafusion-extra-functions-ffi`](https://github.com/s5dsn-eqee/datafusion-extra-functions-ffi)
58
+ package — a required dependency, installed as a prebuilt wheel — exposes its
59
+ aggregate UDFs to datafusion-python via the `__datafusion_aggregate_udf__`
60
+ PyCapsule protocol; they back `Expr.mode`, `Expr.skew`, and `Expr.kurtosis`.
61
+ Its FFI ABI is tied to the `datafusion` major it was compiled against
62
+ (currently 54), which the wheel's own dependency pin enforces.
63
+
64
+ ## API coverage
65
+
66
+ Status of the narwhals public API on this backend (`narwhals==2.25`,
67
+ `datafusion==54`). ⚠️ entries work with the caveat in parentheses; details in
68
+ [Known limitations](#known-limitations-as-of-datafusion-54).
69
+
70
+ | Namespace | ✅ Supported | ⚠️ Partial | ❌ Not supported |
71
+ |---|---|---|---|
72
+ | `Expr` | `abs` `alias` `all` `any` `any_value` `ceil` `clip` `cos` `count` `cum_count` `cum_max` `cum_min` `cum_sum` `diff` `exp` `fill_nan` `first` `floor` `is_between` `is_close` `is_duplicated` `is_finite` `is_first_distinct` `is_in` `is_last_distinct` `is_nan` `is_null` `is_unique` `kurtosis` `last` `len` `log` `max` `mean` `median` `min` `null_count` `over` `pipe` `rank` `rolling_mean` `rolling_std` `rolling_sum` `rolling_var` `round` `shift` `sin` `skew` `sqrt` `std` `sum` `var` | `cast` (no `Enum`) · `fill_null` (no `strategy` + `limit`) · `mode` (`keep="any"` only) · `n_unique` (not over windows) · `replace_strict` (explicit `default` required) | `cum_prod` `quantile` |
73
+ | `Expr.str` | `contains` `ends_with` `head` `len_chars` `pad_end` `pad_start` `replace_all` `slice` `split` `starts_with` `strip_chars` `strip_chars_end` `strip_chars_start` `tail` `to_lowercase` `to_uppercase` `zfill` | `to_date`/`to_datetime` (explicit `format` required) · `to_time` (`"HH:MM:SS"`-style only) · `to_titlecase` (no word breaks on digits) | `replace` (use `replace_all`) |
74
+ | `Expr.dt` | `convert_time_zone` `date` `day` `hour` `microsecond` `millisecond` `minute` `month` `nanosecond` `ordinal_day` `second` `to_string` `truncate` `weekday` `year` | `replace_time_zone` (`None`/`"UTC"` only) | `offset_by` `timestamp` `total_microseconds` `total_milliseconds` `total_minutes` `total_nanoseconds` `total_seconds` |
75
+ | `Expr.list` | `contains` `get` `len` `max` `min` `sort` | `unique` (`maintain_order=False` only) | `mean` `median` `sum` |
76
+ | `Expr.struct` | `field` | | |
77
+ | `LazyFrame` | `collect` `collect_schema` `drop` `drop_nulls` `filter` `group_by` `head` `join` `rename` `select` `sort` `top_k` `unique` `unpivot` `with_columns` `with_row_index` | `explode` (single column) · `sink_parquet` (file path only) | `join_asof` |
78
+
79
+ Not listed: methods narwhals itself doesn't support on *any* lazy/SQL backend
80
+ (`Expr.filter`, `Expr.drop_nulls`, `Expr.unique`, `Expr.map_batches`,
81
+ `Expr.ewm_mean`, `LazyFrame.tail`, `LazyFrame.gather_every`).
82
+
83
+ ## Known limitations (as of datafusion 54)
84
+
85
+ - `join_asof`, exact `quantile`,
86
+ `cum_prod`, `list.sum/mean/median`, `dt.total_*`, `dt.offset_by`,
87
+ `dt.timestamp`, `str.replace`, `Enum` casts — no engine support;
88
+ raise `NotImplementedError`.
89
+ - `n_unique().over(...)` raises: DataFusion silently ignores `DISTINCT` inside
90
+ window aggregates, which would return wrong results. (The same engine quirk
91
+ drops `ORDER BY`/`IGNORE NULLS` declared inside window aggregates — this
92
+ backend moves those modifiers onto the window itself.)
93
+ - `fill_null(strategy=..., limit=n)` raises: bounded window frames with
94
+ `first_value`/`last_value` need `retract_batch`, unimplemented engine-side.
95
+ - `replace_time_zone` supports `None` (strip) and `"UTC"` only; use
96
+ `convert_time_zone` for instant-preserving conversions.
97
+ - `str.to_datetime`/`to_date` require an explicit `format`;
98
+ `str.to_time` parses `"HH:MM:SS"`-style strings via Arrow's cast, ignoring
99
+ custom formats.
100
+ - `replace_strict` requires an explicit `default`.
101
+ - `str.to_titlecase` uses `initcap`, which doesn't break words on digits.
102
+ - No row-order guarantees except after `sort` (standard for SQL engines);
103
+ backward `fill_null` may reorder rows.
104
+
105
+ ## Development
106
+
107
+ ```sh
108
+ git submodule update --init # narwhals, pinned to the targeted release
109
+ uv sync --group tests
110
+ ```
111
+
112
+ ## Running narwhals' own test suite against this backend
113
+
114
+ The narwhals repo is vendored as a git submodule pinned to the targeted
115
+ release:
116
+
117
+ ```sh
118
+ git submodule update --init
119
+ uv run --group tests python run_tests.py # known failures deselected
120
+ ```
121
+
122
+ For the full, unfiltered run:
123
+
124
+ ```sh
125
+ uv run --group tests pytest narwhals/tests -c narwhals/pyproject.toml \
126
+ -p narwhals_datafusion.testing -p env --use-external-constructor
127
+ ```
128
+
129
+ Regenerate the deselect list after fixing tests or bumping the submodule with
130
+ `uv run --group tests python update_run_tests.py`.
@@ -0,0 +1,75 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "narwhals-datafusion"
7
+ version = "0.1.0"
8
+ description = "Apache DataFusion backend for Narwhals, via the Narwhals plugin system"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ dependencies = [
13
+ # targets narwhals 2.25 internals (`narwhals._sql`); widen only after testing
14
+ "narwhals>=2.25,<2.26",
15
+ # must match the FFI shim's compiled ABI (see extra-functions-ffi dependency)
16
+ "datafusion>=54,<55",
17
+ "pyarrow>=14.0.0",
18
+ # FFI shim for `mode`/`skew`/`kurtosis`; prebuilt wheels from
19
+ # https://github.com/s5dsn-eqee/datafusion-extra-functions-ffi
20
+ "datafusion-extra-functions-ffi>=0.1,<0.2",
21
+ ]
22
+ classifiers = [
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ ]
29
+
30
+ [project.urls]
31
+ Repository = "https://github.com/s5dsn-eqee/narwhals-datafusion"
32
+
33
+ [project.entry-points.'narwhals.plugins']
34
+ narwhals-datafusion = 'narwhals_datafusion'
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["src/narwhals_datafusion"]
38
+
39
+ [tool.hatch.build.targets.sdist]
40
+ # ship only what's needed to build the wheel; `narwhals/` is the dev submodule
41
+ exclude = ["docs", "narwhals", "tests", "tmp", ".github", ".hypothesis", "run_tests.py", "update_run_tests.py", "uv.lock"]
42
+
43
+ [dependency-groups]
44
+ dev = ["pytest>=8"]
45
+ # Deps for running narwhals' own suite from the `narwhals/` submodule
46
+ # (daft-style): `uv run --group tests python run_tests.py`.
47
+ tests = [
48
+ "pytest>=9.0.3",
49
+ "pytest-env",
50
+ "pytest-randomly",
51
+ "pytest-xdist",
52
+ "hypothesis>=6.0.0",
53
+ ]
54
+
55
+ [tool.uv.sources]
56
+ # narwhals comes editable from the pinned submodule (run
57
+ # `git submodule update --init` after cloning), so the plugin is always
58
+ # developed and tested against the exact internals it targets.
59
+ narwhals = { path = "narwhals", editable = true }
60
+
61
+ [tool.pytest.ini_options]
62
+ testpaths = ["tests"]
63
+
64
+ # Standalone lint config (decoupled from the narwhals repo's root settings).
65
+ [tool.ruff]
66
+ line-length = 100
67
+ target-version = "py310"
68
+ extend-exclude = ["narwhals"]
69
+
70
+ [tool.ruff.lint]
71
+ select = ["E", "F", "I", "W", "UP", "B", "SIM", "TC"]
72
+ ignore = ["SIM108"]
73
+
74
+ [tool.ruff.lint.isort]
75
+ known-first-party = ["narwhals_datafusion"]
@@ -0,0 +1,28 @@
1
+ """Apache DataFusion backend for Narwhals, registered via the `narwhals.plugins` entry point."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ import datafusion
9
+ from narwhals._utils import Version
10
+ from typing_extensions import TypeIs
11
+
12
+ from narwhals_datafusion.namespace import DataFusionNamespace
13
+
14
+ __version__ = "0.1.0"
15
+
16
+ NATIVE_PACKAGE = "datafusion"
17
+
18
+
19
+ def __narwhals_namespace__(version: Version) -> DataFusionNamespace: # noqa: N807
20
+ from narwhals_datafusion.namespace import DataFusionNamespace
21
+
22
+ return DataFusionNamespace(version=version)
23
+
24
+
25
+ def is_native(native_object: object) -> TypeIs[datafusion.DataFrame]:
26
+ import datafusion
27
+
28
+ return isinstance(native_object, datafusion.DataFrame)