changeset 5395:23b8e6067f7c

Python 3 preparation: update calls to dict methods. Tool-assisted patch. Changes of iterkeys / itervalues / iteritems to keys / values / items are fully automated, but may make things less efficient in Python 2. Automated tools want to add list() around many calls to keys / values / items, but I thought most such list() additions were unnecessary because it seemed the result of keys / values / items was just iterated over while the set of dict keys remained unchanged, rather than used in a way requiring an actual list, or used while the set of keys in the dict could change. It's quite possible I missed some cases where list() was really needed, or left in some unnecessary list() calls. In cases where list() was only needed because the resulting list was then sorted in-place, I changed the code to use calls to sorted().
author Joseph Myers <jsm@polyomino.org.uk>
date Tue, 24 Jul 2018 23:04:42 +0000
parents c26d88ec071e
children 831787cf6694
files detectors/newitemcopy.py frontends/ZRoundup/ZRoundup.py roundup/backends/back_anydbm.py roundup/backends/indexer_common.py roundup/backends/indexer_dbm.py roundup/backends/rdbms_common.py roundup/cgi/PageTemplates/MultiMapping.py roundup/cgi/TAL/HTMLTALParser.py roundup/cgi/TAL/TALParser.py roundup/cgi/TAL/talgettext.py roundup/cgi/actions.py roundup/cgi/client.py roundup/cgi/form_parser.py roundup/cgi/templating.py roundup/configuration.py roundup/hyperdb.py roundup/instance.py roundup/msgfmt.py roundup/roundupdb.py roundup/scripts/roundup_server.py roundup/security.py roundup/xmlrpc.py scripts/schema_diagram.py test/db_test_base.py test/memorydb.py tools/pygettext.py
diffstat 26 files changed, 100 insertions(+), 123 deletions(-) [+]
line wrap: on
line diff
--- a/detectors/newitemcopy.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/detectors/newitemcopy.py	Tue Jul 24 23:04:42 2018 +0000
@@ -15,8 +15,7 @@
 
     # list the values
     m = []
-    prop_items = props.items()
-    prop_items.sort()
+    prop_items = sorted(props.items())
     for propname, prop in prop_items:
         value = cl.get(nodeid, propname, None)
         # skip boring entries
--- a/frontends/ZRoundup/ZRoundup.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/frontends/ZRoundup/ZRoundup.py	Tue Jul 24 23:04:42 2018 +0000
@@ -102,7 +102,7 @@
     def has_key(self, item):
         return item in self.__form
     def keys(self):
-        return self.__form.keys()
+        return list(self.__form.keys())
 
     def __repr__(self):
         return '<ZRoundup.FormWrapper %r>'%self.__form
--- a/roundup/backends/back_anydbm.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/backends/back_anydbm.py	Tue Jul 24 23:04:42 2018 +0000
@@ -261,9 +261,7 @@
 
     def getclasses(self):
         """Return a list of the names of all existing classes."""
-        l = self.classes.keys()
-        l.sort()
-        return l
+        return sorted(self.classes.keys())
 
     def getclass(self, classname):
         """Get the Class object representing a particular class.
@@ -484,7 +482,7 @@
         """
         properties = self.getclass(classname).getprops()
         d = {}
-        for k, v in node.iteritems():
+        for k, v in node.items():
             if k == self.RETIRED_FLAG:
                 d[k] = v
                 continue
@@ -511,7 +509,7 @@
         """
         properties = self.getclass(classname).getprops()
         d = {}
-        for k, v in node.iteritems():
+        for k, v in node.items():
             # if the property doesn't exist, or is the "retired" flag then
             # it won't be in the properties dict
             if k not in properties:
@@ -600,7 +598,7 @@
     def fix_journal(self, classname, journal):
         """ fix password entries to correct type """
         pwprops = {}
-        for pn, prop in self.getclass(classname).properties.iteritems():
+        for pn, prop in self.getclass(classname).properties.items():
             if isinstance(prop, hyperdb.Password):
                 pwprops [pn] = 1
         if not pwprops:
@@ -724,7 +722,7 @@
                 reindex[method(*args)] = 1
         finally:
             # make sure we close all the database files
-            for db in self.databases.itervalues():
+            for db in self.databases.values():
                 db.close()
             del self.databases
 
@@ -916,7 +914,7 @@
 
         # validate propvalues
         num_re = re.compile('^\d+$')
-        for key, value in propvalues.iteritems():
+        for key, value in propvalues.items():
             if key == self.key:
                 try:
                     self.lookup(value)
@@ -1027,7 +1025,7 @@
                     raise TypeError('new property "%s" not boolean'%key)
 
         # make sure there's data where there needs to be
-        for key, prop in self.properties.iteritems():
+        for key, prop in self.properties.items():
             if key in propvalues:
                 continue
             if key == self.key:
@@ -1168,7 +1166,7 @@
 
         self.fireAuditors('set', nodeid, propvalues)
         oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid))
-        for name, prop in self.getprops(protected=0).iteritems():
+        for name, prop in self.getprops(protected=0).items():
             if name in oldvalues:
                 continue
             if isinstance(prop, hyperdb.Multilink):
@@ -1551,7 +1549,7 @@
             db.issue.find(messages='1')
             db.issue.find(messages={'1':1,'3':1}, files={'7':1})
         """
