Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

General infrastructure for packaging Python packages that use PyO3 / maturin to build native Rust extensions #83614

Closed
glittershark opened this issue Mar 28, 2020 · 17 comments
Assignees
Labels

Comments

@glittershark
Copy link
Member

I've encountered two separate (not currently provided by nixpkgs) python packages that are built with maturin in the last week - orjson and tokenizers - and I can only imagine both that there are more out there, and that more are coming. I have orjson packaged very jankily by building it as a Rust crate directly using naersk, then stripping off the env vars from the resulting derivation and throwing them unceremoniously into buildPythonPackage. That works, but not in a way that I'd even dream of submitting it to nixpkgs - now that I've encountered my second package built using maturin it really is beginning to feel like this is something that buildPythonPackage should "just" handle transparently, the way it does Python packages with C extensions built-in. Anyone have any thoughts?

@glittershark glittershark added the 0.kind: packaging request Request for a new package to be added label Mar 28, 2020
@FRidh
Copy link
Member

FRidh commented Mar 28, 2020

In the Python world it is common that a back-end or build tool (often setuptools but also meson) produces a wheel, and a front-end (typically pip) installs it. buildPythonPackage does this using hooks.

I'm unfamiliar with the packages in question. I suppose it's some extension modules, and then some Python code. Ideally a build tool is used that can build a wheel. If not, it indeed becomes hacky.

@FRidh
Copy link
Member

FRidh commented Mar 28, 2020

So orjson has a pyproject.toml. This is the new way for Python packaging. buildPythonPackage supports, just pass format = "pyproject";. You then need to include this maturin in nativeBuildInputs.

@bbigras
Copy link
Contributor

bbigras commented Jun 25, 2020

@FRidh any ideas why I get ModuleNotFoundError: No module named 'maturin' ?

Should I try to add it to PATH?

EDIT: feel free to ping me on #nixos if you want me to test anything.

default.nix

let
  nixpkgs = import <nixpkgs> {};
in
nixpkgs.python3Packages.buildPythonPackage rec {
  name = "something-${version}";
  version = "0.0.1";
  format = "pyproject";

  nativeBuildInputs = [ nixpkgs.maturin ];

  src = ./.;
}

pyproject.toml

[build-system]
requires = ["maturin"]
build-backend = "maturin"

log

❯ nix-build .
these derivations will be built:
  /nix/store/4q9c2gja115rjyricl05x47v8c8gg6d8-python3.8-something-0.0.1.drv
building '/nix/store/4q9c2gja115rjyricl05x47v8c8gg6d8-python3.8-something-0.0.1.drv'...
Sourcing python-recompile-bytecode-hook.sh
Sourcing python-remove-tests-dir-hook
Sourcing python-catch-conflicts-hook.sh
Sourcing python-remove-bin-bytecode-hook.sh
Sourcing pip-build-hook
Using pipBuildPhase
Using pipShellHook
Sourcing pip-install-hook
Using pipInstallPhase
Sourcing python-imports-check-hook.sh
Using pythonImportsCheckPhase
Sourcing python-namespaces-hook
unpacking sources
unpacking source archive /nix/store/9b60bppkmhghwvfjwag5z2lm5xvxhvff-maturin
source root is maturin
setting SOURCE_DATE_EPOCH to timestamp 315619200 of file maturin/pyproject.toml
patching sources
configuring
no configure script, doing nothing
building
Executing pipBuildPhase
Creating a wheel...
WARNING: The directory '/homeless-shelter/.cache/pip' or its parent directory is not owned or is not writable by the current user. The cache has been disabled. Check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Processing /build/maturin
    Preparing wheel metadata ... done
