nix-book/source/new-flake/python-flake/main.adoc0
Amy de Buitléir 00bfdf2c0f temp
2025-09-14 20:49:57 +01:00

260 lines
7.3 KiB
Text

= Python
Start with an empty directory and create a git repository.
....
$ mkdir hello-python
$ cd hello-python
$ git init
....
== A simple Python program
Next, we'll create a simple Python program.
////
$ curl https://codeberg.org/mhwombat/hello-flake-python/raw/branch/main/hello.py --silent --output hello.py
////
[source,python,linenums]
.hello.py
....
$# cat hello.py
....
== Running the program manually (optional)
[NOTE]
====
In this section you will learn how to do some development tasks manually, the "hard" way.
This can help you understand the distinction between Nix's role and
the Python build system you've chosen.
Also, if you have a build problem but you're not sure if the fault is in
your flake definition or some other configuration file,
these commands can help you narrow it down.
But you may wish to skip to the <<_configuring_setuptools,next section>>
and come back here later.
====
Before we package the program, let's verify that it runs. We're going to
need Python. By now you've probably figured out that we can write a
`flake.nix` and define a development shell that includes Python. We'll
do that shortly, but first I want to show you a handy shortcut. We can
launch a _temporary_ shell with any Nix packages we want. This is
convenient when you just want to try out some new software and you're
not sure if you'll use it again. It's also convenient when you're not
ready to write `flake.nix` (perhaps you're not sure what tools and
packages you need), and you want to experiment a bit first.
The command to enter a temporary shell is
`nix shell -p __installables__`
Where __installables__ are flakes and other types of packages that you need.
(You can learn more about these in the
https://nix.dev/manual/nix/stable/command-ref/new-cli/nix#installables[Nix manual].)
Let's enter a shell with Python so we can test the program.
[#python-nix-shell]
....
$# echo '$ nix shell nixpkgs#python3'
$# nix shell nixpkgs#python3 --command sh
$ python hello.py
....
== Configuring setuptools
Next, create a Python script to build the package. We'll use Python's
setuptools, but you can use other build tools. For more information on
setuptools, see the
https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/[Python
Packaging User Guide], especially the section on
https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#setup-args[setup
args].
////
$ curl https://codeberg.org/mhwombat/hello-flake-python/raw/branch/main/setup.py --silent --output setup.py
////
[source,python,linenums]
.setup.py
....
$# cat setup.py
....
== Building the program manually (optional)
[NOTE]
====
In this section you will learn how to do some development tasks manually, the "hard" way.
This can help you understand the distinction between Nix's role and
the Python build system you've chosen.
Also, if you have a build problem but you're not sure if the fault is in
your flake definition or some other configuration file,
these commands can help you narrow it down.
But you may wish to skip to the <<python-flake,next section>>
and come back here later.
====
We won't write `flake.nix` just yet.
First we'll try building the package manually.
(If you didn't run the `nix shell` command from <<python-nix-shell,earlier>>, do so now.)
....
$ python -m build # Won't work
....
The missing module error happens because we don't have `build` available
in the temporary shell. We can fix that by adding "`build`" to the
temporary shell. When you need support for both a language and some of
its packages, it's best to use one of the Nix functions that are
specific to the programming language and build system. For Python, we
can use the `withPackages` function.
....
$# echo '$ nix-shell -p "python3.withPackages (ps: with ps; [ build ])"'
$# nix-shell -p "python3.withPackages (ps: with ps; [ build ])" --command sh
....
Note that we're now inside a temporary shell inside the previous
temporary shell! To get back to the original shell, we have to `exit`
twice.
Alternatively, we could have done `exit` followed by the
second `nix-shell` command.
....
$# echo '$ python -m build'
$# python -m build > /dev/null 2>&1
....
After a lot of output messages, the build succeeds.
[#python-flake]
== The Nix flake
Now we should write `flake.nix`.
We already know how to write most of the flake from the examples we did earlier.
The two parts that will be different are the development shell and the package builder.
Let's start with the development shell. It seems logical to write
something like the following.
....
devShells = rec {
default = pkgs.mkShell {
packages = [ (python.withPackages (ps: with ps; [ build ])) ];
};
};
....
Note that we need the parentheses to prevent `python.withPackages` and
the argument from being processed as two separate tokens. Suppose we
wanted to work with `virtualenv` and `pip` instead of `build`. We could
write something like the following.
....
devShells = rec {
default = pkgs.mkShell {
packages = [
# Python plus helper tools
(python.withPackages (ps: with ps; [
virtualenv # Virtualenv
pip # The pip installer
]))
];
};
};
....
For the package builder, we can use the `buildPythonApplication`
function.
....
packages = rec {
hello = python.pkgs.buildPythonApplication {
name = "hello-flake-python";
buildInputs = with python.pkgs; [ pip ];
src = ./.;
};
default = hello;
};
....
If you put all the pieces together, your `flake.nix` should look
something like this.
////
$ curl https://codeberg.org/mhwombat/hello-flake-python/raw/branch/main/flake.nix --silent --output flake.nix
////
[source,nix,linenums]
.flake.nix
....
$# cat flake.nix
....
== Building the program
Let's try out the new flake.
....
$ nix build # Won't work
....
Nix flakes only "`see`" files that are part of the repository.
We need to add all of the important files to the
repo before building or running the flake.
....
$ git add flake.nix setup.py hello.py
$ nix build
....
We'll deal with those warnings later.
The important thing for now is that the build succeeded.
== Running the program
Now we can run the program.
....
$ nix run
....
By the way, we didn't need to do `nix build` earlier.
The `nix run` command will first build the program for us if needed.
We'd like to share this package with others, but first we should do some
cleanup.
It's time to deal with those warnings.
When the package was built, Nix created a `flake.lock` file.
We need to add this to the repo, and commit all important files.
....
$ git add flake.lock
$ git commit -a -m 'initial commit'
....
You can test that your package is properly configured by going to
another directory and running it from there.
....
$ cd ..
$ nix run ./hello-python
....
If you move the project to a public repo, anyone can run it. Recall from
the beginning of the tutorial that you were able to run `hello-flake`
directly from my repo with the following command.
....
nix run "git+https://codeberg.org/mhwombat/hello-flake"
....
Modify the URL accordingly and invite someone else to run your new
Python flake.
////
Good adoc0 scripts clean up after themselves.
$ rm -rf hello-python # clean up
////