-        for propname, itemids in propspec.iteritems():
+        for propname, itemids in propspec.items():
             # check the prop is OK
             prop = self.properties[propname]
             if not isinstance(prop, hyperdb.Link) and not isinstance(prop, hyperdb.Multilink):
@@ -1566,7 +1564,7 @@
                 item = self.db.getnode(self.classname, id, db=cldb)
                 if self.db.RETIRED_FLAG in item:
                     continue
-                for propname, itemids in propspec.iteritems():
+                for propname, itemids in propspec.items():
                     if type(itemids) is not type({}):
                         itemids = {itemids:1}
 
@@ -1616,7 +1614,7 @@
                 node = self.db.getnode(self.classname, nodeid, cldb)
                 if self.db.RETIRED_FLAG in node:
                     continue
-                for key, value in requirements.iteritems():
+                for key, value in requirements.items():
                     if key not in node:
                         break
                     if node[key] is None or node[key].lower() != value:
@@ -1720,7 +1718,7 @@
         INTERVAL = 'spec:interval'
         OTHER = 'spec:other'
 
-        for k, v in filterspec.iteritems():
+        for k, v in filterspec.items():
             propclass = props[k]
             if isinstance(propclass, hyperdb.Link):
                 if type(v) is not type([]):
@@ -2001,7 +1999,7 @@
     def index(self, nodeid):
         """ Add (or refresh) the node to search indexes """
         # find all the String properties that have indexme
-        for prop, propclass in self.getprops().iteritems():
+        for prop, propclass in self.getprops().items():
             if isinstance(propclass, hyperdb.String) and propclass.indexme:
                 # index them under (classname, nodeid, property)
                 try:
@@ -2107,7 +2105,7 @@
                 date = date.get_tuple()
                 if action == 'set':
                     export_data = {}
-                    for propname, value in params.iteritems():
+                    for propname, value in params.items():
                         if propname not in properties:
                             # property no longer in the schema
                             continue
@@ -2201,7 +2199,7 @@
 
         # create the oldvalues dict - fill in any missing values
         oldvalues = copy.deepcopy(self.db.getnode(self.classname, itemid))
-        for name, prop in self.getprops(protected=0).iteritems():
+        for name, prop in self.getprops(protected=0).items():
             if name in oldvalues:
                 continue
             if isinstance(prop, hyperdb.Multilink):