ERROR: Exception:
Traceback (most recent call last):
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/cli/base_command.py", line 188, in _main
    status = self.run(options, args)
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/cli/req_command.py", line 185, in wrapper
    return func(self, options, args)
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/commands/wheel.py", line 159, in run
    requirement_set = resolver.resolve(
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/resolution/legacy/resolver.py", line 179, in resolve
    discovered_reqs.extend(self._resolve_one(requirement_set, req))
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/resolution/legacy/resolver.py", line 362, in _resolve_one
    abstract_dist = self._get_abstract_dist_for(req_to_install)
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/resolution/legacy/resolver.py", line 314, in _get_abstract_dist_for
    abstract_dist = self.preparer.prepare_linked_requirement(req)
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/operations/prepare.py", line 487, in prepare_linked_requirement
    abstract_dist = _get_prepared_distribution(
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/operations/prepare.py", line 91, in _get_prepared_distribution
    abstract_dist.prepare_distribution_metadata(finder, build_isolation)
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/distributions/sdist.py", line 40, in prepare_distribution_metadata
    self.req.prepare_metadata()
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/req/req_install.py", line 550, in prepare_metadata
    self.metadata_directory = self._generate_metadata()
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/req/req_install.py", line 535, in _generate_metadata
    return generate_metadata(
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_internal/operations/build/metadata.py", line 36, in generate_metadata
    distinfo_dir = backend.prepare_metadata_for_build_wheel(
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_vendor/pep517/wrappers.py", line 176, in prepare_metadata_for_build_wheel
    return self._call_hook('prepare_metadata_for_build_wheel', {
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_vendor/pep517/wrappers.py", line 265, in _call_hook
    raise BackendUnavailable(data.get('traceback', ''))
pip._vendor.pep517.wrappers.BackendUnavailable: Traceback (most recent call last):
  File "/nix/store/8cxkx34jv9jbwxzb5aix53f1bjrpy6aj-python3.8-pip-20.1.1/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py", line 86, in _build_backend
    obj = import_module(mod_path)
  File "/nix/store/jli50q24fvik6pfsnjs1jd90g3dy4p6y-python3-3.8.3/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 973, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'maturin'


builder for '/nix/store/4q9c2gja115rjyricl05x47v8c8gg6d8-python3.8-something-0.0.1.drv' failed with exit code 2
error: build of '/nix/store/4q9c2gja115rjyricl05x47v8c8gg6d8-python3.8-something-0.0.1.drv' failed

@martinetd
Copy link
Member

Hi @bbigras.
Have you tried again since then?

I'm working on anki update (#78449), and since there is a Cargo.toml/Cargo.lock I found it easier to package that with buildRustPackage which does all the heavy lifting on the rust side.
The final product is a *-cp38-cp38-manylinux1_x86_64.whl file (that's a wheel?) so definitely a python thing, not sure what I'm missing out by not using buildPythonPackage on that front... It's for a pythonApplication which aren't gated by python subversion so I don't really care about the parallel availability within each python namespace but there might be other problems?

Here's what it (currently) looks like: https://github.com/martinetd/nixpkgs/blob/anki/pkgs/games/anki/rspy/default.nix

Now I'm finally done with that, I just noticed I actually need orjson next, so if I publish my work on anki so our interests might coincide a bit.
I'm not sure how clean that is but for now I'll just try doing something similar for it and adding it to pkgs/top-level/python-packages.nix as a rustPackage.. Let's see how that fares and how I can reuse that.

Comments/yells/whatever welcome -- I'll probably open a PR just for orjson to test the water :P

@martinetd
Copy link
Member

gah, orjson requires rust nightly! I don't think we can package anything using nightly? And the common workarounds I found don't work... ugh.. I guess it's about time I gave up on an up to date version of anki as well -_-

Sorry for the noise, keep going, nothing to see here!

@FRidh
Copy link
Member

FRidh commented Oct 26, 2020

Had a go at this in #101771.

@bbigras
Copy link
Contributor

bbigras commented Oct 28, 2020

Have you tried again since then?

I still have the same message with the same default.nix and pyproject.toml, without any python files. I wonder if that could be the cause.

@primeos
Copy link
Member

primeos commented Feb 8, 2021

I haven't completely analyzed the situation yet but this will likely be required for Nixpkgs soon as python3Packages.cryptography just introduced a Rust dependency in version 3.4: https://cryptography.io/en/latest/changelog.html#v3-4 (though it looks like we can temporarily disable it for 3.4.x: #112402, https://github.com/pyca/cryptography/blob/3.4.1/setup.py#L45)

Unfortunately it also looks like it needs to fetch some Rust dependencies which might be pretty tricky in Nixpkgs:

I'm not yet familiar with PyO3 and maturin but at least PyO3 and PEP 517 seem to be relevant for Cryptography.

@SuperSandro2000
Copy link
Member

There is also #108447

@FRidh
Copy link
Member

FRidh commented Feb 8, 2021

Upstream cryptography issue pyca/cryptography#5771.

setuptools-rust is problematic. Not only does it want to use cargo to build rust, which is fine, it also wants to fetch dependencies. Builds should not fetch dependencies.

@FRidh
Copy link
Member

FRidh commented Feb 8, 2021

Opened a topic on the packaging forum to see how other distributors handle this.

@FRidh
Copy link
Member

FRidh commented Feb 8, 2021

Example from @danieldk packaging a Python package using maturin https://github.com/danieldk/nix-packages/blob/81b8444e6864c546cd8f41f393d65fe693c27ca0/pkgs/python-modules/alpino-tokenizer/default.nix#L13

If the wheel would be moved to dist/ in the buildPhase, then pipInstallHook could be used.

@danieldk
Copy link
Contributor

danieldk commented Feb 9, 2021

setuptools-rust is problematic. Not only does it want to use cargo to build rust, which is fine, it also wants to fetch dependencies. Builds should not fetch dependencies.

tokenizers switched from maturin to setuptools-rust. We currently use the same approach as with maturin-based packages (let buildRustPackage do the fetching + set up the rust environment):

in rustPlatform.buildRustPackage rec {

@danieldk
Copy link
Contributor

danieldk commented Feb 9, 2021

I have made the cargo vendor handling a hook and I can now build the tokenizers package with buildPythonPackage and without any of the Rust platform stuff. I have to clean it up a bit and integrate the hook in the regular buildRustPackage as well and do a PR.

@FRidh
Copy link
Member

FRidh commented Feb 9, 2021

Great, thank you @danieldk !

@danieldk
Copy link
Contributor

danieldk commented Feb 9, 2021

Draft PR: #112500

@FRidh
Copy link
Member

FRidh commented Mar 13, 2021

Closing this as done.

@FRidh FRidh closed this as completed Mar 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants