Embrace the Power of Nix for Your Python + Rust Workflow
Hussain Sultan
Posted on July 24, 2024
Hello Dev.to community!
At LETSQL, we're passionate about delivering the best tools for data scientists and ML engineers. As part of our journey, we leverage Nix to streamline our development workflows. If you're working with Python and Rust, or simply curious about why Nix is a game-changer, read on!
Why Nix?
Nix is a powerful package manager that offers reproducibility, reliability, and isolation. Here’s why we think it’s great:
Reproducible Builds: Nix ensures that your development environment is consistent across different machines. With Nix, you can avoid the infamous "works on my machine" problem. Every developer and CI pipeline gets the same environment, leading to fewer surprises and smoother collaborations.
Declarative Configuration: Nix uses a declarative approach to specify dependencies. This means you can define your environment in a single configuration file, making it easy to version control and share. Whether you're working on a small script or a large project, managing dependencies becomes straightforward.
Isolation: Nix provides isolated environments, preventing conflicts between dependencies. This is particularly useful when working on multiple projects with different requirements. No more worrying about version clashes or dependency hell!
Nix for Python + Rust Workflow
Combining Python and Rust in a single project can be challenging, but Nix simplifies this process. Here’s how:
Single Source of Truth: With Nix, you can manage both Python and Rust dependencies in a unified way. Define your dependencies in a
flake.nix
file and let Nix handle the rest. This ensures that your Python packages and Rust crates are compatible and work seamlessly together.Easy Setup: Setting up a development environment with Nix is as simple as running a single command. Once your configuration is in place, you can quickly spin up environments with all the necessary dependencies, saving valuable setup time.
Consistent Development and Deployment: By using Nix, you ensure that your development, testing, and production environments are identical. This reduces the risk of bugs caused by environmental discrepancies and makes deployments more predictable and reliable.
Crane Lib: Incremental Artifact Caching for Rust Projects
We use Crane, a Nix library for building Cargo projects. One of Crane’s standout features is incremental artifact caching, which ensures you never build the same artifact twice. This greatly speeds up the build process and reduces redundant work.
Here's how Crane fits into our flake.nix
:
Crane Library Integration: We use Crane to manage our Rust builds. It allows for fine-grained control over the build process and ensures efficient use of cached artifacts.
Cargo Configuration: The
cargo.toml
andCargo.lock
files define our Rust dependencies. Crane leverages these files to manage the build process.Build Command: We use a custom build command for Maturin, integrated into our Nix build script to create Python wheels from our Rust code. This command ensures that all necessary artifacts are built and cached.
Maturin Build: Bridging Rust and Python
Maturin is a fantastic tool for building and publishing Rust crates as Python packages. With Nix, integrating Maturin into our workflow is straightforward. Here's a snippet from our flake.nix
demonstrating the Maturin build setup:
buildPhaseCargoCommand = ''
${pkgs.maturin}/bin/maturin build \
--offline \
--target-dir target \
--manylinux off \
--strip \
--release
'';
This command ensures that our Rust crate is built and packaged as a Python wheel, ready for distribution.
Poetry2nix: Seamless Python Dependency Management
We also utilize Poetry2nix to manage our Python dependencies. Poetry2nix translates pyproject.toml and poetry.lock files into Nix expressions, allowing us to maintain our Python dependencies declaratively.
Here’s how Poetry2nix is configured in our flake.nix:
inputs = {
poetry2nix = {
url = "github:nix-community/poetry2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
...
commonPoetryArgs = {
projectDir = ./.;
src = pySrc;
preferWheels = true;
python = python';
groups = [ "dev" "test" "docs" ];
};
myapp = (mkPoetryApplication (commonPoetryArgs // {
buildInputs = pkgs.lib.optionals pkgs.stdenv.isDarwin [
pkgs.libiconv
];
})).overridePythonAttrs maturinOverride;
Explore Our Configuration
For a full example of how we set up our development environment using Nix, check out our flake.nix file. This configuration includes everything from Crane for Rust builds to Poetry2nix for Python dependencies, ensuring a seamless and reproducible development workflow.
Join Us on GitHub!
We're always looking for contributors and feedback. If you find Nix as exciting as we do, check out our LETSQL GitHub repository and give us a star! 🌟 Your support helps us continue building great tools for the community.
Let's Connect!
Have questions or want to share your experience with Nix? Drop a comment below, or reach out to us on Twitter. We love hearing from you!
Happy coding!
Posted on July 24, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.