@@ -2238,7 +2236,7 @@
         Use the content-type property for the content property.
         """
         # find all the String properties that have indexme
-        for prop, propclass in self.getprops().iteritems():
+        for prop, propclass in self.getprops().items():
             if prop == 'content' and propclass.indexme:
                 mime_type = self.get(nodeid, 'type', self.default_mime_type)
                 self.db.indexer.add_text((self.classname, nodeid, 'content'),
--- a/roundup/backends/indexer_common.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/backends/indexer_common.py	Tue Jul 24 23:04:42 2018 +0000
@@ -40,7 +40,7 @@
             return {}
 
         designator_propname = {}
-        for nm, propclass in klass.getprops().iteritems():
+        for nm, propclass in klass.getprops().items():
             if _isLink(propclass):
                 designator_propname.setdefault(propclass.classname,
                     []).append(nm)
@@ -49,7 +49,7 @@
         # and files
         nodeids = {}      # this is the answer
         propspec = {}     # used to do the klass.find
-        for l in designator_propname.itervalues():
+        for l in designator_propname.values():
             for propname in l:
                 propspec[propname] = {}  # used as a set (value doesn't matter)
 
--- a/roundup/backends/indexer_dbm.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/backends/indexer_dbm.py	Tue Jul 24 23:04:42 2018 +0000
@@ -204,7 +204,7 @@
                 dbslice = marshal.loads(pickle_str)
                 if dbslice.get('WORDS'):
                     # if it has some words, add them
-                    for word, entry in dbslice['WORDS'].iteritems():
+                    for word, entry in dbslice['WORDS'].items():
                         db['WORDS'][word] = entry
                 if dbslice.get('FILES'):
                     # if it has some files, add them
@@ -240,7 +240,7 @@
         segdicts = {}                           # Need batch of empty dicts
         for segment in letters:
             segdicts[segment] = {}
-        for word, entry in self.words.iteritems():  # Split into segment dicts
+        for word, entry in self.words.items():  # Split into segment dicts
             initchar = word[0].upper()
             segdicts[initchar][word] = entry
 
@@ -269,7 +269,7 @@
         del self.fileids[file_index]
 
         # The much harder part, cleanup the word index
-        for key, occurs in self.words.iteritems():
+        for key, occurs in self.words.items():
             if file_index in occurs:
                 del occurs[file_index]
 
--- a/roundup/backends/rdbms_common.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/backends/rdbms_common.py	Tue Jul 24 23:04:42 2018 +0000
@@ -295,7 +295,7 @@
 
         # handle changes in the schema
         tables = self.database_schema['tables']
-        for classname, spec in self.classes.iteritems():
+        for classname, spec in self.classes.items():
             if classname in tables:
                 dbspec = tables[classname]
                 if self.update_class(spec, dbspec):
@@ -383,7 +383,7 @@
     def fix_version_4_tables(self):
         # note this is an explicit call now
         c = self.cursor
-        for cn, klass in self.classes.iteritems():
+        for cn, klass in self.classes.items():
             c.execute('select id from _%s where __retired__<>0'%(cn,))
             for (id,) in c.fetchall():
                 c.execute('update _%s set __retired__=%s where id=%s'%(cn,
@@ -396,7 +396,7 @@
         """Get current journal table contents, drop the table and re-create"""
         c = self.cursor
         cols = ','.join('nodeid date tag action params'.split())
-        for klass in self.classes.itervalues():
+        for klass in self.classes.values():
             # slurp and drop
             sql = 'select %s from %s__journal order by date'%(cols,
                 klass.classname)
@@ -418,9 +418,9 @@
         """Get current Class tables that contain String properties, and
         convert the VARCHAR columns to TEXT"""
         c = self.cursor
-        for klass in self.classes.itervalues():
+        for klass in self.classes.values():
             # slurp and drop
-            cols, mls = self.determine_columns(list(klass.properties.iteritems()))
+            cols, mls = self.determine_columns(list(klass.properties.items()))
             scols = ','.join([i[0] for i in cols])
             sql = 'select id,%s from _%s'%(scols, klass.classname)
             c.execute(sql)
@@ -450,7 +450,7 @@
         if classname:
             classes = [self.getclass(classname)]
         else:
-            classes = list(self.classes.itervalues())
+            classes = list(self.classes.values())
         for klass in classes:
             if show_progress:
                 for nodeid in support.Progress('Reindex %s'%klass.classname,
@@ -485,7 +485,7 @@
         if datatype:
             return datatype
         
-        for k, v in self.hyperdb_to_sql_datatypes.iteritems():
+        for k, v in self.hyperdb_to_sql_datatypes.items():
             if issubclass(propclass, k):
                 return v
 
@@ -622,7 +622,7 @@
         """Figure out the columns from the spec and also add internal columns
 
         """
-        cols, mls = self.determine_columns(list(spec.properties.iteritems()))
+        cols, mls = self.determine_columns(list(spec.properties.items()))
 
         # add on our special columns
         cols.append(('id', 'INTEGER PRIMARY KEY'))
@@ -894,7 +894,7 @@
         if fn:
             return fn
 
-        for k, v in self.hyperdb_to_sql_value.iteritems():
+        for k, v in self.hyperdb_to_sql_value.items():
             if issubclass(propklass, k):
                 return v
 
@@ -923,7 +923,7 @@
 
         # determine the column definitions and multilink tables
         cl = self.classes[classname]
-        cols, mls = self.determine_columns(list(cl.properties.iteritems()))
+        cols, mls = self.determine_columns(list(cl.properties.items()))
 
         # we'll be supplied these props if we're doing an import
         values = node.copy()
@@ -938,7 +938,7 @@
         del props['id']
 
         # default the non-multilink columns
-        for col, prop in props.iteritems():
+        for col, prop in props.items():
             if col not in values:
                 if isinstance(prop, Multilink):
                     values[col] = []
@@ -1074,7 +1074,7 @@
                     self.sql(sql, (entry, nodeid))
 
         # we have multilink changes to apply
-        for col, (add, remove) in multilink_changes.iteritems():
+        for col, (add, remove) in multilink_changes.items():
             tn = '%s_%s'%(classname, col)
             if add:
                 sql = 'insert into %s (nodeid, linkid) values (%s,%s)'%(tn,
@@ -1108,7 +1108,7 @@
         if fn:
             return fn
 
-        for k, v in self.sql_to_hyperdb_value.iteritems():
+        for k, v in self.sql_to_hyperdb_value.items():
             if issubclass(propklass, k):
                 return v
 
@@ -1131,7 +1131,7 @@
         """ get all Multilinks of a node (lazy eval may have skipped this)
         """
         cl = self.classes[classname]
-        props = props or [pn for (pn, p) in cl.properties.iteritems()
+        props = props or [pn for (pn, p) in cl.properties.items()
                           if isinstance(p, Multilink)]
         for propname in props:
             if propname not in node:
@@ -1161,7 +1161,7 @@
 
         # figure the columns we're fetching
         cl = self.classes[classname]
-        cols, mls = self.determine_columns(list(cl.properties.iteritems()))
+        cols, mls = self.determine_columns(list(cl.properties.items()))
         scols = ','.join([col for col,dt in cols])
 
         # perform the basic property fetch
@@ -1224,7 +1224,7 @@
 
         # remove from multilnks
         cl = self.getclass(classname)
-        x, mls = self.determine_columns(list(cl.properties.iteritems()))
+        x, mls = self.determine_columns(list(cl.properties.items()))
         for col in mls:
             # get the link ids
             sql = 'delete from %s_%s where nodeid=%s'%(classname, col, self.arg)
@@ -1336,7 +1336,7 @@
         """Convert the journal params values into safely repr'able and
         eval'able values."""
         properties = self.getclass(classname).getprops()
-        for param, value in params.iteritems():
+        for param, value in params.items():
             if not value:
                 continue
             property = properties[param]
@@ -1367,7 +1367,7 @@
         for nodeid, date_stamp, user, action, params in journal:
             params = eval(params)
             if isinstance(params, type({})):
-                for param, value in params.iteritems():
+                for param, value in params.items():
                     if not value:
                         continue
                     property = properties.get(param, None)
@@ -1517,7 +1517,7 @@
         """ A dumpable version of the schema that we can store in the
             database
         """
-        return (self.key, [(x, repr(y)) for x,y in self.properties.iteritems()])
+        return (self.key, [(x, repr(y)) for x,y in self.properties.items()])
 
     def enableJournalling(self):
         """Turn journalling on for this class
@@ -1571,7 +1571,7 @@
 
         # validate propvalues
         num_re = re.compile('^\d+$')
-        for key, value in propvalues.iteritems():
+        for key, value in propvalues.items():
             if key == self.key:
                 try:
                     self.lookup(value)
@@ -1681,7 +1681,7 @@
                     raise TypeError('new property "%s" not boolean'%key)
 
         # make sure there's data where there needs to be
-        for key, prop in self.properties.iteritems():
+        for key, prop in self.properties.items():
             if key in propvalues:
                 continue
             if key == self.key:
@@ -2174,7 +2174,7 @@
 
         # validate the args
         props = self.getprops()
-        for propname, nodeids in propspec.iteritems():
+        for propname, nodeids in propspec.items():
             # check the prop is OK
             prop = props[propname]
             if not isinstance(prop, Link) and not isinstance(prop, Multilink):
@@ -2185,7 +2185,7 @@
         allvalues = ()
         sql = []
         where = []
-        for prop, values in propspec.iteritems():
+        for prop, values in propspec.items():
             if not isinstance(props[prop], hyperdb.Link):
                 continue
             if type(values) is type({}) and len(values) == 1:
@@ -2210,7 +2210,7 @@
                 and %s"""%(self.classname, a, ' and '.join(where)))
 
         # now multilinks
-        for prop, values in propspec.iteritems():
+        for prop, values in propspec.items():
             if not isinstance(props[prop], hyperdb.Multilink):
                 continue
             if not values:
@@ -2790,14 +2790,14 @@
             row = cursor.fetchone()
             if not row: break
             # populate cache with current items
-            for (classname, ptid), pt in classes.iteritems():
+            for (classname, ptid), pt in classes.items():
                 nodeid = str(row[pt['id'].sql_idx])
                 key = (classname, nodeid)
                 if key in self.db.cache:
                     self.db._cache_refresh(key)
                     continue
                 node = {}
-                for propname, p in pt.iteritems():
+                for propname, p in pt.items():
                     value = row[p.sql_idx]
                     if value is not None:
                         value = p.to_hyperdb(value)
@@ -2865,7 +2865,7 @@
         """Add (or refresh) the node to search indexes
         """
         # find all the String properties that have indexme
-        for prop, propclass in self.getprops().iteritems():
+        for prop, propclass in self.getprops().items():
             if isinstance(propclass, String) and propclass.indexme:
                 self.db.indexer.add_text((self.classname, nodeid, prop),
                     str(self.get(nodeid, prop)))
@@ -2989,7 +2989,7 @@
                 date = date.get_tuple()
                 if action == 'set':
                     export_data = {}
-                    for propname, value in params.iteritems():
+                    for propname, value in params.items():
                         if propname not in properties:
                             # property no longer in the schema
                             continue
