Index: example.py =================================================================== --- example.py (revision 74315) +++ example.py (working copy) @@ -335,6 +335,12 @@ print h.keys()[0] print list(h.iterkeys().next()) for x in h.keys()[0]: print x + # + # Examples with dict views + # + print d.viewkeys() + print d.viewitems() + print d.viewvalues() def dict_negative_examples(): # Index: lib2to3/tests/test_fixers.py =================================================================== --- lib2to3/tests/test_fixers.py (revision 74315) +++ lib2to3/tests/test_fixers.py (working copy) @@ -1209,6 +1209,14 @@ a = "[i for i in d. keys( ) ]" self.check(b, a) + b = "if d. viewkeys ( ) : pass" + a = "if d. keys ( ) : pass" + self.check(b, a) + + b = "[i for i in d. viewkeys( ) ]" + a = "[i for i in d. keys( ) ]" + self.check(b, a) + def test_trailing_comment(self): b = "d.keys() # foo" a = "list(d.keys()) # foo" @@ -1228,6 +1236,16 @@ ]""" self.check(b, a) + b = """[i for i in d.iterkeys() # foo + ]""" + a = """[i for i in d.keys() # foo + ]""" + self.check(b, a) + + b = "d.viewitems() # foo" + a = "d.items() # foo" + self.check(b, a) + def test_unchanged(self): for wrapper in fixer_util.consuming_calls: s = "s = %s(d.keys())" % wrapper @@ -1361,6 +1379,46 @@ a = "for x in list(h.keys())[0]: print x" self.check(b, a) + def test_25(self): + b = "d.viewkeys()" + a = "d.keys()" + self.check(b, a) + + def test_26(self): + b = "d.viewitems()" + a = "d.items()" + self.check(b, a) + + def test_27(self): + b = "d.viewvalues()" + a = "d.values()" + self.check(b, a) + + def test_14(self): + b = "[i for i in d.viewkeys()]" + a = "[i for i in d.keys()]" + self.check(b, a) + + def test_15(self): + b = "(i for i in d.viewkeys())" + a = "(i for i in d.keys())" + self.check(b, a) + + def test_17(self): + b = "iter(d.viewkeys())" + a = "iter(d.keys())" + self.check(b, a) + + def test_18(self): + b = "list(d.viewkeys())" + a = "list(d.keys())" + self.check(b, a) + + def test_19(self): + b = "sorted(d.viewkeys())" + a = "sorted(d.keys())" + self.check(b, a) + class Test_xrange(FixerTestCase): fixer = "xrange" Index: lib2to3/fixes/fix_dict.py =================================================================== --- lib2to3/fixes/fix_dict.py (revision 74315) +++ lib2to3/fixes/fix_dict.py (working copy) @@ -11,6 +11,10 @@ d.iteritems() -> iter(d.items()) d.itervalues() -> iter(d.values()) +d.viewkeys() -> d.keys() +d.viewitems() -> d.items() +d.viewvalues() -> d.values() + Except in certain very specific contexts: the iter() can be dropped when the context is list(), sorted(), iter() or for...in; the list() can be dropped when the context is list() or sorted() (but not iter() @@ -39,7 +43,8 @@ PATTERN = """ power< head=any+ trailer< '.' method=('keys'|'items'|'values'| - 'iterkeys'|'iteritems'|'itervalues') > + 'iterkeys'|'iteritems'|'itervalues'| + 'viewkeys'|'viewitems'|'viewvalues') > parens=trailer< '(' ')' > tail=any* > @@ -52,9 +57,10 @@ syms = self.syms method_name = method.value isiter = method_name.startswith(u"iter") - if isiter: + isview = method_name.startswith(u"view") + if isiter or isview: method_name = method_name[4:] - assert method_name in ("keys", "items", "values"), repr(method) + assert method_name in (u"keys", u"items", u"values"), repr(method) head = [n.clone() for n in head] tail = [n.clone() for n in tail] special = not tail and self.in_special_context(node, isiter) @@ -64,7 +70,7 @@ prefix=method.prefix)]), results["parens"].clone()] new = pytree.Node(syms.power, args) - if not special: + if not (special or isview): new.prefix = u"" new = Call(Name(u"iter" if isiter else u"list"), [new]) if tail: