Skip to main content

polars-hdfs

Read HDFS paths directly with polars.

import polars as pl
import polars_hdfs

df = pl.read_parquet("hdfs://namenode:8020/data/events.parquet")
lf = pl.scan_parquet("hdfs://namenode:8020/data/events/*.parquet")

Importing polars_hdfs adds hdfs:// support to polars' read/scan functions. Calls without an hdfs:// source are passed through unchanged.

The readers are patched in place, so references captured before the import keep working as long as polars_hdfs is imported before the read is executed:

from polars import read_parquet
import polars_hdfs  # patched before the call

df = read_parquet("hdfs://namenode:8020/data/events.parquet")

Install

pip install polars-hdfs

Configuration

HDFS settings go in polars' storage_options, the same place S3, Azure, and GCP settings go. Three keys are supported:

  • hdfs_config_dir: a Hadoop configuration directory
  • hdfs_user: the effective HDFS user
  • hdfs_options: Hadoop configuration keys and values

How configuration is resolved

When a read uses an hdfs:// path, the HDFS client is configured like this:

  1. Choose a Hadoop config directory. hdfs_config_dir wins if you set it; otherwise HADOOP_CONF_DIR is used, then HADOOP_HOME/etc/hadoop. If none is available, no XML is loaded.
  2. Load core-site.xml and hdfs-site.xml from that directory, if present.
  3. Apply hdfs_options on top. Since these are Hadoop keys, they override any matching values from XML.
  4. Choose the NameNode. A URI authority like hdfs://namenode:8020 is used directly. For a URI without an authority (hdfs:///...), the NameNode comes from fs.defaultFS in the resulting configuration.

If no NameNode can be found, the read fails with the HDFS client's missing fs.defaultFS error.

hdfs_user is separate from hdfs_options because it is a client identity setting, not a Hadoop configuration key. When set, it takes precedence over the HADOOP_USER_NAME and HADOOP_PROXY_USER environment variables. When omitted, those variables are used, followed by the operating-system user.

No Hadoop configuration is required for a direct NameNode. If a config directory is available, it is still loaded for Kerberos, HA, datanode settings, and the like; the URI authority only decides which NameNode to use.

Examples

Configuration directory

For authority-less URIs, the NameNode comes from fs.defaultFS in the config:

pl.read_parquet(
    "hdfs:///data/file.parquet",
    storage_options={"hdfs_config_dir": "/etc/hadoop/conf"},
)

Use this for HA nameservices, Kerberos, and other cluster-specific settings.

NameNode without a config file

fs.defaultFS can be supplied directly through hdfs_options:

pl.read_parquet(
    "hdfs:///data/file.parquet",
    storage_options={
        "hdfs_options": {"fs.defaultFS": "hdfs://namenode:8020"},
    },
)

Multiple clusters

Each distinct NameNode, user, Hadoop option set, and config directory gets its own reader. Readers run in parallel, so datasets from different clusters can be scanned lazily and joined in polars.

A single config directory can contain settings for multiple HA nameservices; the URI authority selects the nameservice:

so = {"hdfs_config_dir": "/etc/hadoop/conf"}

cluster_a = pl.scan_parquet("hdfs://cluster-a/data/events/", storage_options=so)
cluster_b = pl.scan_parquet("hdfs://cluster-b/data/users/", storage_options=so)

result = cluster_a.join(cluster_b, on="user_id").collect()

Different config directories can also provide different fs.defaultFS values:

a = pl.scan_parquet(
    "hdfs:///data/events/",
    storage_options={"hdfs_config_dir": "/conf/cluster-a"},
)
b = pl.scan_parquet(
    "hdfs:///data/users/",
    storage_options={"hdfs_config_dir": "/conf/cluster-b"},
)

HDFS options and user

pl.scan_parquet(
    "hdfs:///data/events/",
    storage_options={
        "hdfs_user": "analytics",
        "hdfs_config_dir": "/etc/hadoop/conf",
        "hdfs_options": {
            "dfs.client.use.datanode.hostname": "true",
        },
    },
)

Reader reuse and lifecycle

Reads with the same NameNode selector, user, Hadoop options, and config directory reuse the same reader. Config directory paths are canonicalized, so relative paths, trailing slashes, and equivalent paths share a reader. Different config directories are kept separate even if their contents look similar. Use the same NameNode style for datasets on the same cluster so they share a reader.

Readers start lazily, are reused for matching settings, and stay alive so lazy frames can be collected later — a lazy frame may outlive the call that created it, so there is no automatic eviction. The number of readers is bounded by the number of distinct cluster configurations, not by the number of datasets. Call polars_hdfs.shutdown() to stop them explicitly; they are also stopped at interpreter exit.

Supported calls

Parquet, CSV, IPC, NDJSON, lines, and Delta are supported, including directory and glob sources where polars supports them.

A directory source needs a trailing slash. Without one the path is read as a single key, the same way it is on S3:

pl.scan_parquet("hdfs:///data/events/")           # every file in the directory
pl.scan_parquet("hdfs:///data/events/*.parquet")  # glob
pl.scan_parquet("hdfs:///data/events/a.parquet")  # one file

HDFS support is read-only. Writing, read_avro, and schema-only helpers are not supported.

Development

python -m pytest

That runs the unit tests, which need no cluster: the gateway is faked.

The tests in tests/integration read from real HDFS and are skipped unless POLARS_HDFS_TEST_URI points at a running namenode. The Docker commands in .github/workflows/integration.yml are the reference for starting one (or two) locally:

POLARS_HDFS_TEST_URI=hdfs://localhost:9001 uv run pytest -m integration

POLARS_HDFS_TEST_URI_2 adds the second cluster used by the multi-cluster join test, and POLARS_HDFS_TEST_BASE (default /data) chooses the directory that fixtures are written to and cleaned up from. Seeding uses hdfs-native, which is what the integration extra installs:

uv sync --extra dev --extra integration

Metadata

Release files for polars-hdfs 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for polars-hdfs 0.1.0
File Size Uploaded Provenance Actions
polars_hdfs-0.1.0.tar.gz 13.5 kB With provenance Details for polars_hdfs-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for polars-hdfs 0.1.0
File Interpreter ABI Platform Actions
polars_hdfs-0.1.0-py3-none-any.whl Python 3 none any Details for polars_hdfs-0.1.0-py3-none-any.whl

Total release size: 22.1 kB

Release files / polars_hdfs-0.1.0.tar.gz

Download URL polars_hdfs-0.1.0.tar.gz
Size 13.5 kB
Tags Source
SHA-256 checksum
How to use checksums
673f7abe7c776a77e1004160c687c36532b0a83ade8450c48eff5fac9bb41201
BLAKE2b-256 checksum
How to use checksums
1e178dc6e5b2980edcc52400a893e3307ce49610eacdd769291423d6ab7f8561
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Release files / polars_hdfs-0.1.0-py3-none-any.whl

Download URL polars_hdfs-0.1.0-py3-none-any.whl
Size 8.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
873a9a8a9cdcc54804131c6c7d009b15d449249c0532a0fe5ecc2b379ec33dbd
BLAKE2b-256 checksum
How to use checksums
08fba0a0b17c6f0866be1d7d5ae5b72405ef4f9a4c0c1c9cff8df2214b0235ba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Security for polars-hdfs 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for polars_hdfs-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page