@@ -3115,7 +3115,7 @@
         Use the content-type property for the content property.
         """
         # find all the String properties that have indexme
-        for prop, propclass in self.getprops().iteritems():
+        for prop, propclass in self.getprops().items():
             if prop == 'content' and propclass.indexme:
                 mime_type = self.get(nodeid, 'type', self.default_mime_type)
                 self.db.indexer.add_text((self.classname, nodeid, 'content'),
--- a/roundup/cgi/PageTemplates/MultiMapping.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/cgi/PageTemplates/MultiMapping.py	Tue Jul 24 23:04:42 2018 +0000
@@ -25,5 +25,5 @@
     def items(self):
         l = []
         for store in self.stores:
-            l = l + store.items()
+            l = l + list(store.items())
         return l
--- a/roundup/cgi/TAL/HTMLTALParser.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/cgi/TAL/HTMLTALParser.py	Tue Jul 24 23:04:42 2018 +0000
@@ -61,7 +61,7 @@
     ]
 
 TIGHTEN_IMPLICIT_CLOSE_TAGS = (PARA_LEVEL_HTML_TAGS
-                               + BLOCK_CLOSING_TAG_MAP.keys())
+                               + list(BLOCK_CLOSING_TAG_MAP.keys()))
 
 
 class NestingError(HTMLParseError):
--- a/roundup/cgi/TAL/TALParser.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/cgi/TAL/TALParser.py	Tue Jul 24 23:04:42 2018 +0000
@@ -56,8 +56,7 @@
                 attrlist.append((key, value))
         else:
             # attrs is a dict of {name: value}
-            attrlist = attrs.items()
-            attrlist.sort() # For definiteness
+            attrlist = sorted(attrs.items()) # Sorted for definiteness
         name, attrlist, taldict, metaldict, i18ndict \
               = self.process_ns(name, attrlist)
         attrlist = self.xmlnsattrs() + attrlist
--- a/roundup/cgi/TAL/talgettext.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/cgi/TAL/talgettext.py	Tue Jul 24 23:04:42 2018 +0000
@@ -300,9 +300,8 @@
         print(pot_header % {'time': time.ctime(),
                             'version': __version__}, file=outfile)
 
-    msgids = catalog.keys()
     # XXX: You should not sort by msgid, but by filename and position. (SR)
-    msgids.sort()
+    msgids = sorted(catalog.keys())
     for msgid in msgids:
         positions = catalog[msgid]
         for filename, position in positions:
--- a/roundup/cgi/actions.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/cgi/actions.py	Tue Jul 24 23:04:42 2018 +0000
@@ -1073,7 +1073,7 @@
 
         # generate the one-time-key and store the props for later
         user_props = props[('user', None)]
-        for propname, proptype in self.db.user.getprops().iteritems():
+        for propname, proptype in self.db.user.getprops().items():
             value = user_props.get(propname, None)
             if value is None:
                 pass
--- a/roundup/cgi/client.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/cgi/client.py	Tue Jul 24 23:04:42 2018 +0000
@@ -2059,7 +2059,7 @@
 
         headers = list(headers.items())
 
-        for ((path, name), (value, expire)) in self._cookies.iteritems():
+        for ((path, name), (value, expire)) in self._cookies.items():
             cookie = "%s=%s; Path=%s;"%(name, value, path)
             if expire is not None:
                 cookie += " expires=%s;"%get_cookie_date(expire)
--- a/roundup/cgi/form_parser.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/cgi/form_parser.py	Tue Jul 24 23:04:42 2018 +0000
@@ -589,7 +589,7 @@
         # property to be created. When editing a FileClass node, it should
         # either have a non-empty content property or no property at all. In
         # the latter case, nothing will change.
-        for (cn, id), props in all_props.items():
+        for (cn, id), props in list(all_props.items()):
             if id is not None and id.startswith('-') and not props:
                 # new item (any class) with no content - ignore
                 del all_props[(cn, id)]
--- a/roundup/cgi/templating.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/cgi/templating.py	Tue Jul 24 23:04:42 2018 +0000
@@ -376,8 +376,7 @@
             raise AttributeError(attr)
 
     def classes(self):
-        l = self._client.db.classes.keys()
-        l.sort()
+        l = sorted(self._client.db.classes.keys())
         m = []
         for item in l:
             m.append(HTMLClass(self._client, item))
@@ -670,8 +669,7 @@
     def propnames(self):
         """ Return the list of the names of the properties of this class.
         """
-        idlessprops = self._klass.getprops(protected=0).keys()
-        idlessprops.sort()
+        idlessprops = sorted(self._klass.getprops(protected=0).keys())
         return ['id'] + idlessprops
 
     def filter(self, request=None, filterspec={}, sort=[], group=[]):
@@ -738,8 +736,7 @@
         the "property" belongs to.
         """
         if properties is None:
