@@ -691,6 +691,61 @@ def test_arbitrary_equality(
691691 spec = Specifier (spec_str )
692692 assert spec .contains (version ) == expected
693693
694+ @pytest .mark .parametrize (
695+ ("spec_str" , "version" , "expected" ),
696+ [
697+ # Zero padding: unnormalized spec vs string/Version
698+ # Strings preserve their original form, so "1.01" != "1.1"
699+ ("===1.1" , "1.01" , False ),
700+ ("===1.01" , "1.1" , False ),
701+ ("===1.01" , "1.01" , True ),
702+ ("===1.1" , "1.1" , True ),
703+ # Version objects are normalized, so Version("1.01") -> "1.1"
704+ ("===1.1" , Version ("1.01" ), True ),
705+ ("===1.1" , Version ("1.1" ), True ),
706+ ("===1.01" , Version ("1.01" ), False ),
707+ ("===1.01" , Version ("1.1" ), False ),
708+ # Prerelease separator normalization (issue #766)
709+ # "1.a1" is valid PEP 440, normalizes to "1a1"
710+ ("===1.a1" , "1.a1" , True ),
711+ ("===1a1" , "1.a1" , False ),
712+ ("===1.a1" , "1a1" , False ),
713+ ("===1a1" , "1a1" , True ),
714+ ("===1.a1" , Version ("1.a1" ), False ),
715+ ("===1a1" , Version ("1.a1" ), True ),
716+ # Epoch normalization: "0!1.0" normalizes to "1.0"
717+ ("===0!1.0" , "0!1.0" , True ),
718+ ("===0!1.0" , "1.0" , False ),
719+ ("===1.0" , "0!1.0" , False ),
720+ ("===0!1.0" , Version ("1.0" ), False ),
721+ ("===1.0" , Version ("0!1.0" ), True ),
722+ # Leading zeros in release segments
723+ ("===01.0" , "01.0" , True ),
724+ ("===01.0" , "1.0" , False ),
725+ ("===1.0" , "01.0" , False ),
726+ ("===01.0" , Version ("1.0" ), False ),
727+ ("===1.0" , Version ("01.0" ), True ),
728+ # Post-release normalization: "post" vs "-" separator
729+ ("===1.0.post1" , "1.0.post1" , True ),
730+ ("===1.0-1" , "1.0-1" , True ),
731+ ("===1.0-1" , "1.0.post1" , False ),
732+ ("===1.0.post1" , "1.0-1" , False ),
733+ ("===1.0-1" , Version ("1.0.post1" ), False ),
734+ ("===1.0.post1" , Version ("1.0-1" ), True ),
735+ # Dev normalization
736+ ("===1.0.dev01" , "1.0.dev01" , True ),
737+ ("===1.0.dev01" , "1.0.dev1" , False ),
738+ ("===1.0.dev1" , "1.0.dev01" , False ),
739+ ("===1.0.dev01" , Version ("1.0.dev1" ), False ),
740+ ("===1.0.dev1" , Version ("1.0.dev01" ), True ),
741+ ],
742+ )
743+ def test_arbitrary_equality_normalization (
744+ self , spec_str : str , version : str | Version , expected : bool
745+ ) -> None :
746+ spec = Specifier (spec_str , prereleases = True )
747+ assert spec .contains (version ) == expected
748+
694749 @pytest .mark .parametrize (
695750 ("specifier" , "expected" ),
696751 [
@@ -830,6 +885,33 @@ def test_specifier_filter(
830885
831886 assert result == expected
832887
888+ @pytest .mark .parametrize (
889+ ("specifier" , "input" , "expected" ),
890+ [
891+ # Strings preserve original form
892+ ("===1.01" , ["1.01" , "1.1" , "1.0.1" ], ["1.01" ]),
893+ ("===1.1" , ["1.01" , "1.1" ], ["1.1" ]),
894+ # Version objects use normalized form
895+ (
896+ "===1.1" ,
897+ [Version ("1.01" ), Version ("1.1" )],
898+ [Version ("1.01" ), Version ("1.1" )],
899+ ),
900+ ("===1.01" , [Version ("1.01" ), Version ("1.1" )], []),
901+ # Mixed strings and Version objects
902+ ("===1.1" , ["1.01" , "1.1" , Version ("1.01" )], ["1.1" , Version ("1.01" )]),
903+ ("===1.01" , ["1.01" , "1.1" , Version ("1.01" )], ["1.01" ]),
904+ # Prerelease separator
905+ ("===1.a1" , ["1.a1" , "1a1" ], ["1.a1" ]),
906+ ("===1a1" , ["1.a1" , "1a1" , Version ("1.a1" )], ["1a1" , Version ("1.a1" )]),
907+ ],
908+ )
909+ def test_specifier_filter_arbitrary_equality_normalization (
910+ self , specifier : str , input : list [str | Version ], expected : list [str | Version ]
911+ ) -> None :
912+ spec = Specifier (specifier , prereleases = True )
913+ assert list (spec .filter (input )) == expected
914+
833915 @pytest .mark .parametrize (
834916 ("prereleases" , "expected_indexes" ),
835917 [
@@ -1917,6 +1999,37 @@ def test_contains_arbitrary_equality_contains(
19171999 spec = SpecifierSet (specifier )
19182000 assert spec .contains (version ) == expected
19192001
2002+ @pytest .mark .parametrize (
2003+ ("spec_str" , "version" , "expected" ),
2004+ [
2005+ # Zero padding: string preserves original, Version normalizes
2006+ ("===1.1" , "1.01" , False ),
2007+ ("===1.01" , "1.1" , False ),
2008+ ("===1.01" , "1.01" , True ),
2009+ ("===1.1" , "1.1" , True ),
2010+ ("===1.1" , Version ("1.01" ), True ),
2011+ ("===1.01" , Version ("1.01" ), False ),
2012+ # Prerelease separator normalization
2013+ ("===1.a1" , "1.a1" , True ),
2014+ ("===1a1" , "1.a1" , False ),
2015+ ("===1.a1" , "1a1" , False ),
2016+ ("===1a1" , "1a1" , True ),
2017+ ("===1.a1" , Version ("1.a1" ), False ),
2018+ ("===1a1" , Version ("1.a1" ), True ),
2019+ # Combined with other operators
2020+ (">=1.0,===1.01" , "1.01" , True ),
2021+ (">=1.0,===1.1" , "1.1" , True ),
2022+ (">=1.0,===1.1" , "1.01" , False ),
2023+ (">=1.0,===1.1" , Version ("1.01" ), True ),
2024+ (">=1.0,===1.01" , Version ("1.01" ), False ),
2025+ ],
2026+ )
2027+ def test_contains_arbitrary_equality_normalization (
2028+ self , spec_str : str , version : str | Version , expected : bool
2029+ ) -> None :
2030+ spec = SpecifierSet (spec_str , prereleases = True )
2031+ assert spec .contains (version ) == expected
2032+
19202033 @pytest .mark .parametrize (
19212034 ("specifier" , "expected" ),
19222035 [
0 commit comments