changeset 5381:0942fe89e82e

Python 3 preparation: change "x.has_key(y)" to "y in x". (Also likewise "not in" where appropriate.) Tool-generated patch.
author Joseph Myers <jsm@polyomino.org.uk>
date Tue, 24 Jul 2018 22:08:17 +0000
parents 64c4e43fbb84
children 1556b39fde7c
files detectors/creator_resolution.py detectors/emailauditor.py frontends/ZRoundup/ZRoundup.py frontends/ZRoundup/__init__.py roundup/backends/back_mysql.py roundup/cgi/PageTemplates/MultiMapping.py roundup/cgi/PageTemplates/PageTemplate.py roundup/cgi/PageTemplates/TALES.py roundup/cgi/TAL/DummyEngine.py roundup/cgi/TAL/HTMLTALParser.py roundup/cgi/TAL/TALDefs.py roundup/cgi/TAL/TALGenerator.py roundup/cgi/TAL/TALInterpreter.py roundup/cgi/engine_zopetal.py roundup/cgi/form_parser.py roundup/cgi/templating.py roundup/dist/command/build_scripts.py roundup/hyperdb.py roundup/instance.py roundup/mailgw.py roundup/roundupdb.py roundup/scripts/roundup_server.py roundup/support.py roundup/token.py roundup/xmlrpc.py scripts/imapServer.py scripts/import_sf.py share/roundup/templates/classic/detectors/messagesummary.py share/roundup/templates/classic/detectors/nosyreaction.py share/roundup/templates/classic/detectors/statusauditor.py share/roundup/templates/classic/detectors/userauditor.py share/roundup/templates/devel/detectors/messagesummary.py share/roundup/templates/devel/detectors/no_texthtml.py share/roundup/templates/devel/detectors/nosyreaction.py share/roundup/templates/devel/detectors/patches.py share/roundup/templates/devel/detectors/severityauditor.py share/roundup/templates/devel/detectors/statusauditor.py share/roundup/templates/devel/detectors/userauditor.py share/roundup/templates/devel/extensions/spambayes.py share/roundup/templates/jinja2/detectors/messagesummary.py share/roundup/templates/jinja2/detectors/nosyreaction.py share/roundup/templates/jinja2/detectors/statusauditor.py share/roundup/templates/jinja2/detectors/userauditor.py share/roundup/templates/minimal/detectors/userauditor.py share/roundup/templates/responsive/detectors/messagesummary.py share/roundup/templates/responsive/detectors/no_texthtml.py share/roundup/templates/responsive/detectors/nosyreaction.py share/roundup/templates/responsive/detectors/patches.py share/roundup/templates/responsive/detectors/severityauditor.py share/roundup/templates/responsive/detectors/statusauditor.py share/roundup/templates/responsive/detectors/userauditor.py share/roundup/templates/responsive/extensions/spambayes.py test/db_test_base.py test/memorydb.py test/test_cgi.py test/test_mailgw.py tools/pygettext.py website/issues/detectors/messagesummary.py website/issues/detectors/no_texthtml.py website/issues/detectors/nosyreaction.py website/issues/detectors/patches.py website/issues/detectors/severityauditor.py website/issues/detectors/statusauditor.py website/issues/detectors/userauditor.py website/issues/extensions/spambayes.py
diffstat 65 files changed, 184 insertions(+), 185 deletions(-) [+]
line wrap: on
line diff
--- a/detectors/creator_resolution.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/detectors/creator_resolution.py	Tue Jul 24 22:08:17 2018 +0000
@@ -9,7 +9,7 @@
     "confirm-done" first though, but "classic" Roundup doesn't have that
     status)
     '''
-    if not newvalues.has_key('status'):
+    if 'status' not in newvalues:
         return
 
     # get the resolved state ID
--- a/detectors/emailauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/detectors/emailauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -29,7 +29,7 @@
 
     So... we do that. :)'''
     if newvalues.get('type', '').lower() == "message/rfc822":
-        if not newvalues.has_key('name'):
+        if 'name' not in newvalues:
             newvalues['name'] = 'email.mht'
             return
         name = newvalues['name']
--- a/frontends/ZRoundup/ZRoundup.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/frontends/ZRoundup/ZRoundup.py	Tue Jul 24 22:08:17 2018 +0000
@@ -95,12 +95,12 @@
     def __iter__(self):
         return iter(self.__form)
     def getvalue(self, key, default=None):
-        if self.__form.has_key(key):
+        if key in self.__form:
             return self.__form[key]
         else:
             return default
     def has_key(self, item):
-        return self.__form.has_key(item)
+        return item in self.__form
     def keys(self):
         return self.__form.keys()
 
--- a/frontends/ZRoundup/__init__.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/frontends/ZRoundup/__init__.py	Tue Jul 24 22:08:17 2018 +0000
@@ -19,7 +19,7 @@
 import os
 # figure where ZRoundup is installed
 here = None
-if os.environ.has_key('INSTANCE_HOME'):
+if 'INSTANCE_HOME' in os.environ:
     here = os.environ['INSTANCE_HOME']
     path = os.path.join(here, 'Products', 'ZRoundup')
     if not os.path.exists(path):
--- a/roundup/backends/back_mysql.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/backends/back_mysql.py	Tue Jul 24 22:08:17 2018 +0000
@@ -49,10 +49,10 @@
 
 def connection_dict(config, dbnamestr=None):
     d = rdbms_common.connection_dict(config, dbnamestr)
-    if d.has_key('password'):
+    if 'password' in d:
         d['passwd'] = d['password']
         del d['password']
-    if d.has_key('port'):
+    if 'port' in d:
         d['port'] = int(d['port'])
     return d
 
@@ -252,12 +252,12 @@
             for name, s_prop in old_spec[1]:
                 # s_prop is a repr() string of a hyperdb type object
                 if s_prop.find('Multilink') == -1:
-                    if properties.has_key(name):
+                    if name in properties:
                         propnames.append(name)
                     continue
                 tn = '%s_%s'%(cn, name)
 
-                if properties.has_key(name):
+                if name in properties:
                     # grabe the current values
                     sql = 'select linkid, nodeid from %s'%tn
                     self.sql(sql)
@@ -268,7 +268,7 @@
                 sql = 'drop table %s'%tn
                 self.sql(sql)
 
-                if properties.has_key(name):
+                if name in properties:
                     # re-create and populate the new table
                     self.create_multilink_table(klass, name)
                     sql = '''insert into %s (linkid, nodeid) values
--- a/roundup/cgi/PageTemplates/MultiMapping.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/cgi/PageTemplates/MultiMapping.py	Tue Jul 24 22:08:17 2018 +0000
@@ -5,13 +5,13 @@
         self.stores = list(stores)
     def __getitem__(self, key):
         for store in self.stores:
-            if store.has_key(key):
+            if key in store:
                 return store[key]
         raise KeyError(key)
     _marker = []
     def get(self, key, default=_marker):
         for store in self.stores:
-            if store.has_key(key):
+            if key in store:
                 return store[key]
         if default is self._marker:
             raise KeyError(key)
--- a/roundup/cgi/PageTemplates/PageTemplate.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/cgi/PageTemplates/PageTemplate.py	Tue Jul 24 22:08:17 2018 +0000
@@ -93,7 +93,7 @@
         return output.getvalue()
 
     def __call__(self, *args, **kwargs):
-        if not kwargs.has_key('args'):
+        if 'args' not in kwargs:
             kwargs['args'] = args
         return self.pt_render(extra_context={'options': kwargs})
 
--- a/roundup/cgi/PageTemplates/TALES.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/cgi/PageTemplates/TALES.py	Tue Jul 24 22:08:17 2018 +0000
@@ -114,7 +114,7 @@
         if not _valid_name(name):
             raise RegistrationError('Invalid Expression type "%s".' % name)
         types = self.types
-        if types.has_key(name):
+        if name in types:
             raise RegistrationError(
                 'Multiple registrations for Expression type "%s".' %
                 name)
--- a/roundup/cgi/TAL/DummyEngine.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/cgi/TAL/DummyEngine.py	Tue Jul 24 22:08:17 2018 +0000
@@ -27,7 +27,7 @@
 ustr = str
 
 IDomain = None
-if sys.modules.has_key('Zope'):
+if 'Zope' in sys.modules:
     try:
         from Zope.I18n.ITranslationService import ITranslationService
         from Zope.I18n.IDomain import IDomain
@@ -114,7 +114,7 @@
         if type == "not":
             return not self.evaluate(expr)
         if type == "exists":
-            return self.locals.has_key(expr) or self.globals.has_key(expr)
+            return expr in self.locals or expr in self.globals
         if type == "python":
             try:
                 return eval(expr, self.globals, self.locals)
@@ -132,9 +132,9 @@
 
     def evaluatePathOrVar(self, expr):
         expr = expr.strip()
-        if self.locals.has_key(expr):
+        if expr in self.locals:
             return self.locals[expr]
-        elif self.globals.has_key(expr):
+        elif expr in self.globals:
             return self.globals[expr]
         else:
             raise TALESError("unknown variable: %s" % repr(expr))
--- a/roundup/cgi/TAL/HTMLTALParser.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/cgi/TAL/HTMLTALParser.py	Tue Jul 24 22:08:17 2018 +0000
@@ -183,7 +183,7 @@
         if tag in EMPTY_HTML_TAGS:
             return
         close_to = -1
-        if BLOCK_CLOSING_TAG_MAP.has_key(tag):
+        if tag in BLOCK_CLOSING_TAG_MAP:
             blocks_to_close = BLOCK_CLOSING_TAG_MAP[tag]
             for i in range(len(self.tagstack)):
                 t = self.tagstack[i]
@@ -295,17 +295,17 @@
             if ns and ns != 'unknown':
                 item = (key, value, ns)
             if ns == 'tal':
-                if taldict.has_key(keybase):
+                if keybase in taldict:
                     raise TALError("duplicate TAL attribute " +
                                    repr(keybase), self.getpos())
                 taldict[keybase] = value
             elif ns == 'metal':
-                if metaldict.has_key(keybase):
+                if keybase in metaldict:
                     raise METALError("duplicate METAL attribute " +
                                      repr(keybase), self.getpos())
                 metaldict[keybase] = value
             elif ns == 'i18n':
-                if i18ndict.has_key(keybase):
+                if keybase in i18ndict:
                     raise I18NError("duplicate i18n attribute " +
                                     repr(keybase), self.getpos())
                 i18ndict[keybase] = value
--- a/roundup/cgi/TAL/TALDefs.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/cgi/TAL/TALDefs.py	Tue Jul 24 22:08:17 2018 +0000
@@ -126,7 +126,7 @@
         name, expr = m.group(1, 2)
         if not xml:
             name = name.lower()
-        if dict.has_key(name):
+        if name in dict:
             raise TALError("Duplicate attribute name in attributes: " + repr(part))
         dict[name] = expr
     return dict
--- a/roundup/cgi/TAL/TALGenerator.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/cgi/TAL/TALGenerator.py	Tue Jul 24 22:08:17 2018 +0000
@@ -369,7 +369,7 @@
     def emitDefineMacro(self, macroName):
         program = self.popProgram()
         macroName = macroName.strip()
-        if self.macros.has_key(macroName):
+        if macroName in self.macros:
             raise METALError("duplicate macro definition: %s" % repr(macroName),
                              self.position)
         if not re.match('%s$' % NAME_RE, macroName):
@@ -396,7 +396,7 @@
     def emitFillSlot(self, slotName):
         program = self.popProgram()
         slotName = slotName.strip()
-        if self.slots.has_key(slotName):
+        if slotName in self.slots:
             raise METALError("duplicate fill-slot name: %s" % repr(slotName),
                              self.position)
         if not re.match('%s$' % NAME_RE, slotName):
@@ -464,7 +464,7 @@
         newlist = []
         for item in attrlist:
             key = item[0]
-            if repldict.has_key(key):
+            if key in repldict:
                 expr, xlat, msgid = repldict[key]
                 item = item[:2] + ("replace", expr, xlat, msgid)
                 del repldict[key]
@@ -685,7 +685,7 @@
                 ce = self.compileExpression(value)
                 repldict[key] = ce, key in i18nattrs, i18nattrs.get(key)
             for key in i18nattrs:
-                if not repldict.has_key(key):
+                if key not in repldict:
                     repldict[key] = None, 1, i18nattrs.get(key)
         else:
             repldict = {}
--- a/roundup/cgi/TAL/TALInterpreter.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/cgi/TAL/TALInterpreter.py	Tue Jul 24 22:08:17 2018 +0000
@@ -67,7 +67,7 @@
     # Now substitute with the variables in mapping.
     for string in to_replace:
         var = _get_var_regex.findall(string)[0]
-        if mapping.has_key(var):
+        if var in mapping:
             # Call ustr because we may have an integer for instance.
             subst = ustr(mapping[var])
             try:
--- a/roundup/cgi/engine_zopetal.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/cgi/engine_zopetal.py	Tue Jul 24 22:08:17 2018 +0000
@@ -32,7 +32,7 @@
             if error.errno != errno.ENOENT:
                 raise
 
-        if self.templates.has_key(src) and \
+        if src in self.templates and \
                 stime <= self.templates[src].mtime:
             # compiled template is up to date
             return self.templates[src]
--- a/roundup/cgi/form_parser.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/cgi/form_parser.py	Tue Jul 24 22:08:17 2018 +0000
@@ -271,13 +271,13 @@
 
             # get more info about the class, and the current set of
             # form props for it
-            if not all_propdef.has_key(cn):
+            if cn not in all_propdef:
                 all_propdef[cn] = cl.getprops()
             propdef = all_propdef[cn]
-            if not all_props.has_key(this):
+            if this not in all_props:
                 all_props[this] = {}
             props = all_props[this]
-            if not got_props.has_key(this):
+            if this not in got_props:
                 got_props[this] = {}
 
             # is this a link command?
@@ -294,11 +294,11 @@
                     lcn = m.group(1)
                     lcl = self.db.classes[lcn]
                     lnodeid = m.group(2)
-                    if not all_propdef.has_key(lcn):
+                    if lcn not in all_propdef:
                         all_propdef[lcn] = lcl.getprops()
-                    if not all_props.has_key((lcn, lnodeid)):
+                    if (lcn, lnodeid) not in all_props:
                         all_props[(lcn, lnodeid)] = {}
-                    if not got_props.has_key((lcn, lnodeid)):
+                    if (lcn, lnodeid) not in got_props:
                         got_props[(lcn, lnodeid)] = {}
 
                 # make sure the link property is valid
@@ -323,7 +323,7 @@
                     if m.group('classname'):
                         this = (m.group('classname'), m.group('id'))
                         entry = m.group('propname')
-                    if not all_required.has_key(this):
+                    if this not in all_required:
                         all_required[this] = []
                     all_required[this].append(entry)
                 continue
@@ -336,7 +336,7 @@
                 mlaction = 'add'
 
             # does the property exist?
-            if not propdef.has_key(propname):
+            if propname not in propdef:
                 if mlaction != 'set':
                     raise FormError (self._('You have submitted a %(action)s '
                         'action for the property "%(property)s" '
@@ -406,7 +406,7 @@
                 fcn = 'file'
                 fcl = self.db.classes[fcn]
                 fpropname = 'content'
-                if not all_propdef.has_key(fcn):
+                if fcn not in all_propdef:
                     all_propdef[fcn] = fcl.getprops()
                 fpropdef = all_propdef[fcn]
                 have_file = []
@@ -440,7 +440,7 @@
                     value = l
                 else:
                     # we're modifying the list - get the current list of ids
-                    if props.has_key(propname):
+                    if propname in props:
                         existing = props[propname]
                     elif nodeid and not nodeid.startswith('-'):
                         existing = cl.get(nodeid, propname, [])
@@ -500,7 +500,7 @@
                 except KeyError:
                     # this might be a new property for which there is
                     # no existing value
-                    if not propdef.has_key(propname):
+                    if propname not in propdef:
                         raise
                 except IndexError as message:
                     raise FormError(str(message))
@@ -553,7 +553,7 @@
             # register the values we got
             got = got_props.get(thing, {})
             for entry in required[:]:
-                if got.has_key(entry):
+                if entry in got:
                     required.remove(entry)
 
             # If a user doesn't have edit permission for a given property,
@@ -604,7 +604,7 @@
                 #  if content is defined, let it pass through even if
                 #     content is empty. Yes people can upload/create
                 #     empty files.
-                if props.has_key('content'):
+                if 'content' in props:
                     if id is not None and \
                        not id.startswith('-') and \
                        not props['content']:
@@ -622,10 +622,10 @@
     def parse_file(self, fpropdef, fprops, v):
         # try to determine the file content-type
         fn = v.filename.split('\\')[-1]
-        if fpropdef.has_key('name'):
+        if 'name' in fpropdef:
             fprops['name'] = fn
         # use this info as the type/filename properties
-        if fpropdef.has_key('type'):
+        if 'type' in fpropdef:
             if hasattr(v, 'type') and v.type:
                 fprops['type'] = v.type
             elif mimetypes.guess_type(fn)[0]:
--- a/roundup/cgi/templating.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/cgi/templating.py	Tue Jul 24 22:08:17 2018 +0000
@@ -343,7 +343,7 @@
     if client.nodeid:
         c['context'] = HTMLItem(client, classname, client.nodeid,
             anonymous=1)
-    elif client.db.classes.has_key(classname):
+    elif classname in client.db.classes:
         c['context'] = HTMLClass(client, classname, anonymous=1)
     return c
 
@@ -432,7 +432,7 @@
     # but for CSS usage it should be present
     dic.setdefault('type', 'text')
     # useful e.g for HTML LABELs:
-    if not dic.has_key('id'):
+    if 'id' not in dic:
         try:
             if dic['text'] in ('radio', 'checkbox'):
                 dic['id'] = '%(name)s-%(value)s' % dic
@@ -960,7 +960,7 @@
                 continue
             current[prop_n] = prop.plain(escape=1)
             # make link if hrefable
-            if (self._props.has_key(prop_n) and
+            if (prop_n in self._props and
                     isinstance(self._props[prop_n], hyperdb.Link)):
                 classname = self._props[prop_n].classname
                 try:
@@ -1098,7 +1098,7 @@
                             else:
                                 old = label;
                             cell.append('%s: %s' % (self._(k), old))
-                            if current.has_key(k):
+                            if k in current:
                                 cell[-1] += ' -> %s'%current[k]
                                 current[k] = old
 
@@ -1109,7 +1109,7 @@
                             d = date.Date(args[k],
                                 translator=self._client).local(timezone)
                         cell.append('%s: %s'%(self._(k), str(d)))
-                        if current.has_key(k):
+                        if k in current:
                             cell[-1] += ' -> %s' % current[k]
                             current[k] = str(d)
 
@@ -1117,33 +1117,33 @@
                         val = str(date.Interval(args[k],
                             translator=self._client))
                         cell.append('%s: %s'%(self._(k), val))
-                        if current.has_key(k):
+                        if k in current:
                             cell[-1] += ' -> %s'%current[k]
                             current[k] = val
 
                     elif isinstance(prop, hyperdb.String) and args[k]:
                         val = cgi.escape(args[k])
                         cell.append('%s: %s'%(self._(k), val))
-                        if current.has_key(k):
+                        if k in current:
                             cell[-1] += ' -> %s'%current[k]
                             current[k] = val
 
                     elif isinstance(prop, hyperdb.Boolean) and args[k] is not None:
                         val = args[k] and ''"Yes" or ''"No"
                         cell.append('%s: %s'%(self._(k), val))
-                        if current.has_key(k):
+                        if k in current:
                             cell[-1] += ' -> %s'%current[k]
                             current[k] = val
 
                     elif isinstance(prop, hyperdb.Password) and args[k] is not None:
                         val = args[k].dummystr()
                         cell.append('%s: %s'%(self._(k), val))
-                        if current.has_key(k):
+                        if k in current:
                             cell[-1] += ' -> %s'%current[k]
                             current[k] = val
 
                     elif not args[k]:
-                        if current.has_key(k):
+                        if k in current:
                             cell.append('%s: %s'%(self._(k), current[k]))
                             current[k] = '(no value)'
                         else:
@@ -1151,7 +1151,7 @@
 
                     else:
                         cell.append('%s: %s'%(self._(k), str(args[k])))
-                        if current.has_key(k):
+                        if k in current:
                             cell[-1] += ' -> %s'%current[k]
                             current[k] = str(args[k])
 
@@ -1309,7 +1309,7 @@
         # is specified in the current form.
         form = self._client.form
         try:
-            is_in = form.has_key(self._formname)
+            is_in = self._formname in form
         except TypeError:
             is_in = False
         if is_in and (not self._value or self._client.form_wins):
@@ -2633,7 +2633,7 @@
 
     def _form_has_key(self, name):
         try:
-            return self.form.has_key(name)
+            return name in self.form
         except TypeError:
             pass
         return False
@@ -2739,7 +2739,7 @@
         """ Update my attributes using the keyword args
         """
         self.__dict__.update(kwargs)
-        if kwargs.has_key('columns'):
+        if 'columns' in kwargs:
             self.show = support.TruthDict(self.columns)
 
     def description(self):
@@ -2865,9 +2865,9 @@
                 specials[key[1:]] = args[key]
 
         # ok, now handle the specials we received in the request
-        if self.columns and not specials.has_key('columns'):
+        if self.columns and 'columns' not in specials:
             l.append(sc+'columns=%s'%(','.join(self.columns)))
-        if self.sort and not specials.has_key('sort'):
+        if self.sort and 'sort' not in specials:
             val = []
             for dir, attr in self.sort:
                 if dir == '-':
@@ -2875,7 +2875,7 @@
                 else:
                     val.append(attr)
             l.append(sc+'sort=%s'%(','.join(val)))
-        if self.group and not specials.has_key('group'):
+        if self.group and 'group' not in specials:
             val = []
             for dir, attr in self.group:
                 if dir == '-':
@@ -2883,20 +2883,20 @@
                 else:
                     val.append(attr)
             l.append(sc+'group=%s'%(','.join(val)))
-        if self.filter and not specials.has_key('filter'):
+        if self.filter and 'filter' not in specials:
             l.append(sc+'filter=%s'%(','.join(self.filter)))
-        if self.search_text and not specials.has_key('search_text'):
+        if self.search_text and 'search_text' not in specials:
             l.append(sc+'search_text=%s'%q(self.search_text))
-        if not specials.has_key('pagesize'):
+        if 'pagesize' not in specials:
             l.append(sc+'pagesize=%s'%self.pagesize)
-        if not specials.has_key('startwith'):
+        if 'startwith' not in specials:
             l.append(sc+'startwith=%s'%self.startwith)
 
         # finally, the remainder of the filter args in the request
         if self.classname and self.filterspec:
             cls = self.client.db.getclass(self.classname)
             for k,v in self.filterspec.items():
-                if not args.has_key(k):
+                if k not in args:
                     if type(v) == type([]):
                         prop = cls.get_transitive_prop(k)
                         if k != 'id' and isinstance(prop, hyperdb.String):
@@ -3079,7 +3079,7 @@
         if not hasattr(self.client.instance, 'templating_utils'):
             # backwards-compatibility
             raise AttributeError(name)
-        if not self.client.instance.templating_utils.has_key(name):
+        if name not in self.client.instance.templating_utils:
             raise AttributeError(name)
         return self.client.instance.templating_utils[name]
 
--- a/roundup/dist/command/build_scripts.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/dist/command/build_scripts.py	Tue Jul 24 22:08:17 2018 +0000
@@ -46,9 +46,9 @@
         if self.target_platform:
             # TODO? allow explicit setting from command line
             target = self.target_platform
-        if cmdopt.has_key("bdist_wininst"):
+        if "bdist_wininst" in cmdopt:
             target = "win32"
-        elif cmdopt.get("bdist", {}).has_key("formats"):
+        elif "formats" in cmdopt.get("bdist", {}):
             formats = cmdopt["bdist"]["formats"][1].split(",")
             if formats[0] == "wininst":
                 target = "win32"
@@ -82,7 +82,7 @@
             self.scripts = [script + ".bat" for script in self.scripts]
 
         # tweak python path for installations outside main python library
-        if cmdopt.get("install", {}).has_key("prefix"):
+        if "prefix" in cmdopt.get("install", {}):
             prefix = os.path.expanduser(cmdopt['install']['prefix'][1])
             version = '%d.%d'%sys.version_info[:2]
             self.script_preamble = """
--- a/roundup/hyperdb.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/hyperdb.py	Tue Jul 24 22:08:17 2018 +0000
@@ -852,7 +852,7 @@
         must map names to property objects, or a TypeError is raised.
         """
         for name in 'creation activity creator actor'.split():
-            if properties.has_key(name):
+            if name in properties:
                 raise ValueError('"creation", "activity", "creator" and '\
                     '"actor" are reserved')
 
@@ -1192,9 +1192,9 @@
         if  k:
             return k
         props = self.getprops()
-        if props.has_key('name'):
+        if 'name' in props:
             return 'name'
-        elif props.has_key('title'):
+        elif 'title' in props:
             return 'title'
         if default_to_id:
             return 'id'
@@ -1216,7 +1216,7 @@
         if hasattr(self, '_orderprop'):
             return self._orderprop
         props = self.getprops()
-        if props.has_key('order'):
+        if 'order' in props:
             return 'order'
         return self.labelprop()
 
@@ -1624,7 +1624,7 @@
         """The newly-created class automatically includes the "content"
         property.
         """
-        if not properties.has_key('content'):
+        if 'content' not in properties:
             properties['content'] = String(indexme='yes')
 
     def export_propnames(self):
@@ -1664,7 +1664,7 @@
 
         mime_type = None
         props = self.getprops()
-        if props.has_key('type'):
+        if 'type' in props:
             mime_type = self.get(nodeid, 'type')
         if not mime_type:
             mime_type = self.default_mime_type
@@ -1691,14 +1691,14 @@
             l.append((name, self.cl.get(self.nodeid, name)))
         return l
     def has_key(self, name):
-        return self.cl.getprops().has_key(name)
+        return name in self.cl.getprops()
     def get(self, name, default=None):
-        if self.has_key(name):
+        if name in self:
             return self[name]
         else:
             return default
     def __getattr__(self, name):
-        if self.__dict__.has_key(name):
+        if name in self.__dict__:
             return self.__dict__[name]
         try:
             return self.cl.get(self.nodeid, name)
--- a/roundup/instance.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/instance.py	Tue Jul 24 22:08:17 2018 +0000
@@ -288,7 +288,7 @@
                 raise TrackerError('File "%s.py" missing from tracker '\
                     'home "%s"'%(reqd, tracker_home))
 
-        if self.trackers.has_key(tracker_home):
+        if tracker_home in self.trackers:
             return imp.load_package(self.trackers[tracker_home],
                 tracker_home)
         # register all available backend modules
--- a/roundup/mailgw.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/mailgw.py	Tue Jul 24 22:08:17 2018 +0000
@@ -1006,10 +1006,9 @@
 
         # set the issue title to the subject
         title = title.strip()
-        if (title and self.properties.has_key('title') and not
-                issue_props.has_key('title')):
+        if (title and 'title' in self.properties and 'title' not in issue_props):
             issue_props['title'] = title
-        if (self.nodeid and self.properties.has_key('title') and not
+        if (self.nodeid and 'title' in self.properties and not
                 self.config['MAILGW_SUBJECT_UPDATES_TITLE']):
             issue_props['title'] = self.cl.get(self.nodeid,'title')
 
@@ -1017,7 +1016,7 @@
         # the props dictionary because function(**props, **issue_props)
         # is a syntax error.
         for prop in issue_props.keys() :
-            if not props.has_key(prop) :
+            if prop not in props :
                 props[prop] = issue_props[prop]
 
         self.props = props
@@ -1105,7 +1104,7 @@
     def create_files(self):
         ''' Create a file for each attachment in the message
         '''
-        if not self.properties.has_key('files'):
+        if 'files' not in self.properties:
             return
         files = []
         file_props = self.mailgw.get_class_arguments('file')
@@ -1144,7 +1143,7 @@
     def create_msg(self):
         ''' Create msg containing all the relevant information from the message
         '''
-        if not self.properties.has_key('messages'):
+        if 'messages' not in self.properties:
             return
         msg_props = self.mailgw.get_class_arguments('msg')
         self.msg_props.update (msg_props)
@@ -1768,7 +1767,7 @@
 
     # try the user alternate addresses if possible
     props = db.user.getprops()
-    if props.has_key('alternate_addresses'):
+    if 'alternate_addresses' in props:
         users = db.user.filter(None, {'alternate_addresses': address})
         # We want an exact match of the email, not just a substring
         # match. Otherwise e.g. support@example.com would match
--- a/roundup/roundupdb.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/roundupdb.py	Tue Jul 24 22:08:17 2018 +0000
@@ -313,7 +313,7 @@
                             'View', userid, 'msg', prop, msgid)
             return (userid and
                     (self.db.user.get(userid, 'username') != 'anonymous') and
-                    allowed and not seen_message.has_key(userid))
+                    allowed and userid not in seen_message)
 
         # possibly send the message to the author, as long as they aren't
         # anonymous
--- a/roundup/scripts/roundup_server.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/scripts/roundup_server.py	Tue Jul 24 22:08:17 2018 +0000
@@ -410,7 +410,7 @@
             # config option to control its use.
             # Made available for extensions if the user trusts it.
             env['HTTP_X-FORWARDED-PROTO'] = xfp
-        if os.environ.has_key('CGI_SHOW_TIMING'):
+        if 'CGI_SHOW_TIMING' in os.environ:
             env['CGI_SHOW_TIMING'] = os.environ['CGI_SHOW_TIMING']
         env['HTTP_ACCEPT_LANGUAGE'] = self.headers.get('accept-language')
         referer = self.headers.get('Referer')
--- a/roundup/support.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/support.py	Tue Jul 24 22:08:17 2018 +0000
@@ -19,7 +19,7 @@
             self.__getitem__ = lambda name: 1
 
     def __getitem__(self, name):
-        return self.keys.has_key(name)
+        return name in self.keys
 
 def ensureParentsExist(dest):
     if not os.path.exists(os.path.dirname(dest)):
--- a/roundup/token.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/token.py	Tue Jul 24 22:08:17 2018 +0000
@@ -106,7 +106,7 @@
             # escaped-char conversions (t, r, n)
             # TODO: octal, hexdigit
             state = oldstate
-            if escaped.has_key(c):
+            if c in escaped:
                 c = escaped[c]
         # just add this char to the token and move along
         token = token + c
--- a/roundup/xmlrpc.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/roundup/xmlrpc.py	Tue Jul 24 22:08:17 2018 +0000
@@ -140,7 +140,7 @@
 
         # check for the key property
         key = cl.getkey()
-        if key and not props.has_key(key):
+        if key and key not in props:
             raise UsageError('you must provide the "%s" property.'%key)
 
         for key in props:
--- a/scripts/imapServer.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/scripts/imapServer.py	Tue Jul 24 22:08:17 2018 +0000
@@ -137,19 +137,19 @@
         each mailbox is associated with 1 database home
         """
         log.info('Adding mailbox %s', mailbox)
-        if not self.mailboxes.has_key(mailbox.server):
+        if mailbox.server not in self.mailboxes:
             self.mailboxes[mailbox.server] = {'protocol':'imaps', 'users':{}}
         server = self.mailboxes[mailbox.server]
         if mailbox.protocol:
             server['protocol'] = mailbox.protocol
 
-        if not server['users'].has_key(mailbox.username):
+        if mailbox.username not in server['users']:
             server['users'][mailbox.username] = {'password':'', 'mailboxes':{}}
         user = server['users'][mailbox.username]
         if mailbox.password:
             user['password'] = mailbox.password
 
-        if user['mailboxes'].has_key(mailbox.mailbox):
+        if mailbox.mailbox in user['mailboxes']:
             raise ValueError('Mailbox is already defined')
 
         user['mailboxes'][mailbox.mailbox] = mailbox.dbhome
--- a/scripts/import_sf.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/scripts/import_sf.py	Tue Jul 24 22:08:17 2018 +0000
@@ -85,7 +85,7 @@
 
     for aid, fid in support.Progress('Fetching files', list(to_fetch)):
         if fid in got: continue
-        if not urls.has_key(aid):
+        if aid not in urls:
             urls[aid] = get_url(aid)
             f = open(os.path.join(file_dir, 'urls.txt'), 'a')
             f.write('%s %s\n'%(aid, urls[aid]))
@@ -175,7 +175,7 @@
 
         categories.add(d['category'])
 
-        if op.has_key('body'):
+        if 'body' in op:
             l = d.setdefault('messages', [])
             l.insert(0, op)
 
@@ -361,7 +361,7 @@
             if name == 'is retired':
                 continue
             prop = props[name]
-            if entry.has_key(name):
+            if name in entry:
                 if isinstance(prop, hyperdb.Date) or \
                         isinstance(prop, hyperdb.Interval):
                     row.append(repr(entry[name].get_tuple()))
--- a/share/roundup/templates/classic/detectors/messagesummary.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/classic/detectors/messagesummary.py	Tue Jul 24 22:08:17 2018 +0000
@@ -3,7 +3,7 @@
 def summarygenerator(db, cl, nodeid, newvalues):
     ''' If the message doesn't have a summary, make one for it.
     '''
-    if newvalues.has_key('summary') or not newvalues.has_key('content'):
+    if 'summary' in newvalues or 'content' not in newvalues:
         return
 
     summary, content = parseContent(newvalues['content'], config=db.config)
--- a/share/roundup/templates/classic/detectors/nosyreaction.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/classic/detectors/nosyreaction.py	Tue Jul 24 22:08:17 2018 +0000
@@ -48,7 +48,7 @@
     if oldvalues is None:
         # the action was a create, so use all the messages in the create
         messages = cl.get(nodeid, 'messages')
-    elif oldvalues.has_key('messages'):
+    elif 'messages' in oldvalues:
         # the action was a set (so adding new messages to an existing issue)
         m = {}
         for msgid in oldvalues['messages']:
@@ -56,7 +56,7 @@
         messages = []
         # figure which of the messages now on the issue weren't there before
         for msgid in cl.get(nodeid, 'messages'):
-            if not m.has_key(msgid):
+            if msgid not in m:
                 messages.append(msgid)
     return messages
 
@@ -71,13 +71,13 @@
         ok = ('yes',)
         # old node, get the current values from the node if they haven't
         # changed
-        if not newvalues.has_key('nosy'):
+        if 'nosy' not in newvalues:
             nosy = cl.get(nodeid, 'nosy')
             for value in nosy:
                 current_nosy.add(value)
 
     # if the nosy list changed in this transaction, init from the new value
-    if newvalues.has_key('nosy'):
+    if 'nosy' in newvalues:
         nosy = newvalues.get('nosy', [])
         for value in nosy:
             if not db.hasnode('user', value):
@@ -87,7 +87,7 @@
     new_nosy = set(current_nosy)
 
     # add assignedto(s) to the nosy list
-    if newvalues.has_key('assignedto') and newvalues['assignedto'] is not None:
+    if 'assignedto' in newvalues and newvalues['assignedto'] is not None:
         propdef = cl.getprops()
         if isinstance(propdef['assignedto'], hyperdb.Link):
             assignedto_ids = [newvalues['assignedto']]
@@ -98,7 +98,7 @@
 
     # see if there's any new messages - if so, possibly add the author and
     # recipient to the nosy
-    if newvalues.has_key('messages'):
+    if 'messages' in newvalues:
         if nodeid is None:
             ok = ('new', 'yes')
             messages = newvalues['messages']
--- a/share/roundup/templates/classic/detectors/statusauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/classic/detectors/statusauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -24,7 +24,7 @@
         then set it to 'chatting'
     '''
     # don't fire if there's no new message (ie. chat)
-    if not newvalues.has_key('messages'):
+    if 'messages' not in newvalues:
         return
     if newvalues['messages'] == cl.get(nodeid, 'messages'):
         return
@@ -40,7 +40,7 @@
     current_status = cl.get(nodeid, 'status')
 
     # see if there's an explicit change in this transaction
-    if newvalues.has_key('status'):
+    if 'status' in newvalues:
         # yep, skip
         return
 
@@ -62,7 +62,7 @@
 def presetunread(db, cl, nodeid, newvalues):
     ''' Make sure the status is set on new issues
     '''
-    if newvalues.has_key('status') and newvalues['status']:
+    if 'status' in newvalues and newvalues['status']:
         return
 
     # get the unread state ID
--- a/share/roundup/templates/classic/detectors/userauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/classic/detectors/userauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -41,7 +41,7 @@
     ''' iterate over all known addresses in a newvalues dict
         this takes of the address/alterate_addresses handling
     '''
-    if user.has_key('address'):
+    if 'address' in user:
         yield user['address']
     if user.get('alternate_addresses', None):
         for address in user['alternate_addresses'].split('\n'):
@@ -69,7 +69,7 @@
     newroles = newvalues.get('roles')
     if newroles:
         for rolename in [r.lower().strip() for r in newroles.split(',')]:
-            if rolename and not db.security.role.has_key(rolename):
+            if rolename and rolename not in db.security.role:
                 raise ValueError('Role "%s" does not exist'%rolename)
 
     tz = newvalues.get('timezone', None)
--- a/share/roundup/templates/devel/detectors/messagesummary.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/devel/detectors/messagesummary.py	Tue Jul 24 22:08:17 2018 +0000
@@ -3,7 +3,7 @@
 def summarygenerator(db, cl, nodeid, newvalues):
     ''' If the message doesn't have a summary, make one for it.
     '''
-    if newvalues.has_key('summary') or not newvalues.has_key('content'):
+    if 'summary' in newvalues or 'content' not in newvalues:
         return
 
     summary, content = parseContent(newvalues['content'], config=db.config)
--- a/share/roundup/templates/devel/detectors/no_texthtml.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/devel/detectors/no_texthtml.py	Tue Jul 24 22:08:17 2018 +0000
@@ -1,6 +1,6 @@
 
 def audit_html_files(db, cl, nodeid, newvalues):
-    if newvalues.has_key('type') and newvalues['type'] == 'text/html':
+    if 'type' in newvalues and newvalues['type'] == 'text/html':
         newvalues['type'] = 'text/plain'
     
 
--- a/share/roundup/templates/devel/detectors/nosyreaction.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/devel/detectors/nosyreaction.py	Tue Jul 24 22:08:17 2018 +0000
@@ -30,7 +30,7 @@
     if oldvalues is None:
         # the action was a create, so use all the messages in the create
         messages = cl.get(nodeid, 'messages')
-    elif oldvalues.has_key('messages'):
+    elif 'messages' in oldvalues:
         # the action was a set (so adding new messages to an existing issue)
         m = {}
         for msgid in oldvalues['messages']:
@@ -38,7 +38,7 @@
         messages = []
         # figure which of the messages now on the issue weren't there before
         for msgid in cl.get(nodeid, 'messages'):
-            if not m.has_key(msgid):
+            if msgid not in m:
                 messages.append(msgid)
     return messages
 
@@ -53,13 +53,13 @@
         ok = ('yes',)
         # old node, get the current values from the node if they haven't
         # changed
-        if not newvalues.has_key('nosy'):
+        if 'nosy' not in newvalues:
             nosy = cl.get(nodeid, 'nosy')
             for value in nosy:
                 current_nosy.add(value)
 
     # if the nosy list changed in this transaction, init from the new value
-    if newvalues.has_key('nosy'):
+    if 'nosy' in newvalues:
         nosy = newvalues.get('nosy', [])
         for value in nosy:
             if not db.hasnode('user', value):
@@ -69,7 +69,7 @@
     new_nosy = set(current_nosy)
 
     # add assignedto(s) to the nosy list
-    if newvalues.has_key('assignedto') and newvalues['assignedto'] is not None:
+    if 'assignedto' in newvalues and newvalues['assignedto'] is not None:
         propdef = cl.getprops()
         if isinstance(propdef['assignedto'], hyperdb.Link):
             assignedto_ids = [newvalues['assignedto']]
@@ -80,7 +80,7 @@
 
     # see if there's any new messages - if so, possibly add the author and
     # recipient to the nosy
-    if newvalues.has_key('messages'):
+    if 'messages' in newvalues:
         if nodeid is None:
             ok = ('new', 'yes')
             messages = newvalues['messages']