-            properties = self._klass.getprops(protected=0).keys()
-            properties.sort()
+            properties = sorted(self._klass.getprops(protected=0).keys())
             properties = ','.join(properties)
         if sort is None:
             if 'username' in properties.split( ',' ):
--- a/roundup/configuration.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/configuration.py	Tue Jul 24 23:04:42 2018 +0000
@@ -1538,7 +1538,7 @@
     def _adjust_options(self, config):
         # config defaults appear in all sections.
         # we'll need to filter them out.
-        defaults = config.defaults().keys()
+        defaults = list(config.defaults().keys())
         # see what options are already defined and add missing ones
         preset = [(option.section, option.setting) for option in self.items()]
         for section in config.sections():
--- a/roundup/hyperdb.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/hyperdb.py	Tue Jul 24 23:04:42 2018 +0000
@@ -418,7 +418,7 @@
 
     def append_retr_props(self):
         """Append properties for retrieval."""
-        for name, prop in self.cls.getprops(protected=1).iteritems():
+        for name, prop in self.cls.getprops(protected=1).items():
             if isinstance(prop, Multilink):
                 continue
             self.append(name, need_for='retrieve')
@@ -1198,8 +1198,7 @@
             return 'title'
         if default_to_id:
             return 'id'
-        props = props.keys()
-        props.sort()
+        props = sorted(props.keys())
         return props[0]
 
     def orderprop(self):
@@ -1262,7 +1261,7 @@
         can contain NULL values.
         """
         proptree = Proptree(self.db, self, '', self.getprops(), retr=retr)
-        for key, v in filterspec.iteritems():
+        for key, v in filterspec.items():
             keys = key.split('.')
             p = proptree
             mlseen = False
@@ -1292,7 +1291,7 @@
                 continue
             p.sort_direction = s[0]
             proptree.sortattr.append (p)
-        for p in multilinks.iterkeys():
+        for p in multilinks.keys():
             sattr = {}
             for c in p:
                 if c.sort_direction:
@@ -1411,7 +1410,7 @@
         """
         props = self.getprops(protected = False)
         pdict = dict([(p, props[p]) for p in propnames])
-        pdict.update([(k, v) for k, v in props.iteritems() if v.required])
+        pdict.update([(k, v) for k, v in props.items() if v.required])
         return pdict
 
     def addprop(self, **properties):
@@ -1472,8 +1471,7 @@
     #
     def export_propnames(self):
         """List the property names for export from this Class"""
-        propnames = self.getprops().keys()
-        propnames.sort()
+        propnames = sorted(self.getprops().keys())
         return propnames
 
     def import_journals(self, entries):
@@ -1505,7 +1503,7 @@
                 r = []
 
             if action == 'set':
-                for propname, value in params.iteritems():
+                for propname, value in params.items():
                     prop = properties[propname]
                     if value is None:
                         pass
@@ -1630,7 +1628,7 @@
     def export_propnames(self):
         """ Don't export the "content" property
         """
-        propnames = self.getprops().keys()
+        propnames = list(self.getprops().keys())
         propnames.remove('content')
         propnames.sort()
         return propnames
@@ -1679,7 +1677,7 @@
         self.__dict__['cl'] = cl
         self.__dict__['nodeid'] = nodeid
     def keys(self, protected=1):
-        return self.cl.getprops(protected=protected).keys()
+        return list(self.cl.getprops(protected=protected).keys())
     def values(self, protected=1):
         l = []
         for name in self.cl.getprops(protected=protected).keys():
--- a/roundup/instance.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/instance.py	Tue Jul 24 23:04:42 2018 +0000
@@ -167,7 +167,7 @@
             classes = db.getclasses()
             for classname in classes:
                 cl = db.getclass(classname)
-                for propname, prop in cl.getprops().iteritems():
+                for propname, prop in cl.getprops().items():
                     if not isinstance(prop, (hyperdb.Link,
                                              hyperdb.Multilink)):
                         continue
--- a/roundup/msgfmt.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/msgfmt.py	Tue Jul 24 23:04:42 2018 +0000
@@ -58,9 +58,8 @@
 def generate():
     "Return the generated output."
     global MESSAGES
-    keys = MESSAGES.keys()
     # the keys are sorted in the .mo file
-    keys.sort()
+    keys = sorted(MESSAGES.keys())
     offsets = []
     ids = strs = ''
     for id in keys:
--- a/roundup/roundupdb.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/roundupdb.py	Tue Jul 24 23:04:42 2018 +0000
@@ -710,8 +710,7 @@
 
         # list the values
         m = []
-        prop_items = props.items()
-        prop_items.sort()
+        prop_items = sorted(props.items())
         for propname, prop in prop_items:
             # Omit quiet properties from history/changelog
             if prop.quiet:
