| <!--- |
| 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. |
| --> |
| |
| # Introduction |
| |
| We welcome and encourage contributions of all kinds, such as: |
| |
| 1. Tickets with issue reports of feature requests |
| 2. Documentation improvements |
| 3. Code, both PR and (especially) PR Review. |
| |
| In addition to submitting new PRs, we have a healthy tradition of community members reviewing each other’s PRs. |
| Doing so is a great way to help the community as well as get more familiar with Rust and the relevant codebases. |
| |
| Before opening a pull request that touches PyO3 bindings, please review the |
| {ref}`PyO3 class mutability guidelines <ffi_pyclass_mutability>` so you can flag missing |
| `#[pyclass(frozen)]` annotations during development and review. |
| |
| ## How to develop |
| |
| This assumes that you have rust and cargo installed. We use the workflow recommended by |
| [pyo3](https://github.com/PyO3/pyo3) and [maturin](https://github.com/PyO3/maturin). We recommend using |
| [uv](https://docs.astral.sh/uv/) for python package management. |
| |
| By default `uv` will attempt to build the datafusion python package. For our development we prefer to build manually. This means |
| that when creating your virtual environment using `uv sync` you need to pass in the additional `--no-install-package datafusion` |
| and for `uv run` commands the additional parameter `--no-project` |
| |
| Bootstrap: |
| |
| ```shell |
| # fetch this repo |
| git clone git@github.com:apache/datafusion-python.git |
| # create the virtual environment |
| uv sync --dev --no-install-package datafusion |
| # activate the environment |
| source .venv/bin/activate |
| ``` |
| |
| The tests rely on test data in git submodules. |
| |
| ```shell |
| git submodule init |
| git submodule update |
| ``` |
| |
| Whenever rust code changes (your changes or via `git pull`): |
| |
| ```shell |
| # make sure you activate the venv using "source .venv/bin/activate" first |
| maturin develop -uv |
| python -m pytest |
| ``` |
| |
| ## Running & Installing pre-commit hooks |
| |
| arrow-datafusion-python takes advantage of [pre-commit](https://pre-commit.com/) to assist developers with code linting to help reduce the number of commits that ultimately fail in CI due to linter errors. Using the pre-commit hooks is optional for the developer but certainly helpful for keeping PRs clean and concise. |
| |
| Our pre-commit hooks can be installed by running {code}`pre-commit install`, which will install the configurations in your ARROW_DATAFUSION_PYTHON_ROOT/.github directory and run each time you perform a commit, failing to complete the commit if an offending lint is found allowing you to make changes locally before pushing. |
| |
| The pre-commit hooks can also be run adhoc without installing them by simply running {code}`pre-commit run --all-files` |
| |
| ## Guidelines for Separating Python and Rust Code |
| |
| Version 40 of `datafusion-python` introduced `python` wrappers around the `pyo3` generated code to vastly improve the user experience. (See the [blog post](https://datafusion.apache.org/blog/2024/08/20/python-datafusion-40.0.0/) and [pull request](https://github.com/apache/datafusion-python/pull/750) for more details.) |
| |
| Mostly, the `python` code is limited to pure wrappers with type hints and good docstrings, but there are a few reasons for when the code does more: |
| |
| 1. Trivial aliases like {py:func}`~datafusion.functions.array_append` and {py:func}`~datafusion.functions.list_append`. |
| 2. Simple type conversion, like from a `path` to a `string` of the path or from `number` to `lit(number)`. |
| 3. The additional code makes an API **much** more pythonic, like we do for {py:func}`~datafusion.functions.named_struct` (see [source code](https://github.com/apache/datafusion-python/blob/a0913c728f5f323c1eb4913e614c9d996083e274/python/datafusion/functions.py#L1040-L1046)). |
| |
| ## Update Dependencies |
| |
| To change test dependencies, change the `pyproject.toml` and run |
| |
| To update dependencies, run |
| |
| ```shell |
| uv sync --dev --no-install-package datafusion |
| ``` |
| |
| ## Improving Build Speed |
| |
| The [pyo3](https://github.com/PyO3/pyo3) dependency of this project contains a `build.rs` file which |
| can cause it to rebuild frequently. You can prevent this from happening by defining a `PYO3_CONFIG_FILE` |
| environment variable that points to a file with your build configuration. Whenever your build configuration |
| changes, such as during some major version updates, you will need to regenerate this file. This variable |
| should point to a fully resolved path on your build machine. |
| |
| To generate this file, use the following command: |
| |
| ```shell |
| PYO3_PRINT_CONFIG=1 cargo build |
| ``` |
| |
| This will generate some output that looks like the following. You will want to copy these contents intro |
| a file. If you place this file in your project directory with filename `.pyo3_build_config` it will |
| be ignored by `git`. |
| |
| ``` |
| implementation=CPython |
| version=3.9 |
| shared=true |
| abi3=true |
| lib_name=python3.12 |
| lib_dir=/opt/homebrew/opt/python@3.12/Frameworks/Python.framework/Versions/3.12/lib |
| executable=/Users/myusername/src/datafusion-python/.venv/bin/python |
| pointer_width=64 |
| build_flags= |
| suppress_build_script_link_lines=false |
| ``` |
| |
| Add the environment variable to your system. |
| |
| ```shell |
| export PYO3_CONFIG_FILE="/Users//myusername/src/datafusion-python/.pyo3_build_config" |
| ``` |
| |
| If you are on a Mac and you use VS Code for your IDE, you will want to add these variables |
| to your settings. You can find the appropriate rust flags by looking in the |
| `.cargo/config.toml` file. |
| |
| ``` |
| "rust-analyzer.cargo.extraEnv": { |
| "RUSTFLAGS": "-C link-arg=-undefined -C link-arg=dynamic_lookup", |
| "PYO3_CONFIG_FILE": "/Users/myusername/src/datafusion-python/.pyo3_build_config" |
| }, |
| "rust-analyzer.runnables.extraEnv": { |
| "RUSTFLAGS": "-C link-arg=-undefined -C link-arg=dynamic_lookup", |
| "PYO3_CONFIG_FILE": "/Users/myusername/src/personal/datafusion-python/.pyo3_build_config" |
| } |
| ``` |