--- a/share/roundup/templates/devel/detectors/patches.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/devel/detectors/patches.py	Tue Jul 24 22:08:17 2018 +0000
@@ -35,7 +35,7 @@
             if patchid in oldkeywords:
                 # This is already marked as a patch
                 return
-        if not newvalues.has_key('keywords'):
+        if 'keywords' not in newvalues:
             newvalues['keywords'] = oldkeywords
         newvalues['keywords'].append(patchid)
 
--- a/share/roundup/templates/devel/detectors/severityauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/devel/detectors/severityauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -1,7 +1,7 @@
 
 def init_severity(db, cl, nodeid, newvalues):
     """Make sure severity is set on new bugs"""
-    if newvalues.has_key('severity') and newvalues['severity']:
+    if 'severity' in newvalues and newvalues['severity']:
         return
 
     normal = db.severity.lookup('normal')
--- a/share/roundup/templates/devel/detectors/statusauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/devel/detectors/statusauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -1,7 +1,7 @@
 def preset_new(db, cl, nodeid, newvalues):
     """ Make sure the status is set on new bugs"""
 
-    if newvalues.has_key('status') and newvalues['status']:
+    if 'status' in newvalues and newvalues['status']:
         return
 
     new = db.status.lookup('new')
--- a/share/roundup/templates/devel/detectors/userauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/devel/detectors/userauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -41,7 +41,7 @@
     ''' iterate over all known addresses in a newvalues dict
         this takes of the address/alterate_addresses handling
     '''
-    if user.has_key('address'):
+    if 'address' in user:
         yield user['address']
     if user.get('alternate_addresses', None):
         for address in user['alternate_addresses'].split('\n'):
@@ -69,7 +69,7 @@
     newroles = newvalues.get('roles')
     if newroles:
         for rolename in [r.lower().strip() for r in newroles.split(',')]:
-            if rolename and not db.security.role.has_key(rolename):
+            if rolename and rolename not in db.security.role:
                 raise ValueError('Role "%s" does not exist'%rolename)
 
     tz = newvalues.get('timezone', None)
--- a/share/roundup/templates/devel/extensions/spambayes.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/devel/extensions/spambayes.py	Tue Jul 24 22:08:17 2018 +0000
@@ -41,9 +41,9 @@
         (content, tokens) = extract_classinfo(self.db,
                                               self.classname, self.nodeid)
 
-        if self.form.has_key("trainspam"):
+        if "trainspam" in self.form:
             is_spam = True