@@ -784,8 +783,7 @@
 
         # list the changes
         m = []
-        changed_items = changed.items()
-        changed_items.sort()
+        changed_items = sorted(changed.items())
         for propname, oldvalue in changed_items:
             prop = props[propname]
             # Omit quiet properties from history/changelog
--- a/roundup/scripts/roundup_server.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/scripts/roundup_server.py	Tue Jul 24 23:04:42 2018 +0000
@@ -245,7 +245,7 @@
     def index(self):
         ''' Print up an index of the available trackers
         '''
-        keys = self.TRACKER_HOMES.keys()
+        keys = list(self.TRACKER_HOMES.keys())
         if len(keys) == 1:
             self.send_response(302)
             self.send_header('Location', urllib.quote(keys[0]) + '/index')
@@ -623,7 +623,7 @@
             return
         # config defaults appear in all sections.
         # filter them out.
-        defaults = config.defaults().keys()
+        defaults = list(config.defaults().keys())
         for name in config.options("trackers"):
             if name not in defaults:
                 self.add_option(TrackerHomeOption(self, "trackers", name))
--- a/roundup/security.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/security.py	Tue Jul 24 23:04:42 2018 +0000
@@ -329,7 +329,7 @@
                 except KeyError:
                     return 0
                 props = dict.fromkeys(('id', cls.labelprop(), cls.orderprop()))
-                for p in props.iterkeys():
+                for p in props.keys():
                     for perm in perms:
                         if perm.searchable(prop.classname, p):
                             break
@@ -413,7 +413,7 @@
     def filterFilterspec(self, userid, classname, filterspec):
         """ Return a filterspec that has all non-allowed properties removed.
         """
