Python: Difference between revisions
imported>Tobias.bora Add example with buildPythonApplication |
imported>Tobias.bora mNo edit summary |
||
Line 92: | Line 92: | ||
We can now build with: | We can now build with: | ||
< | <syntaxhighlight> | ||
$ nix-build | $ nix-build | ||
[...] | [...] | ||
Line 98: | Line 98: | ||
* Serving Flask app ".web_interface" (lazy loading) | * Serving Flask app ".web_interface" (lazy loading) | ||
[...] | [...] | ||
</ | </syntaxhighlight> | ||
or just enter a nix-shell if it's easier to develop: | or just enter a nix-shell, and directly execute your program or python if it's easier to develop: | ||
< | <syntaxhighlight> | ||
$ nix-shell | $ nix-shell | ||
[...] | [...] | ||
Line 107: | Line 107: | ||
* Serving Flask app "web_interface" (lazy loading) | * Serving Flask app "web_interface" (lazy loading) | ||
[...] | [...] | ||
[nix-shell]$ python | [nix-shell]$ python | ||
Python 3.8.7 (default, Dec 21 2020, 17:18:55) | Python 3.8.7 (default, Dec 21 2020, 17:18:55) | ||
Line 113: | Line 114: | ||
>>> import flask | >>> import flask | ||
>>> | >>> | ||
</ | </syntaxhighlight> | ||
=== Python virtual environment === | === Python virtual environment === |
Revision as of 15:53, 25 May 2021
The Python packages available to the interpreter must be declared when installing Python.
To install, say Python 3 with pandas
and requests
, define a new package python-with-my-packages
:
with pkgs;
let
my-python-packages = python-packages: with python-packages; [
pandas
requests
# other python packages you want
];
python-with-my-packages = python3.withPackages my-python-packages;
in ...
You can put python-with-my-packages
into your environment.systemPackages for a system-wide installation, for instance. Be mindful that pythonX.withPackages
creates a pythonX-Y.Z.W-env
package which is read only, so you can't use pip
to install packages in, say, a virtual environment (as described below). Put python
in your configuration.nix
if you want to use the solution as described in the section "Python Virtual Environment".
There are several versions of Python available. Replace python3
with python2
or pypy
in the above snippet according to your needs.
Explanation (optional)
We defined a function my-python-packages
which takes as input a set python-packages
and returns a list of attributes thereof.
Using alternative packages
We saw above how to install Python packages using nixpkgs. Since these are written by hand by nixpkgs maintainers, it isn't uncommon for packages you want to be missing or out of date. To create a custom Python environment with your own package(s), first create a derivation for each python package (look at examples in the python-modules
subfolder in Nixpkgs). Then, use those derivations with callPackage
as follows:
with pkgs;
let
my-python-package = ps: ps.callPackage ./my-package.nix {};
python-with-my-packages = python3.withPackages(ps: with ps; [
(my-python-package ps)
]);
in ...
Package and development shell for a python project
It is possible to use buildPythonApplication
to package python applications. As explained in the nixpkgs manual, it uses the widely used `setup.py` file in order to package properly the application. We now show how to package a simple python application: a basic flask web server.
First, we write the python code, say in a file web_interface.py
. Here we create a basic flask web server;
#!/usr/bin/env python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8080)
Then, we create the setup.py
file, which basically explains which are the executables:
#!/usr/bin/env python
from setuptools import setup, find_packages
setup(name='demo-flask-vuejs-rest',
version='1.0',
# Modules to import from other scripts:
packages=find_packages(),
# Executables
scripts=["web_interface.py"],
)
Finally, our nix derivation is now trivial: the file derivation.nix
just needs to provide the python packages (here flask):
{ lib, python3Packages }:
with python3Packages;
buildPythonApplication {
pname = "demo-flask-vuejs-rest";
version = "1.0";
propagatedBuildInputs = [ flask ];
src = ./.;
}
and we can now load this derivation from our file default.py
:
{ pkgs ? import <nixpkgs> {} }:
pkgs.callPackage ./derivation.nix {}
We can now build with:
$ nix-build
[...]
$ ./result/bin/web_interface.py
* Serving Flask app ".web_interface" (lazy loading)
[...]
or just enter a nix-shell, and directly execute your program or python if it's easier to develop:
$ nix-shell
[...]
[nix-shell]$ chmod +x web_interface.py
[nix-shell]$ ./web_interface.py
* Serving Flask app "web_interface" (lazy loading)
[...]
[nix-shell]$ python
Python 3.8.7 (default, Dec 21 2020, 17:18:55)
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import flask
>>>
Python virtual environment
Starting from Python 3 virtual environment is natively supported. The Python 3 venv approach has the benefit of forcing you to choose a specific version of the Python 3 interpreter that should be used to create the virtual environment. This avoids any confusion as to which Python installation the new environment is based on.
Recommended usage:
- Python 3.3-3.4 (old): the recommended way to create a virtual environment was to use the pyvenv command-line tool that also comes included with your Python 3 installation by default.
- Python 3.6+:
python3 -m venv
is the way to go.
Put your packages in a requirements.txt:
pandas
requests
Then setup the virtualenv:
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Emulating virtualenv with nix-shell
In some cases virtualenv fails to install a library because it requires patching on NixOS (example 1, example 2, general issue). In this cases it is better to replace those libraries with ones from Nix.
Let's say, that nanomsg library fails to install in virtualenv. Then write a shell.nix
file:
let
pkgs = import <nixpkgs> {};
nanomsg-py = ...build expression for this python library...;
in pkgs.mkShell {
buildInputs = [
pkgs.python3
pkgs.python3.pkgs.requests
nanomsg-py
];
shellHook = ''
# Tells pip to put packages into $PIP_PREFIX instead of the usual locations.
# See https://pip.pypa.io/en/stable/user_guide/#environment-variables.
export PIP_PREFIX=$(pwd)/_build/pip_packages
export PYTHONPATH="$PIP_PREFIX/${pkgs.python3.sitePackages}:$PYTHONPATH"
export PATH="$PIP_PREFIX/bin:$PATH"
unset SOURCE_DATE_EPOCH
'';
}
After entering the environment with `nix-shell`, you can install new python libraries with dump `pip install`, but nanomsg will be detected as installed.
Discussion and consequences of this approach are in PR https://github.com/NixOS/nixpkgs/pull/55265.
mach-nix
Install the package
nix-env -if https://github.com/DavHau/mach-nix/tarball/3.1.1 -A mach-nix
or
pip install git+git://github.com/DavHau/mach-nix@3.1.1
and run
mach-nix env ./env -r requirements.txt
# This will generate the python environment into ./env. To activate it, execute:
nix-shell ./env
or
# nix.extraOptions = '' experimental-features = nix-command flakes '';
nix-shell -p nixFlakes --run "nix run github:davhau/mach-nix#with.ipython.geopandas --show-trace "
cmd-line tool:
let
mach-nix = import (builtins.fetchGit {
url = "https://github.com/DavHau/mach-nix/";
ref = "refs/tags/3.1.1";
}) {};
in
mach-nix.mkPython {
requirements = ''
pillow
numpy
requests
'';
}
Please have a look at more examples.
conda
Install the package conda
and run
conda-shell
conda-install
conda env update --file environment.yml
Imperative use
It is also possible to use conda-install
directly.
On first use, run
conda-shell
conda-install
to set up conda in ~/.conda
pypi2nix
The pypi2nix project is abandoned. Also see the pip2nix project.
Contribution guidelines
Libraries
According to the official guidelines for python new package expressions for libraries should be placed in pkgs/development/python-modules/<name>/default.nix
. Those expressions are then referenced from pkgs/top-level/python-packages.nix
like in this example:
{
aenum = callPackage ../development/python-modules/aenum { };
}
The reasoning behind this is the large size of pkgs/top-level/python-packages.nix
.
Applications
Python applications instead should be referenced directly from pkgs/top-level/all-packages.nix
.
The expression should take pythonPackages
as one of the arguments, which guarantees that packages belong to the same set. For example:
{ lib
, pythonPackages
}:
with pythonPackages;
buildPythonApplication rec {
# ...
Special Modules
GNOME
gobject-introspection
based python modules need some environment variables to work correctly. For standalone
applications, wrapGAppsHook
(see the relevant documentation) wraps the executable with the necessary variables. But this is not fit for development.
In this case use a nix-shell
with gobject-introspection
and all the libraries you are using (gtk and so on) as buildInputs
.
For example:
$ nix-shell -p gobjectIntrospection gtk3 'python2.withPackages (ps: with ps; [ pygobject3 ])' --run "python -c \"import pygtkcompat; pygtkcompat.enable_gtk(version='3.0')\""
Or, if you want to use matplotlib interactively:
$ nix-shell -p gobjectIntrospection gtk3 'python36.withPackages(ps : with ps; [ matplotlib pygobject3 ipython ])'
$ ipython
In [1]: import matplotlib
In [2]: matplotlib.use('gtk3agg')
In [3]: import matplotlib.pyplot as plt
In [4]: plt.ion()
In [5]: plt.plot([1,3,2,4])
You can also set backend : GTK3Agg
in your ~/.config/matplotlib/matplotlibrc
file to avoid having to call matplotlib.use('gtk3agg')
.