-        elif self.form.has_key("trainham"):
+        elif "trainham" in self.form:
             is_spam = False
 
         (status, errmsg) = train_spambayes(self.db, content, tokens,
--- a/share/roundup/templates/jinja2/detectors/messagesummary.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/jinja2/detectors/messagesummary.py	Tue Jul 24 22:08:17 2018 +0000
@@ -3,7 +3,7 @@
 def summarygenerator(db, cl, nodeid, newvalues):
     ''' If the message doesn't have a summary, make one for it.
     '''
-    if newvalues.has_key('summary') or not newvalues.has_key('content'):
+    if 'summary' in newvalues or 'content' not in newvalues:
         return
 
     summary, content = parseContent(newvalues['content'], config=db.config)
--- a/share/roundup/templates/jinja2/detectors/nosyreaction.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/jinja2/detectors/nosyreaction.py	Tue Jul 24 22:08:17 2018 +0000
@@ -48,7 +48,7 @@
     if oldvalues is None:
         # the action was a create, so use all the messages in the create
         messages = cl.get(nodeid, 'messages')
-    elif oldvalues.has_key('messages'):
+    elif 'messages' in oldvalues:
         # the action was a set (so adding new messages to an existing issue)
         m = {}
         for msgid in oldvalues['messages']:
@@ -56,7 +56,7 @@
         messages = []
         # figure which of the messages now on the issue weren't there before
         for msgid in cl.get(nodeid, 'messages'):
-            if not m.has_key(msgid):
+            if msgid not in m:
                 messages.append(msgid)
     return messages
 
@@ -71,13 +71,13 @@
         ok = ('yes',)
         # old node, get the current values from the node if they haven't
         # changed
-        if not newvalues.has_key('nosy'):
+        if 'nosy' not in newvalues:
             nosy = cl.get(nodeid, 'nosy')
             for value in nosy:
                 current_nosy.add(value)
 
     # if the nosy list changed in this transaction, init from the new value
-    if newvalues.has_key('nosy'):
+    if 'nosy' in newvalues:
         nosy = newvalues.get('nosy', [])
         for value in nosy:
             if not db.hasnode('user', value):
@@ -87,7 +87,7 @@
     new_nosy = set(current_nosy)
 
     # add assignedto(s) to the nosy list
-    if newvalues.has_key('assignedto') and newvalues['assignedto'] is not None:
+    if 'assignedto' in newvalues and newvalues['assignedto'] is not None:
         propdef = cl.getprops()
         if isinstance(propdef['assignedto'], hyperdb.Link):
             assignedto_ids = [newvalues['assignedto']]
@@ -98,7 +98,7 @@
 
     # see if there's any new messages - if so, possibly add the author and
     # recipient to the nosy
-    if newvalues.has_key('messages'):
+    if 'messages' in newvalues:
         if nodeid is None:
             ok = ('new', 'yes')
             messages = newvalues['messages']
--- a/share/roundup/templates/jinja2/detectors/statusauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/jinja2/detectors/statusauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -24,7 +24,7 @@
         then set it to 'chatting'
     '''
     # don't fire if there's no new message (ie. chat)
-    if not newvalues.has_key('messages'):
+    if 'messages' not in newvalues:
         return
     if newvalues['messages'] == cl.get(nodeid, 'messages'):
         return
@@ -40,7 +40,7 @@
     current_status = cl.get(nodeid, 'status')
 
     # see if there's an explicit change in this transaction
-    if newvalues.has_key('status'):
+    if 'status' in newvalues:
         # yep, skip
         return
 
@@ -62,7 +62,7 @@
 def presetunread(db, cl, nodeid, newvalues):
     ''' Make sure the status is set on new issues
     '''
-    if newvalues.has_key('status') and newvalues['status']:
+    if 'status' in newvalues and newvalues['status']:
         return
 
     # get the unread state ID
--- a/share/roundup/templates/jinja2/detectors/userauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/jinja2/detectors/userauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -41,7 +41,7 @@
     ''' iterate over all known addresses in a newvalues dict
         this takes of the address/alterate_addresses handling
     '''
-    if user.has_key('address'):
+    if 'address' in user:
         yield user['address']
     if user.get('alternate_addresses', None):
         for address in user['alternate_addresses'].split('\n'):
@@ -69,7 +69,7 @@
     newroles = newvalues.get('roles')
     if newroles:
         for rolename in [r.lower().strip() for r in newroles.split(',')]:
-            if rolename and not db.security.role.has_key(rolename):
+            if rolename and rolename not in db.security.role:
                 raise ValueError('Role "%s" does not exist'%rolename)
 
     tz = newvalues.get('timezone', None)
--- a/share/roundup/templates/minimal/detectors/userauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/minimal/detectors/userauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -41,7 +41,7 @@
     ''' iterate over all known addresses in a newvalues dict
         this takes of the address/alterate_addresses handling
     '''
-    if user.has_key('address'):
+    if 'address' in user:
         yield user['address']
     if user.get('alternate_addresses', None):
         for address in user['alternate_addresses'].split('\n'):
@@ -69,7 +69,7 @@
     newroles = newvalues.get('roles')
     if newroles:
         for rolename in [r.lower().strip() for r in newroles.split(',')]:
-            if rolename and not db.security.role.has_key(rolename):
+            if rolename and rolename not in db.security.role:
                 raise ValueError('Role "%s" does not exist'%rolename)
 
     tz = newvalues.get('timezone', None)
--- a/share/roundup/templates/responsive/detectors/messagesummary.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/responsive/detectors/messagesummary.py	Tue Jul 24 22:08:17 2018 +0000
@@ -3,7 +3,7 @@
 def summarygenerator(db, cl, nodeid, newvalues):
     ''' If the message doesn't have a summary, make one for it.
     '''
-    if newvalues.has_key('summary') or not newvalues.has_key('content'):
+    if 'summary' in newvalues or 'content' not in newvalues:
         return
 
     summary, content = parseContent(newvalues['content'], config=db.config)
--- a/share/roundup/templates/responsive/detectors/no_texthtml.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/responsive/detectors/no_texthtml.py	Tue Jul 24 22:08:17 2018 +0000
@@ -1,6 +1,6 @@
 
 def audit_html_files(db, cl, nodeid, newvalues):
-    if newvalues.has_key('type') and newvalues['type'] == 'text/html':
+    if 'type' in newvalues and newvalues['type'] == 'text/html':
         newvalues['type'] = 'text/plain'
     
 
--- a/share/roundup/templates/responsive/detectors/nosyreaction.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/responsive/detectors/nosyreaction.py	Tue Jul 24 22:08:17 2018 +0000
@@ -30,7 +30,7 @@
     if oldvalues is None:
         # the action was a create, so use all the messages in the create
         messages = cl.get(nodeid, 'messages')
-    elif oldvalues.has_key('messages'):
+    elif 'messages' in oldvalues:
         # the action was a set (so adding new messages to an existing issue)
         m = {}
         for msgid in oldvalues['messages']:
@@ -38,7 +38,7 @@
         messages = []
         # figure which of the messages now on the issue weren't there before
         for msgid in cl.get(nodeid, 'messages'):
-            if not m.has_key(msgid):
+            if msgid not in m:
                 messages.append(msgid)
     return messages
 
@@ -53,13 +53,13 @@
         ok = ('yes',)
         # old node, get the current values from the node if they haven't
         # changed
-        if not newvalues.has_key('nosy'):
+        if 'nosy' not in newvalues:
             nosy = cl.get(nodeid, 'nosy')
             for value in nosy:
                 current_nosy.add(value)
 
     # if the nosy list changed in this transaction, init from the new value
-    if newvalues.has_key('nosy'):
+    if 'nosy' in newvalues:
         nosy = newvalues.get('nosy', [])
         for value in nosy:
             if not db.hasnode('user', value):
@@ -69,7 +69,7 @@
     new_nosy = set(current_nosy)
 
     # add assignedto(s) to the nosy list
-    if newvalues.has_key('assignedto') and newvalues['assignedto'] is not None:
+    if 'assignedto' in newvalues and newvalues['assignedto'] is not None:
         propdef = cl.getprops()
         if isinstance(propdef['assignedto'], hyperdb.Link):
             assignedto_ids = [newvalues['assignedto']]
@@ -80,7 +80,7 @@
 
     # see if there's any new messages - if so, possibly add the author and
     # recipient to the nosy
-    if newvalues.has_key('messages'):
+    if 'messages' in newvalues:
         if nodeid is None:
             ok = ('new', 'yes')
             messages = newvalues['messages']
--- a/share/roundup/templates/responsive/detectors/patches.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/responsive/detectors/patches.py	Tue Jul 24 22:08:17 2018 +0000
@@ -35,7 +35,7 @@
             if patchid in oldkeywords:
                 # This is already marked as a patch
                 return
-        if not newvalues.has_key('keywords'):
+        if 'keywords' not in newvalues:
             newvalues['keywords'] = oldkeywords
         newvalues['keywords'].append(patchid)
 
--- a/share/roundup/templates/responsive/detectors/severityauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/responsive/detectors/severityauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -1,7 +1,7 @@
 
 def init_severity(db, cl, nodeid, newvalues):
     """Make sure severity is set on new bugs"""
-    if newvalues.has_key('severity') and newvalues['severity']:
+    if 'severity' in newvalues and newvalues['severity']:
         return
 
     normal = db.severity.lookup('normal')
--- a/share/roundup/templates/responsive/detectors/statusauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/responsive/detectors/statusauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -1,7 +1,7 @@
 def preset_new(db, cl, nodeid, newvalues):
     """ Make sure the status is set on new bugs"""
 
-    if newvalues.has_key('status') and newvalues['status']:
+    if 'status' in newvalues and newvalues['status']:
         return
 
     new = db.status.lookup('new')
--- a/share/roundup/templates/responsive/detectors/userauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/responsive/detectors/userauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -41,7 +41,7 @@
     ''' iterate over all known addresses in a newvalues dict
         this takes of the address/alterate_addresses handling
     '''
-    if user.has_key('address'):
+    if 'address' in user:
         yield user['address']
     if user.get('alternate_addresses', None):
         for address in user['alternate_addresses'].split('\n'):
@@ -69,7 +69,7 @@
     newroles = newvalues.get('roles')
     if newroles:
         for rolename in [r.lower().strip() for r in newroles.split(',')]:
-            if rolename and not db.security.role.has_key(rolename):
+            if rolename and rolename not in db.security.role:
                 raise ValueError('Role "%s" does not exist'%rolename)
 
     tz = newvalues.get('timezone', None)
--- a/share/roundup/templates/responsive/extensions/spambayes.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/share/roundup/templates/responsive/extensions/spambayes.py	Tue Jul 24 22:08:17 2018 +0000
@@ -41,9 +41,9 @@
         (content, tokens) = extract_classinfo(self.db,
                                               self.classname, self.nodeid)
 
-        if self.form.has_key("trainspam"):
+        if "trainspam" in self.form:
             is_spam = True
-        elif self.form.has_key("trainham"):
+        elif "trainham" in self.form:
             is_spam = False
 
         (status, errmsg) = train_spambayes(self.db, content, tokens,
--- a/test/db_test_base.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/test/db_test_base.py	Tue Jul 24 22:08:17 2018 +0000
@@ -150,7 +150,7 @@
         self.db = self.module.Database(config, user)
 
 
-if os.environ.has_key('LOGGING_LEVEL'):
+if 'LOGGING_LEVEL' in os.environ:
     logger = logging.getLogger('roundup.hyperdb')
     logger.setLevel(os.environ['LOGGING_LEVEL'])
 
@@ -1443,7 +1443,7 @@
         i1 = self.db.issue.create(files=[f1, f2])
         self.db.commit()
         d = self.db.indexer.search(['hello'], self.db.issue)
-        self.assert_(d.has_key(i1))
+        self.assert_(i1 in d)
         d[i1]['files'].sort()
         self.assertEquals(d, {i1: {'files': [f1, f2]}})
         self.assertEquals(self.db.indexer.search(['world'], self.db.issue),
@@ -3316,7 +3316,7 @@
         self.instance.registerAction('special', SpecialAction)
         self.issue = self.db.issue.create (title = "hello", status='1')
         self.db.commit ()
-        if not os.environ.has_key('SENDMAILDEBUG'):
+        if 'SENDMAILDEBUG' not in os.environ:
             os.environ['SENDMAILDEBUG'] = 'mail-test2.log'
         self.SENDMAILDEBUG = os.environ['SENDMAILDEBUG']
         page_template = """
--- a/test/memorydb.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/test/memorydb.py	Tue Jul 24 22:08:17 2018 +0000
@@ -265,13 +265,13 @@
     #
     def __getattr__(self, classname):
         """A convenient way of calling self.getclass(classname)."""
-        if self.classes.has_key(classname):
+        if classname in self.classes:
             return self.classes[classname]
         raise AttributeError(classname)
 
     def addclass(self, cl):
         cn = cl.classname
-        if self.classes.has_key(cn):
+        if cn in self.classes:
             raise ValueError(cn)
         self.classes[cn] = cl
         if cn not in self.items:
@@ -392,9 +392,9 @@
 
 class FileClass(back_anydbm.FileClass):
     def __init__(self, db, classname, **properties):
-        if not properties.has_key('content'):
+        if 'content' not in properties:
             properties['content'] = hyperdb.String(indexme='yes')
-        if not properties.has_key('type'):
+        if 'type' not in properties:
             properties['type'] = hyperdb.String()
         back_anydbm.Class.__init__(self, db, classname, **properties)
 
@@ -412,7 +412,7 @@
         f.close()
         mime_type = None
         props = self.getprops()
-        if props.has_key('type'):
+        if 'type' in props:
             mime_type = self.get(nodeid, 'type')
         if not mime_type:
             mime_type = self.default_mime_type
@@ -429,17 +429,17 @@
         dictionary attempts to specify any of these properties or a
         "creation" or "activity" property, a ValueError is raised.
         """
-        if not properties.has_key('title'):
+        if 'title' not in properties:
             properties['title'] = hyperdb.String(indexme='yes')
-        if not properties.has_key('messages'):
+        if 'messages' not in properties:
             properties['messages'] = hyperdb.Multilink("msg")
-        if not properties.has_key('files'):
+        if 'files' not in properties:
             properties['files'] = hyperdb.Multilink("file")
-        if not properties.has_key('nosy'):
+        if 'nosy' not in properties:
             # note: journalling is turned off as it really just wastes
             # space. this behaviour may be overridden in an instance
             properties['nosy'] = hyperdb.Multilink("user", do_journal="no")
-        if not properties.has_key('superseder'):
+        if 'superseder' not in properties:
             properties['superseder'] = hyperdb.Multilink(classname)
         Class.__init__(self, db, classname, **properties)
 
--- a/test/test_cgi.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/test/test_cgi.py	Tue Jul 24 22:08:17 2018 +0000
@@ -808,7 +808,7 @@
         # issue creation. Also delete the file afterwards
         # just tomake sure that someother test looking for
         # SENDMAILDEBUG won't trip over ours.
-        if not os.environ.has_key('SENDMAILDEBUG'):
+        if 'SENDMAILDEBUG' not in os.environ:
             os.environ['SENDMAILDEBUG'] = 'mail-test1.log'
         SENDMAILDEBUG = os.environ['SENDMAILDEBUG']
 
--- a/test/test_mailgw.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/test/test_mailgw.py	Tue Jul 24 22:08:17 2018 +0000
@@ -30,7 +30,7 @@
 
 from cStringIO import StringIO
 
-if not os.environ.has_key('SENDMAILDEBUG'):
+if 'SENDMAILDEBUG' not in os.environ:
     os.environ['SENDMAILDEBUG'] = 'mail-test.log'
 SENDMAILDEBUG = os.environ['SENDMAILDEBUG']
 
--- a/tools/pygettext.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/tools/pygettext.py	Tue Jul 24 22:08:17 2018 +0000
@@ -266,7 +266,7 @@
 def _visit_pyfiles(list, dirname, names):
     """Helper for getFilesForName()."""
     # get extension for python source files
-    if not globals().has_key('_py_ext'):
+    if '_py_ext' not in globals():
         global _py_ext
         _py_ext = [triple[0] for triple in imp.get_suffixes()
                    if triple[2] == imp.PY_SOURCE][0]
--- a/website/issues/detectors/messagesummary.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/website/issues/detectors/messagesummary.py	Tue Jul 24 22:08:17 2018 +0000
@@ -3,7 +3,7 @@
 def summarygenerator(db, cl, nodeid, newvalues):
     ''' If the message doesn't have a summary, make one for it.
     '''
-    if newvalues.has_key('summary') or not newvalues.has_key('content'):
+    if 'summary' in newvalues or 'content' not in newvalues:
         return
 
     summary, content = parseContent(newvalues['content'], config=db.config)
--- a/website/issues/detectors/no_texthtml.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/website/issues/detectors/no_texthtml.py	Tue Jul 24 22:08:17 2018 +0000
@@ -1,6 +1,6 @@
 
 def audit_html_files(db, cl, nodeid, newvalues):
-    if newvalues.has_key('type') and newvalues['type'] == 'text/html':
+    if 'type' in newvalues and newvalues['type'] == 'text/html':
         newvalues['type'] = 'text/plain'
     
 
--- a/website/issues/detectors/nosyreaction.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/website/issues/detectors/nosyreaction.py	Tue Jul 24 22:08:17 2018 +0000
@@ -30,7 +30,7 @@
     if oldvalues is None:
         # the action was a create, so use all the messages in the create
         messages = cl.get(nodeid, 'messages')
-    elif oldvalues.has_key('messages'):
+    elif 'messages' in oldvalues:
         # the action was a set (so adding new messages to an existing issue)
         m = {}
         for msgid in oldvalues['messages']:
@@ -38,7 +38,7 @@
         messages = []
         # figure which of the messages now on the issue weren't there before
         for msgid in cl.get(nodeid, 'messages'):
-            if not m.has_key(msgid):
+            if msgid not in m:
                 messages.append(msgid)
     return messages
 
@@ -53,13 +53,13 @@
         ok = ('yes',)
         # old node, get the current values from the node if they haven't
         # changed
-        if not newvalues.has_key('nosy'):
+        if 'nosy' not in newvalues:
             nosy = cl.get(nodeid, 'nosy')
             for value in nosy:
                 current_nosy.add(value)
 
     # if the nosy list changed in this transaction, init from the new value
-    if newvalues.has_key('nosy'):
+    if 'nosy' in newvalues:
         nosy = newvalues.get('nosy', [])
         for value in nosy:
             if not db.hasnode('user', value):
@@ -69,7 +69,7 @@
     new_nosy = set(current_nosy)
 
     # add assignedto(s) to the nosy list
-    if newvalues.has_key('assignedto') and newvalues['assignedto'] is not None:
+    if 'assignedto' in newvalues and newvalues['assignedto'] is not None:
         propdef = cl.getprops()
         if isinstance(propdef['assignedto'], hyperdb.Link):
             assignedto_ids = [newvalues['assignedto']]
@@ -80,7 +80,7 @@
 
     # see if there's any new messages - if so, possibly add the author and
     # recipient to the nosy
-    if newvalues.has_key('messages'):
+    if 'messages' in newvalues:
         if nodeid is None:
             ok = ('new', 'yes')
             messages = newvalues['messages']
--- a/website/issues/detectors/patches.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/website/issues/detectors/patches.py	Tue Jul 24 22:08:17 2018 +0000
@@ -35,7 +35,7 @@
             if patchid in oldkeywords:
                 # This is already marked as a patch
                 return
-        if not newvalues.has_key('keywords'):
+        if 'keywords' not in newvalues:
             newvalues['keywords'] = oldkeywords
         newvalues['keywords'].append(patchid)
 
--- a/website/issues/detectors/severityauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/website/issues/detectors/severityauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -1,7 +1,7 @@
 
 def init_severity(db, cl, nodeid, newvalues):
     """Make sure severity is set on new issues"""
-    if newvalues.has_key('severity') and newvalues['severity']:
+    if 'severity' in newvalues and newvalues['severity']:
         return
 
     normal = db.severity.lookup('normal')
--- a/website/issues/detectors/statusauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/website/issues/detectors/statusauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -1,7 +1,7 @@
 def preset_new(db, cl, nodeid, newvalues):
     """ Make sure the status is set on new issues"""
 
-    if newvalues.has_key('status') and newvalues['status']:
+    if 'status' in newvalues and newvalues['status']:
         return
 
     new = db.status.lookup('new')
--- a/website/issues/detectors/userauditor.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/website/issues/detectors/userauditor.py	Tue Jul 24 22:08:17 2018 +0000
@@ -41,7 +41,7 @@
     ''' iterate over all known addresses in a newvalues dict
         this takes of the address/alterate_addresses handling
     '''
-    if user.has_key('address'):
+    if 'address' in user:
         yield user['address']
     if user.get('alternate_addresses', None):
         for address in user['alternate_addresses'].split('\n'):
@@ -69,7 +69,7 @@
     newroles = newvalues.get('roles')
     if newroles:
         for rolename in [r.lower().strip() for r in newroles.split(',')]:
-            if rolename and not db.security.role.has_key(rolename):
+            if rolename and rolename not in db.security.role:
                 raise ValueError('Role "%s" does not exist'%rolename)
 
     tz = newvalues.get('timezone', None)
--- a/website/issues/extensions/spambayes.py	Tue Jul 24 21:43:32 2018 +0000
+++ b/website/issues/extensions/spambayes.py	Tue Jul 24 22:08:17 2018 +0000
@@ -41,9 +41,9 @@
         (content, tokens) = extract_classinfo(self.db,
                                               self.classname, self.nodeid)
 
-        if self.form.has_key("trainspam"):
+        if "trainspam" in self.form:
             is_spam = True
-        elif self.form.has_key("trainham"):
+        elif "trainham" in self.form:
             is_spam = False
 
         (status, errmsg) = train_spambayes(self.db, content, tokens,

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