-        return dict ([(k, v) for k, v in filterspec.iteritems()
+        return dict ([(k, v) for k, v in filterspec.items()
             if self.hasSearchPermission(userid,classname,k)])
 
     def filterSortspec(self, userid, classname, sort):
--- a/roundup/xmlrpc.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/roundup/xmlrpc.py	Tue Jul 24 23:04:42 2018 +0000
@@ -118,7 +118,7 @@
     def display(self, designator, *properties):
         classname, itemid = hyperdb.splitDesignator(designator)
         cl = self.db.getclass(classname)
-        props = properties and list(properties) or cl.properties.keys()
+        props = properties and list(properties) or list(cl.properties.keys())
         props.sort()
         for p in props:
             if not self.db.security.hasPermission('View', self.db.getuid(),
@@ -165,7 +165,7 @@
         classname, itemid = hyperdb.splitDesignator(designator)
         cl = self.db.getclass(classname)
         props = props_from_args(self.db, cl, args, itemid) # convert types
-        for p in props.iterkeys():
+        for p in props.keys():
             if not self.db.security.hasPermission('Edit', self.db.getuid(),
                                                   classname, p, itemid):
                 raise Unauthorised('Permission to edit %s of %s denied'%
--- a/scripts/schema_diagram.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/scripts/schema_diagram.py	Tue Jul 24 23:04:42 2018 +0000
@@ -25,7 +25,7 @@
 print('edge [taillabel="1" headlabel="1" dir=back arrowtail=ediamond]')
 
 # get all the classes
-types = db.classes.keys()
+types = list(db.classes.keys())
 
 # one record node per class
 for i in range(len(types)):
--- a/test/db_test_base.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/test/db_test_base.py	Tue Jul 24 23:04:42 2018 +0000
@@ -1230,8 +1230,7 @@
         self.assertEqual(nodeid, '1')
         self.assertEqual(journaltag, self.db.user.lookup('admin'))
         self.assertEqual(action, 'create')
-        keys = params.keys()
-        keys.sort()
+        keys = sorted(params.keys())
         self.assertEqual(keys, [])
 
         # journal entry for link
@@ -2279,11 +2278,11 @@
             shutil.rmtree('_test_export')
 
         # compare with snapshot of the database
-        for cn, items in orig.iteritems():
+        for cn, items in orig.items():
             klass = self.db.classes[cn]
             propdefs = klass.getprops(1)
             # ensure retired items are retired :)
-            l = items.keys(); l.sort()
+            l = sorted(items.keys())
             m = klass.list(); m.sort()
             ae(l, m, '%s id list wrong %r vs. %r'%(cn, l, m))
             for id, props in items.items():
@@ -2299,8 +2298,8 @@
                             raise
                         # don't get hung up on rounding errors
                         assert not l.__cmp__(value, int_seconds=1)
-        for jc, items in origj.iteritems():
-            for id, oj in items.iteritems():
+        for jc, items in origj.items():
+            for id, oj in items.items():
                 rj = self.db.getjournal(jc, id)
                 # Both mysql and postgresql have some minor issues with
                 # rounded seconds on export/import, so we compare only
@@ -2525,8 +2524,7 @@
         # force any post-init stuff to happen
         self.db.post_init()
         props = self.db.issue.getprops()
-        keys = props.keys()
-        keys.sort()
+        keys = sorted(props.keys())
         self.assertEqual(keys, ['activity', 'actor', 'assignedto', 'creation',
             'creator', 'deadline', 'feedback', 'files', 'fixer', 'foo', 'id', 'messages',
             'nosy', 'priority', 'spam', 'status', 'superseder', 'title'])
@@ -2539,8 +2537,7 @@
         del self.db.issue.properties['title']
         self.db.post_init()
         props = self.db.issue.getprops()
-        keys = props.keys()
-        keys.sort()
+        keys = sorted(props.keys())
         self.assertEqual(keys, ['activity', 'actor', 'assignedto', 'creation',
             'creator', 'deadline', 'feedback', 'files', 'foo', 'id', 'messages',
             'nosy', 'priority', 'spam', 'status', 'superseder'])
@@ -2554,8 +2551,7 @@
         del self.db.issue.properties['title']
         self.db.post_init()
         props = self.db.issue.getprops()
-        keys = props.keys()
-        keys.sort()
+        keys = sorted(props.keys())
         self.assertEqual(keys, ['activity', 'actor', 'assignedto', 'creation',
             'creator', 'deadline', 'feedback', 'files', 'fixer', 'foo', 'id',
             'messages', 'nosy', 'priority', 'spam', 'status', 'superseder'])
@@ -2716,8 +2712,7 @@
     def test_fileClassProps(self):
         self.open_database()
         a = self.module.FileClass(self.db, 'a')
-        l = a.getprops().keys()
-        l.sort()
+        l = sorted(a.getprops().keys())
         self.assert_(l, ['activity', 'actor', 'content', 'created',
             'creation', 'type'])
 
@@ -3005,7 +3000,7 @@
             for x in range(4):
                 assert(('user', nodeid) in self.db.cache)
                 n = self.db.user.getnode(nodeid)
-                for k, v in user_result[nodeid].iteritems():
+                for k, v in user_result[nodeid].items():
                     ae((k, n[k]), (k, v))
                 for k in 'creation', 'activity':
                     assert(n[k])
@@ -3022,7 +3017,7 @@
             result.append(id)
             assert(('issue', id) in self.db.cache)
             n = self.db.issue.getnode(id)
-            for k, v in issue_result[id].iteritems():
+            for k, v in issue_result[id].items():
                 ae((k, n[k]), (k, v))
             for k in 'creation', 'activity':
                 assert(n[k])
@@ -3030,7 +3025,7 @@
             for x in range(4):
                 assert(('user', nodeid) in self.db.cache)
                 n = self.db.user.getnode(nodeid)
-                for k, v in user_result[nodeid].iteritems():
+                for k, v in user_result[nodeid].items():
                     ae((k, n[k]), (k, v))
                 for k in 'creation', 'activity':
                     assert(n[k])
--- a/test/memorydb.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/test/memorydb.py	Tue Jul 24 23:04:42 2018 +0000
@@ -134,7 +134,7 @@
     def set(self, infoid, **newvalues):
         self[infoid].update(newvalues)
     def list(self):
-        return self.keys()
+        return list(self.keys())
     def destroy(self, infoid):
         del self[infoid]
     def commit(self):
@@ -290,9 +290,7 @@
 
     def getclasses(self):
         """Return a list of the names of all existing classes."""
-        l = self.classes.keys()
-        l.sort()
-        return l
+        return sorted(self.classes.keys())
 
     def getclass(self, classname):
         """Get the Class object representing a particular class.
--- a/tools/pygettext.py	Tue Jul 24 23:03:35 2018 +0000
+++ b/tools/pygettext.py	Tue Jul 24 23:04:42 2018 +0000
@@ -454,11 +454,9 @@
         # sort all the entries by their first item.
         reverse = {}
         for k, v in self.__messages.items():
-            keys = v.keys()
-            keys.sort()
+            keys = sorted(v.keys())
             reverse.setdefault(tuple(keys), []).append((k, v))
-        rkeys = reverse.keys()
-        rkeys.sort()
+        rkeys = sorted(reverse.keys())
         for rkey in rkeys:
             rentries = reverse[rkey]
             rentries.sort()
@@ -472,8 +470,7 @@
                 # k is the message string, v is a dictionary-set of (filename,
                 # lineno) tuples.  We want to sort the entries in v first by
                 # file name and then by line number.
-                v = v.keys()
-                v.sort()
+                v = sorted(v.keys())
                 if not options.writelocations:
                     pass
                 # location comments are different b/w Solaris and GNU:

Roundup Issue Tracker: http://roundup-tracker.org/