From 0a3aaad7438d00ab907f3c0e83ee50565fab8097 Mon Sep 17 00:00:00 2001 From: gquetel Date: Sun, 10 May 2026 22:34:07 +0200 Subject: [PATCH] python3Packages.mlflow: build from PyPI wheel, fix server subprocess --- .../python-modules/mlflow/default.nix | 39 +++++++++++-------- .../mlflow/subprocess-pythonpath.patch | 14 +++++++ 2 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 pkgs/development/python-modules/mlflow/subprocess-pythonpath.patch diff --git a/pkgs/development/python-modules/mlflow/default.nix b/pkgs/development/python-modules/mlflow/default.nix index 8f3d77ca8b50..6804cae7e86a 100644 --- a/pkgs/development/python-modules/mlflow/default.nix +++ b/pkgs/development/python-modules/mlflow/default.nix @@ -1,10 +1,7 @@ { lib, buildPythonPackage, - fetchFromGitHub, - - # build-system - setuptools, + fetchPypi, # dependencies aiohttp, @@ -31,26 +28,32 @@ buildPythonPackage (finalAttrs: { pname = "mlflow"; version = "3.12.0"; - pyproject = true; + format = "wheel"; __structuredAttrs = true; - src = fetchFromGitHub { - owner = "mlflow"; - repo = "mlflow"; - tag = "v${finalAttrs.version}"; - hash = "sha256-OxhM+KCem0sb9cwtyzrUD/MGfoiiCfgU47qipYRDaFk="; + # We build from the PyPI wheel rather than fetchFromGitHub, because the mlflow-server + # JS UI is absent from GitHub but provided in the wheel. + src = fetchPypi { + pname = "mlflow"; + inherit (finalAttrs) version; + format = "wheel"; + dist = "py3"; + python = "py3"; + hash = "sha256-4cKO1MSFV8xSx2bxfxylgmdT3fJB1D8w+ZxF9+prPOA="; }; - # ppyproject.release.toml is the one shipped in the Pypi package, so we use it too. - postPatch = '' - mv pyproject.release.toml pyproject.toml + # Nix-wrapped python populates sys.path via NIX_PYTHONPATH/site hooks, + # but PYTHONPATH stays unset in os.environ. mlflow spawns the server + # in a subprocess with a curated env, so without this patch the child + # interpreter cannot import uvicorn / mlflow itself. + postInstall = '' + patch -p1 -d "$out/lib/python"*/site-packages < ${./subprocess-pythonpath.patch} ''; - build-system = [ setuptools ]; - pythonRelaxDeps = [ "cryptography" ]; + dependencies = [ aiohttp alembic @@ -85,8 +88,12 @@ buildPythonPackage (finalAttrs: { description = "Open source platform for the machine learning lifecycle"; mainProgram = "mlflow"; homepage = "https://github.com/mlflow/mlflow"; - changelog = "https://github.com/mlflow/mlflow/blob/${finalAttrs.src.tag}/CHANGELOG.md"; + changelog = "https://github.com/mlflow/mlflow/blob/v${finalAttrs.version}/CHANGELOG.md"; license = lib.licenses.asl20; + # Build from wheel which contains pure Python and pre-built JS bundle. + sourceProvenance = with lib.sourceTypes; [ + binaryBytecode + ]; maintainers = with lib.maintainers; [ GaetanLepage ]; diff --git a/pkgs/development/python-modules/mlflow/subprocess-pythonpath.patch b/pkgs/development/python-modules/mlflow/subprocess-pythonpath.patch new file mode 100644 index 000000000000..9e97218fdf92 --- /dev/null +++ b/pkgs/development/python-modules/mlflow/subprocess-pythonpath.patch @@ -0,0 +1,14 @@ +--- a/mlflow/server/__init__.py ++++ b/mlflow/server/__init__.py +@@ -471,6 +471,11 @@ + else tempfile.mkdtemp() + ) ++ # In Nix-packaged environments sys.path is populated by wrappers but ++ # PYTHONPATH is never set in os.environ, so subprocesses (uvicorn, ++ # gunicorn) cannot find packages. Propagate it when not already set. ++ if "PYTHONPATH" not in os.environ: ++ env_map.setdefault("PYTHONPATH", os.pathsep.join(p for p in sys.path if p)) + + server_proc = _exec_cmd( + full_command, + extra_env=env_map,