diff --git a/pkgs/development/python-modules/django-cache-memoize/default.nix b/pkgs/development/python-modules/django-cache-memoize/default.nix new file mode 100644 index 0000000000000..440bad6c27da8 --- /dev/null +++ b/pkgs/development/python-modules/django-cache-memoize/default.nix @@ -0,0 +1,53 @@ +{ + lib, + buildPythonPackage, + fetchFromGitHub, + setuptools, + django, + pytestCheckHook, + pytest-cov-stub, + pytest-django, + nix-update-script, +}: + +buildPythonPackage rec { + pname = "django-cache-memoize"; + version = "0.2.1"; + pyproject = true; + + src = fetchFromGitHub { + owner = "peterbe"; + repo = "django-cache-memoize"; + # No tags. See . + rev = "9a0dc28315b9bd2848973d38b6f63a400a0e0526"; + hash = "sha256-oORTN53s9GVHiY9tbx5FKb7ygkYUKWgPRJusdB0RfcA="; + }; + + build-system = [ + setuptools + ]; + + dependencies = [ + django + ]; + + nativeCheckInputs = [ + pytestCheckHook + pytest-cov-stub + pytest-django + ]; + + pythonImportsCheck = [ "cache_memoize" ]; + + passthru.updateScript = nix-update-script { + extraArgs = [ "--version=branch" ]; + }; + + meta = { + description = "Django utility for a memoization decorator that uses the Django cache framework"; + homepage = "https://github.com/peterbe/django-cache-memoize"; + changelog = "https://github.com/peterbe/django-cache-memoize/blob/${src.rev}/CHANGELOG.rst"; + license = lib.licenses.mpl20; + teams = [ lib.teams.ngi ]; + }; +} diff --git a/pkgs/development/python-modules/lb-matching-tools/default.nix b/pkgs/development/python-modules/lb-matching-tools/default.nix new file mode 100644 index 0000000000000..856a641689eca --- /dev/null +++ b/pkgs/development/python-modules/lb-matching-tools/default.nix @@ -0,0 +1,41 @@ +{ + lib, + buildPythonPackage, + fetchFromGitHub, + setuptools, + setuptools-scm, + regex, + pytestCheckHook, +}: + +buildPythonPackage rec { + pname = "lb-matching-tools"; + version = "2024.01.30.1"; + pyproject = true; + + src = fetchFromGitHub { + owner = "metabrainz"; + repo = "listenbrainz-matching-tools"; + tag = "v${version}"; + hash = "sha256-RQ4X6DKigQsNxaAWXB1meATKP+ddMUgkoAIyX8iIisU="; + }; + + build-system = [ + setuptools + setuptools-scm + ]; + + dependencies = [ regex ]; + + nativeCheckInputs = [ pytestCheckHook ]; + + pythonImportsCheck = [ "lb_matching_tools" ]; + + meta = { + description = "ListenBrainz tools for matching metadata to and from MusicBrainz"; + homepage = "https://github.com/metabrainz/listenbrainz-matching-tools"; + changelog = "https://github.com/metabrainz/listenbrainz-matching-tools/releases/tag/${src.tag}"; + license = lib.licenses.gpl2Plus; + teams = [ lib.teams.ngi ]; + }; +} diff --git a/pkgs/development/python-modules/liblistenbrainz/default.nix b/pkgs/development/python-modules/liblistenbrainz/default.nix new file mode 100644 index 0000000000000..efa5e20e68928 --- /dev/null +++ b/pkgs/development/python-modules/liblistenbrainz/default.nix @@ -0,0 +1,45 @@ +{ + lib, + buildPythonPackage, + fetchFromGitHub, + setuptools, + setuptools-scm, + requests, + pytestCheckHook, + requests-mock, +}: + +buildPythonPackage rec { + pname = "liblistenbrainz"; + version = "0.6.0"; + pyproject = true; + + src = fetchFromGitHub { + owner = "metabrainz"; + repo = "liblistenbrainz"; + tag = "v${version}"; + hash = "sha256-mUw+x9SEHrPocZRdtazqInMGLBDv1KUR5/mmfF3CbVg="; + }; + + build-system = [ + setuptools + setuptools-scm + ]; + + dependencies = [ requests ]; + + nativeCheckInputs = [ + pytestCheckHook + requests-mock + ]; + + pythonImportsCheck = [ "liblistenbrainz" ]; + + meta = { + description = "Simple ListenBrainz client library for Python"; + homepage = "https://github.com/metabrainz/liblistenbrainz"; + changelog = "https://github.com/metabrainz/liblistenbrainz/releases/tag/${src.tag}"; + license = lib.licenses.gpl3Plus; + teams = [ lib.teams.ngi ]; + }; +} diff --git a/pkgs/development/python-modules/pluralizer/default.nix b/pkgs/development/python-modules/pluralizer/default.nix new file mode 100644 index 0000000000000..b798c810ac7e0 --- /dev/null +++ b/pkgs/development/python-modules/pluralizer/default.nix @@ -0,0 +1,34 @@ +{ + lib, + buildPythonPackage, + fetchFromGitHub, + hatchling, + pytestCheckHook, +}: + +buildPythonPackage rec { + pname = "pluralizer"; + version = "2.0.0"; + pyproject = true; + + src = fetchFromGitHub { + owner = "weixu365"; + repo = "pluralizer-py"; + tag = "v${version}"; + hash = "sha256-2m7E4cwAdmny/5R5FqaCzk8mu9so/ZCgNPBckTdIc/0="; + }; + + build-system = [ hatchling ]; + + nativeCheckInputs = [ pytestCheckHook ]; + + pythonImportsCheck = [ "pluralizer" ]; + + meta = { + description = "Singularize or pluralize a given word using a pre-defined list of rules"; + homepage = "https://github.com/weixu365/pluralizer-py"; + changelog = "https://github.com/weixu365/pluralizer-py/releases/tag/${src.tag}"; + license = lib.licenses.mit; + teams = [ lib.teams.ngi ]; + }; +} diff --git a/pkgs/development/python-modules/python-ffmpeg/default.nix b/pkgs/development/python-modules/python-ffmpeg/default.nix index 1991f74926670..0641ab386b5fe 100644 --- a/pkgs/development/python-modules/python-ffmpeg/default.nix +++ b/pkgs/development/python-modules/python-ffmpeg/default.nix @@ -1,24 +1,46 @@ { lib, buildPythonPackage, + fetchFromGitHub, + ffmpeg-headless, + setuptools, pyee, - fetchPypi, - setuptools-scm, + pytestCheckHook, + pytest-asyncio, }: buildPythonPackage rec { - pname = "python_ffmpeg"; + pname = "python-ffmpeg"; version = "2.0.12"; pyproject = true; - src = fetchPypi { - inherit pname version; - sha256 = "GayAr1oGSi9TwkWvGpCbLXZI6gRVANltO81Qe4jUPcc="; + src = fetchFromGitHub { + owner = "jonghwanhyeon"; + repo = "python-ffmpeg"; + tag = "v${version}"; + hash = "sha256-1dhkjrg7QUtYSyEV9c88HphdcFuSCSaGJqVAQmMF/5E="; }; - propagatedBuildInputs = [ pyee ]; + postPatch = '' + substituteInPlace ffmpeg/{ffmpeg.py,asyncio/ffmpeg.py,protocol.py} \ + --replace-fail 'executable: str = "ffmpeg"' 'executable: str = "${lib.getExe ffmpeg-headless}"' + substituteInPlace tests/helpers.py \ + --replace-fail '"ffprobe"' '"${lib.getExe' ffmpeg-headless "ffprobe"}"' + + # Some systems can finish before the `0.1` timeout. + substituteInPlace tests/test_{,asyncio_}timeout.py \ + --replace-fail 'ffmpeg.execute(timeout=0.1)' 'ffmpeg.execute(timeout=0.01)' + ''; + + build-system = [ setuptools ]; + + dependencies = [ pyee ]; + + nativeCheckInputs = [ + pytestCheckHook + pytest-asyncio + ]; - nativeBuildInputs = [ setuptools-scm ]; pythonImportsCheck = [ "ffmpeg" ]; meta = { diff --git a/pkgs/development/python-modules/requests-http-message-signatures/default.nix b/pkgs/development/python-modules/requests-http-message-signatures/default.nix new file mode 100644 index 0000000000000..94662782c8272 --- /dev/null +++ b/pkgs/development/python-modules/requests-http-message-signatures/default.nix @@ -0,0 +1,42 @@ +{ + lib, + buildPythonPackage, + fetchFromGitLab, + poetry-core, + cryptography, + requests, +}: + +buildPythonPackage rec { + pname = "requests-http-message-signatures"; + version = "0.3.0"; + pyproject = true; + + src = fetchFromGitLab { + domain = "dev.funkwhale.audio"; + owner = "funkwhale"; + repo = "requests-http-message-signatures"; + tag = version; + hash = "sha256-1GObY+bF5wwgjDORUlO61bmIadK+EpZtyYGMgS9Bqzg="; + }; + + build-system = [ poetry-core ]; + + dependencies = [ + cryptography + requests + ]; + + # Tests require network access. + doCheck = false; + + pythonImportsCheck = [ "requests_http_message_signatures" ]; + + meta = { + description = "Request authentication plugin implementing IETF HTTP Message Signatures"; + homepage = "https://dev.funkwhale.audio/funkwhale/requests-http-message-signatures"; + changelog = "https://dev.funkwhale.audio/funkwhale/requests-http-message-signatures/-/blob/${src.tag}/CHANGELOG.md"; + license = lib.licenses.asl20; + teams = [ lib.teams.ngi ]; + }; +} diff --git a/pkgs/development/python-modules/troi/default.nix b/pkgs/development/python-modules/troi/default.nix new file mode 100644 index 0000000000000..32367c46f5390 --- /dev/null +++ b/pkgs/development/python-modules/troi/default.nix @@ -0,0 +1,89 @@ +{ + lib, + buildPythonPackage, + fetchFromGitHub, + setuptools, + setuptools-scm, + click, + lb-matching-tools, + liblistenbrainz, + more-itertools, + mutagen, + peewee, + psycopg2-binary, + py-sonic, + python-dateutil, + regex, + requests, + scikit-learn, + spotipy, + tqdm, + ujson, + unidecode, + pytestCheckHook, + requests-mock, +}: + +buildPythonPackage rec { + pname = "troi"; + version = "2025.08.06.3"; + pyproject = true; + + src = fetchFromGitHub { + owner = "metabrainz"; + repo = "troi-recommendation-playground"; + tag = "v${version}"; + hash = "sha256-qLnXaNb1Kon+XPJYCPe31EgXpukIfzTa+LADOzFjE9Q="; + }; + + build-system = [ + setuptools + setuptools-scm + ]; + + pythonRelaxDeps = [ "mutagen" ]; + pythonRemoveDeps = [ + # It's not used anywhere in the code. + # TODO: Remove in next update. See + "countryinfo" + ]; + + dependencies = [ + click + lb-matching-tools + liblistenbrainz + more-itertools + mutagen + peewee + psycopg2-binary + py-sonic + python-dateutil + regex + requests + scikit-learn + spotipy + tqdm + ujson + unidecode + ]; + + optional-dependencies = { + # Not packaged yet. + # nmslib = [ "nmslib-metabrainz" ]; + }; + + nativeCheckInputs = [ + pytestCheckHook + requests-mock + ]; + + pythonImportsCheck = [ "troi" ]; + + meta = { + description = "ListenBrainz' empathic music recommendation/playlisting engine"; + homepage = "https://github.com/metabrainz/troi-recommendation-playground"; + changelog = "https://github.com/metabrainz/troi-recommendation-playground/releases/tag/${src.tag}"; + license = lib.licenses.gpl2Only; + teams = [ lib.teams.ngi ]; + }; +} diff --git a/pkgs/development/python-modules/typesense/default.nix b/pkgs/development/python-modules/typesense/default.nix new file mode 100644 index 0000000000000..78441621aa8dc --- /dev/null +++ b/pkgs/development/python-modules/typesense/default.nix @@ -0,0 +1,78 @@ +{ + lib, + buildPythonPackage, + fetchFromGitHub, + setuptools, + requests, + pytestCheckHook, + typesense, + curl, + pytest-mock, + requests-mock, + python-dotenv, + faker, + isort, +}: + +buildPythonPackage rec { + pname = "typesense"; + version = "1.1.1"; + pyproject = true; + + src = fetchFromGitHub { + owner = "typesense"; + repo = "typesense-python"; + tag = "v${version}"; + hash = "sha256-vo9DW4kinb00zWW4yX8ibyelQxW3eVabn+oMddPEd18="; + }; + + patches = [ + # See . + ./linux-only-metrics.patch + ./generated-temp-path.patch + ]; + + build-system = [ setuptools ]; + + dependencies = [ requests ]; + + nativeCheckInputs = [ + pytestCheckHook + typesense + curl + pytest-mock + requests-mock + python-dotenv + faker + isort + ]; + disabledTestMarks = [ "open_ai" ]; + disabledTests = [ "import_typing_extensions" ]; + + __darwinAllowLocalNetworking = true; + + preCheck = '' + TYPESENSE_API_KEY="xyz" \ + TYPESENSE_DATA_DIR="$(mktemp -d)" \ + typesense-server & + + typesense_pid=$! + + # Wait for typesense to finish starting. + timeout 20 bash -c ' + while ! curl -s --fail localhost:8108/health; do sleep 1; done + ' || false + ''; + postCheck = '' + kill $typesense_pid + ''; + + pythonImportsCheck = [ "typesense" ]; + + meta = { + description = "Python client for Typesense, an open source and typo tolerant search engine"; + homepage = "https://github.com/typesense/typesense-python"; + license = lib.licenses.asl20; + teams = [ lib.teams.ngi ]; + }; +} diff --git a/pkgs/development/python-modules/typesense/generated-temp-path.patch b/pkgs/development/python-modules/typesense/generated-temp-path.patch new file mode 100644 index 0000000000000..ddb15e2ea64de --- /dev/null +++ b/pkgs/development/python-modules/typesense/generated-temp-path.patch @@ -0,0 +1,18 @@ +diff --git a/tests/operations_test.py b/tests/operations_test.py +index 34bb74c..39f9265 100644 +--- a/tests/operations_test.py ++++ b/tests/operations_test.py +@@ -51,11 +51,11 @@ def test_cache_clear(actual_operations: Operations) -> None: + assert response["success"] + + +-def test_snapshot(actual_operations: Operations) -> None: ++def test_snapshot(actual_operations: Operations, tmp_path) -> None: + """Test that the Operations object can perform the snapshot operation.""" + response = actual_operations.perform( + "snapshot", +- {"snapshot_path": "/tmp"}, # noqa: S108 ++ {"snapshot_path": str(tmp_path)}, # noqa: S108 + ) + + assert response["success"] diff --git a/pkgs/development/python-modules/typesense/linux-only-metrics.patch b/pkgs/development/python-modules/typesense/linux-only-metrics.patch new file mode 100644 index 0000000000000..ac667a3e42f14 --- /dev/null +++ b/pkgs/development/python-modules/typesense/linux-only-metrics.patch @@ -0,0 +1,48 @@ +From 9f0024ae3a0377d15788256c72af7a692d250afc Mon Sep 17 00:00:00 2001 +From: dotlambda +Date: Thu, 23 Oct 2025 13:40:33 -0700 +Subject: [PATCH] test(metrics): some metrics only exists on Linux + +--- + tests/metrics_test.py | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/tests/metrics_test.py b/tests/metrics_test.py +index 1e1ea47..fecf3c0 100644 +--- a/tests/metrics_test.py ++++ b/tests/metrics_test.py +@@ -2,6 +2,8 @@ + + from __future__ import annotations + ++import platform ++ + from typesense.metrics import Metrics + + +@@ -9,13 +11,15 @@ def test_actual_retrieve(actual_metrics: Metrics) -> None: + """Test that the Debug object can retrieve a debug on Typesense server and verify response structure.""" + response = actual_metrics.retrieve() + +- assert "system_cpu_active_percentage" in response ++ if platform.system() == "Linux": ++ assert "system_cpu_active_percentage" in response ++ assert "system_network_received_bytes" in response ++ assert "system_network_sent_bytes" in response ++ + assert "system_disk_total_bytes" in response + assert "system_disk_used_bytes" in response + assert "system_memory_total_bytes" in response + assert "system_memory_used_bytes" in response +- assert "system_network_received_bytes" in response +- assert "system_network_sent_bytes" in response + assert "typesense_memory_active_bytes" in response + assert "typesense_memory_allocated_bytes" in response + assert "typesense_memory_fragmentation_ratio" in response +@@ -23,4 +27,4 @@ def test_actual_retrieve(actual_metrics: Metrics) -> None: + assert "typesense_memory_mapped_bytes" in response + assert "typesense_memory_metadata_bytes" in response + assert "typesense_memory_resident_bytes" in response +- assert "typesense_memory_retained_bytes" in response +\ No newline at end of file ++ assert "typesense_memory_retained_bytes" in response diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix index fb31006dc9404..97883ea18093a 100644 --- a/pkgs/top-level/python-packages.nix +++ b/pkgs/top-level/python-packages.nix @@ -3866,6 +3866,8 @@ self: super: with self; { django-cachalot = callPackage ../development/python-modules/django-cachalot { }; + django-cache-memoize = callPackage ../development/python-modules/django-cache-memoize { }; + django-cache-url = callPackage ../development/python-modules/django-cache-url { }; django-cacheops = callPackage ../development/python-modules/django-cacheops { }; @@ -8089,6 +8091,8 @@ self: super: with self; { lazy-object-proxy = callPackage ../development/python-modules/lazy-object-proxy { }; + lb-matching-tools = callPackage ../development/python-modules/lb-matching-tools { }; + lc7001 = callPackage ../development/python-modules/lc7001 { }; lcd-i2c = callPackage ../development/python-modules/lcd-i2c { }; @@ -8268,6 +8272,8 @@ self: super: with self; { liblarch = callPackage ../development/python-modules/liblarch { }; + liblistenbrainz = callPackage ../development/python-modules/liblistenbrainz { }; + liblp = callPackage ../development/python-modules/liblp { }; liblzfse = callPackage ../development/python-modules/liblzfse { inherit (pkgs) lzfse; }; @@ -11989,6 +11995,8 @@ self: super: with self; { plumlightpad = callPackage ../development/python-modules/plumlightpad { }; + pluralizer = callPackage ../development/python-modules/pluralizer { }; + pluthon = callPackage ../development/python-modules/pluthon { }; plux = callPackage ../development/python-modules/plux { }; @@ -15945,6 +15953,10 @@ self: super: with self; { requests-hawk = callPackage ../development/python-modules/requests-hawk { }; + requests-http-message-signatures = + callPackage ../development/python-modules/requests-http-message-signatures + { }; + requests-http-signature = callPackage ../development/python-modules/requests-http-signature { }; requests-kerberos = callPackage ../development/python-modules/requests-kerberos { }; @@ -18820,6 +18832,8 @@ self: super: with self; { } ); + troi = callPackage ../development/python-modules/troi { }; + troposphere = callPackage ../development/python-modules/troposphere { }; trove-classifiers = callPackage ../development/python-modules/trove-classifiers { }; @@ -19408,6 +19422,10 @@ self: super: with self; { types-xxhash = callPackage ../development/python-modules/types-xxhash { }; + typesense = callPackage ../development/python-modules/typesense { + inherit (pkgs) typesense curl; + }; + typesentry = callPackage ../development/python-modules/typesentry { }; typeshed-client = callPackage ../development/python-modules/typeshed-client { };