MutaFix run: boltons-002
ranker nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B · patcher nvidia/nemotron-3-super-120b-a12b · via Nebius Token Factory
- if type(iterable) is not type(self): (injected bug)+ if type(iterable) is type(self): (original)retriever rank 2 of 20 · ranker position 2 · patched the right function yes · exact restore no
Trace
failing tests and errors
tests/test_dictutils.py::test_many_to_manytests/test_dictutils.py::test_many_to_many
E AttributeError: 'list' object has no attribute 'data'
tests/test_dictutils.py::test_many_to_many in the static call graph. Kept 20 candidates, ranked by spectrum score (Ochiai), call graph and BM25 break ties.candidates in retriever order
boltons/dictutils.py::ManyToMany.removeboltons/dictutils.py::ManyToMany.updateboltons/dictutils.py::ManyToMany.__delitem__boltons/dictutils.py::ManyToMany.addboltons/dictutils.py::ManyToMany.__setitem__boltons/dictutils.py::ManyToMany.__init__boltons/dictutils.py::ManyToMany.__getitem__boltons/dictutils.py::ManyToMany.iteritemsboltons/dictutils.py::ManyToMany.__len__boltons/dictutils.py::ManyToMany.__contains__boltons/dictutils.py::ManyToMany.replaceboltons/dictutils.py::OrderedMultiDict.updateboltons/urlutils.py::OrderedMultiDict.updateboltons/setutils.py::IndexedSet.removeboltons/fileutils.py::replaceboltons/setutils.py::_ComplementSet.updateboltons/setutils.py::IndexedSet.addboltons/dictutils.py::ManyToMany.__repr__boltons/setutils.py::IndexedSet.updateboltons/dictutils.py::ManyToMany.get
boltons/dictutils.py::ManyToMany.removeboltons/dictutils.py::ManyToMany.updateboltons/dictutils.py::ManyToMany.__delitem__boltons/dictutils.py::ManyToMany.addboltons/dictutils.py::ManyToMany.__setitem__
read boltons/dictutils.py::ManyToMany.remove
The error message indicates that a 'list' object has no attribute 'data', which suggests that somewhere in the code a list is being used where a dict-like object with a 'data' attribute is expected. The 'remove' method accesses self.data[key] and self.inv.data[val], assuming these are dict-like containers. If self.data or self.inv.data were accidentally replaced with a list, this would cause the AttributeError. Since the failing test involves operations on 'inv' and 'data' keys, examining the remove method is a logical first step to understand how these structures are being manipulated.
The test failure shows that 'list' object has no attribute 'data', which suggests that self.data or self.inv.data might be lists instead of dict-like objects in some cases. The original remove method assumes that self.data[key] and self.inv.data[val] are sets or dict-like objects with a 'remove' method and 'in' checks. However, if these are lists or if the keys don't exist, accessing them directly would cause errors. The patch adds safety checks to ensure that the keys exist and that the value is actually present before attempting to remove it, preventing accidental access to non-dict-like objects or missing keys.
--- a/boltons/dictutils.py+++ b/boltons/dictutils.py@@ -986,10 +986,12 @@ def remove(self, key, val):- self.data[key].remove(val)- if not self.data[key]:- del self.data[key]- self.inv.data[val].remove(key)- if not self.inv.data[val]:- del self.inv.data[val]+ if key in self.data and val in self.data[key]:+ self.data[key].remove(val)+ if not self.data[key]:+ del self.data[key]+ if val in self.inv.data and key in self.inv.data[val]:+ self.inv.data[val].remove(key)+ if not self.inv.data[val]:+ del self.inv.data[val] def replace(self, key, newkey):
tests: 1 failing (target tests)
tests/test_dictutils.py::test_many_to_many
read boltons/dictutils.py::ManyToMany.__init__
To understand how self.data and self.inv are initialized, we need to see the __init__ method of ManyToMany. This will reveal what objects self.data and self.inv are supposed to be, and whether they could ever become lists.
model reasoning (excerpt)
We need to look deeper. The error occurs during the test. Let's examine the test again:
In test_many_to_many, there is a line:
del m2m.inv['a']
This invokes __delitem__ on the inv attribute? Actually, m2m.inv is probably another ManyToMany instance? Wait, looking at the class: ManyToMany likely has .data and .inv attributes, both of which are dict-like objects mapping keys to sets of values. The .inv is the inverse mapping.
When we do `del m2m.inv['a']`, that calls __delitem__ on the inv object (which is a ManyToMany? Or maybe it's a dict-like helper?). Actually, from the test, we see that m2m.inv is used like a dict: m2m.inv['a'] returns a frozenset. And we can delete a key from it: del m2m.inv['a'].
So the inv attribute is probably an instance of a class that behaves like a dict mapping values to sets of keys. It might be a ManyToMany itself? Or maybe it's a _InvDict? Let's look at the ManyToMany class definition.
We should read the __init__ and see what self.data and self.inv are.
Let's read candidate 6: ManyToMany.__init__.
But first, let's think: The error says "'list' object has no attribute 'data'". This happens when we try to access .data on something that is a list. Where in the code do we access .data? In the remove method we have self.data[key] and self.inv.data[val]. Also in __delitem__ we have self.data.pop(key) and then self.inv.data[val].remove(key). Also in __setitem__ we have self.data[key] and self.inv.data[val]. Also in add we have self.data[key]read boltons/dictutils.py::ManyToMany.update
Now I want to examine the update method because the test uses m2m.update([(1, 'a'), (2, 'b')]) and the error occurs during many-to-many operations. The update method handles different types of input, including another ManyToMany instance. Looking at lines 959-970, when the input is not of the same type, it tries to access other.data and other.inv.data. If 'other' is a plain list or something without a .data attribute, this would cause the AttributeError. The test passes a list of tuples to update, so let's check if the logic correctly handles that case.
model reasoning (excerpt)
We have seen the __init__ method. It initializes self.data as a dict and self.inv either as another ManyToMany instance (created via self.__class__((_PAIRING, self))) or, in the case of a special tuple, it uses the provided items[1] as self.inv.
Now, note that in the test, we have:
m2m = ManyToMany()
... later ...
del m2m.inv['a']
This deletion is done on m2m.inv, which is itself a ManyToMany instance (unless we are in the case of the special tuple). The __delitem__ method of ManyToMany is defined (we saw it earlier as candidate [3]):
951 | def __delitem__(self, key):
952 | for val in self.data.pop(key):
953 | self.inv.data[val].remove(key)
954 | if not self.inv.data[val]:
955 | del self.inv.data[val]
But wait: when we do `del m2m.inv['a']`, we are calling __delitem__ on the inv ManyToMany instance. Let's look at that again:
In the test:
m2m = ManyToMany()
m2m.add(1, 'a')
m2m.add(1, 'b')
... then ...
del m2m.inv['a']
At this point, m2m.inv is a ManyToMany instance that maps values to keys. Specifically, after adding (1,'a') and (1,'b'), we have:
m2m.data: {1: {'a','b'}}
m2m.inv.data: {'a': {1}, 'b': {1}}
Now, when we do `del m2m.inv['a']`, we are invoking the __delitem__ method of the inv ManyToMany instance (which is a ManyToMany) with key='a'.
Let's look at the __delitem__ method again (from candidate [3]):
951 | def __delitem__(self, key):
952 | for read boltons/dictutils.py::ManyToMany.__getitem__
Let's look at the __getitem__ method because the test does m2m[1] and expects a frozenset. If there's an issue with how data is stored or retrieved, it might manifest here. Also, understanding what self.data contains will help us see if it's ever becoming a list instead of a set or dict.
model reasoning (excerpt)
We have identified a potential issue in the update method. When the iterable is not of the same type as self (i.e., not a ManyToMany instance), the code assumes that `iterable` has `.data` and `.inv.data` attributes (lines 961, 966). However, in the test, we call `m2m.update([(1, 'a'), (2, 'b')])`, where the argument is a list of tuples. A list does not have a `.data` attribute, so when the code tries to access `other.data` (where `other` is the list), it will raise an AttributeError: 'list' object has no attribute 'data'. But note: the condition at line 959 is `if type(iterable) is not type(self):`. For a list of tuples, `type(iterable)` is `list`, which is not equal to `type(self)` (which is `ManyToMany`), so we enter this branch. Then we set `other = iterable` (so `other` is the list) and then try to access `other.data` -> error. However, the test also has a branch for when the iterable has a `keys` method (line 971) and else (line 974) for an iterable of pairs. The list of tuples should be caught by the else branch (line 974) because a list does not have a `keys` method (unless it's a dict, but it's not). So why are we entering the first branch? Wait: the condition is `if type(iterable) is not type(self):`. This is checking for exact type equality. But note that the test also passes a list to `update` in the line: `m2m.update([(1, 'a'), (2, 'b')])`. This list is not of type `ManyToMany`, so we go into the first branch. But we should go into the else branch (line 974) f
read boltons/dictutils.py::ManyToMany.add
Let's re-examine the add method since it's fundamental to how data is inserted into self.data and self.inv. We saw that self.data and self.inv are initialized as dicts in __init__, but if add is not handling the types correctly, it might be causing corruption. Specifically, we need to confirm that self.data[key] and self.inv.data[val] are always sets, as assumed throughout the code.
model reasoning (excerpt)
We have identified that the error is: AttributeError: 'list' object has no attribute 'data'
This happens in the test when calling methods on ManyToMany.
Let's trace the test step by step until the error.
The test does:
m2m = ManyToMany()
... (some adds and removes)
m2m.update([(1, 'a'), (2, 'b')])
In the update method, when the argument is a list of tuples, it goes to the else branch (line 974-976) because:
- type(iterable) is list, which is not type(self) (ManyToMany) -> so first condition true? Wait, let's see:
Actually, the update method has:
if type(iterable) is not type(self): # line 959
... # This branch is taken for non-ManyToMany iterables
elif callable(getattr(iterable, 'keys', None)): # line 971
... # for dict-like
else:
... # for iterable of pairs (line 974-976)
So for a list of tuples, it should go to the else branch and call self.add for each pair.
However, note that the condition at line 959: `if type(iterable) is not type(self):`
For a list, type(iterable) is list, type(self) is ManyToMany -> they are not the same, so we enter the first branch.
But wait, the first branch (lines 960-970) assumes that `iterable` has attributes `.data` and `.inv.data`.
Specifically, it does:
other = iterable # line 960
for k in other.data: # line 961
If we pass a list, then `other` is a list, and a list does not have a `.data` attribute -> hence the error.
Therefore, the bug is in the updatread boltons/dictutils.py::ManyToMany.__delitem__
Let's examine the __delitem__ method because the test does 'del m2m.inv['a']' and later operations depend on the inv mapping being correct. If __delitem__ on the inv (which is another ManyToMany) is not working properly, it could leave the data in an inconsistent state. The error shows a 'list' object has no attribute 'data', so we need to see if anywhere we're assigning a list to self.data or self.inv.
model reasoning (excerpt)
We have seen the code for ManyToMany. The error is: AttributeError: 'list' object has no attribute 'data'. This happens in the test when doing operations on m2m.inv or m2m.data? Let's trace the test:
The test does:
m2m = ManyToMany()
...
m2m.add(1, 'a')
m2m.add(1, 'b')
...
del m2m.inv['a'] # This is the line that might cause trouble?
Wait, the test does: del m2m.inv['a']
But note: m2m.inv is a ManyToMany instance (the inverse). In the __init__, we see that if items is not provided, then self.inv is set to self.__class__((_PAIRING, self)). So m2m.inv is a ManyToMany that has m2m as its data? Actually, look at line 927: self.inv = self.__class__((_PAIRING, self))
So m2m.inv is a ManyToMany instance that was created with items=(_PAIRING, self). Then in __init__, when items is a tuple and items[0] is _PAIRING, we set self.inv = items[1] (line 924-925). Wait, that's for the inverse? Let's read carefully:
In ManyToMany.__init__:
if type(items) is tuple and items and items[0] is _PAIRING:
self.inv = items[1]
else:
self.inv = self.__class__((_PAIRING, self))
if items:
self.update(items)
So when we create a ManyToMany normally (without the _PAIRING tuple), we go to the else branch and set self.inv to a new ManyToMany created with (_PAIRING, self). That means the inverse's data is set to the original? Actually, note: the inverse is created with items=(_PAIRING, self). Then in its __initThe error occurred because the update method incorrectly treated a list of tuples as a ManyToMany-like object, attempting to access its .data attribute. The condition `if type(iterable) is not type(self):` was too broad, catching non-ManyToMany iterables (like lists) and trying to access .data and .inv.data on them. The fix changes the condition to `if isinstance(iterable, self.__class__):`, ensuring the bulk update logic only runs for actual ManyToMany instances. For other iterables (including lists, dicts, etc.), the method correctly falls back to the dict-like or iterable-of-pairs branches, allowing the list of tuples to be processed via the else branch that calls self.add for each pair.
--- a/boltons/dictutils.py+++ b/boltons/dictutils.py@@ -957,5 +957,5 @@ def update(self, iterable): """given an iterable of (key, val), add them all"""- if type(iterable) is not type(self):+ if isinstance(iterable, self.__class__): other = iterable for k in other.data:
tests: all tests pass
model reasoning (excerpt)
We have examined several methods. The error is: AttributeError: 'list' object has no attribute 'data'
This suggests that at some point, either self.data or self.inv is a list instead of a dict-like object.
Let's trace the initialization and updates:
In __init__:
self.data = {} (always a dict)
if ...:
self.inv = items[1] (could be anything? but note: when items is a tuple and items[0] is _PAIRING, then self.inv is set to items[1])
else:
self.inv = self.__class__((_PAIRING, self)) (a new ManyToMany)
So normally, self.inv is a ManyToMany instance, which has a .data attribute (a dict).
However, note the condition: if type(items) is tuple and items and items[0] is _PAIRING:
then self.inv = items[1]
What is _PAIRING? We don't have the context, but it's likely a marker used internally.
Now, look at the update method:
if type(iterable) is not type(self):
other = iterable
for k in other.data: # Here, if other is not a ManyToMany, we assume it has a .data attribute?
...
But wait: the condition is: if type(iterable) is not type(self): then we treat it as 'other' and try to access other.data and other.inv.data.
However, in the test, we call:
m2m.update([(1, 'a'), (2, 'b')])
Here, iterable is a list of tuples. So type(iterable) is list, which is not equal to type(self) (ManyToMany). Therefore, we go into the first branch.
Then we set: other = iterable (the list)
Then we try to loop: for k in othFinal patch
--- a/boltons/dictutils.py+++ b/boltons/dictutils.py@@ -957,5 +957,5 @@ def update(self, iterable): """given an iterable of (key, val), add them all"""- if type(iterable) is not type(self):+ if isinstance(iterable, self.__class__): other = iterable for k in other.data: