22
33import operator
44
5- from typing import Any
65from typing import Callable
76from typing import ClassVar
87
1110from poetry .core .constraints .generic .empty_constraint import EmptyConstraint
1211
1312
14- OperatorType = Callable [[object , object ], Any ]
13+ OperatorType = Callable [[object , object ], bool ]
14+
15+
16+ def contains (a : object , b : object , / ) -> bool :
17+ return operator .contains (a , b ) # type: ignore[arg-type]
18+
19+
20+ def not_contains (a : object , b : object , / ) -> bool :
21+ return not contains (a , b )
1522
1623
1724class Constraint (BaseConstraint ):
1825 OP_EQ = operator .eq
1926 OP_NE = operator .ne
27+ OP_IN = contains
28+ OP_NC = not_contains
2029
2130 _trans_op_str : ClassVar [dict [str , OperatorType ]] = {
2231 "=" : OP_EQ ,
2332 "==" : OP_EQ ,
2433 "!=" : OP_NE ,
34+ "in" : OP_IN ,
35+ "not in" : OP_NC ,
2536 }
2637
27- _trans_op_int : ClassVar [dict [OperatorType , str ]] = {OP_EQ : "==" , OP_NE : "!=" }
38+ _trans_op_int : ClassVar [dict [OperatorType , str ]] = {
39+ OP_EQ : "==" ,
40+ OP_NE : "!=" ,
41+ OP_IN : "in" ,
42+ OP_NC : "not in" ,
43+ }
44+
45+ _trans_op_inv : ClassVar [dict [str , str ]] = {
46+ "!=" : "==" ,
47+ "==" : "!=" ,
48+ "not in" : "in" ,
49+ "in" : "not in" ,
50+ }
2851
2952 def __init__ (self , value : str , operator : str = "==" ) -> None :
3053 if operator == "=" :
@@ -49,14 +72,8 @@ def allows(self, other: BaseConstraint) -> bool:
4972 f' ("other" must be a constraint with operator "=="): { other } '
5073 )
5174
52- is_equal_op = self ._operator == "=="
53- is_non_equal_op = self ._operator == "!="
54-
55- if is_equal_op :
56- return self ._value == other .value
57-
58- if is_non_equal_op :
59- return self ._value != other .value
75+ if op := self ._trans_op_str .get (self ._operator ):
76+ return op (other .value , self ._value )
6077
6178 return False
6279
@@ -68,6 +85,15 @@ def allows_all(self, other: BaseConstraint) -> bool:
6885 if other .operator == "==" :
6986 return self .allows (other )
7087
88+ if other .operator == "in" and self ._operator == "in" :
89+ return self .value in other .value
90+
91+ if other .operator == "not in" :
92+ if self ._operator == "not in" :
93+ return other .value in self .value
94+ if self ._operator == "!=" :
95+ return self .value not in other .value
96+
7197 return self == other
7298
7399 if isinstance (other , MultiConstraint ):
@@ -82,36 +108,36 @@ def allows_any(self, other: BaseConstraint) -> bool:
82108 from poetry .core .constraints .generic import MultiConstraint
83109 from poetry .core .constraints .generic import UnionConstraint
84110
85- is_equal_op = self ._operator == "=="
86- is_non_equal_op = self ._operator == "!="
87-
88- if is_equal_op :
111+ if self ._operator == "==" :
89112 return other .allows (self )
90113
91114 if isinstance (other , Constraint ):
92- is_other_equal_op = other .operator == "=="
93- is_other_non_equal_op = other .operator == "!="
94-
95- if is_other_equal_op :
115+ if other .operator == "==" :
96116 return self .allows (other )
97117
98- if is_equal_op and is_other_non_equal_op :
118+ if other . operator == "!=" and self . _operator == "==" :
99119 return self ._value != other .value
100120
101- return is_non_equal_op and is_other_non_equal_op
121+ if other .operator == "not in" and self ._operator == "in" :
122+ return other .value not in self .value
123+
124+ if other .operator == "in" and self ._operator == "not in" :
125+ return self .value not in other .value
126+
127+ return True
102128
103129 elif isinstance (other , MultiConstraint ):
104- return is_non_equal_op
130+ return self . _operator == "!="
105131
106132 elif isinstance (other , UnionConstraint ):
107- return is_non_equal_op and any (
133+ return self . _operator == "!=" and any (
108134 self .allows_any (c ) for c in other .constraints
109135 )
110136
111137 return other .is_any ()
112138
113139 def invert (self ) -> Constraint :
114- return Constraint (self ._value , "!=" if self ._operator == "==" else "==" )
140+ return Constraint (self ._value , self ._trans_op_inv [ self . operator ] )
115141
116142 def difference (self , other : BaseConstraint ) -> Constraint | EmptyConstraint :
117143 if other .allows (self ):
@@ -126,16 +152,16 @@ def intersect(self, other: BaseConstraint) -> BaseConstraint:
126152 if other == self :
127153 return self
128154
129- if self .operator == "!=" and other . operator == "==" and self . allows (other ):
155+ if self .allows_all (other ):
130156 return other
131157
132- if other .operator == "!=" and self . operator == "==" and other . allows (self ):
158+ if other .allows_all (self ):
133159 return self
134160
135- if other . operator == "!=" and self .operator == "!=" :
136- return MultiConstraint ( self , other )
161+ if not self .allows_any ( other ) or not other . allows_any ( self ) :
162+ return EmptyConstraint ( )
137163
138- return EmptyConstraint ( )
164+ return MultiConstraint ( self , other )
139165
140166 return other .intersect (self )
141167
@@ -146,16 +172,25 @@ def union(self, other: BaseConstraint) -> BaseConstraint:
146172 if other == self :
147173 return self
148174
149- if self .operator == "!=" and other . operator == "==" and self . allows (other ):
175+ if self .allows_all (other ):
150176 return self
151177
152- if other .operator == "!=" and self . operator == "==" and other . allows (self ):
178+ if other .allows_all (self ):
153179 return other
154180
155- if other .operator == "==" and self .operator == "==" :
156- return UnionConstraint (self , other )
181+ ops = {self .operator , other .operator }
182+ if (
183+ (ops in ({"!=" }, {"not in" }))
184+ or (
185+ ops in ({"in" , "!=" }, {"in" , "not in" })
186+ and (self .operator == "in" and self .value in other .value )
187+ or (other .operator == "in" and other .value in self .value )
188+ )
189+ or self .invert () == other
190+ ):
191+ return AnyConstraint ()
157192
158- return AnyConstraint ( )
193+ return UnionConstraint ( self , other )
159194
160195 # to preserve order (functionally not necessary)
161196 if isinstance (other , UnionConstraint ):
@@ -179,5 +214,7 @@ def __hash__(self) -> int:
179214 return hash ((self ._operator , self ._value ))
180215
181216 def __str__ (self ) -> str :
217+ if self ._operator in {"in" , "not in" }:
218+ return f"'{ self ._value } ' { self ._operator } "
182219 op = self ._operator if self ._operator != "==" else ""
183220 return f"{ op } { self ._value } "
0 commit comments