55from django_filters import CharFilter
66from django_filters .rest_framework import filters as drf_filters
77from drf_spectacular .utils import extend_schema , extend_schema_view
8+ from packaging .specifiers import InvalidSpecifier , SpecifierSet
89from packaging .utils import canonicalize_name
10+ from packaging .version import InvalidVersion , Version
911from rest_framework import status
1012from rest_framework .decorators import action
1113from rest_framework .mixins import (
@@ -466,6 +468,28 @@ class NormalizedNameInFilter(drf_filters.BaseInFilter, NormalizedNameFilter):
466468 """In-filter that normalizes each input value and queries name_normalized."""
467469
468470
471+ class VersionSpecifierFilter (CharFilter ):
472+ """Filter that matches versions against a PEP 440 specifier string."""
473+
474+ def filter (self , qs , value ):
475+ if not value :
476+ return qs
477+ try :
478+ spec = SpecifierSet (value , prereleases = True )
479+ except InvalidSpecifier :
480+ raise ValidationError (
481+ {"version_specifier" : f"Invalid PEP 440 version specifier: { value } " }
482+ )
483+ matching_pks = []
484+ for pk , version in qs .values_list ("pk" , self .field_name ):
485+ try :
486+ if Version (version ) in spec :
487+ matching_pks .append (pk )
488+ except InvalidVersion :
489+ pass
490+ return qs .filter (pk__in = matching_pks )
491+
492+
469493class PythonPackageContentFilter (core_viewsets .ContentFilter ):
470494 """
471495 FilterSet for PythonPackageContent.
@@ -474,6 +498,10 @@ class PythonPackageContentFilter(core_viewsets.ContentFilter):
474498 name = NormalizedNameFilter (field_name = "name_normalized" , lookup_expr = "exact" )
475499 name__in = NormalizedNameInFilter (field_name = "name_normalized" , lookup_expr = "in" )
476500 name__contains = CharFilter (field_name = "name" , lookup_expr = "contains" )
501+ version_specifier = VersionSpecifierFilter (
502+ field_name = "version" ,
503+ help_text = "Filter by PEP 440 version specifier (e.g., >=2.4,<3.0 or ~=1.26)" ,
504+ )
477505
478506 class Meta :
479507 model = python_models .PythonPackageContent
0 commit comments