---
hide:
  - navigation
---

<!--
  - Licensed to the Apache Software Foundation (ASF) under one
  - or more contributor license agreements.  See the NOTICE file
  - distributed with this work for additional information
  - regarding copyright ownership.  The ASF licenses this file
  - to you under the Apache License, Version 2.0 (the
  - "License"); you may not use this file except in compliance
  - with the License.  You may obtain a copy of the License at
  -
  -   http://www.apache.org/licenses/LICENSE-2.0
  -
  - Unless required by applicable law or agreed to in writing,
  - software distributed under the License is distributed on an
  - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  - KIND, either express or implied.  See the License for the
  - specific language governing permissions and limitations
  - under the License.
  -->

# Contributing

We welcome contributions to Apache Iceberg! To learn more about contributing to Apache Iceberg, please refer to the [official Iceberg contribution guidelines](https://iceberg.apache.org/contribute/). These guidelines are intended as helpful suggestions to make the contribution process as seamless as possible, and are not strict rules.

If you would like to discuss your proposed change before contributing, we encourage you to visit our [Community](https://iceberg.apache.org/community/) page. There, you will find various ways to connect with the community, including Slack and our mailing lists. Alternatively, you can open a [new issue](https://github.com/apache/iceberg-python/issues) directly in the GitHub repository.

For first-time contributors, feel free to check out our [good first issues](https://github.com/apache/iceberg-python/issues/?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) for an easy way to get started.

## Contributing to PyIceberg

The PyIceberg Project is hosted on GitHub at <https://github.com/apache/iceberg-python>.

For development, [uv](https://docs.astral.sh/uv/) is used for dependency management and packaging. uv is a Python package installer and resolver, written in Rust, that serves as a drop-in replacement for pip, and virtualenv.

### Getting Started

Install uv and set up the development environment:

```bash
make install
```

This will install uv if needed, create a virtual environment in `.venv`, and install all dependencies.

If you only want to just install uv:

```bash
make install-uv
```

### Python Version Selection

You can specify which Python version to use when creating your virtual environment:

```bash
PYTHON=3.12 make install # Create environment with Python 3.12
make test # Run tests against Python 3.12
```

> **Tip:** `uv python list` shows available interpreters. `uv python install 3.12` can install one if needed.

### IDE Setup

After running `make install`, configure your IDE to use the Python interpreter at `.venv/bin/python`.

**To set up IDEA with uv:**

- Open up the Python project in IntelliJ
- Make sure that you're on latest main
- Go to File -> Project Structure (⌘;)
- Go to Platform Settings -> SDKs
- Add Python SDK -> Virtualenv Environment -> Existing environment
- Point to `.venv/bin/python`

**VS Code:**

- Press Cmd/Ctrl+Shift+P -> "Python: Select Interpreter"
- Choose `.venv/bin/python`

### Advanced uv Usage

For full control over your environment, you can use uv commands directly. See the [uv documentation](https://docs.astral.sh/uv/) to learn more about:

- Managing dependencies with `uv add` and `uv remove`
- Python version management with `uv python`
- Running commands with `uv run`
- Lock file management with `uv lock`

### Lock File Management

`uv.lock` is a cross-platform lockfile that contains the exact versions of all project dependencies. See the [uv.lock documentation](https://docs.astral.sh/uv/guides/projects/#uvlock) for more details.

**Automatic lock file management:**
When you commit changes to `pyproject.toml` or dependencies, the [`uv-pre-commit`](https://github.com/astral-sh/uv-pre-commit) pre-commit hook will automatically update `uv.lock` if needed. If the lockfile changes, your commit will be stopped and you will be prompted to add the updated `uv.lock` to your commit.

**Manual update:**
If you want to update the lockfile yourself (for example, after editing `pyproject.toml`), run:

```bash
uv lock
```

**CI enforcement:**
Our continuous integration (CI) system checks that `uv.lock` is up to date with `pyproject.toml` on every pull request or push. If they are out of sync, the CI build will fail. This ensures that dependencies are always consistent and up to date for all contributors.

## Installation from source

Clone the repository for local development:

```sh
git clone https://github.com/apache/iceberg-python.git
cd iceberg-python
pip3 install -e ".[s3fs,hive]"
```

Install it directly for GitHub (not recommended), but sometimes handy:

```shell
pip install "git+https://github.com/apache/iceberg-python.git#egg=pyiceberg[pyarrow]"
```

## Linting

`prek` is used for autoformatting and linting:

```bash
make lint
```

`prek` will automatically fix the violations such as import orders, formatting etc. Pylint errors you need to fix yourself.

In addition to manually running `make lint`, you can install the pre-commit hooks in your local repo with `prek install`. By doing this, linting is run automatically every time you make a commit.

You can bump the integrations to the latest version using `prek auto-update`. This will check if there is a newer version of `{ruff,mypy,...}` and update the yaml.

## Cleaning

Removal of old cached files generated during the Cython build process:

```bash
make clean
```

Helps prevent build failures and unexpected behavior by removing outdated files, ensuring that only up-to-date sources are used & the build environment is always clean.

## Testing

For Python, `pytest` is used a testing framework in combination with `coverage` to enforce 90%+ code coverage.

```bash
make test
```

By default, S3 and ADLS tests are ignored because that require minio and azurite to be running.
To run the S3 suite:

```bash
make test-s3
```

To run the ADLS suite:

```bash
make test-adls
```

To pass additional arguments to pytest, you can use `PYTEST_ARGS`.

### Run pytest in verbose mode

```sh
make test PYTEST_ARGS="-v"
```

### Run pytest with pdb enabled

```sh
make test PYTEST_ARGS="--pdb"
```

To see all available pytest arguments, run `make test PYTEST_ARGS="--help"`.

### Integration tests

PyIceberg has integration tests with Apache Spark. Spark will create a new database and provision some tables that PyIceberg can query against.

```sh
make test-integration
```

This will restart the containers, to get to a clean state, and then run the PyTest suite. In case something changed in the Dockerfile or the provision script, you can run:

```sh
make test-integration-rebuild
```

To rebuild the containers from scratch.

#### Running Integration Tests against REST Catalogs

!!! warning "Do not run against production catalogs"
    The integration tests will delete data throughout the entirety of your catalog. Running these integration tests against production catalogs will result in data loss.

PyIceberg supports the ability to run our catalog tests against an arbitrary REST Catalog.

In order to run the test catalog, you will need to specify which REST catalog to run against with the `PYICEBERG_TEST_CATALOG` environment variable

```sh
export PYICEBERG_TEST_CATALOG=test_catalog
```

The catalog in question can be configured either through the ~/.pyiceberg.yaml file or through environment variables.

```yaml
catalog:
  test_catalog:
    uri: http://rest-catalog/ws/
    credential: t-1234:secret
```

```sh
export PYICEBERG_CATALOG__TEST_CATALOG__URI=thrift://localhost:9083
export PYICEBERG_CATALOG__TEST_CATALOG__ACCESS_KEY_ID=username
export PYICEBERG_CATALOG__TEST_CATALOG__SECRET_ACCESS_KEY=password
```

## Notebooks for Experimentation

PyIceberg provides Jupyter notebooks for quick experimentation and learning. Two Make commands are available depending on your needs:

### PyIceberg Examples (`make notebook`)

For basic PyIceberg experimentation without additional infrastructure:

```bash
make notebook
```

This will install notebook dependencies and launch Jupyter Lab in the `notebooks/` directory.

**PyIceberg Example Notebook** (`notebooks/pyiceberg_example.ipynb`) is based on the [Getting Started with PyIceberg](https://py.iceberg.apache.org/#getting-started-with-pyiceberg) page. It demonstrates basic PyIceberg operations like creating catalogs, schemas, and querying tables without requiring any external services.

### Spark Integration Examples (`make notebook-infra`)

For working with PyIceberg alongside Spark, use the infrastructure-enabled notebook environment:

```bash
make notebook-infra
```

This command spins up the full integration test infrastructure via Docker Compose, including:

- **Spark** (with Spark Connect)
- **Iceberg REST Catalog** (using the [`apache/iceberg-rest-fixture`](https://hub.docker.com/r/apache/iceberg-rest-fixture) image)
- **Hive Metastore**
- **S3-compatible object storage** (Minio)

**Spark Example Notebook** (`notebooks/spark_integration_example.ipynb`) is based on the [Spark Getting Started](https://iceberg.apache.org/docs/nightly/spark-getting-started/) guide. This notebook demonstrates how to work with PyIceberg alongside Spark, leveraging the Docker-based testing setup for a complete local development environment.

After running `make notebook-infra`, open `spark_integration_example.ipynb` in the Jupyter Lab interface to explore Spark integration capabilities.

## Code standards

Below are the formalized conventions that we adhere to in the PyIceberg project. The goal of this is to have a common agreement on how to evolve the codebase, but also using it as guidelines for newcomers to the project.

### API Compatibility

It is important to keep the Python public API compatible across versions. The Python official [PEP-8](https://peps.python.org/pep-0008/) defines public methods as: _Public attributes should have no leading underscores_. This means not removing any methods without any notice, or removing or renaming any existing parameters. Adding new optional parameters is okay.

If you want to remove a method, please add a deprecation notice by annotating the function using `@deprecated`:

```python
from pyiceberg.utils.deprecated import deprecated


@deprecated(
    deprecated_in="0.1.0",
    removed_in="0.2.0",
    help_message="Please use load_something_else() instead",
)
def load_something():
    pass
```

Which will warn:

```text
Call to load_something, deprecated in 0.1.0, will be removed in 0.2.0. Please use load_something_else() instead.
```

If you want to remove a property or notify about a behavior change, please add a deprecation notice by calling the deprecation_message function:

```python
from pyiceberg.utils.deprecated import deprecation_message

deprecation_message(
    deprecated_in="0.1.0",
    removed_in="0.2.0",
    help_message="The old_property is deprecated. Please use the something_else property instead.",
)
```

Which will warn:

```text
Deprecated in 0.1.0, will be removed in 0.2.0. The old_property is deprecated. Please use the something_else property instead.
```

### Logging

PyIceberg uses Python's standard logging module. You can control the logging level using either:

**CLI option:**

```bash
pyiceberg --log-level DEBUG describe my_table
```

**Environment variable:**

```bash
export PYICEBERG_LOG_LEVEL=DEBUG
pyiceberg describe my_table
```

Valid log levels are: `DEBUG`, `INFO`, `WARNING` (default), `ERROR`, `CRITICAL`.

Debug logging is particularly useful for troubleshooting issues with FileIO implementations, catalog connections, and other integration points.

### Type annotations

For the type annotation the types from the `Typing` package are used.

### Third party libraries

PyIceberg naturally integrates into the rich Python ecosystem, however it is important to be hesitant adding third party packages. Adding a lot of packages makes the library heavyweight, and causes incompatibilities with other projects if they use a different version of the library. Also, big libraries such as `s3fs`, `adlfs`, `pyarrow`, `thrift` should be optional to avoid downloading everything, while not being sure if is actually being used.
