Skip to content

Commit eb3c500

Browse files
author
Sylvain MARIE
committed
Fixed Lazy default equality method to avoid infinite recursion, and changed LazyTuple implementation so that its id works both before and after retrieving the value
1 parent 12127b2 commit eb3c500

File tree

1 file changed

+20
-19
lines changed

1 file changed

+20
-19
lines changed

pytest_cases/common_pytest_lazy_values.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,10 @@ def __str__(self):
4949

5050
def __eq__(self, other):
5151
"""Default equality method based on the _field_names"""
52-
if isinstance(self, type(other)):
53-
# self is a subclass of other
52+
try:
5453
return all(getattr(self, k) == getattr(other, k) for k in self._field_names)
55-
else:
56-
# other is from a subclass of self
57-
return super(type(self), self).__eq__(other)
54+
except:
55+
return False
5856

5957
def __repr__(self):
6058
"""Default repr method based on the _field_names"""
@@ -228,7 +226,7 @@ def __repr__(self):
228226
"""Override the inherited method to avoid infinite recursion"""
229227
vals_to_display = (
230228
('item', self.item), # item number first for easier debug
231-
('tuple', self.host.value), # lazy value tuple or retrieved tuple
229+
('tuple', self.host.value if self.host.retrieved else self.host.valuegetter), # lazy value tuple or retrieved tuple
232230
)
233231
return "%s(%s)" % (self.__class__.__name__, ", ".join("%s=%r" % (k, v) for k, v in vals_to_display))
234232

@@ -243,11 +241,16 @@ class LazyTuple(Lazy):
243241
"""
244242
A wrapper representing a lazy_value used as a tuple = for several argvalues at once.
245243
246-
-
247-
while not calling the lazy value
248-
-
244+
Its `.get()` method caches the tuple obtained from the value getter, so that it is not called several times (once
245+
for each LazyTupleItem)
246+
247+
It is only used directly by pytest when a lazy_value is used in a @ parametrize to decorate a fixture.
248+
Indeed in that case pytest does not unpack the tuple, we do it in our custom @fixture.
249+
250+
In all other cases (when @parametrize is used on a test function), pytest unpacks the tuple so it directly
251+
manipulates the underlying LazyTupleItem instances.
249252
"""
250-
__slots__ = 'value', 'theoretical_size', 'retrieved'
253+
__slots__ = 'valuegetter', 'theoretical_size', 'retrieved', 'value'
251254
_field_names = __slots__
252255

253256
@classmethod
@@ -256,33 +259,32 @@ def copy_from(cls,
256259
):
257260
new_obj = cls(valueref=obj.value, theoretical_size=obj.theoretical_size)
258261
new_obj.retrieved = obj.retrieved
262+
if new_obj.retrieved:
263+
new_obj.value = obj.value
259264
return new_obj
260265

261266
# noinspection PyMissingConstructor
262267
def __init__(self,
263268
valueref, # type: Union[LazyValue, Sequence]
264269
theoretical_size # type: int
265270
):
266-
self.value = valueref
271+
self.valuegetter = valueref
267272
self.theoretical_size = theoretical_size
268273
self.retrieved = False
274+
self.value = None
269275

270276
def __len__(self):
271277
return self.theoretical_size
272278

273279
def get_id(self):
274280
"""return the id to use by pytest"""
275-
if self.retrieved:
276-
raise ValueError("id is lost once the tuple has been retrieved, this is ok since at this stage it should "
277-
"not be needed anymore...")
278-
else:
279-
return self.value.get_id()
281+
return self.valuegetter.get_id()
280282

281283
def get(self):
282284
""" Call the underlying value getter, then return the tuple (not self) """
283285
if not self.retrieved:
284286
# retrieve
285-
self.value = self.value.get()
287+
self.value = self.valuegetter.get()
286288
self.retrieved = True
287289
return self.value
288290

@@ -303,15 +305,14 @@ def __getitem__(self, item):
303305

304306
def force_getitem(self, item):
305307
""" Call the underlying value getter, then return self[i]. """
306-
getter = self.value
307308
argvalue = self.get()
308309
try:
309310
return argvalue[item]
310311
except TypeError as e:
311312
raise ValueError("(lazy_value) The parameter value returned by `%r` is not compliant with the number"
312313
" of argnames in parametrization (%s). A %s-tuple-like was expected. "
313314
"Returned lazy argvalue is %r and argvalue[%s] raised %s: %s"
314-
% (getter.valuegetter, self.theoretical_size, self.theoretical_size,
315+
% (self.valuegetter, self.theoretical_size, self.theoretical_size,
315316
argvalue, item, e.__class__, e))
316317

317318

0 commit comments

Comments
 (0)