66from packaging .requirements import Requirement
77from packaging .version import Version
88
9- from fromager import constraints
9+ from fromager . constraints import Constraints , InvalidConstraintError
1010
1111
1212def test_constraint_is_satisfied_by () -> None :
13- c = constraints . Constraints ()
13+ c = Constraints ()
1414 c .add_constraint ("foo<=1.1" )
1515 assert c .is_satisfied_by ("foo" , Version ("1.1" ))
1616 assert c .is_satisfied_by ("foo" , Version ("1.0" ))
1717 assert c .is_satisfied_by ("bar" , Version ("2.0" ))
1818
1919
2020def test_constraint_canonical_name () -> None :
21- c = constraints . Constraints ()
21+ c = Constraints ()
2222 c .add_constraint ("flash_attn<=1.1" )
2323 assert c .is_satisfied_by ("flash_attn" , Version ("1.1" ))
2424 assert c .is_satisfied_by ("flash-attn" , Version ("1.1" ))
@@ -27,7 +27,7 @@ def test_constraint_canonical_name() -> None:
2727
2828
2929def test_constraint_not_is_satisfied_by () -> None :
30- c = constraints . Constraints ()
30+ c = Constraints ()
3131 c .add_constraint ("foo<=1.1" )
3232 c .add_constraint ("bar>=2.0" )
3333 assert not c .is_satisfied_by ("foo" , Version ("1.2" ))
@@ -36,28 +36,24 @@ def test_constraint_not_is_satisfied_by() -> None:
3636
3737
3838def test_add_constraint_conflict () -> None :
39- c = constraints . Constraints ()
39+ c = Constraints ()
4040 c .add_constraint ("foo<=1.1" )
4141 c .add_constraint ("flit_core==2.0rc3" )
4242
43- # Exact duplicate should raise error (same package, same marker)
44- with pytest .raises (KeyError ):
45- c .add_constraint ("foo<=1.1" )
46-
47- # Different version, same marker (no marker) should raise error
48- with pytest .raises (KeyError ):
43+ # Conflicting version, same marker (no marker) should raise error
44+ with pytest .raises (InvalidConstraintError ):
4945 c .add_constraint ("foo>1.1" )
5046
51- # Different version for flit_core should raise error
52- with pytest .raises (KeyError ):
47+ # Conflicting version for flit_core should raise error
48+ with pytest .raises (InvalidConstraintError ):
5349 c .add_constraint ("flit_core>2.0.0" )
5450
5551 # Normalized name conflict should raise error
56- with pytest .raises (KeyError ):
52+ with pytest .raises (InvalidConstraintError ):
5753 c .add_constraint ("flit-core>2.0.0" )
5854
5955 # Different, but equivalent markers should raise KeyError
60- with pytest .raises (KeyError ):
56+ with pytest .raises (InvalidConstraintError ):
6157 # arm64 -> macos; aarch64 -> linux
6258 for arch in ["x86_64" , "arm64" , "aarch64" ]:
6359 c .add_constraint (
@@ -72,7 +68,7 @@ def test_add_constraint_conflict() -> None:
7268 c .add_constraint ("baz==1.1; platform_machine == 'ppc64le'" )
7369
7470 # But same package with same marker should raise error
75- with pytest .raises (KeyError ):
71+ with pytest .raises (InvalidConstraintError ):
7672 c .add_constraint ("foo==1.2; platform_machine != 'ppc64le'" )
7773
7874 # Verify multiple constraints for same package are stored
@@ -98,7 +94,7 @@ def test_add_constraint_conflict() -> None:
9894
9995
10096def test_allow_prerelease () -> None :
101- c = constraints . Constraints ()
97+ c = Constraints ()
10298 c .add_constraint ("foo>=1.1" )
10399 assert not c .allow_prerelease ("foo" )
104100 c .add_constraint ("bar>=1.1a0" )
@@ -109,15 +105,40 @@ def test_allow_prerelease() -> None:
109105
110106def test_load_non_existant_constraints_file (tmp_path : pathlib .Path ) -> None :
111107 non_existant_file = tmp_path / "non_existant.txt"
112- c = constraints . Constraints ()
108+ c = Constraints ()
113109 with pytest .raises (FileNotFoundError ):
114110 c .load_constraints_file (non_existant_file )
115111
116112
117113def test_load_constraints_file (tmp_path : pathlib .Path ) -> None :
118114 constraint_file = tmp_path / "constraint.txt"
119- constraint_file .write_text ("egg\n torch==3.1.0 # comment\n " )
120- c = constraints . Constraints ()
115+ constraint_file .write_text ("egg==1.0 \n torch==3.1.0 # comment\n " )
116+ c = Constraints ()
121117 c .load_constraints_file (constraint_file )
122118 assert list (c ) == ["egg" , "torch" ] # type: ignore
123119 assert c .get_constraint ("torch" ) == Requirement ("torch==3.1.0" )
120+
121+
122+ def test_invalid_constraints () -> None :
123+ c = Constraints ()
124+ with pytest .raises (InvalidConstraintError , match = r".*no specifier" ):
125+ c .add_constraint ("foo" )
126+ with pytest .raises (InvalidConstraintError , match = r".*has extras" ):
127+ c .add_constraint ("foo[extra]>=1.0" )
128+ with pytest .raises (InvalidConstraintError , match = r".*has an url" ):
129+ c .add_constraint ("foo@https://foo.test" )
130+
131+
132+ def test_unsatisfiable () -> None :
133+ c = Constraints ()
134+ with pytest .raises (InvalidConstraintError ):
135+ c .add_constraint ("foo<1.0,>2.0" )
136+
137+
138+ def test_combine_constraints () -> None :
139+ c = Constraints ()
140+ c .add_constraint ("foo>=1.0" )
141+ c .add_constraint ("foo<2.0" )
142+ assert c .get_constraint ("foo" ) == Requirement ("foo<2.0,>=1.0" )
143+ c .add_constraint ("foo!=1.1.0" )
144+ assert c .get_constraint ("foo" ) == Requirement ("foo<2.0,>=1.0,!=1.1.0" )
0 commit comments