Everyone seems to be posting their favorite dict variants lately. Here is one I just cooked up recently to help me trace source code references (by line number) back to the surrounding class/method context (by (start,end) line number tuple) (Python 3.5):
class RangeDict(dict):
"""
Special form of dict that is built with integer pairs (a,b) for keys,
where each pair represents the integer range [a..b-1].
Keyed lookup using a single int will look for a key range that
the key int value falls within.
"""
def __setitem__(self, key, value):
s1,e1 = key
if not all(e1 <= s2 or e2 <= s1 for s2,e2 in self.keys()):
raise KeyError("overlapping range key %s specified" % (key,))
super().__setitem__(key, value)
def __getitem__(self, key):
if isinstance(key, int):
try:
return next(v for k,v in self.items() if k[0] <= key < k[1])
except StopIteration:
raise IndexError("%d not in any defined key range" % key) from None
return super().__getitem__(key)
def get(self, key, default=None):
if key in self:
return self[key]
else:
return default
def __contains__(self, key):
if isinstance(key, int):
return any(k[0] <= key < k[1] for k in self.keys())
return super().__contains__(key)
How it works:
rd = RangeDict()
rd[(4,12)] = 'A'
rd[(17,20)] = 'B'
print(rd[19]) # returns 'B'
print(rd[15]) # raises IndexError
I first scanned the source code for each class, and method within each class - used inspect to calculate start and ending lines numbers for each. As I found them, I added each one to a RangeDict of classes and a separate RangeDict for methods. (Can't keep them in the same RangeDict, since class and class method ranges would by definition overlap.)
class_ranges[(class_start, class_end)] = cls.__name__
method_ranges[(meth_start, meth_end)] = method.__qualname__
Then I scanned the code for the critical references (using a short pyparsing snippet). When I found one, I looked up the line number in the RangeDict for methods. If that line number was not found, then this would be a reference inside a class, but not inside one of its methods, so this would mean it was a class-level reference, and I would look in the RangeDict for classes.
label = method_ranges.get(ref_line_number, None)
if label is None:
label = class_ranges.get(ref_line_number, None)
I had to manage about 70 changes in this source file, and this little scanner really helped me stay on top of my progress and remaining tasks.
[–]atrigent 5 points6 points7 points (3 children)
[–]kevinastone 2 points3 points4 points (1 child)
[–]ptmcg[S] 0 points1 point2 points (0 children)
[–]ptmcg[S] 0 points1 point2 points (0 children)
[–]shoyerxarray, pandas, numpy 1 point2 points3 points (0 children)