]> git.ipfire.org Git - ipfire-2.x.git/commitdiff
Revert "client175: Convert python2 modules in source tarball to python3"
authorArne Fitzenreiter <arne_f@ipfire.org>
Sun, 12 Sep 2021 14:51:10 +0000 (14:51 +0000)
committerArne Fitzenreiter <arne_f@ipfire.org>
Sun, 12 Sep 2021 14:51:10 +0000 (14:51 +0000)
This reverts commit 6d11cd4375f46f5f811ec5ce2c2a517b6add120d.

At my tests the server will not start because missing deps of cherrypy and not all files are properly converted yet.

lfs/client175
src/initscripts/packages/client175
src/patches/client175_0.7-convert-to-python3-using-2to3-converter.patch [deleted file]

index f2d23dda93fdc2656f230228b843a05e6e4caf23..e69e8e70543fd4309e4e3d0a19688b4425227194 100644 (file)
@@ -78,7 +78,6 @@ $(subst %,%_MD5,$(objects)) :
 $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
        @$(PREBUILD)
        @rm -rf $(DIR_APP) && cd $(DIR_SRC) && tar Jxf $(DIR_DL)/$(DL_FILE)
-       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/client175_0.7-convert-to-python3-using-2to3-converter.patch        
        @rm -rf /srv/client175
        mkdir -pv /srv/client175
 
index 1dde39800f89ed0309e60e8a216cc3eb8cdbeb8e..9f279b7fbb59f89d332a93570c8840fe1aef5498 100644 (file)
@@ -26,7 +26,7 @@ case "$1" in
                        fi
                fi
                boot_mesg "Starting Client175 MPD WebIF..."
-               sudo -u nobody python3 /srv/client175/server.py > /var/log/client175 2>&1 &
+               sudo -u nobody python /srv/client175/server.py > /var/log/client175 2>&1 &
                echo $! > /var/run/client175.pid
                evaluate_retval
                ;;
diff --git a/src/patches/client175_0.7-convert-to-python3-using-2to3-converter.patch b/src/patches/client175_0.7-convert-to-python3-using-2to3-converter.patch
deleted file mode 100644 (file)
index 61cd8d9..0000000
+++ /dev/null
@@ -1,4923 +0,0 @@
-diff -Naur client175_0.7-original/BeautifulSoup.py client175_0.7/BeautifulSoup.py
---- client175_0.7-original/BeautifulSoup.py    2010-05-14 12:57:39.000000000 +0200
-+++ client175_0.7/BeautifulSoup.py     2021-08-03 14:39:30.213509172 +0200
-@@ -76,7 +76,7 @@
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE, DAMMIT.
- """
--from __future__ import generators
-+
- __author__ = "Leonard Richardson (leonardr@segfault.org)"
- __version__ = "3.0.8.1"
-@@ -85,12 +85,12 @@
- from sgmllib import SGMLParser, SGMLParseError
- import codecs
--import markupbase
-+import _markupbase
- import types
- import re
- import sgmllib
- try:
--  from htmlentitydefs import name2codepoint
-+  from html.entities import name2codepoint
- except ImportError:
-   name2codepoint = {}
- try:
-@@ -100,7 +100,7 @@
- #These hacks make Beautiful Soup able to parse XML with namespaces
- sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*')
--markupbase._declname_match = re.compile(r'[a-zA-Z][-_.:a-zA-Z0-9]*\s*').match
-+_markupbase._declname_match = re.compile(r'[a-zA-Z][-_.:a-zA-Z0-9]*\s*').match
- DEFAULT_OUTPUT_ENCODING = "utf-8"
-@@ -162,7 +162,7 @@
-         #this element (and any children) hadn't been parsed. Connect
-         #the two.
-         lastChild = self._lastRecursiveChild()
--        nextElement = lastChild.next
-+        nextElement = lastChild.__next__
-         if self.previous:
-             self.previous.next = nextElement
-@@ -187,7 +187,7 @@
-         return lastChild
-     def insert(self, position, newChild):
--        if isinstance(newChild, basestring) \
-+        if isinstance(newChild, str) \
-             and not isinstance(newChild, NavigableString):
-             newChild = NavigableString(newChild)
-@@ -241,7 +241,7 @@
-                 newChild.nextSibling.previousSibling = newChild
-             newChildsLastElement.next = nextChild
--        if newChildsLastElement.next:
-+        if newChildsLastElement.__next__:
-             newChildsLastElement.next.previous = newChildsLastElement
-         self.contents.insert(position, newChild)
-@@ -342,7 +342,7 @@
-                 return [element for element in generator()
-                         if isinstance(element, Tag)]
-             # findAll*('tag-name')
--            elif isinstance(name, basestring):
-+            elif isinstance(name, str):
-                 return [element for element in generator()
-                         if isinstance(element, Tag) and
-                         element.name == name]
-@@ -355,7 +355,7 @@
-         g = generator()
-         while True:
-             try:
--                i = g.next()
-+                i = next(g)
-             except StopIteration:
-                 break
-             if i:
-@@ -371,7 +371,7 @@
-     def nextGenerator(self):
-         i = self
-         while i is not None:
--            i = i.next
-+            i = i.__next__
-             yield i
-     def nextSiblingGenerator(self):
-@@ -406,22 +406,22 @@
-     def toEncoding(self, s, encoding=None):
-         """Encodes an object to a string in some encoding, or to Unicode.
-         ."""
--        if isinstance(s, unicode):
-+        if isinstance(s, str):
-             if encoding:
-                 s = s.encode(encoding)
-         elif isinstance(s, str):
-             if encoding:
-                 s = s.encode(encoding)
-             else:
--                s = unicode(s)
-+                s = str(s)
-         else:
-             if encoding:
-                 s  = self.toEncoding(str(s), encoding)
-             else:
--                s = unicode(s)
-+                s = str(s)
-         return s
--class NavigableString(unicode, PageElement):
-+class NavigableString(str, PageElement):
-     def __new__(cls, value):
-         """Create a new NavigableString.
-@@ -431,9 +431,9 @@
-         passed in to the superclass's __new__ or the superclass won't know
-         how to handle non-ASCII characters.
-         """
--        if isinstance(value, unicode):
--            return unicode.__new__(cls, value)
--        return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)
-+        if isinstance(value, str):
-+            return str.__new__(cls, value)
-+        return str.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)
-     def __getnewargs__(self):
-         return (NavigableString.__str__(self),)
-@@ -445,7 +445,7 @@
-         if attr == 'string':
-             return self
-         else:
--            raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr)
-+            raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, attr))
-     def __unicode__(self):
-         return str(self).decode(DEFAULT_OUTPUT_ENCODING)
-@@ -483,7 +483,7 @@
-     def _invert(h):
-         "Cheap function to invert a hash."
-         i = {}
--        for k,v in h.items():
-+        for k,v in list(h.items()):
-             i[v] = k
-         return i
-@@ -502,23 +502,23 @@
-         escaped."""
-         x = match.group(1)
-         if self.convertHTMLEntities and x in name2codepoint:
--            return unichr(name2codepoint[x])
-+            return chr(name2codepoint[x])
-         elif x in self.XML_ENTITIES_TO_SPECIAL_CHARS:
-             if self.convertXMLEntities:
-                 return self.XML_ENTITIES_TO_SPECIAL_CHARS[x]
-             else:
--                return u'&%s;' % x
-+                return '&%s;' % x
-         elif len(x) > 0 and x[0] == '#':
-             # Handle numeric entities
-             if len(x) > 1 and x[1] == 'x':
--                return unichr(int(x[2:], 16))
-+                return chr(int(x[2:], 16))
-             else:
--                return unichr(int(x[1:]))
-+                return chr(int(x[1:]))
-         elif self.escapeUnrecognizedEntities:
--            return u'&amp;%s;' % x
-+            return '&amp;%s;' % x
-         else:
--            return u'&%s;' % x
-+            return '&%s;' % x
-     def __init__(self, parser, name, attrs=None, parent=None,
-                  previous=None):
-@@ -541,11 +541,11 @@
-         self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities
-         # Convert any HTML, XML, or numeric entities in the attribute values.
--        convert = lambda(k, val): (k,
-+        convert = lambda k_val: (k_val[0],
-                                    re.sub("&(#\d+|#x[0-9a-fA-F]+|\w+);",
-                                           self._convertEntities,
--                                          val))
--        self.attrs = map(convert, self.attrs)
-+                                          k_val[1]))
-+        self.attrs = list(map(convert, self.attrs))
-     def getString(self):
-         if (len(self.contents) == 1
-@@ -559,16 +559,16 @@
-     string = property(getString, setString)
--    def getText(self, separator=u""):
-+    def getText(self, separator=""):
-         if not len(self.contents):
--            return u""
--        stopNode = self._lastRecursiveChild().next
-+            return ""
-+        stopNode = self._lastRecursiveChild().__next__
-         strings = []
-         current = self.contents[0]
-         while current is not stopNode:
-             if isinstance(current, NavigableString):
-                 strings.append(current.strip())
--            current = current.next
-+            current = current.__next__
-         return separator.join(strings)
-     text = property(getText)
-@@ -591,7 +591,7 @@
-         raise ValueError("Tag.index: element not in tag")
-     def has_key(self, key):
--        return self._getAttrMap().has_key(key)
-+        return key in self._getAttrMap()
-     def __getitem__(self, key):
-         """tag[key] returns the value of the 'key' attribute for the tag,
-@@ -609,7 +609,7 @@
-     def __contains__(self, x):
-         return x in self.contents
--    def __nonzero__(self):
-+    def __bool__(self):
-         "A tag is non-None even if it has no contents."
-         return True
-@@ -635,14 +635,14 @@
-                 #We don't break because bad HTML can define the same
-                 #attribute multiple times.
-             self._getAttrMap()
--            if self.attrMap.has_key(key):
-+            if key in self.attrMap:
-                 del self.attrMap[key]
-     def __call__(self, *args, **kwargs):
-         """Calling a tag like a function is the same as calling its
-         findAll() method. Eg. tag('a') returns a list of all the A tags
-         found within this tag."""
--        return apply(self.findAll, args, kwargs)
-+        return self.findAll(*args, **kwargs)
-     def __getattr__(self, tag):
-         #print "Getattr %s.%s" % (self.__class__, tag)
-@@ -650,7 +650,7 @@
-             return self.find(tag[:-3])
-         elif tag.find('__') != 0:
-             return self.find(tag)
--        raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__, tag)
-+        raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__, tag))
-     def __eq__(self, other):
-         """Returns true iff this tag has the same name, the same attributes,
-@@ -703,7 +703,7 @@
-         if self.attrs:
-             for key, val in self.attrs:
-                 fmt = '%s="%s"'
--                if isinstance(val, basestring):
-+                if isinstance(val, str):
-                     if self.containsSubstitutions and '%SOUP-ENCODING%' in val:
-                         val = self.substituteEncoding(val, encoding)
-@@ -780,7 +780,7 @@
-             return
-         current = self.contents[0]
-         while current is not None:
--            next = current.next
-+            next = current.__next__
-             if isinstance(current, Tag):
-                 del current.contents[:]
-             current.parent = None
-@@ -873,11 +873,11 @@
-     def recursiveChildGenerator(self):
-         if not len(self.contents):
-             raise StopIteration
--        stopNode = self._lastRecursiveChild().next
-+        stopNode = self._lastRecursiveChild().__next__
-         current = self.contents[0]
-         while current is not stopNode:
-             yield current
--            current = current.next
-+            current = current.__next__
- # Next, a couple classes to represent queries and their results.
-@@ -887,7 +887,7 @@
-     def __init__(self, name=None, attrs={}, text=None, **kwargs):
-         self.name = name
--        if isinstance(attrs, basestring):
-+        if isinstance(attrs, str):
-             kwargs['class'] = _match_css_class(attrs)
-             attrs = None
-         if kwargs:
-@@ -923,7 +923,7 @@
-             else:
-                 match = True
-                 markupAttrMap = None
--                for attr, matchAgainst in self.attrs.items():
-+                for attr, matchAgainst in list(self.attrs.items()):
-                     if not markupAttrMap:
-                          if hasattr(markupAttrs, 'get'):
-                             markupAttrMap = markupAttrs
-@@ -961,12 +961,12 @@
-                 found = self.searchTag(markup)
-         # If it's text, make sure the text matches.
-         elif isinstance(markup, NavigableString) or \
--                 isinstance(markup, basestring):
-+                 isinstance(markup, str):
-             if self._matches(markup, self.text):
-                 found = markup
-         else:
--            raise Exception, "I don't know how to match against a %s" \
--                  % markup.__class__
-+            raise Exception("I don't know how to match against a %s" \
-+                  % markup.__class__)
-         return found
-     def _matches(self, markup, matchAgainst):
-@@ -981,8 +981,8 @@
-             #other ways of matching match the tag name as a string.
-             if isinstance(markup, Tag):
-                 markup = markup.name
--            if markup and not isinstance(markup, basestring):
--                markup = unicode(markup)
-+            if markup and not isinstance(markup, str):
-+                markup = str(markup)
-             #Now we know that chunk is either a string, or None.
-             if hasattr(matchAgainst, 'match'):
-                 # It's a regexp object.
-@@ -990,10 +990,10 @@
-             elif hasattr(matchAgainst, '__iter__'): # list-like
-                 result = markup in matchAgainst
-             elif hasattr(matchAgainst, 'items'):
--                result = markup.has_key(matchAgainst)
--            elif matchAgainst and isinstance(markup, basestring):
--                if isinstance(markup, unicode):
--                    matchAgainst = unicode(matchAgainst)
-+                result = matchAgainst in markup
-+            elif matchAgainst and isinstance(markup, str):
-+                if isinstance(markup, str):
-+                    matchAgainst = str(matchAgainst)
-                 else:
-                     matchAgainst = str(matchAgainst)
-@@ -1018,7 +1018,7 @@
-     for portion in args:
-         if hasattr(portion, 'items'):
-             #It's a map. Merge it.
--            for k,v in portion.items():
-+            for k,v in list(portion.items()):
-                 built[k] = v
-         elif hasattr(portion, '__iter__'): # is a list
-             #It's a list. Map each item to the default.
-@@ -1061,7 +1061,7 @@
-                        lambda x: '<!' + x.group(1) + '>')
-                       ]
--    ROOT_TAG_NAME = u'[document]'
-+    ROOT_TAG_NAME = '[document]'
-     HTML_ENTITIES = "html"
-     XML_ENTITIES = "xml"
-@@ -1157,14 +1157,14 @@
-     def _feed(self, inDocumentEncoding=None, isHTML=False):
-         # Convert the document to Unicode.
-         markup = self.markup
--        if isinstance(markup, unicode):
-+        if isinstance(markup, str):
-             if not hasattr(self, 'originalEncoding'):
-                 self.originalEncoding = None
-         else:
-             dammit = UnicodeDammit\
-                      (markup, [self.fromEncoding, inDocumentEncoding],
-                       smartQuotesTo=self.smartQuotesTo, isHTML=isHTML)
--            markup = dammit.unicode
-+            markup = dammit.str
-             self.originalEncoding = dammit.originalEncoding
-             self.declaredHTMLEncoding = dammit.declaredHTMLEncoding
-         if markup:
-@@ -1203,8 +1203,8 @@
-     def isSelfClosingTag(self, name):
-         """Returns true iff the given string is the name of a
-         self-closing tag according to this parser."""
--        return self.SELF_CLOSING_TAGS.has_key(name) \
--               or self.instanceSelfClosingTags.has_key(name)
-+        return name in self.SELF_CLOSING_TAGS \
-+               or name in self.instanceSelfClosingTags
-     def reset(self):
-         Tag.__init__(self, self, self.ROOT_TAG_NAME)
-@@ -1233,7 +1233,7 @@
-     def endData(self, containerClass=NavigableString):
-         if self.currentData:
--            currentData = u''.join(self.currentData)
-+            currentData = ''.join(self.currentData)
-             if (currentData.translate(self.STRIP_ASCII_SPACES) == '' and
-                 not set([tag.name for tag in self.tagStack]).intersection(
-                     self.PRESERVE_WHITESPACE_TAGS)):
-@@ -1296,7 +1296,7 @@
-         nestingResetTriggers = self.NESTABLE_TAGS.get(name)
-         isNestable = nestingResetTriggers != None
--        isResetNesting = self.RESET_NESTING_TAGS.has_key(name)
-+        isResetNesting = name in self.RESET_NESTING_TAGS
-         popTo = None
-         inclusive = True
-         for i in range(len(self.tagStack)-1, 0, -1):
-@@ -1309,7 +1309,7 @@
-             if (nestingResetTriggers is not None
-                 and p.name in nestingResetTriggers) \
-                 or (nestingResetTriggers is None and isResetNesting
--                    and self.RESET_NESTING_TAGS.has_key(p.name)):
-+                    and p.name in self.RESET_NESTING_TAGS):
-                 #If we encounter one of the nesting reset triggers
-                 #peculiar to this tag, or we encounter another tag
-@@ -1380,7 +1380,7 @@
-         object, possibly one with a %SOUP-ENCODING% slot into which an
-         encoding will be plugged later."""
-         if text[:3] == "xml":
--            text = u"xml version='1.0' encoding='%SOUP-ENCODING%'"
-+            text = "xml version='1.0' encoding='%SOUP-ENCODING%'"
-         self._toStringSubclass(text, ProcessingInstruction)
-     def handle_comment(self, text):
-@@ -1390,7 +1390,7 @@
-     def handle_charref(self, ref):
-         "Handle character references as data."
-         if self.convertEntities:
--            data = unichr(int(ref))
-+            data = chr(int(ref))
-         else:
-             data = '&#%s;' % ref
-         self.handle_data(data)
-@@ -1402,7 +1402,7 @@
-         data = None
-         if self.convertHTMLEntities:
-             try:
--                data = unichr(name2codepoint[ref])
-+                data = chr(name2codepoint[ref])
-             except KeyError:
-                 pass
-@@ -1511,7 +1511,7 @@
-     BeautifulStoneSoup before writing your own subclass."""
-     def __init__(self, *args, **kwargs):
--        if not kwargs.has_key('smartQuotesTo'):
-+        if 'smartQuotesTo' not in kwargs:
-             kwargs['smartQuotesTo'] = self.HTML_ENTITIES
-         kwargs['isHTML'] = True
-         BeautifulStoneSoup.__init__(self, *args, **kwargs)
-@@ -1694,7 +1694,7 @@
-             parent._getAttrMap()
-             if (isinstance(tag, Tag) and len(tag.contents) == 1 and
-                 isinstance(tag.contents[0], NavigableString) and
--                not parent.attrMap.has_key(tag.name)):
-+                tag.name not in parent.attrMap):
-                 parent[tag.name] = tag.contents[0]
-         BeautifulStoneSoup.popTag(self)
-@@ -1768,9 +1768,9 @@
-                      self._detectEncoding(markup, isHTML)
-         self.smartQuotesTo = smartQuotesTo
-         self.triedEncodings = []
--        if markup == '' or isinstance(markup, unicode):
-+        if markup == '' or isinstance(markup, str):
-             self.originalEncoding = None
--            self.unicode = unicode(markup)
-+            self.str = str(markup)
-             return
-         u = None
-@@ -1783,7 +1783,7 @@
-                 if u: break
-         # If no luck and we have auto-detection library, try that:
--        if not u and chardet and not isinstance(self.markup, unicode):
-+        if not u and chardet and not isinstance(self.markup, str):
-             u = self._convertFrom(chardet.detect(self.markup)['encoding'])
-         # As a last resort, try utf-8 and windows-1252:
-@@ -1792,7 +1792,7 @@
-                 u = self._convertFrom(proposed_encoding)
-                 if u: break
--        self.unicode = u
-+        self.str = u
-         if not u: self.originalEncoding = None
-     def _subMSChar(self, orig):
-@@ -1819,7 +1819,7 @@
-                                                       "iso-8859-1",
-                                                       "iso-8859-2"):
-             markup = re.compile("([\x80-\x9f])").sub \
--                     (lambda(x): self._subMSChar(x.group(1)),
-+                     (lambda x: self._subMSChar(x.group(1)),
-                       markup)
-         try:
-@@ -1827,7 +1827,7 @@
-             u = self._toUnicode(markup, proposed)
-             self.markup = u
-             self.originalEncoding = proposed
--        except Exception, e:
-+        except Exception as e:
-             # print "That didn't work!"
-             # print e
-             return None
-@@ -1856,7 +1856,7 @@
-         elif data[:4] == '\xff\xfe\x00\x00':
-             encoding = 'utf-32le'
-             data = data[4:]
--        newdata = unicode(data, encoding)
-+        newdata = str(data, encoding)
-         return newdata
-     def _detectEncoding(self, xml_data, isHTML=False):
-@@ -1869,41 +1869,41 @@
-             elif xml_data[:4] == '\x00\x3c\x00\x3f':
-                 # UTF-16BE
-                 sniffed_xml_encoding = 'utf-16be'
--                xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
-+                xml_data = str(xml_data, 'utf-16be').encode('utf-8')
-             elif (len(xml_data) >= 4) and (xml_data[:2] == '\xfe\xff') \
-                      and (xml_data[2:4] != '\x00\x00'):
-                 # UTF-16BE with BOM
-                 sniffed_xml_encoding = 'utf-16be'
--                xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
-+                xml_data = str(xml_data[2:], 'utf-16be').encode('utf-8')
-             elif xml_data[:4] == '\x3c\x00\x3f\x00':
-                 # UTF-16LE
-                 sniffed_xml_encoding = 'utf-16le'
--                xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
-+                xml_data = str(xml_data, 'utf-16le').encode('utf-8')
-             elif (len(xml_data) >= 4) and (xml_data[:2] == '\xff\xfe') and \
-                      (xml_data[2:4] != '\x00\x00'):
-                 # UTF-16LE with BOM
-                 sniffed_xml_encoding = 'utf-16le'
--                xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
-+                xml_data = str(xml_data[2:], 'utf-16le').encode('utf-8')
-             elif xml_data[:4] == '\x00\x00\x00\x3c':
-                 # UTF-32BE
-                 sniffed_xml_encoding = 'utf-32be'
--                xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
-+                xml_data = str(xml_data, 'utf-32be').encode('utf-8')
-             elif xml_data[:4] == '\x3c\x00\x00\x00':
-                 # UTF-32LE
-                 sniffed_xml_encoding = 'utf-32le'
--                xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
-+                xml_data = str(xml_data, 'utf-32le').encode('utf-8')
-             elif xml_data[:4] == '\x00\x00\xfe\xff':
-                 # UTF-32BE with BOM
-                 sniffed_xml_encoding = 'utf-32be'
--                xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
-+                xml_data = str(xml_data[4:], 'utf-32be').encode('utf-8')
-             elif xml_data[:4] == '\xff\xfe\x00\x00':
-                 # UTF-32LE with BOM
-                 sniffed_xml_encoding = 'utf-32le'
--                xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
-+                xml_data = str(xml_data[4:], 'utf-32le').encode('utf-8')
-             elif xml_data[:3] == '\xef\xbb\xbf':
-                 # UTF-8 with BOM
-                 sniffed_xml_encoding = 'utf-8'
--                xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
-+                xml_data = str(xml_data[3:], 'utf-8').encode('utf-8')
-             else:
-                 sniffed_xml_encoding = 'ascii'
-                 pass
-@@ -1966,7 +1966,7 @@
-                     250,251,252,253,254,255)
-             import string
-             c.EBCDIC_TO_ASCII_MAP = string.maketrans( \
--            ''.join(map(chr, range(256))), ''.join(map(chr, emap)))
-+            ''.join(map(chr, list(range(256)))), ''.join(map(chr, emap)))
-         return s.translate(c.EBCDIC_TO_ASCII_MAP)
-     MS_CHARS = { '\x80' : ('euro', '20AC'),
-@@ -2009,4 +2009,4 @@
- if __name__ == '__main__':
-     import sys
-     soup = BeautifulSoup(sys.stdin)
--    print soup.prettify()
-+    print(soup.prettify())
-diff -Naur client175_0.7-original/cherrypy/cherryd client175_0.7/cherrypy/cherryd
---- client175_0.7-original/cherrypy/cherryd    2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/cherryd     2021-08-03 15:37:40.098963967 +0200
-@@ -12,7 +12,7 @@
-     """Subscribe all engine plugins and start the engine."""
-     sys.path = [''] + sys.path
-     for i in imports or []:
--        exec "import %s" % i
-+        exec("import %s" % i)
-     
-     for c in configfiles or []:
-         cherrypy.config.update(c)
-diff -Naur client175_0.7-original/cherrypy/_cpcgifs.py client175_0.7/cherrypy/_cpcgifs.py
---- client175_0.7-original/cherrypy/_cpcgifs.py        2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cpcgifs.py 2021-08-03 14:41:19.199896214 +0200
-@@ -6,7 +6,7 @@
-     def __init__(self, *args, **kwds):
-         try:
-             cgi.FieldStorage.__init__(self, *args, **kwds)
--        except ValueError, ex:
-+        except ValueError as ex:
-             if str(ex) == 'Maximum content length exceeded':
-                 raise cherrypy.HTTPError(status=413)
-             else:
-diff -Naur client175_0.7-original/cherrypy/_cpchecker.py client175_0.7/cherrypy/_cpchecker.py
---- client175_0.7-original/cherrypy/_cpchecker.py      2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cpchecker.py       2021-08-03 14:41:30.971721551 +0200
-@@ -47,7 +47,7 @@
-     global_config_contained_paths = False
-     
-     def check_skipped_app_config(self):
--        for sn, app in cherrypy.tree.apps.iteritems():
-+        for sn, app in cherrypy.tree.apps.items():
-             if not isinstance(app, cherrypy.Application):
-                 continue
-             if not app.config:
-@@ -64,7 +64,7 @@
-     def check_static_paths(self):
-         # Use the dummy Request object in the main thread.
-         request = cherrypy.request
--        for sn, app in cherrypy.tree.apps.iteritems():
-+        for sn, app in cherrypy.tree.apps.items():
-             if not isinstance(app, cherrypy.Application):
-                 continue
-             request.app = app
-@@ -130,9 +130,9 @@
-     
-     def _compat(self, config):
-         """Process config and warn on each obsolete or deprecated entry."""
--        for section, conf in config.iteritems():
-+        for section, conf in config.items():
-             if isinstance(conf, dict):
--                for k, v in conf.iteritems():
-+                for k, v in conf.items():
-                     if k in self.obsolete:
-                         warnings.warn("%r is obsolete. Use %r instead.\n"
-                                       "section: [%s]" %
-@@ -152,7 +152,7 @@
-     def check_compatibility(self):
-         """Process config and warn on each obsolete or deprecated entry."""
-         self._compat(cherrypy.config)
--        for sn, app in cherrypy.tree.apps.iteritems():
-+        for sn, app in cherrypy.tree.apps.items():
-             if not isinstance(app, cherrypy.Application):
-                 continue
-             self._compat(app.config)
-@@ -164,16 +164,16 @@
-     
-     def _known_ns(self, app):
-         ns = ["wsgi"]
--        ns.extend(app.toolboxes.keys())
--        ns.extend(app.namespaces.keys())
--        ns.extend(app.request_class.namespaces.keys())
--        ns.extend(cherrypy.config.namespaces.keys())
-+        ns.extend(list(app.toolboxes.keys()))
-+        ns.extend(list(app.namespaces.keys()))
-+        ns.extend(list(app.request_class.namespaces.keys()))
-+        ns.extend(list(cherrypy.config.namespaces.keys()))
-         ns += self.extra_config_namespaces
-         
--        for section, conf in app.config.iteritems():
-+        for section, conf in app.config.items():
-             is_path_section = section.startswith("/")
-             if is_path_section and isinstance(conf, dict):
--                for k, v in conf.iteritems():
-+                for k, v in conf.items():
-                     atoms = k.split(".")
-                     if len(atoms) > 1:
-                         if atoms[0] not in ns:
-@@ -197,7 +197,7 @@
-     
-     def check_config_namespaces(self):
-         """Process config and warn on each unknown config namespace."""
--        for sn, app in cherrypy.tree.apps.iteritems():
-+        for sn, app in cherrypy.tree.apps.items():
-             if not isinstance(app, cherrypy.Application):
-                 continue
-             self._known_ns(app)
-@@ -210,8 +210,8 @@
-     known_config_types = {}
-     
-     def _populate_known_types(self):
--        import __builtin__
--        builtins = [x for x in vars(__builtin__).values()
-+        import builtins
-+        builtins = [x for x in list(vars(__builtin__).values())
-                     if type(x) is type(str)]
-         
-         def traverse(obj, namespace):
-@@ -230,9 +230,9 @@
-         msg = ("The config entry %r in section %r is of type %r, "
-                "which does not match the expected type %r.")
-         
--        for section, conf in config.iteritems():
-+        for section, conf in config.items():
-             if isinstance(conf, dict):
--                for k, v in conf.iteritems():
-+                for k, v in conf.items():
-                     if v is not None:
-                         expected_type = self.known_config_types.get(k, None)
-                         vtype = type(v)
-@@ -251,7 +251,7 @@
-     def check_config_types(self):
-         """Assert that config values are of the same type as default values."""
-         self._known_types(cherrypy.config)
--        for sn, app in cherrypy.tree.apps.iteritems():
-+        for sn, app in cherrypy.tree.apps.items():
-             if not isinstance(app, cherrypy.Application):
-                 continue
-             self._known_types(app.config)
-@@ -261,7 +261,7 @@
-     
-     def check_localhost(self):
-         """Warn if any socket_host is 'localhost'. See #711."""
--        for k, v in cherrypy.config.iteritems():
-+        for k, v in cherrypy.config.items():
-             if k == 'server.socket_host' and v == 'localhost':
-                 warnings.warn("The use of 'localhost' as a socket host can "
-                     "cause problems on newer systems, since 'localhost' can "
-diff -Naur client175_0.7-original/cherrypy/_cpconfig.py client175_0.7/cherrypy/_cpconfig.py
---- client175_0.7-original/cherrypy/_cpconfig.py       2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cpconfig.py        2021-08-03 14:41:39.459596077 +0200
-@@ -93,7 +93,7 @@
- style) context manager.
- """
--import ConfigParser
-+import configparser
- try:
-     set
- except NameError:
-@@ -138,7 +138,7 @@
- def as_dict(config):
-     """Return a dict from 'config' whether it is a dict, file, or filename."""
--    if isinstance(config, basestring):
-+    if isinstance(config, str):
-         config = _Parser().dict_from_file(config)
-     elif hasattr(config, 'read'):
-         config = _Parser().dict_from_file(config)
-@@ -150,11 +150,11 @@
-     If the given config is a filename, it will be appended to
-     the list of files to monitor for "autoreload" changes.
-     """
--    if isinstance(other, basestring):
-+    if isinstance(other, str):
-         cherrypy.engine.autoreload.files.add(other)
-     
-     # Load other into base
--    for section, value_map in as_dict(other).iteritems():
-+    for section, value_map in as_dict(other).items():
-         base.setdefault(section, {}).update(value_map)
-@@ -196,14 +196,14 @@
-         #     with handler as callable:
-         #         for k, v in ns_confs.get(ns, {}).iteritems():
-         #             callable(k, v)
--        for ns, handler in self.iteritems():
-+        for ns, handler in self.items():
-             exit = getattr(handler, "__exit__", None)
-             if exit:
-                 callable = handler.__enter__()
-                 no_exc = True
-                 try:
-                     try:
--                        for k, v in ns_confs.get(ns, {}).iteritems():
-+                        for k, v in ns_confs.get(ns, {}).items():
-                             callable(k, v)
-                     except:
-                         # The exceptional case is handled here
-@@ -218,7 +218,7 @@
-                     if no_exc and exit:
-                         exit(None, None, None)
-             else:
--                for k, v in ns_confs.get(ns, {}).iteritems():
-+                for k, v in ns_confs.get(ns, {}).items():
-                     handler(k, v)
-     
-     def __repr__(self):
-@@ -257,7 +257,7 @@
-     
-     def update(self, config):
-         """Update self from a dict, file or filename."""
--        if isinstance(config, basestring):
-+        if isinstance(config, str):
-             # Filename
-             cherrypy.engine.autoreload.files.add(config)
-             config = _Parser().dict_from_file(config)
-@@ -333,7 +333,7 @@
- Config.namespaces["tree"] = _tree_namespace_handler
--class _Parser(ConfigParser.ConfigParser):
-+class _Parser(configparser.ConfigParser):
-     """Sub-class of ConfigParser that keeps the case of options and that raises
-     an exception if the file cannot be read.
-     """
-@@ -342,7 +342,7 @@
-         return optionstr
-     
-     def read(self, filenames):
--        if isinstance(filenames, basestring):
-+        if isinstance(filenames, str):
-             filenames = [filenames]
-         for filename in filenames:
-             # try:
-@@ -367,7 +367,7 @@
-                 value = self.get(section, option, raw, vars)
-                 try:
-                     value = unrepr(value)
--                except Exception, x:
-+                except Exception as x:
-                     msg = ("Config error in section: %r, option: %r, "
-                            "value: %r. Config values must be valid Python." %
-                            (section, option, value))
-diff -Naur client175_0.7-original/cherrypy/_cpdispatch.py client175_0.7/cherrypy/_cpdispatch.py
---- client175_0.7-original/cherrypy/_cpdispatch.py     2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cpdispatch.py      2021-08-03 14:41:48.627460479 +0200
-@@ -23,7 +23,7 @@
-     def __call__(self):
-         try:
-             return self.callable(*self.args, **self.kwargs)
--        except TypeError, x:
-+        except TypeError as x:
-             test_callable_spec(self.callable, self.args, self.kwargs)
-             raise
-@@ -62,7 +62,7 @@
-         except IndexError:
-             vararg_usage += 1
--    for key in callable_kwargs.keys():
-+    for key in list(callable_kwargs.keys()):
-         try:
-             arg_usage[key] += 1
-         except KeyError:
-@@ -76,7 +76,7 @@
-     missing_args = []
-     multiple_args = []
--    for key, usage in arg_usage.iteritems():
-+    for key, usage in arg_usage.items():
-         if usage == 0:
-             missing_args.append(key)
-         elif usage > 1:
-@@ -258,7 +258,7 @@
-         
-         # Try successive objects (reverse order)
-         num_candidates = len(object_trail) - 1
--        for i in xrange(num_candidates, -1, -1):
-+        for i in range(num_candidates, -1, -1):
-             
-             name, candidate, nodeconf, curpath = object_trail[i]
-             if candidate is None:
-diff -Naur client175_0.7-original/cherrypy/_cperror.py client175_0.7/cherrypy/_cperror.py
---- client175_0.7-original/cherrypy/_cperror.py        2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cperror.py 2021-08-03 14:41:56.939337474 +0200
-@@ -3,7 +3,7 @@
- from cgi import escape as _escape
- from sys import exc_info as _exc_info
- from traceback import format_exception as _format_exception
--from urlparse import urljoin as _urljoin
-+from urllib.parse import urljoin as _urljoin
- from cherrypy.lib import http as _http
-@@ -57,7 +57,7 @@
-         import cherrypy
-         request = cherrypy.request
-         
--        if isinstance(urls, basestring):
-+        if isinstance(urls, str):
-             urls = [urls]
-         
-         abs_urls = []
-@@ -161,7 +161,7 @@
-     for key in ["Accept-Ranges", "Age", "ETag", "Location", "Retry-After",
-                 "Vary", "Content-Encoding", "Content-Length", "Expires",
-                 "Content-Location", "Content-MD5", "Last-Modified"]:
--        if respheaders.has_key(key):
-+        if key in respheaders:
-             del respheaders[key]
-     
-     if status != 416:
-@@ -171,7 +171,7 @@
-         # specifies the current length of the selected resource.
-         # A response with status code 206 (Partial Content) MUST NOT
-         # include a Content-Range field with a byte-range- resp-spec of "*".
--        if respheaders.has_key("Content-Range"):
-+        if "Content-Range" in respheaders:
-             del respheaders["Content-Range"]
-@@ -277,7 +277,7 @@
-     
-     try:
-         code, reason, message = _http.valid_status(status)
--    except ValueError, x:
-+    except ValueError as x:
-         raise cherrypy.HTTPError(500, x.args[0])
-     
-     # We can't use setdefault here, because some
-@@ -291,7 +291,7 @@
-     if kwargs.get('version') is None:
-         kwargs['version'] = cherrypy.__version__
-     
--    for k, v in kwargs.iteritems():
-+    for k, v in kwargs.items():
-         if v is None:
-             kwargs[k] = ""
-         else:
-diff -Naur client175_0.7-original/cherrypy/_cplogging.py client175_0.7/cherrypy/_cplogging.py
---- client175_0.7-original/cherrypy/_cplogging.py      2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cplogging.py       2021-08-03 14:42:04.739222052 +0200
-@@ -88,8 +88,8 @@
-                  'f': inheaders.get('Referer', ''),
-                  'a': inheaders.get('User-Agent', ''),
-                  }
--        for k, v in atoms.items():
--            if isinstance(v, unicode):
-+        for k, v in list(atoms.items()):
-+            if isinstance(v, str):
-                 v = v.encode('utf8')
-             elif not isinstance(v, str):
-                 v = str(v)
-diff -Naur client175_0.7-original/cherrypy/_cpmodpy.py client175_0.7/cherrypy/_cpmodpy.py
---- client175_0.7-original/cherrypy/_cpmodpy.py        2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cpmodpy.py 2021-08-03 14:42:11.807117510 +0200
-@@ -56,7 +56,7 @@
- """
- import logging
--import StringIO
-+import io
- import cherrypy
- from cherrypy._cperror import format_exc, bare_error
-@@ -183,7 +183,7 @@
-             path = req.uri
-             qs = req.args or ""
-             reqproto = req.protocol
--            headers = req.headers_in.items()
-+            headers = list(req.headers_in.items())
-             rfile = _ReadOnlyRequest(req)
-             prev = None
-             
-@@ -202,7 +202,7 @@
-                     try:
-                         request.run(method, path, qs, reqproto, headers, rfile)
-                         break
--                    except cherrypy.InternalRedirect, ir:
-+                    except cherrypy.InternalRedirect as ir:
-                         app.release_serving()
-                         prev = request
-                         
-@@ -220,7 +220,7 @@
-                         method = "GET"
-                         path = ir.path
-                         qs = ir.query_string
--                        rfile = StringIO.StringIO()
-+                        rfile = io.StringIO()
-                 
-                 send_response(req, response.status, response.header_list,
-                               response.body, response.stream)
-@@ -251,7 +251,7 @@
-         req.flush()
-     
-     # Set response body
--    if isinstance(body, basestring):
-+    if isinstance(body, str):
-         req.write(body)
-     else:
-         for seg in body:
-diff -Naur client175_0.7-original/cherrypy/_cprequest.py client175_0.7/cherrypy/_cprequest.py
---- client175_0.7-original/cherrypy/_cprequest.py      2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cprequest.py       2021-08-03 14:42:19.091009678 +0200
-@@ -1,5 +1,5 @@
--import Cookie
-+import http.cookies
- import os
- import sys
- import time
-@@ -11,11 +11,9 @@
- from cherrypy.lib import http, file_generator
--class Hook(object):
-+class Hook(object, metaclass=cherrypy._AttributeDocstrings):
-     """A callback and its metadata: failsafe, priority, and kwargs."""
-     
--    __metaclass__ = cherrypy._AttributeDocstrings
--    
-     callback = None
-     callback__doc = """
-     The bare callable that this Hook object is wrapping, which will
-@@ -63,7 +61,7 @@
-                 % (cls.__module__, cls.__name__, self.callback,
-                    self.failsafe, self.priority,
-                    ", ".join(['%s=%r' % (k, v)
--                              for k, v in self.kwargs.iteritems()])))
-+                              for k, v in self.kwargs.items()])))
- class HookMap(dict):
-@@ -111,14 +109,14 @@
-         newmap = self.__class__()
-         # We can't just use 'update' because we want copies of the
-         # mutable values (each is a list) as well.
--        for k, v in self.iteritems():
-+        for k, v in self.items():
-             newmap[k] = v[:]
-         return newmap
-     copy = __copy__
-     
-     def __repr__(self):
-         cls = self.__class__
--        return "%s.%s(points=%r)" % (cls.__module__, cls.__name__, self.keys())
-+        return "%s.%s(points=%r)" % (cls.__module__, cls.__name__, list(self.keys()))
- # Config namespace handlers
-@@ -129,7 +127,7 @@
-     # hookpoint per path (e.g. "hooks.before_handler.1").
-     # Little-known fact you only get from reading source ;)
-     hookpoint = k.split(".", 1)[0]
--    if isinstance(v, basestring):
-+    if isinstance(v, str):
-         v = cherrypy.lib.attributes(v)
-     if not isinstance(v, Hook):
-         v = Hook(v)
-@@ -156,7 +154,7 @@
-               'before_error_response', 'after_error_response']
--class Request(object):
-+class Request(object, metaclass=cherrypy._AttributeDocstrings):
-     """An HTTP request.
-     
-     This object represents the metadata of an HTTP request message;
-@@ -169,8 +167,6 @@
-     the given URL, and the execution plan for generating a response.
-     """
-     
--    __metaclass__ = cherrypy._AttributeDocstrings
--    
-     prev = None
-     prev__doc = """
-     The previous Request object (if any). This should be None
-@@ -251,7 +247,7 @@
-     values (decoded according to RFC 2047 if necessary). See also:
-     http.HeaderMap, http.HeaderElement."""
-     
--    cookie = Cookie.SimpleCookie()
-+    cookie = http.cookies.SimpleCookie()
-     cookie__doc = """See help(Cookie)."""
-     
-     rfile = None
-@@ -529,7 +525,7 @@
-             self.header_list = list(headers)
-             self.rfile = rfile
-             self.headers = http.HeaderMap()
--            self.cookie = Cookie.SimpleCookie()
-+            self.cookie = http.cookies.SimpleCookie()
-             self.handler = None
-             
-             # path_info should be the path from the
-@@ -608,7 +604,7 @@
-                     self.stage = 'before_finalize'
-                     self.hooks.run('before_finalize')
-                     cherrypy.response.finalize()
--                except (cherrypy.HTTPRedirect, cherrypy.HTTPError), inst:
-+                except (cherrypy.HTTPRedirect, cherrypy.HTTPError) as inst:
-                     inst.set_response()
-                     self.stage = 'before_finalize (HTTPError)'
-                     self.hooks.run('before_finalize')
-@@ -648,7 +644,7 @@
-             if name == 'Cookie':
-                 try:
-                     self.cookie.load(value)
--                except Cookie.CookieError:
-+                except http.cookies.CookieError:
-                     msg = "Illegal cookie name %s" % value.split('=')[0]
-                     raise cherrypy.HTTPError(400, msg)
-         
-@@ -709,7 +705,7 @@
-         # won't parse the request body for params if the client
-         # didn't provide a "Content-Type" header.
-         if 'Content-Type' not in self.headers:
--            h = http.HeaderMap(self.headers.items())
-+            h = http.HeaderMap(list(self.headers.items()))
-             h['Content-Type'] = ''
-         else:
-             h = self.headers
-@@ -720,7 +716,7 @@
-                                           # FieldStorage only recognizes POST.
-                                           environ={'REQUEST_METHOD': "POST"},
-                                           keep_blank_values=1)
--        except Exception, e:
-+        except Exception as e:
-             if e.__class__.__name__ == 'MaxSizeExceeded':
-                 # Post data is too big
-                 raise cherrypy.HTTPError(413)
-@@ -746,7 +742,7 @@
-                 self.error_response()
-             self.hooks.run("after_error_response")
-             cherrypy.response.finalize()
--        except cherrypy.HTTPRedirect, inst:
-+        except cherrypy.HTTPRedirect as inst:
-             inst.set_response()
-             cherrypy.response.finalize()
-@@ -763,7 +759,7 @@
-     
-     def __set__(self, obj, value):
-         # Convert the given value to an iterable object.
--        if isinstance(value, basestring):
-+        if isinstance(value, str):
-             # strings get wrapped in a list because iterating over a single
-             # item list is much faster than iterating over every character
-             # in a long string.
-@@ -779,7 +775,7 @@
-         obj._body = value
--class Response(object):
-+class Response(object, metaclass=cherrypy._AttributeDocstrings):
-     """An HTTP Response, including status, headers, and body.
-     
-     Application developers should use Response.headers (a dict) to
-@@ -788,8 +784,6 @@
-     (key, value) tuples.
-     """
-     
--    __metaclass__ = cherrypy._AttributeDocstrings
--    
-     # Class attributes for dev-time introspection.
-     status = ""
-     status__doc = """The HTTP Status-Code and Reason-Phrase."""
-@@ -808,7 +802,7 @@
-     values (decoded according to RFC 2047 if necessary). See also:
-     http.HeaderMap, http.HeaderElement."""
-     
--    cookie = Cookie.SimpleCookie()
-+    cookie = http.cookies.SimpleCookie()
-     cookie__doc = """See help(Cookie)."""
-     
-     body = Body()
-@@ -842,7 +836,7 @@
-             "Server": "CherryPy/" + cherrypy.__version__,
-             "Date": http.HTTPDate(self.time),
-         })
--        self.cookie = Cookie.SimpleCookie()
-+        self.cookie = http.cookies.SimpleCookie()
-     
-     def collapse_body(self):
-         """Collapse self.body to a single string; replace it and return it."""
-@@ -854,7 +848,7 @@
-         """Transform headers (and cookies) into self.header_list. (Core)"""
-         try:
-             code, reason, _ = http.valid_status(self.status)
--        except ValueError, x:
-+        except ValueError as x:
-             raise cherrypy.HTTPError(500, x.args[0])
-         
-         self.status = "%s %s" % (code, reason)
-diff -Naur client175_0.7-original/cherrypy/_cpserver.py client175_0.7/cherrypy/_cpserver.py
---- client175_0.7-original/cherrypy/_cpserver.py       2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cpserver.py        2021-08-03 14:42:27.582884114 +0200
-@@ -73,7 +73,7 @@
-         if httpserver is None:
-             from cherrypy import _cpwsgi_server
-             httpserver = _cpwsgi_server.CPWSGIServer()
--        if isinstance(httpserver, basestring):
-+        if isinstance(httpserver, str):
-             httpserver = attributes(httpserver)()
-         
-         if self.socket_file:
-diff -Naur client175_0.7-original/cherrypy/_cptools.py client175_0.7/cherrypy/_cptools.py
---- client175_0.7-original/cherrypy/_cptools.py        2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cptools.py 2021-08-03 14:43:38.721831788 +0200
-@@ -30,8 +30,8 @@
-     # Use this instead of importing inspect for less mem overhead.
-     import types
-     if isinstance(func, types.MethodType):
--        func = func.im_func
--    co = func.func_code
-+        func = func.__func__
-+    co = func.__code__
-     return co.co_varnames[:co.co_argcount]
-@@ -105,7 +105,7 @@
-                 f._cp_config = {}
-             subspace = self.namespace + "." + self._name + "."
-             f._cp_config[subspace + "on"] = True
--            for k, v in kwargs.iteritems():
-+            for k, v in kwargs.items():
-                 f._cp_config[subspace + k] = v
-             return f
-         return tool_decorator
-@@ -286,7 +286,7 @@
-         sess.regenerate()
-         
-         # Grab cookie-relevant tool args
--        conf = dict([(k, v) for k, v in self._merged_args().iteritems()
-+        conf = dict([(k, v) for k, v in self._merged_args().items()
-                      if k in ('path', 'path_header', 'name', 'timeout',
-                               'domain', 'secure')])
-         _sessions.set_response_cookie(**conf)
-@@ -346,7 +346,7 @@
-             # if a method is not found, an xmlrpclib.Fault should be returned
-             # raising an exception here will do that; see
-             # cherrypy.lib.xmlrpc.on_error
--            raise Exception, 'method "%s" is not supported' % attr
-+            raise Exception('method "%s" is not supported' % attr)
-         
-         conf = cherrypy.request.toolmaps['tools'].get("xmlrpc", {})
-         _xmlrpc.respond(body,
-@@ -399,7 +399,7 @@
-             cherrypy._cache = kwargs.pop("cache_class", _caching.MemoryCache)()
-             
-             # Take all remaining kwargs and set them on the Cache object.
--            for k, v in kwargs.iteritems():
-+            for k, v in kwargs.items():
-                 setattr(cherrypy._cache, k, v)
-         
-         if _caching.get(invalid_methods=invalid_methods):
-@@ -452,7 +452,7 @@
-         """Run tool._setup() for each tool in our toolmap."""
-         map = cherrypy.request.toolmaps.get(self.namespace)
-         if map:
--            for name, settings in map.items():
-+            for name, settings in list(map.items()):
-                 if settings.get("on", False):
-                     tool = getattr(self, name)
-                     tool._setup()
-diff -Naur client175_0.7-original/cherrypy/_cptree.py client175_0.7/cherrypy/_cptree.py
---- client175_0.7-original/cherrypy/_cptree.py 2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cptree.py  2021-08-03 14:43:50.457658068 +0200
-@@ -6,7 +6,7 @@
- from cherrypy.lib import http as _http
--class Application(object):
-+class Application(object, metaclass=cherrypy._AttributeDocstrings):
-     """A CherryPy Application.
-     
-     Servers and gateways should not instantiate Request objects directly.
-@@ -16,8 +16,6 @@
-     (WSGI application object) for itself.
-     """
-     
--    __metaclass__ = cherrypy._AttributeDocstrings
--    
-     root = None
-     root__doc = """
-     The top-most container of page handlers for this app. Handlers should
-@@ -103,7 +101,7 @@
-         req = self.request_class(local, remote, scheme, sproto)
-         req.app = self
-         
--        for name, toolbox in self.toolboxes.iteritems():
-+        for name, toolbox in self.toolboxes.items():
-             req.namespaces[name] = toolbox
-         
-         resp = self.response_class()
-@@ -171,7 +169,7 @@
-         if isinstance(root, Application):
-             app = root
-             if script_name != "" and script_name != app.script_name:
--                raise ValueError, "Cannot specify a different script name and pass an Application instance to cherrypy.mount"
-+                raise ValueError("Cannot specify a different script name and pass an Application instance to cherrypy.mount")
-             script_name = app.script_name
-         else:
-             app = Application(root, script_name)
-diff -Naur client175_0.7-original/cherrypy/_cpwsgi.py client175_0.7/cherrypy/_cpwsgi.py
---- client175_0.7-original/cherrypy/_cpwsgi.py 2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/_cpwsgi.py  2021-08-03 14:44:08.117396886 +0200
-@@ -1,6 +1,6 @@
- """WSGI interface (see PEP 333)."""
--import StringIO as _StringIO
-+import io as _StringIO
- import sys as _sys
- import cherrypy as _cherrypy
-@@ -82,7 +82,7 @@
-         except self.throws:
-             self.close()
-             raise
--        except _cherrypy.InternalRedirect, ir:
-+        except _cherrypy.InternalRedirect as ir:
-             self.environ['cherrypy.previous_request'] = _cherrypy.serving.request
-             self.close()
-             self.iredirect(ir.path, ir.query_string)
-@@ -158,9 +158,9 @@
-     def __iter__(self):
-         return self
-     
--    def next(self):
-+    def __next__(self):
-         try:
--            chunk = self.iter_response.next()
-+            chunk = next(self.iter_response)
-             # WSGI requires all data to be of type "str". This coercion should
-             # not take any time at all if chunk is already of type "str".
-             # If it's unicode, it could be a big performance hit (x ~500).
-@@ -170,7 +170,7 @@
-         except self.throws:
-             self.close()
-             raise
--        except _cherrypy.InternalRedirect, ir:
-+        except _cherrypy.InternalRedirect as ir:
-             self.environ['cherrypy.previous_request'] = _cherrypy.serving.request
-             self.close()
-             self.iredirect(ir.path, ir.query_string)
-diff -Naur client175_0.7-original/cherrypy/__init__.py client175_0.7/cherrypy/__init__.py
---- client175_0.7-original/cherrypy/__init__.py        2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/__init__.py 2021-08-03 14:44:38.280950778 +0200
-@@ -59,7 +59,7 @@
- __version__ = "3.1.2"
--from urlparse import urljoin as _urljoin
-+from urllib.parse import urljoin as _urljoin
- class _AttributeDocstrings(type):
-@@ -126,7 +126,7 @@
-         
-         newdoc = [cls.__doc__ or ""]
-         
--        dctnames = dct.keys()
-+        dctnames = list(dct.keys())
-         dctnames.sort()
-         
-         for name in dctnames:
-@@ -254,7 +254,7 @@
- except ImportError:
-     from cherrypy._cpthreadinglocal import local as _local
--class _Serving(_local):
-+class _Serving(_local, metaclass=_AttributeDocstrings):
-     """An interface for registering request and response objects.
-     
-     Rather than have a separate "thread local" object for the request and
-@@ -265,8 +265,6 @@
-     thread-safe way.
-     """
-     
--    __metaclass__ = _AttributeDocstrings
--    
-     request = _cprequest.Request(_http.Host("127.0.0.1", 80),
-                                  _http.Host("127.0.0.1", 1111))
-     request__doc = """
-@@ -338,7 +336,7 @@
-         child = getattr(serving, self.__attrname__)
-         return len(child)
-     
--    def __nonzero__(self):
-+    def __bool__(self):
-         child = getattr(serving, self.__attrname__)
-         return bool(child)
-@@ -410,7 +408,7 @@
-     def expose_(func):
-         func.exposed = True
-         if alias is not None:
--            if isinstance(alias, basestring):
-+            if isinstance(alias, str):
-                 parents[alias.replace(".", "_")] = func
-             else:
-                 for a in alias:
-diff -Naur client175_0.7-original/cherrypy/lib/auth.py client175_0.7/cherrypy/lib/auth.py
---- client175_0.7-original/cherrypy/lib/auth.py        2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/auth.py 2021-08-03 14:45:01.296612330 +0200
-@@ -19,7 +19,7 @@
-                 users = users() # expect it to return a dictionary
-                 if not isinstance(users, dict):
--                    raise ValueError, "Authentication users must be a dictionary"
-+                    raise ValueError("Authentication users must be a dictionary")
-                 
-                 # fetch the user password
-                 password = users.get(ah["username"], None)
-@@ -28,7 +28,7 @@
-                 password = users(ah["username"])
-         else:
-             if not isinstance(users, dict):
--                raise ValueError, "Authentication users must be a dictionary"
-+                raise ValueError("Authentication users must be a dictionary")
-             
-             # fetch the user password
-             password = users.get(ah["username"], None)
-diff -Naur client175_0.7-original/cherrypy/lib/caching.py client175_0.7/cherrypy/lib/caching.py
---- client175_0.7-original/cherrypy/lib/caching.py     2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/caching.py      2021-08-03 14:45:08.464504417 +0200
-@@ -45,7 +45,7 @@
-         # See tickets #99 and #180 for more information.
-         while time:
-             now = time.time()
--            for expiration_time, objects in self.expirations.items():
-+            for expiration_time, objects in list(self.expirations.items()):
-                 if expiration_time <= now:
-                     for obj_size, obj_key in objects:
-                         try:
-@@ -161,7 +161,7 @@
-             # this was put into the cached copy, and should have been
-             # resurrected just above (response.headers = cache_data[1]).
-             cptools.validate_since()
--        except cherrypy.HTTPRedirect, x:
-+        except cherrypy.HTTPRedirect as x:
-             if x.status == 304:
-                 cherrypy._cache.tot_non_modified += 1
-             raise
-@@ -188,7 +188,7 @@
-                     cherrypy.response.headers.elements('Vary')]
-             if vary:
-                 sel_headers = dict([(k, v) for k, v
--                                    in cherrypy.request.headers.iteritems()
-+                                    in cherrypy.request.headers.items()
-                                     if k in vary])
-             else:
-                 sel_headers = {}
-diff -Naur client175_0.7-original/cherrypy/lib/covercp.py client175_0.7/cherrypy/lib/covercp.py
---- client175_0.7-original/cherrypy/lib/covercp.py     2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/covercp.py      2021-08-03 14:45:14.876409627 +0200
-@@ -21,14 +21,14 @@
- import re
- import sys
- import cgi
--import urllib
-+import urllib.request, urllib.parse, urllib.error
- import os, os.path
- localFile = os.path.join(os.path.dirname(__file__), "coverage.cache")
- try:
--    import cStringIO as StringIO
-+    import io as StringIO
- except ImportError:
--    import StringIO
-+    import io
- try:
-     from coverage import the_coverage as coverage
-@@ -190,7 +190,7 @@
- def _show_branch(root, base, path, pct=0, showpct=False, exclude=""):
-     
-     # Show the directory name and any of our children
--    dirs = [k for k, v in root.iteritems() if v]
-+    dirs = [k for k, v in root.items() if v]
-     dirs.sort()
-     for name in dirs:
-         newpath = os.path.join(path, name)
-@@ -199,7 +199,7 @@
-             relpath = newpath[len(base):]
-             yield "| " * relpath.count(os.sep)
-             yield "<a class='directory' href='menu?base=%s&exclude=%s'>%s</a>\n" % \
--                   (newpath, urllib.quote_plus(exclude), name)
-+                   (newpath, urllib.parse.quote_plus(exclude), name)
-         
-         for chunk in _show_branch(root[name], base, newpath, pct, showpct, exclude):
-             yield chunk
-@@ -207,7 +207,7 @@
-     # Now list the files
-     if path.lower().startswith(base):
-         relpath = path[len(base):]
--        files = [k for k, v in root.iteritems() if not v]
-+        files = [k for k, v in root.items() if not v]
-         files.sort()
-         for name in files:
-             newpath = os.path.join(path, name)
-@@ -257,7 +257,7 @@
-     """Return covered module names as a nested dict."""
-     tree = {}
-     coverage.get_ready()
--    runs = coverage.cexecuted.keys()
-+    runs = list(coverage.cexecuted.keys())
-     if runs:
-         for path in runs:
-             if not _skip_file(path, exclude) and not os.path.isdir(path):
-@@ -287,7 +287,7 @@
-         for atom in atoms:
-             path += atom + os.sep
-             yield ("<a href='menu?base=%s&exclude=%s'>%s</a> %s"
--                   % (path, urllib.quote_plus(exclude), atom, os.sep))
-+                   % (path, urllib.parse.quote_plus(exclude), atom, os.sep))
-         yield "</div>"
-         
-         yield "<div id='tree'>"
-diff -Naur client175_0.7-original/cherrypy/lib/cptools.py client175_0.7/cherrypy/lib/cptools.py
---- client175_0.7-original/cherrypy/lib/cptools.py     2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/cptools.py      2021-08-03 14:45:22.384298594 +0200
-@@ -236,7 +236,7 @@
-         if error_msg:
-             body = self.login_screen(from_page, username, error_msg)
-             cherrypy.response.body = body
--            if cherrypy.response.headers.has_key("Content-Length"):
-+            if "Content-Length" in cherrypy.response.headers:
-                 # Delete Content-Length header so finalize() recalcs it.
-                 del cherrypy.response.headers["Content-Length"]
-             return True
-@@ -265,7 +265,7 @@
-             sess[self.session_key] = username = self.anonymous()
-         if not username:
-             cherrypy.response.body = self.login_screen(cherrypy.url(qs=request.query_string))
--            if cherrypy.response.headers.has_key("Content-Length"):
-+            if "Content-Length" in cherrypy.response.headers:
-                 # Delete Content-Length header so finalize() recalcs it.
-                 del cherrypy.response.headers["Content-Length"]
-             return True
-@@ -287,7 +287,7 @@
- def session_auth(**kwargs):
-     sa = SessionAuth()
--    for k, v in kwargs.iteritems():
-+    for k, v in kwargs.items():
-         setattr(sa, k, v)
-     return sa.run()
- session_auth.__doc__ = """Session authentication hook.
-@@ -314,7 +314,7 @@
-     # Sort by the standard points if possible.
-     from cherrypy import _cprequest
-     points = _cprequest.hookpoints
--    for k in cherrypy.request.hooks.keys():
-+    for k in list(cherrypy.request.hooks.keys()):
-         if k not in points:
-             points.append(k)
-     
-@@ -395,7 +395,7 @@
-     """
-     if not media:
-         return
--    if isinstance(media, basestring):
-+    if isinstance(media, str):
-         media = [media]
-     
-     # Parse the Accept request header, and try to match one
-diff -Naur client175_0.7-original/cherrypy/lib/encoding.py client175_0.7/cherrypy/lib/encoding.py
---- client175_0.7-original/cherrypy/lib/encoding.py    2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/encoding.py     2021-08-03 14:45:29.700190398 +0200
-@@ -33,7 +33,7 @@
- def decode_params(encoding):
-     decoded_params = {}
--    for key, value in cherrypy.request.params.items():
-+    for key, value in list(cherrypy.request.params.items()):
-         if not hasattr(value, 'file'):
-             # Skip the value if it is an uploaded file
-             if isinstance(value, list):
-@@ -73,7 +73,7 @@
-     """
-     def encoder(body):
-         for chunk in body:
--            if isinstance(chunk, unicode):
-+            if isinstance(chunk, str):
-                 chunk = chunk.encode(encoding, errors)
-             yield chunk
-     cherrypy.response.body = encoder(cherrypy.response.body)
-@@ -84,7 +84,7 @@
-     try:
-         body = []
-         for chunk in cherrypy.response.body:
--            if isinstance(chunk, unicode):
-+            if isinstance(chunk, str):
-                 chunk = chunk.encode(encoding, errors)
-             body.append(chunk)
-         cherrypy.response.body = body
-@@ -101,7 +101,7 @@
-     else:
-         response.collapse_body()
-         encoder = encode_string
--        if response.headers.has_key("Content-Length"):
-+        if "Content-Length" in response.headers:
-             # Delete Content-Length header so finalize() recalcs it.
-             # Encoded strings may be of different lengths from their
-             # unicode equivalents, and even from each other. For example:
-@@ -179,7 +179,7 @@
-     yield '\037\213'      # magic header
-     yield '\010'         # compression method
-     yield '\0'
--    yield struct.pack("<L", long(time.time()))
-+    yield struct.pack("<L", int(time.time()))
-     yield '\002'
-     yield '\377'
-     
-@@ -194,12 +194,12 @@
-         yield zobj.compress(line)
-     yield zobj.flush()
-     yield struct.pack("<l", crc)
--    yield struct.pack("<L", size & 0xFFFFFFFFL)
-+    yield struct.pack("<L", size & 0xFFFFFFFF)
- def decompress(body):
--    import gzip, StringIO
-+    import gzip, io
-     
--    zbuf = StringIO.StringIO()
-+    zbuf = io.StringIO()
-     zbuf.write(body)
-     zbuf.seek(0)
-     zfile = gzip.GzipFile(mode='rb', fileobj=zbuf)
-@@ -258,7 +258,7 @@
-                 
-                 response.headers['Content-Encoding'] = 'gzip'
-                 response.body = compress(response.body, compress_level)
--                if response.headers.has_key("Content-Length"):
-+                if "Content-Length" in response.headers:
-                     # Delete Content-Length header so finalize() recalcs it.
-                     del response.headers["Content-Length"]
-             return
-diff -Naur client175_0.7-original/cherrypy/lib/httpauth.py client175_0.7/cherrypy/lib/httpauth.py
---- client175_0.7-original/cherrypy/lib/httpauth.py    2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/httpauth.py     2021-08-03 14:45:46.595938962 +0200
-@@ -68,7 +68,7 @@
- import time
- import base64
--import urllib2
-+import urllib.request, urllib.error, urllib.parse
- MD5 = "MD5"
- MD5_SESS = "MD5-sess"
-@@ -144,17 +144,17 @@
-     # Check for required parameters
-     required = ["username", "realm", "nonce", "uri", "response"]
-     for k in required:
--        if not params.has_key(k):
-+        if k not in params:
-             return None
-     # If qop is sent then cnonce and nc MUST be present
--    if params.has_key("qop") and not (params.has_key("cnonce") \
--                                      and params.has_key("nc")):
-+    if "qop" in params and not ("cnonce" in params \
-+                                      and "nc" in params):
-         return None
-     # If qop is not sent, neither cnonce nor nc can be present
--    if (params.has_key("cnonce") or params.has_key("nc")) and \
--       not params.has_key("qop"):
-+    if ("cnonce" in params or "nc" in params) and \
-+       "qop" not in params:
-         return None
-     return params
-diff -Naur client175_0.7-original/cherrypy/lib/http.py client175_0.7/cherrypy/lib/http.py
---- client175_0.7-original/cherrypy/lib/http.py        2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/http.py 2021-08-03 14:45:40.288033948 +0200
-@@ -6,7 +6,7 @@
- # FuManChu will personally hang you up by your thumbs and submit you
- # to a public caning.
--from BaseHTTPServer import BaseHTTPRequestHandler
-+from http.server import BaseHTTPRequestHandler
- response_codes = BaseHTTPRequestHandler.responses.copy()
- # From http://www.cherrypy.org/ticket/361
-@@ -61,7 +61,7 @@
-         if start:
-             if not stop:
-                 stop = content_length - 1
--            start, stop = map(int, (start, stop))
-+            start, stop = list(map(int, (start, stop)))
-             if start >= content_length:
-                 # From rfc 2616 sec 14.16:
-                 # "If the server receives a request (other than one
-@@ -101,8 +101,8 @@
-         self.params = params
-     
-     def __unicode__(self):
--        p = [";%s=%s" % (k, v) for k, v in self.params.iteritems()]
--        return u"%s%s" % (self.value, "".join(p))
-+        p = [";%s=%s" % (k, v) for k, v in self.params.items()]
-+        return "%s%s" % (self.value, "".join(p))
-     
-     def __str__(self):
-         return str(self.__unicode__())
-@@ -264,14 +264,14 @@
-         pm = {'x': int(pm[0]), 'y': int(pm[1])}
-     else:
-         pm = cgi.parse_qs(query_string, keep_blank_values)
--        for key, val in pm.items():
-+        for key, val in list(pm.items()):
-             if len(val) == 1:
-                 pm[key] = val[0]
-     return pm
- def params_from_CGI_form(form):
-     params = {}
--    for key in form.keys():
-+    for key in list(form.keys()):
-         value_list = form[key]
-         if isinstance(value_list, list):
-             params[key] = []
-@@ -315,7 +315,7 @@
-         return dict.has_key(self, str(key).title())
-     
-     def update(self, E):
--        for k in E.keys():
-+        for k in list(E.keys()):
-             self[str(k).title()] = E[k]
-     
-     def fromkeys(cls, seq, value=None):
-@@ -357,8 +357,8 @@
-     def output(self, protocol=(1, 1)):
-         """Transform self into a list of (name, value) tuples."""
-         header_list = []
--        for key, v in self.iteritems():
--            if isinstance(v, unicode):
-+        for key, v in self.items():
-+            if isinstance(v, str):
-                 # HTTP/1.0 says, "Words of *TEXT may contain octets
-                 # from character sets other than US-ASCII." and
-                 # "Recipients of header field TEXT containing octets
-diff -Naur client175_0.7-original/cherrypy/lib/__init__.py client175_0.7/cherrypy/lib/__init__.py
---- client175_0.7-original/cherrypy/lib/__init__.py    2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/__init__.py     2021-08-03 14:46:02.407667654 +0200
-@@ -18,7 +18,7 @@
-     """Load a module and retrieve an attribute of that module."""
-     
-     # Parse out the path, module, and attribute
--    last_dot = full_attribute_name.rfind(u".")
-+    last_dot = full_attribute_name.rfind(".")
-     attr_name = full_attribute_name[last_dot + 1:]
-     mod_path = full_attribute_name[:last_dot]
-     
-@@ -52,7 +52,7 @@
-         return expr[subs]
-     
-     def build_CallFunc(self, o):
--        children = map(self.build, o.getChildren())
-+        children = list(map(self.build, o.getChildren()))
-         callee = children.pop(0)
-         kwargs = children.pop() or {}
-         starargs = children.pop() or ()
-@@ -60,7 +60,7 @@
-         return callee(*args, **kwargs)
-     
-     def build_List(self, o):
--        return map(self.build, o.getChildren())
-+        return list(map(self.build, o.getChildren()))
-     
-     def build_Const(self, o):
-         return o.value
-@@ -69,7 +69,7 @@
-         d = {}
-         i = iter(map(self.build, o.getChildren()))
-         for el in i:
--            d[el] = i.next()
-+            d[el] = next(i)
-         return d
-     
-     def build_Tuple(self, o):
-@@ -91,7 +91,7 @@
-         
-         # See if the Name is in __builtin__.
-         try:
--            import __builtin__
-+            import builtins
-             return getattr(__builtin__, o.name)
-         except AttributeError:
-             pass
-@@ -99,7 +99,7 @@
-         raise TypeError("unrepr could not resolve the name %s" % repr(o.name))
-     
-     def build_Add(self, o):
--        left, right = map(self.build, o.getChildren())
-+        left, right = list(map(self.build, o.getChildren()))
-         return left + right
-     
-     def build_Getattr(self, o):
-diff -Naur client175_0.7-original/cherrypy/lib/profiler.py client175_0.7/cherrypy/lib/profiler.py
---- client175_0.7-original/cherrypy/lib/profiler.py    2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/profiler.py     2021-08-03 14:46:29.643218230 +0200
-@@ -61,9 +61,9 @@
- import sys
- try:
--    import cStringIO as StringIO
-+    import io as StringIO
- except ImportError:
--    import StringIO
-+    import io
- _count = 0
-@@ -94,7 +94,7 @@
-     
-     def stats(self, filename, sortby='cumulative'):
-         """stats(index) -> output of print_stats() for the given profile."""
--        sio = StringIO.StringIO()
-+        sio = io.StringIO()
-         if sys.version_info >= (2, 5):
-             s = pstats.Stats(os.path.join(self.path, filename), stream=sio)
-             s.strip_dirs()
-diff -Naur client175_0.7-original/cherrypy/lib/safemime.py client175_0.7/cherrypy/lib/safemime.py
---- client175_0.7-original/cherrypy/lib/safemime.py    2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/safemime.py     2021-08-03 14:46:40.883037698 +0200
-@@ -95,13 +95,13 @@
-     def __iter__(self):
-         return self.rfile
-     
--    def next(self):
-+    def __next__(self):
-         if self.clen:
-             # Return '' if we've read all the data.
-             if self.bytes_read >= self.clen:
-                 return ''
-         
--        data = self.rfile.next()
-+        data = next(self.rfile)
-         self.bytes_read += len(data)
-         return data
-diff -Naur client175_0.7-original/cherrypy/lib/sessions.py client175_0.7/cherrypy/lib/sessions.py
---- client175_0.7-original/cherrypy/lib/sessions.py    2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/sessions.py     2021-08-03 14:46:48.498916658 +0200
-@@ -9,7 +9,7 @@
- import datetime
- import os
- try:
--    import cPickle as pickle
-+    import pickle as pickle
- except ImportError:
-     import pickle
- import random
-@@ -31,11 +31,9 @@
- missing = object()
--class Session(object):
-+class Session(object, metaclass=cherrypy._AttributeDocstrings):
-     """A CherryPy dict-like Session object (one per request)."""
-     
--    __metaclass__ = cherrypy._AttributeDocstrings
--    
-     _id = None
-     id_observers = None
-     id_observers__doc = "A list of callbacks to which to pass new id's."
-@@ -72,7 +70,7 @@
-         self.id_observers = []
-         self._data = {}
-         
--        for k, v in kwargs.iteritems():
-+        for k, v in kwargs.items():
-             setattr(self, k, v)
-         
-         if id is None:
-@@ -192,7 +190,7 @@
-     def has_key(self, key):
-         """D.has_key(k) -> True if D has a key k, else False."""
-         if not self.loaded: self.load()
--        return self._data.has_key(key)
-+        return key in self._data
-     
-     def get(self, key, default=None):
-         """D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None."""
-@@ -217,17 +215,17 @@
-     def keys(self):
-         """D.keys() -> list of D's keys."""
-         if not self.loaded: self.load()
--        return self._data.keys()
-+        return list(self._data.keys())
-     
-     def items(self):
-         """D.items() -> list of D's (key, value) pairs, as 2-tuples."""
-         if not self.loaded: self.load()
--        return self._data.items()
-+        return list(self._data.items())
-     
-     def values(self):
-         """D.values() -> list of D's values."""
-         if not self.loaded: self.load()
--        return self._data.values()
-+        return list(self._data.values())
- class RamSession(Session):
-@@ -239,7 +237,7 @@
-     def clean_up(self):
-         """Clean up expired sessions."""
-         now = datetime.datetime.now()
--        for id, (data, expiration_time) in self.cache.items():
-+        for id, (data, expiration_time) in list(self.cache.items()):
-             if expiration_time < now:
-                 try:
-                     del self.cache[id]
-@@ -302,7 +300,7 @@
-         # The 'storage_path' arg is required for file-based sessions.
-         kwargs['storage_path'] = os.path.abspath(kwargs['storage_path'])
-         
--        for k, v in kwargs.iteritems():
-+        for k, v in kwargs.items():
-             setattr(cls, k, v)
-         
-         # Warn if any lock files exist at startup.
-@@ -426,7 +424,7 @@
-         This should only be called once per process; this will be done
-         automatically when using sessions.init (as the built-in Tool does).
-         """
--        for k, v in kwargs.iteritems():
-+        for k, v in kwargs.items():
-             setattr(cls, k, v)
-         
-         self.db = self.get_db()
-@@ -502,7 +500,7 @@
-         This should only be called once per process; this will be done
-         automatically when using sessions.init (as the built-in Tool does).
-         """
--        for k, v in kwargs.iteritems():
-+        for k, v in kwargs.items():
-             setattr(cls, k, v)
-         
-         import memcache
-diff -Naur client175_0.7-original/cherrypy/lib/static.py client175_0.7/cherrypy/lib/static.py
---- client175_0.7-original/cherrypy/lib/static.py      2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/static.py       2021-08-03 14:46:56.870784689 +0200
-@@ -7,7 +7,7 @@
- import re
- import stat
- import time
--import urllib
-+import urllib.request, urllib.parse, urllib.error
- import cherrypy
- from cherrypy.lib import cptools, http, file_generator_limited
-@@ -99,7 +99,7 @@
-                 boundary = mimetools.choose_boundary()
-                 ct = "multipart/byteranges; boundary=%s" % boundary
-                 response.headers['Content-Type'] = ct
--                if response.headers.has_key("Content-Length"):
-+                if "Content-Length" in response.headers:
-                     # Delete Content-Length header so finalize() recalcs it.
-                     del response.headers["Content-Length"]
-                 
-@@ -189,7 +189,7 @@
-         section = "/"
-     section = section.rstrip(r"\/")
-     branch = cherrypy.request.path_info[len(section) + 1:]
--    branch = urllib.unquote(branch.lstrip(r"\/"))
-+    branch = urllib.parse.unquote(branch.lstrip(r"\/"))
-     
-     # If branch is "", filename will end in a slash
-     filename = os.path.join(dir, branch)
-diff -Naur client175_0.7-original/cherrypy/lib/tidy.py client175_0.7/cherrypy/lib/tidy.py
---- client175_0.7-original/cherrypy/lib/tidy.py        2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/tidy.py 2021-08-03 14:47:04.222669487 +0200
-@@ -2,7 +2,7 @@
- import cgi
- import os
--import StringIO
-+import io
- import traceback
- import cherrypy
-@@ -79,7 +79,7 @@
-         
-         if new_errs:
-             response.body = wrong_content('<br />'.join(new_errs), orig_body)
--            if response.headers.has_key("Content-Length"):
-+            if "Content-Length" in response.headers:
-                 # Delete Content-Length header so finalize() recalcs it.
-                 del response.headers["Content-Length"]
-             return
-@@ -95,23 +95,23 @@
-                 enctag = '<?xml version="1.0" encoding="%s"?>' % encoding
-                 orig_body = enctag + orig_body
-             
--            f = StringIO.StringIO(orig_body)
-+            f = io.StringIO(orig_body)
-             try:
-                 tree = parse(f)
-             except:
-                 # Wrong XML
--                body_file = StringIO.StringIO()
-+                body_file = io.StringIO()
-                 traceback.print_exc(file = body_file)
-                 body_file = '<br />'.join(body_file.getvalue())
-                 response.body = wrong_content(body_file, orig_body, "XML")
--                if response.headers.has_key("Content-Length"):
-+                if "Content-Length" in response.headers:
-                     # Delete Content-Length header so finalize() recalcs it.
-                     del response.headers["Content-Length"]
-                 return
-         
-         if use_output:
-             response.body = [output]
--            if response.headers.has_key("Content-Length"):
-+            if "Content-Length" in response.headers:
-                 # Delete Content-Length header so finalize() recalcs it.
-                 del response.headers["Content-Length"]
-@@ -178,7 +178,7 @@
-         
-         if new_errs:
-             response.body = wrong_content('<br />'.join(new_errs), orig_body)
--            if response.headers.has_key("Content-Length"):
-+            if "Content-Length" in response.headers:
-                 # Delete Content-Length header so finalize() recalcs it.
-                 del response.headers["Content-Length"]
-diff -Naur client175_0.7-original/cherrypy/lib/wsgiapp.py client175_0.7/cherrypy/lib/wsgiapp.py
---- client175_0.7-original/cherrypy/lib/wsgiapp.py     2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/wsgiapp.py      2021-08-03 14:47:15.738490577 +0200
-@@ -43,7 +43,7 @@
-     headers = request.headers
-     environ["CONTENT_TYPE"] = headers.get("Content-type", "")
-     environ["CONTENT_LENGTH"] = headers.get("Content-length", "")
--    for (k, v) in headers.iteritems():
-+    for (k, v) in headers.items():
-         envname = "HTTP_" + k.upper().replace("-","_")
-         environ[envname] = v
-     return environ
-diff -Naur client175_0.7-original/cherrypy/lib/xmlrpc.py client175_0.7/cherrypy/lib/xmlrpc.py
---- client175_0.7-original/cherrypy/lib/xmlrpc.py      2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/lib/xmlrpc.py       2021-08-03 14:47:22.378387654 +0200
-@@ -6,8 +6,8 @@
- def process_body():
-     """Return (params, method) from request body."""
-     try:
--        import xmlrpclib
--        return xmlrpclib.loads(cherrypy.request.body.read())
-+        import xmlrpc.client
-+        return xmlrpc.client.loads(cherrypy.request.body.read())
-     except Exception:
-         return ('ERROR PARAMS', ), 'ERRORMETHOD'
-@@ -35,15 +35,15 @@
- def respond(body, encoding='utf-8', allow_none=0):
--    import xmlrpclib
--    if not isinstance(body, xmlrpclib.Fault):
-+    import xmlrpc.client
-+    if not isinstance(body, xmlrpc.client.Fault):
-         body = (body,)
--    _set_response(xmlrpclib.dumps(body, methodresponse=1,
-+    _set_response(xmlrpc.client.dumps(body, methodresponse=1,
-                                   encoding=encoding,
-                                   allow_none=allow_none))
- def on_error(*args, **kwargs):
-     body = str(sys.exc_info()[1])
--    import xmlrpclib
--    _set_response(xmlrpclib.dumps(xmlrpclib.Fault(1, body)))
-+    import xmlrpc.client
-+    _set_response(xmlrpc.client.dumps(xmlrpc.client.Fault(1, body)))
-diff -Naur client175_0.7-original/cherrypy/process/plugins.py client175_0.7/cherrypy/process/plugins.py
---- client175_0.7-original/cherrypy/process/plugins.py 2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/process/plugins.py  2021-08-03 14:48:07.409700439 +0200
-@@ -49,7 +49,7 @@
-     
-     # Map from signal numbers to names
-     signals = {}
--    for k, v in vars(_signal).items():
-+    for k, v in list(vars(_signal).items()):
-         if k.startswith('SIG') and not k.startswith('SIG_'):
-             signals[v] = k
-     del k, v
-@@ -65,14 +65,14 @@
-         self._previous_handlers = {}
-     
-     def subscribe(self):
--        for sig, func in self.handlers.iteritems():
-+        for sig, func in self.handlers.items():
-             try:
-                 self.set_handler(sig, func)
-             except ValueError:
-                 pass
-     
-     def unsubscribe(self):
--        for signum, handler in self._previous_handlers.iteritems():
-+        for signum, handler in self._previous_handlers.items():
-             signame = self.signals[signum]
-             
-             if handler is None:
-@@ -100,7 +100,7 @@
-         If the given signal name or number is not available on the current
-         platform, ValueError is raised.
-         """
--        if isinstance(signal, basestring):
-+        if isinstance(signal, str):
-             signum = getattr(_signal, signal, None)
-             if signum is None:
-                 raise ValueError("No such signal: %r" % signal)
-@@ -162,7 +162,7 @@
-                 self.bus.log("pwd module not available; ignoring uid.",
-                              level=30)
-                 val = None
--            elif isinstance(val, basestring):
-+            elif isinstance(val, str):
-                 val = pwd.getpwnam(val)[2]
-         self._uid = val
-     uid = property(_get_uid, _set_uid, doc="The uid under which to run.")
-@@ -175,7 +175,7 @@
-                 self.bus.log("grp module not available; ignoring gid.",
-                              level=30)
-                 val = None
--            elif isinstance(val, basestring):
-+            elif isinstance(val, str):
-                 val = grp.getgrnam(val)[2]
-         self._gid = val
-     gid = property(_get_gid, _set_gid, doc="The gid under which to run.")
-@@ -296,7 +296,7 @@
-                 # This is the first parent. Exit, now that we've forked.
-                 self.bus.log('Forking once.')
-                 os._exit(0)
--        except OSError, exc:
-+        except OSError as exc:
-             # Python raises OSError rather than returning negative numbers.
-             sys.exit("%s: fork #1 failed: (%d) %s\n"
-                      % (sys.argv[0], exc.errno, exc.strerror))
-@@ -309,7 +309,7 @@
-             if pid > 0:
-                 self.bus.log('Forking twice.')
-                 os._exit(0) # Exit second parent
--        except OSError, exc:
-+        except OSError as exc:
-             sys.exit("%s: fork #2 failed: (%d) %s\n"
-                      % (sys.argv[0], exc.errno, exc.strerror))
-         
-@@ -440,7 +440,7 @@
-     def run(self):
-         """Reload the process if registered files have been modified."""
-         sysfiles = set()
--        for k, m in sys.modules.items():
-+        for k, m in list(sys.modules.items()):
-             if re.match(self.match, k):
-                 if hasattr(m, '__loader__'):
-                     if hasattr(m.__loader__, 'archive'):
-@@ -522,7 +522,7 @@
-     
-     def stop(self):
-         """Release all threads and run all 'stop_thread' listeners."""
--        for thread_ident, i in self.threads.iteritems():
-+        for thread_ident, i in self.threads.items():
-             self.bus.publish('stop_thread', i)
-         self.threads.clear()
-     graceful = stop
-diff -Naur client175_0.7-original/cherrypy/process/servers.py client175_0.7/cherrypy/process/servers.py
---- client175_0.7-original/cherrypy/process/servers.py 2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/process/servers.py  2021-08-03 14:48:16.165568167 +0200
-@@ -71,11 +71,11 @@
-         """
-         try:
-             self.httpserver.start()
--        except KeyboardInterrupt, exc:
-+        except KeyboardInterrupt as exc:
-             self.bus.log("<Ctrl-C> hit: shutting down HTTP server")
-             self.interrupt = exc
-             self.bus.exit()
--        except SystemExit, exc:
-+        except SystemExit as exc:
-             self.bus.log("SystemExit raised: shutting down HTTP server")
-             self.interrupt = exc
-             self.bus.exit()
-@@ -238,7 +238,7 @@
-     if not host:
-         raise ValueError("Host values of '' or None are not allowed.")
-     
--    for trial in xrange(50):
-+    for trial in range(50):
-         try:
-             # we are expecting a free port, so reduce the timeout
-             check_port(host, port, timeout=0.1)
-@@ -255,7 +255,7 @@
-     if not host:
-         raise ValueError("Host values of '' or None are not allowed.")
-     
--    for trial in xrange(50):
-+    for trial in range(50):
-         try:
-             check_port(host, port)
-         except IOError:
-diff -Naur client175_0.7-original/cherrypy/process/win32.py client175_0.7/cherrypy/process/win32.py
---- client175_0.7-original/cherrypy/process/win32.py   2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/process/win32.py    2021-08-03 14:48:22.801468164 +0200
-@@ -1,7 +1,7 @@
- """Windows service. Requires pywin32."""
- import os
--import thread
-+import _thread
- import win32api
- import win32con
- import win32event
-@@ -84,7 +84,7 @@
-             return self.events[state]
-         except KeyError:
-             event = win32event.CreateEvent(None, 0, 0,
--                                           u"WSPBus %s Event (pid=%r)" %
-+                                           "WSPBus %s Event (pid=%r)" %
-                                            (state.name, os.getpid()))
-             self.events[state] = event
-             return event
-@@ -128,7 +128,7 @@
-     
-     def key_for(self, obj):
-         """For the given value, return its corresponding key."""
--        for key, val in self.iteritems():
-+        for key, val in self.items():
-             if val is obj:
-                 return key
-         raise ValueError("The given object could not be found: %r" % obj)
-diff -Naur client175_0.7-original/cherrypy/process/wspbus.py client175_0.7/cherrypy/process/wspbus.py
---- client175_0.7-original/cherrypy/process/wspbus.py  2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/process/wspbus.py   2021-08-03 14:48:29.181372091 +0200
-@@ -147,7 +147,7 @@
-                 output.append(listener(*args, **kwargs))
-             except KeyboardInterrupt:
-                 raise
--            except SystemExit, e:
-+            except SystemExit as e:
-                 # If we have previous errors ensure the exit code is non-zero
-                 if exc and e.code == 0:
-                     e.code = 1
-@@ -195,7 +195,7 @@
-             except:
-                 # Any stop/exit errors will be logged inside publish().
-                 pass
--            raise e_info[0], e_info[1], e_info[2]
-+            raise e_info[0](e_info[1]).with_traceback(e_info[2])
-     
-     def exit(self):
-         """Stop all services and prepare to exit the process."""
-diff -Naur client175_0.7-original/cherrypy/wsgiserver/__init__.py client175_0.7/cherrypy/wsgiserver/__init__.py
---- client175_0.7-original/cherrypy/wsgiserver/__init__.py     2010-04-20 13:10:10.000000000 +0200
-+++ client175_0.7/cherrypy/wsgiserver/__init__.py      2021-08-03 14:49:05.556826062 +0200
-@@ -79,24 +79,24 @@
- import base64
- import os
--import Queue
-+import queue
- import re
- quoted_slash = re.compile("(?i)%2F")
- import rfc822
- import socket
- try:
--    import cStringIO as StringIO
-+    import io as StringIO
- except ImportError:
--    import StringIO
-+    import io
--_fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
-+_fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, str)
- import sys
- import threading
- import time
- import traceback
--from urllib import unquote
--from urlparse import urlparse
-+from urllib.parse import unquote
-+from urllib.parse import urlparse
- import warnings
- try:
-@@ -117,7 +117,7 @@
-     errno_names = dir(errno)
-     nums = [getattr(errno, k) for k in errnames if k in errno_names]
-     # de-dupe the list
--    return dict.fromkeys(nums).keys()
-+    return list(dict.fromkeys(nums).keys())
- socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
-@@ -153,7 +153,7 @@
-     
-     def __init__(self, apps):
-         try:
--            apps = apps.items()
-+            apps = list(apps.items())
-         except AttributeError:
-             pass
-         
-@@ -239,8 +239,8 @@
-     def __iter__(self):
-         return self
-     
--    def next(self):
--        data = self.rfile.next()
-+    def __next__(self):
-+        data = next(self.rfile)
-         self.bytes_read += len(data)
-         self._check_length()
-         return data
-@@ -401,7 +401,7 @@
-         # then all the http headers
-         try:
-             self.read_headers()
--        except ValueError, ex:
-+        except ValueError as ex:
-             self.simple_response("400 Bad Request", repr(ex.args))
-             return
-         
-@@ -500,7 +500,7 @@
-     def decode_chunked(self):
-         """Decode the 'chunked' transfer coding."""
-         cl = 0
--        data = StringIO.StringIO()
-+        data = io.StringIO()
-         while True:
-             line = self.rfile.readline().strip().split(";", 1)
-             chunk_size = int(line.pop(0), 16)
-@@ -592,7 +592,7 @@
-         
-         try:
-             self.wfile.sendall("".join(buf))
--        except socket.error, x:
-+        except socket.error as x:
-             if x.args[0] not in socket_errors_to_ignore:
-                 raise
-     
-@@ -609,7 +609,7 @@
-         # exc_info tuple."
-         if self.sent_headers:
-             try:
--                raise exc_info[0], exc_info[1], exc_info[2]
-+                raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
-             finally:
-                 exc_info = None
-         
-@@ -728,7 +728,7 @@
-                 try:
-                     bytes_sent = self.send(data)
-                     data = data[bytes_sent:]
--                except socket.error, e:
-+                except socket.error as e:
-                     if e.args[0] not in socket_errors_nonblocking:
-                         raise
-@@ -745,7 +745,7 @@
-             while True:
-                 try:
-                     return self._sock.recv(size)
--                except socket.error, e:
-+                except socket.error as e:
-                     if (e.args[0] not in socket_errors_nonblocking
-                         and e.args[0] not in socket_error_eintr):
-                         raise
-@@ -762,7 +762,7 @@
-             buf.seek(0, 2)  # seek end
-             if size < 0:
-                 # Read until EOF
--                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-+                self._rbuf = io.StringIO()  # reset _rbuf.  we consume it via buf.
-                 while True:
-                     data = self.recv(rbufsize)
-                     if not data:
-@@ -776,11 +776,11 @@
-                     # Already have size bytes in our buffer?  Extract and return.
-                     buf.seek(0)
-                     rv = buf.read(size)
--                    self._rbuf = StringIO.StringIO()
-+                    self._rbuf = io.StringIO()
-                     self._rbuf.write(buf.read())
-                     return rv
--                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-+                self._rbuf = io.StringIO()  # reset _rbuf.  we consume it via buf.
-                 while True:
-                     left = size - buf_len
-                     # recv() will malloc the amount of memory given as its
-@@ -818,7 +818,7 @@
-                 buf.seek(0)
-                 bline = buf.readline(size)
-                 if bline.endswith('\n') or len(bline) == size:
--                    self._rbuf = StringIO.StringIO()
-+                    self._rbuf = io.StringIO()
-                     self._rbuf.write(buf.read())
-                     return bline
-                 del bline
-@@ -828,7 +828,7 @@
-                     # Speed up unbuffered case
-                     buf.seek(0)
-                     buffers = [buf.read()]
--                    self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-+                    self._rbuf = io.StringIO()  # reset _rbuf.  we consume it via buf.
-                     data = None
-                     recv = self.recv
-                     while data != "\n":
-@@ -839,7 +839,7 @@
-                     return "".join(buffers)
-                 buf.seek(0, 2)  # seek end
--                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-+                self._rbuf = io.StringIO()  # reset _rbuf.  we consume it via buf.
-                 while True:
-                     data = self.recv(self._rbufsize)
-                     if not data:
-@@ -860,10 +860,10 @@
-                 if buf_len >= size:
-                     buf.seek(0)
-                     rv = buf.read(size)
--                    self._rbuf = StringIO.StringIO()
-+                    self._rbuf = io.StringIO()
-                     self._rbuf.write(buf.read())
-                     return rv
--                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
-+                self._rbuf = io.StringIO()  # reset _rbuf.  we consume it via buf.
-                 while True:
-                     data = self.recv(self._rbufsize)
-                     if not data:
-@@ -906,7 +906,7 @@
-                 try:
-                     bytes_sent = self.send(data)
-                     data = data[bytes_sent:]
--                except socket.error, e:
-+                except socket.error as e:
-                     if e.args[0] not in socket_errors_nonblocking:
-                         raise
-@@ -923,7 +923,7 @@
-             while True:
-                 try:
-                     return self._sock.recv(size)
--                except socket.error, e:
-+                except socket.error as e:
-                     if (e.args[0] not in socket_errors_nonblocking
-                         and e.args[0] not in socket_error_eintr):
-                         raise
-@@ -1065,7 +1065,7 @@
-                 time.sleep(self.ssl_retry)
-             except SSL.WantWriteError:
-                 time.sleep(self.ssl_retry)
--            except SSL.SysCallError, e:
-+            except SSL.SysCallError as e:
-                 if is_reader and e.args == (-1, 'Unexpected EOF'):
-                     return ""
-                 
-@@ -1073,7 +1073,7 @@
-                 if is_reader and errnum in socket_errors_to_ignore:
-                     return ""
-                 raise socket.error(errnum)
--            except SSL.Error, e:
-+            except SSL.Error as e:
-                 if is_reader and e.args == (-1, 'Unexpected EOF'):
-                     return ""
-                 
-@@ -1175,7 +1175,7 @@
-                 if req.close_connection:
-                     return
-         
--        except socket.error, e:
-+        except socket.error as e:
-             errnum = e.args[0]
-             if errnum == 'timed out':
-                 if req and not req.sent_headers:
-@@ -1187,7 +1187,7 @@
-             return
-         except (KeyboardInterrupt, SystemExit):
-             raise
--        except FatalSSLAlert, e:
-+        except FatalSSLAlert as e:
-             # Close the connection.
-             return
-         except NoSSLError:
-@@ -1198,7 +1198,7 @@
-                     "The client sent a plain HTTP request, but "
-                     "this server only speaks HTTPS on this port.")
-                 self.linger = True
--        except Exception, e:
-+        except Exception as e:
-             if req and not req.sent_headers:
-                 req.simple_response("500 Internal Server Error", format_exc())
-     
-@@ -1272,7 +1272,7 @@
-                 finally:
-                     conn.close()
-                     self.conn = None
--        except (KeyboardInterrupt, SystemExit), exc:
-+        except (KeyboardInterrupt, SystemExit) as exc:
-             self.server.interrupt = exc
-@@ -1288,12 +1288,12 @@
-         self.min = min
-         self.max = max
-         self._threads = []
--        self._queue = Queue.Queue()
-+        self._queue = queue.Queue()
-         self.get = self._queue.get
-     
-     def start(self):
-         """Start the pool of threads."""
--        for i in xrange(self.min):
-+        for i in range(self.min):
-             self._threads.append(WorkerThread(self.server))
-         for worker in self._threads:
-             worker.setName("CP WSGIServer " + worker.getName())
-@@ -1314,7 +1314,7 @@
-     
-     def grow(self, amount):
-         """Spawn new worker threads (not above self.max)."""
--        for i in xrange(amount):
-+        for i in range(amount):
-             if self.max > 0 and len(self._threads) >= self.max:
-                 break
-             worker = WorkerThread(self.server)
-@@ -1332,7 +1332,7 @@
-                 amount -= 1
-         
-         if amount > 0:
--            for i in xrange(min(amount, len(self._threads) - self.min)):
-+            for i in range(min(amount, len(self._threads) - self.min)):
-                 # Put a number of shutdown requests on the queue equal
-                 # to 'amount'. Once each of those is processed by a worker,
-                 # that worker will terminate and be culled from our list
-@@ -1369,7 +1369,7 @@
-                 except (AssertionError,
-                         # Ignore repeated Ctrl-C.
-                         # See http://www.cherrypy.org/ticket/691.
--                        KeyboardInterrupt), exc1:
-+                        KeyboardInterrupt) as exc1:
-                     pass
-@@ -1392,13 +1392,13 @@
-               'sock_shutdown', 'get_peer_certificate', 'want_read',
-               'want_write', 'set_connect_state', 'set_accept_state',
-               'connect_ex', 'sendall', 'settimeout'):
--        exec """def %s(self, *args):
-+        exec("""def %s(self, *args):
-         self._lock.acquire()
-         try:
-             return self._ssl_conn.%s(*args)
-         finally:
-             self._lock.release()
--""" % (f, f)
-+""" % (f, f))
- try:
-@@ -1557,7 +1557,7 @@
-         self._interrupt = None
-         
-         # Select the appropriate socket
--        if isinstance(self.bind_addr, basestring):
-+        if isinstance(self.bind_addr, str):
-             # AF_UNIX socket
-             
-             # So we can reuse the socket...
-@@ -1565,7 +1565,7 @@
-             except: pass
-             
-             # So everyone can access the socket...
--            try: os.chmod(self.bind_addr, 0777)
-+            try: os.chmod(self.bind_addr, 0o777)
-             except: pass
-             
-             info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
-@@ -1586,14 +1586,14 @@
-             af, socktype, proto, canonname, sa = res
-             try:
-                 self.bind(af, socktype, proto)
--            except socket.error, msg:
-+            except socket.error as msg:
-                 if self.socket:
-                     self.socket.close()
-                 self.socket = None
-                 continue
-             break
-         if not self.socket:
--            raise socket.error, msg
-+            raise socket.error(msg)
-         
-         # Timeout so KeyboardInterrupt can be caught on Win32
-         self.socket.settimeout(1)
-@@ -1632,7 +1632,7 @@
-             
-             # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
-             # activate dual-stack. See http://www.cherrypy.org/ticket/871.
--            if (not isinstance(self.bind_addr, basestring)
-+            if (not isinstance(self.bind_addr, str)
-                 and self.bind_addr[0] == '::' and family == socket.AF_INET6):
-                 try:
-                     self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
-@@ -1664,7 +1664,7 @@
-             environ["ACTUAL_SERVER_PROTOCOL"] = self.protocol
-             environ["SERVER_NAME"] = self.server_name
-             
--            if isinstance(self.bind_addr, basestring):
-+            if isinstance(self.bind_addr, str):
-                 # AF_UNIX. This isn't really allowed by WSGI, which doesn't
-                 # address unix domain sockets. But it's better than nothing.
-                 environ["SERVER_PORT"] = ""
-@@ -1682,7 +1682,7 @@
-             # notice keyboard interrupts on Win32, which don't interrupt
-             # accept() by default
-             return
--        except socket.error, x:
-+        except socket.error as x:
-             if x.args[0] in socket_error_eintr:
-                 # I *think* this is right. EINTR should occur when a signal
-                 # is received during the accept() call; all docs say retry
-@@ -1715,11 +1715,11 @@
-         
-         sock = getattr(self, "socket", None)
-         if sock:
--            if not isinstance(self.bind_addr, basestring):
-+            if not isinstance(self.bind_addr, str):
-                 # Touch our own socket to make accept() return immediately.
-                 try:
-                     host, port = sock.getsockname()[:2]
--                except socket.error, x:
-+                except socket.error as x:
-                     if x.args[0] not in socket_errors_to_ignore:
-                         raise
-                 else:
-diff -Naur client175_0.7-original/covers.py client175_0.7/covers.py
---- client175_0.7-original/covers.py   2011-04-02 03:51:24.000000000 +0200
-+++ client175_0.7/covers.py    2021-08-03 14:39:46.713265947 +0200
-@@ -23,7 +23,7 @@
- #       Exaile (http://www.exaile.org/).
--import hashlib, re, urllib, os, time, shutil, threading
-+import hashlib, re, urllib.request, urllib.parse, urllib.error, os, time, shutil, threading
- from xml.etree import ElementTree as ET
- from datetime import datetime, timedelta
-@@ -76,7 +76,7 @@
-     def _findMusicBrainz(self, vars):
-         self._delay('last_MB_lookup')
--        data = urllib.urlopen(self.urlMB % vars).read()
-+        data = urllib.request.urlopen(self.urlMB % vars).read()
-         m = self.regexMB.search(data)
-         if not m:
-             return False
-@@ -85,7 +85,7 @@
-             url = "http://images.amazon.com/images/P/%s.01.%sZZZZZZZ.jpg"
-             for sz in ['L', 'M']:
-                 image = url % (asin, sz)
--                h = urllib.urlopen(image)
-+                h = urllib.request.urlopen(image)
-                 data = h.read()
-                 h.close()
-                 if len(data) > 1000:
-@@ -95,21 +95,21 @@
-     def _findLastFM_album(self, vars):
-         self._delay('last_FM_lookup')
--        data = urllib.urlopen(self.urlFM % vars).read()
-+        data = urllib.request.urlopen(self.urlFM % vars).read()
-         x = ET.XML(data)
-         if len(x) == 0:
--            print 'LASTFM SEARCH: ERROR PARSING LASTFM DATA!'
-+            print('LASTFM SEARCH: ERROR PARSING LASTFM DATA!')
-             return False
-         c = x.find('coverart')
-         if len(c) == 0:
--            print 'LASTFM SEARCH: NO COVERART NODE IN LASTFM DATA!'
-+            print('LASTFM SEARCH: NO COVERART NODE IN LASTFM DATA!')
-             return False
-         for sz in ['large', 'medium', 'small']:
-             image = c.findtext(sz, '')
-             if image > '' and not image.lower().endswith('.gif'):
--                h = urllib.urlopen(image)
-+                h = urllib.request.urlopen(image)
-                 data = h.read()
-                 h.close()
-                 if hashlib.sha1(data).hexdigest() != "57b2c37343f711c94e83a37bd91bc4d18d2ed9d5":
-@@ -120,13 +120,13 @@
-     def _findLastFM_artist(self, vars):
-         self._delay('last_FM_lookup')
--        data = urllib.urlopen(self.urlFM_artist % vars['artist']).read()
-+        data = urllib.request.urlopen(self.urlFM_artist % vars['artist']).read()
-         m = self.regexFM_artist.search(data)
-         if m:
-             image = m.group(1)
-             if image.lower().endswith('.gif'):
-                 return False
--            h = urllib.urlopen(image)
-+            h = urllib.request.urlopen(image)
-             data = h.read()
-             h.close()
-             if hashlib.sha1(data).hexdigest() != "57b2c37343f711c94e83a37bd91bc4d18d2ed9d5":
-@@ -147,8 +147,8 @@
-                     try:
-                         shutil.copy2(coverpath, cover_destination)
-                     except IOError:
--                        print "Could not save cover to: " + cover_destination
--                        print "For best performance, please ensure that the directory exists and is writable."
-+                        print("Could not save cover to: " + cover_destination)
-+                        print("For best performance, please ensure that the directory exists and is writable.")
-                         h = open(coverpath, 'r')
-                         data = h.read()
-                         h.close()
-@@ -175,8 +175,8 @@
-             return covername, None
-         vars = {
--            'album': urllib.quote_plus(album.encode("utf-8")),
--            'artist': urllib.quote_plus(artist.encode("utf-8"))
-+            'album': urllib.parse.quote_plus(album.encode("utf-8")),
-+            'artist': urllib.parse.quote_plus(artist.encode("utf-8"))
-         }
-         for fn in lookups:
-@@ -188,8 +188,8 @@
-                         h.write(data)
-                         h.close()
-                     except:
--                        print "Could not save cover to: " + coverpath
--                        print "For best performance, please ensure that the directory exists and is writable."
-+                        print("Could not save cover to: " + coverpath)
-+                        print("For best performance, please ensure that the directory exists and is writable.")
-                         covername = ""
-                     return covername, data
-             except:
-diff -Naur client175_0.7-original/lyricwiki.py client175_0.7/lyricwiki.py
---- client175_0.7-original/lyricwiki.py        2010-11-20 18:43:24.000000000 +0100
-+++ client175_0.7/lyricwiki.py 2021-08-03 14:40:00.301064572 +0200
-@@ -18,7 +18,7 @@
- #       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- #       MA 02110-1301, USA.
--import json, urllib, os, hashlib, time
-+import json, urllib.request, urllib.parse, urllib.error, os, hashlib, time
- def _download(args):
-     """
-@@ -31,9 +31,9 @@
-     for key in args:
-         str_args[key] = args[key].encode("utf-8")
-     
--    args = urllib.urlencode(str_args)
-+    args = urllib.parse.urlencode(str_args)
-     
--    return urllib.urlopen(base + args).read()
-+    return urllib.request.urlopen(base + args).read()
- def _get_page_titles(artist, title):
-     """
-diff -Naur client175_0.7-original/metadata/_base.py client175_0.7/metadata/_base.py
---- client175_0.7-original/metadata/_base.py   2010-05-01 21:15:15.000000000 +0200
-+++ client175_0.7/metadata/_base.py    2021-08-03 14:51:24.062763658 +0200
-@@ -66,7 +66,7 @@
-         self.mutagen = None
-         self.load()
-         self._reverse_mapping = dict((
--            (v,k) for k,v in self.tag_mapping.iteritems() ))
-+            (v,k) for k,v in self.tag_mapping.items() ))
-     def load(self):
-         """
-@@ -99,7 +99,7 @@
-     def _get_keys(self):
-         keys = []
--        for k in self._get_raw().keys():
-+        for k in list(self._get_raw().keys()):
-             if k in self._reverse_mapping:
-                 keys.append(self._reverse_mapping[k])
-             else:
-@@ -149,11 +149,11 @@
-             if t == None and tag in self.tag_mapping:
-                 try:
-                     t = self._get_tag(raw, self.tag_mapping[tag])
--                    if type(t) in [str, unicode]:
-+                    if type(t) in [str, str]:
-                         t = [t]
-                     else:
-                         try:
--                            t = [unicode(u) for u in list(t)]
-+                            t = [str(u) for u in list(t)]
-                         except UnicodeDecodeError:
-                             t = t
-                 except (KeyError, TypeError):
-@@ -161,10 +161,10 @@
-             if t == None and self.others:
-                 try:
-                     t = self._get_tag(raw, tag)
--                    if type(t) in [str, unicode]:
-+                    if type(t) in [str, str]:
-                         t = [t]
-                     else:
--                        t = [unicode(u) for u in list(t)]
-+                        t = [str(u) for u in list(t)]
-                 except (KeyError, TypeError):
-                     pass
-@@ -207,7 +207,7 @@
-                     pass
-             # tags starting with __ are internal and should not be written
--            for tag in tagdict.keys():
-+            for tag in list(tagdict.keys()):
-                 if tag.startswith("__"):
-                     try:
-                         del tagdict[tag]
-diff -Naur client175_0.7-original/metadata/_id3.py client175_0.7/metadata/_id3.py
---- client175_0.7-original/metadata/_id3.py    2010-05-04 23:50:41.000000000 +0200
-+++ client175_0.7/metadata/_id3.py     2021-08-03 14:51:42.866483930 +0200
-@@ -70,7 +70,7 @@
-     def _get_tag(self, raw, t):
-         if not raw.tags: return []
--        if t not in self.tag_mapping.itervalues():
-+        if t not in iter(self.tag_mapping.values()):
-             t = "TXXX:" + t
-         field = raw.tags.getall(t)
-         if len(field) <= 0:
-@@ -78,27 +78,27 @@
-         ret = []
-         if t == 'TDRC' or t == 'TDOR': # values are ID3TimeStamps
-             for value in field:
--                ret.extend([unicode(x) for x in value.text])
-+                ret.extend([str(x) for x in value.text])
-         elif t == 'USLT': # Lyrics are stored in plain old strings
-             for value in field:
--                ret.append(unicode(value.text))
-+                ret.append(str(value.text))
-         elif t == 'WOAR': # URLS are stored in url not text
-             for value in field:
--                ret.extend([unicode(x.replace('\n','').replace('\r','')) \
-+                ret.extend([str(x.replace('\n','').replace('\r','')) \
-                         for x in value.url])
-         elif t == 'APIC':
-             ret = [x.data for x in field]
-         else:
-             for value in field:
-                 try:
--                    ret.extend([unicode(x.replace('\n','').replace('\r','')) \
-+                    ret.extend([str(x.replace('\n','').replace('\r','')) \
-                         for x in value.text])
-                 except:
-                     pass
-         return ret
-     def _set_tag(self, raw, tag, data):
--        if tag not in self.tag_mapping.itervalues():
-+        if tag not in iter(self.tag_mapping.values()):
-             tag = "TXXX:" + tag
-         if raw.tags is not None:
-             raw.tags.delall(tag)
-@@ -107,7 +107,7 @@
-             raw.tags.add(frame)
-     def _del_tag(self, raw, tag):
--        if tag not in self.tag_mapping.itervalues():
-+        if tag not in iter(self.tag_mapping.values()):
-             tag = "TXXX:" + tag
-         if raw.tags is not None:
-             raw.tags.delall(tag)
-diff -Naur client175_0.7-original/metadata/__init__.py client175_0.7/metadata/__init__.py
---- client175_0.7-original/metadata/__init__.py        2010-05-01 21:15:15.000000000 +0200
-+++ client175_0.7/metadata/__init__.py 2021-08-03 14:50:50.391263894 +0200
-@@ -30,7 +30,7 @@
- import os
- from metadata._base import BaseFormat, NotWritable, NotReadable
--import urlparse
-+import urllib.parse
- from metadata import (ape, asf, flac, mod, mp3, mp4, mpc, ogg, sid, speex,
-         tta, wav, wv)
-diff -Naur client175_0.7-original/metadata/mp4.py client175_0.7/metadata/mp4.py
---- client175_0.7-original/metadata/mp4.py     2010-05-01 21:15:15.000000000 +0200
-+++ client175_0.7/metadata/mp4.py      2021-08-03 14:52:08.706100110 +0200
-@@ -45,7 +45,7 @@
-     writable = True
-     def _get_tag(self, f, name):
--        if not f.has_key(name):
-+        if name not in f:
-             return []
-         elif name in ['trkn', 'disk']:
-             ret = []
-@@ -60,7 +60,7 @@
-             try:
-                 f[name] = []
-                 for val in value:
--                    tmp = map(int, val.split('/'))
-+                    tmp = list(map(int, val.split('/')))
-                     f[name].append(tuple(tmp))
-             except TypeError:
-                 pass
-diff -Naur client175_0.7-original/mpd.py client175_0.7/mpd.py
---- client175_0.7-original/mpd.py      2010-08-27 00:38:39.000000000 +0200
-+++ client175_0.7/mpd.py       2021-08-03 14:40:15.808834774 +0200
-@@ -64,7 +64,7 @@
-     
-         
- def extend_database(item):
--    keys = item.keys()
-+    keys = list(item.keys())
-     if 'file' in keys:
-         item = extend_file(item)
-     elif 'directory' in keys:
-@@ -192,8 +192,8 @@
-         self.lock.acquire()
-         try:
-             return self._execute(command, args, retval)
--        except (ConnectionError, socket.error), e:
--            print "%s\n    reconnecting..." % e
-+        except (ConnectionError, socket.error) as e:
-+            print("%s\n    reconnecting..." % e)
-             try:
-                 self.disconnect()
-             except:
-@@ -343,13 +343,13 @@
-         raise StopIteration
-     def _fetch_songs(self):
--        return map(extend_file, self._read_songs())
-+        return list(map(extend_file, self._read_songs()))
-     def _fetch_playlists(self):
-         return self._fetch_objects(["playlist"])
-     def _fetch_database(self):
--        return map(extend_database, self._read_objects(["file", "directory", "playlist"]))
-+        return list(map(extend_database, self._read_objects(["file", "directory", "playlist"])))
-     def _fetch_outputs(self):
-         return self._fetch_objects(["outputid"])
-@@ -397,7 +397,7 @@
-             try:
-                 sock = socket.socket(af, socktype, proto)
-                 sock.connect(sa)
--            except socket.error, msg:
-+            except socket.error as msg:
-                 if sock:
-                     sock.close()
-                 sock = None
-@@ -425,8 +425,8 @@
-                 self.password(_password)
-             self._TAGS = self.tagtypes()
-             self._TAGS.extend(['Pos', 'Time', 'Id'])
--            self._TAGS_LOWER = map(str.lower, self._TAGS)
--            self._TAGMAP = dict(zip(self._TAGS, self._TAGS_LOWER))
-+            self._TAGS_LOWER = list(map(str.lower, self._TAGS))
-+            self._TAGMAP = dict(list(zip(self._TAGS, self._TAGS_LOWER)))
-         except:
-             self.disconnect()
-             raise
-diff -Naur client175_0.7-original/mutagen/apev2.py client175_0.7/mutagen/apev2.py
---- client175_0.7-original/mutagen/apev2.py    2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/apev2.py     2021-08-03 14:53:33.292843836 +0200
-@@ -33,7 +33,7 @@
- __all__ = ["APEv2", "APEv2File", "Open", "delete"]
- import struct
--from cStringIO import StringIO
-+from io import StringIO
- def is_valid_apev2_key(key):
-     return (2 <= len(key) <= 255 and min(key) >= ' ' and max(key) <= '~' and
-@@ -44,11 +44,11 @@
- #  1: Item contains binary information
- #  2: Item is a locator of external stored information [e.g. URL]
- #  3: reserved"
--TEXT, BINARY, EXTERNAL = range(3)
-+TEXT, BINARY, EXTERNAL = list(range(3))
--HAS_HEADER = 1L << 31
--HAS_NO_FOOTER = 1L << 30
--IS_HEADER  = 1L << 29
-+HAS_HEADER = 1 << 31
-+HAS_NO_FOOTER = 1 << 30
-+IS_HEADER  = 1 << 29
- class error(IOError): pass
- class APENoHeaderError(error, ValueError): pass
-@@ -199,7 +199,7 @@
-     def pprint(self):
-         """Return tag key=value pairs in a human-readable format."""
--        items = self.items()
-+        items = list(self.items())
-         items.sort()
-         return "\n".join(["%s=%s" % (k, v.pprint()) for k, v in items])
-@@ -271,7 +271,7 @@
-         if not isinstance(value, _APEValue):
-             # let's guess at the content if we're not already a value...
--            if isinstance(value, unicode):
-+            if isinstance(value, str):
-                 # unicode? we've got to be text.
-                 value = APEValue(utf8(value), TEXT)
-             elif isinstance(value, list):
-@@ -289,7 +289,7 @@
-         self.__dict[key.lower()] = value
-     def keys(self):
--        return [self.__casemap.get(key, key) for key in self.__dict.keys()]
-+        return [self.__casemap.get(key, key) for key in list(self.__dict.keys())]
-     def save(self, filename=None):
-         """Save changes to a file.
-@@ -318,7 +318,7 @@
-         # "APE tags items should be sorted ascending by size... This is
-         # not a MUST, but STRONGLY recommended. Actually the items should
-         # be sorted by importance/byte, but this is not feasible."
--        tags = [v._internal(k) for k, v in self.items()]
-+        tags = [v._internal(k) for k, v in list(self.items())]
-         tags.sort(lambda a, b: cmp(len(a), len(b)))
-         num_tags = len(tags)
-         tags = "".join(tags)
-@@ -401,20 +401,20 @@
-     strings (with a null seperating the values), or arrays of strings."""
-     def __unicode__(self):
--        return unicode(str(self), "utf-8")
-+        return str(str(self), "utf-8")
-     def __iter__(self):
-         """Iterate over the strings of the value (not the characters)"""
--        return iter(unicode(self).split("\0"))
-+        return iter(str(self).split("\0"))
-     def __getitem__(self, index):
--        return unicode(self).split("\0")[index]
-+        return str(self).split("\0")[index]
-     def __len__(self):
-         return self.value.count("\0") + 1
-     def __cmp__(self, other):
--        return cmp(unicode(self), other)
-+        return cmp(str(self), other)
-     def __setitem__(self, index, value):
-         values = list(self)
-@@ -434,7 +434,7 @@
-     External values are usually URI or IRI strings.
-     """
--    def pprint(self): return "[External] %s" % unicode(self)
-+    def pprint(self): return "[External] %s" % str(self)
- class APEv2File(FileType):
-     class _Info(object):
-diff -Naur client175_0.7-original/mutagen/asf.py client175_0.7/mutagen/asf.py
---- client175_0.7-original/mutagen/asf.py      2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/asf.py       2021-08-03 14:53:40.512736644 +0200
-@@ -49,14 +49,14 @@
-         """
-         values = [value for (k, value) in self if k == key]
--        if not values: raise KeyError, key
-+        if not values: raise KeyError(key)
-         else: return values
-     def __delitem__(self, key):
-         """Delete all values associated with the key."""
--        to_delete = filter(lambda x: x[0] == key, self)
--        if not to_delete: raise KeyError, key
--        else: map(self.remove, to_delete)
-+        to_delete = [x for x in self if x[0] == key]
-+        if not to_delete: raise KeyError(key)
-+        else: list(map(self.remove, to_delete))
-     def __contains__(self, key):
-         """Return true if the key has any values."""
-@@ -78,15 +78,15 @@
-         except KeyError: pass
-         for value in values:
-             if key in _standard_attribute_names:
--                value = unicode(value)
-+                value = str(value)
-             elif not isinstance(value, ASFBaseAttribute):
--                if isinstance(value, basestring):
-+                if isinstance(value, str):
-                     value = ASFUnicodeAttribute(value)
-                 elif isinstance(value, bool):
-                     value = ASFBoolAttribute(value)
-                 elif isinstance(value, int):
-                     value = ASFDWordAttribute(value)
--                elif isinstance(value, long):
-+                elif isinstance(value, int):
-                     value = ASFQWordAttribute(value)
-             self.append((key, value))
-@@ -162,7 +162,7 @@
-         return self.value
-     def __cmp__(self, other):
--        return cmp(unicode(self), other)
-+        return cmp(str(self), other)
- class ASFByteArrayAttribute(ASFBaseAttribute):
-@@ -294,7 +294,7 @@
- GUID = ASFGUIDAttribute.TYPE
- def ASFValue(value, kind, **kwargs):
--    for t, c in _attribute_types.items():
-+    for t, c in list(_attribute_types.items()):
-         if kind == t:
-             return c(value=value, **kwargs)
-     raise ValueError("Unknown value type")
-@@ -362,12 +362,12 @@
-                 texts.append(None)
-             pos = end
-         title, author, copyright, desc, rating = texts
--        for key, value in dict(
-+        for key, value in list(dict(
-             Title=title,
-             Author=author,
-             Copyright=copyright,
-             Description=desc,
--            Rating=rating).items():
-+            Rating=rating).items()):
-             if value is not None:
-                 asf.tags[key] = value
-@@ -378,8 +378,8 @@
-                 return value[0].encode("utf-16-le") + "\x00\x00"
-             else:
-                 return ""
--        texts = map(render_text, _standard_attribute_names)
--        data = struct.pack("<HHHHH", *map(len, texts)) + "".join(texts)
-+        texts = list(map(render_text, _standard_attribute_names))
-+        data = struct.pack("<HHHHH", *list(map(len, texts))) + "".join(texts)
-         return self.GUID + struct.pack("<Q", 24 + len(data)) + data
-@@ -405,7 +405,7 @@
-             asf.tags.append((name, attr))
-     def render(self, asf):
--        attrs = asf.to_extended_content_description.items()
-+        attrs = list(asf.to_extended_content_description.items())
-         data = "".join([attr.render(name) for (name, attr) in attrs])
-         data = struct.pack("<QH", 26 + len(data), len(attrs)) + data
-         return self.GUID + data
-@@ -485,7 +485,7 @@
-             asf.tags.append((name, attr))
-     def render(self, asf):
--        attrs = asf.to_metadata.items()
-+        attrs = list(asf.to_metadata.items())
-         data = "".join([attr.render_m(name) for (name, attr) in attrs])
-         return (self.GUID + struct.pack("<QH", 26 + len(data), len(attrs)) +
-                 data)
-@@ -614,7 +614,7 @@
-     def __read_file(self, fileobj):
-         header = fileobj.read(30)
-         if len(header) != 30 or header[:16] != HeaderObject.GUID:
--            raise ASFHeaderError, "Not an ASF file."
-+            raise ASFHeaderError("Not an ASF file.")
-         self.extended_content_description_obj = None
-         self.content_description_obj = None
-diff -Naur client175_0.7-original/mutagen/_constants.py client175_0.7/mutagen/_constants.py
---- client175_0.7-original/mutagen/_constants.py       2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/_constants.py        2021-08-03 14:54:27.292044501 +0200
-@@ -1,153 +1,153 @@
- """Constants used by Mutagen."""
- GENRES = [
--    u"Blues",
--    u"Classic Rock",
--    u"Country",
--    u"Dance",
--    u"Disco",
--    u"Funk",
--    u"Grunge",
--    u"Hip-Hop",
--    u"Jazz",
--    u"Metal",
--    u"New Age",
--    u"Oldies",
--    u"Other",
--    u"Pop",
--    u"R&B",
--    u"Rap",
--    u"Reggae",
--    u"Rock",
--    u"Techno",
--    u"Industrial",
--    u"Alternative",
--    u"Ska",
--    u"Death Metal",
--    u"Pranks",
--    u"Soundtrack",
--    u"Euro-Techno",
--    u"Ambient",
--    u"Trip-Hop",
--    u"Vocal",
--    u"Jazz+Funk",
--    u"Fusion",
--    u"Trance",
--    u"Classical",
--    u"Instrumental",
--    u"Acid",
--    u"House",
--    u"Game",
--    u"Sound Clip",
--    u"Gospel",
--    u"Noise",
--    u"Alt. Rock",
--    u"Bass",
--    u"Soul",
--    u"Punk",
--    u"Space",
--    u"Meditative",
--    u"Instrumental Pop",
--    u"Instrumental Rock",
--    u"Ethnic",
--    u"Gothic",
--    u"Darkwave",
--    u"Techno-Industrial",
--    u"Electronic",
--    u"Pop-Folk",
--    u"Eurodance",
--    u"Dream",
--    u"Southern Rock",
--    u"Comedy",
--    u"Cult",
--    u"Gangsta",
--    u"Top 40",
--    u"Christian Rap",
--    u"Pop/Funk",
--    u"Jungle",
--    u"Native American",
--    u"Cabaret",
--    u"New Wave",
--    u"Psychadelic",
--    u"Rave",
--    u"Showtunes",
--    u"Trailer",
--    u"Lo-Fi",
--    u"Tribal",
--    u"Acid Punk",
--    u"Acid Jazz",
--    u"Polka",
--    u"Retro",
--    u"Musical",
--    u"Rock & Roll",
--    u"Hard Rock",
--    u"Folk",
--    u"Folk/Rock",
--    u"National Folk",
--    u"Swing",
--    u"Fusion",
--    u"Bebob",
--    u"Latin",
--    u"Revival",
--    u"Celtic",
--    u"Bluegrass",
--    u"Avantgarde",
--    u"Gothic Rock",
--    u"Progressive Rock",
--    u"Psychadelic Rock",
--    u"Symphonic Rock",
--    u"Slow Rock",
--    u"Big Band",
--    u"Chorus",
--    u"Easy Listening",
--    u"Acoustic",
--    u"Humour",
--    u"Speech",
--    u"Chanson",
--    u"Opera",
--    u"Chamber Music",
--    u"Sonata",
--    u"Symphony",
--    u"Booty Bass",
--    u"Primus",
--    u"Porn Groove",
--    u"Satire",
--    u"Slow Jam",
--    u"Club",
--    u"Tango",
--    u"Samba",
--    u"Folklore",
--    u"Ballad",
--    u"Power Ballad",
--    u"Rhythmic Soul",
--    u"Freestyle",
--    u"Duet",
--    u"Punk Rock",
--    u"Drum Solo",
--    u"A Capella",
--    u"Euro-House",
--    u"Dance Hall",
--    u"Goa",
--    u"Drum & Bass",
--    u"Club-House",
--    u"Hardcore",
--    u"Terror",
--    u"Indie",
--    u"BritPop",
--    u"Negerpunk",
--    u"Polsk Punk",
--    u"Beat",
--    u"Christian Gangsta Rap",
--    u"Heavy Metal",
--    u"Black Metal",
--    u"Crossover",
--    u"Contemporary Christian",
--    u"Christian Rock",
--    u"Merengue",
--    u"Salsa",
--    u"Thrash Metal",
--    u"Anime",
--    u"Jpop",
--    u"Synthpop"
-+    "Blues",
-+    "Classic Rock",
-+    "Country",
-+    "Dance",
-+    "Disco",
-+    "Funk",
-+    "Grunge",
-+    "Hip-Hop",
-+    "Jazz",
-+    "Metal",
-+    "New Age",
-+    "Oldies",
-+    "Other",
-+    "Pop",
-+    "R&B",
-+    "Rap",
-+    "Reggae",
-+    "Rock",
-+    "Techno",
-+    "Industrial",
-+    "Alternative",
-+    "Ska",
-+    "Death Metal",
-+    "Pranks",
-+    "Soundtrack",
-+    "Euro-Techno",
-+    "Ambient",
-+    "Trip-Hop",
-+    "Vocal",
-+    "Jazz+Funk",
-+    "Fusion",
-+    "Trance",
-+    "Classical",
-+    "Instrumental",
-+    "Acid",
-+    "House",
-+    "Game",
-+    "Sound Clip",
-+    "Gospel",
-+    "Noise",
-+    "Alt. Rock",
-+    "Bass",
-+    "Soul",
-+    "Punk",
-+    "Space",
-+    "Meditative",
-+    "Instrumental Pop",
-+    "Instrumental Rock",
-+    "Ethnic",
-+    "Gothic",
-+    "Darkwave",
-+    "Techno-Industrial",
-+    "Electronic",
-+    "Pop-Folk",
-+    "Eurodance",
-+    "Dream",
-+    "Southern Rock",
-+    "Comedy",
-+    "Cult",
-+    "Gangsta",
-+    "Top 40",
-+    "Christian Rap",
-+    "Pop/Funk",
-+    "Jungle",
-+    "Native American",
-+    "Cabaret",
-+    "New Wave",
-+    "Psychadelic",
-+    "Rave",
-+    "Showtunes",
-+    "Trailer",
-+    "Lo-Fi",
-+    "Tribal",
-+    "Acid Punk",
-+    "Acid Jazz",
-+    "Polka",
-+    "Retro",
-+    "Musical",
-+    "Rock & Roll",
-+    "Hard Rock",
-+    "Folk",
-+    "Folk/Rock",
-+    "National Folk",
-+    "Swing",
-+    "Fusion",
-+    "Bebob",
-+    "Latin",
-+    "Revival",
-+    "Celtic",
-+    "Bluegrass",
-+    "Avantgarde",
-+    "Gothic Rock",
-+    "Progressive Rock",
-+    "Psychadelic Rock",
-+    "Symphonic Rock",
-+    "Slow Rock",
-+    "Big Band",
-+    "Chorus",
-+    "Easy Listening",
-+    "Acoustic",
-+    "Humour",
-+    "Speech",
-+    "Chanson",
-+    "Opera",
-+    "Chamber Music",
-+    "Sonata",
-+    "Symphony",
-+    "Booty Bass",
-+    "Primus",
-+    "Porn Groove",
-+    "Satire",
-+    "Slow Jam",
-+    "Club",
-+    "Tango",
-+    "Samba",
-+    "Folklore",
-+    "Ballad",
-+    "Power Ballad",
-+    "Rhythmic Soul",
-+    "Freestyle",
-+    "Duet",
-+    "Punk Rock",
-+    "Drum Solo",
-+    "A Capella",
-+    "Euro-House",
-+    "Dance Hall",
-+    "Goa",
-+    "Drum & Bass",
-+    "Club-House",
-+    "Hardcore",
-+    "Terror",
-+    "Indie",
-+    "BritPop",
-+    "Negerpunk",
-+    "Polsk Punk",
-+    "Beat",
-+    "Christian Gangsta Rap",
-+    "Heavy Metal",
-+    "Black Metal",
-+    "Crossover",
-+    "Contemporary Christian",
-+    "Christian Rock",
-+    "Merengue",
-+    "Salsa",
-+    "Thrash Metal",
-+    "Anime",
-+    "Jpop",
-+    "Synthpop"
-     ]
- """The ID3v1 genre list."""
-diff -Naur client175_0.7-original/mutagen/easyid3.py client175_0.7/mutagen/easyid3.py
---- client175_0.7-original/mutagen/easyid3.py  2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/easyid3.py   2021-08-03 14:54:43.607811287 +0200
-@@ -146,7 +146,7 @@
-                 enc = 0
-                 # Store 8859-1 if we can, per MusicBrainz spec.
-                 for v in value:
--                    if max(v) > u'\x7f':
-+                    if max(v) > '\x7f':
-                         enc = 3
-                 id3.add(mutagen.id3.TXXX(encoding=enc, text=value, desc=desc))
-             else:
-@@ -182,7 +182,7 @@
-     def __setitem__(self, key, value):
-         key = key.lower()
--        if isinstance(value, basestring):
-+        if isinstance(value, str):
-             value = [value]
-         func = dict_match(self.Set, key, self.SetFallback)
-         if func is not None:
-@@ -200,7 +200,7 @@
-     def keys(self):
-         keys = []
--        for key in self.Get.keys():
-+        for key in list(self.Get.keys()):
-             if key in self.List:
-                 keys.extend(self.List[key](self.__id3, key))
-             elif key in self:
-@@ -332,7 +332,7 @@
-     except KeyError:
-         raise EasyID3KeyError(key)
-     else:
--        return [u"%+f dB" % frame.gain]
-+        return ["%+f dB" % frame.gain]
- def gain_set(id3, key, value):
-     if len(value) != 1:
-@@ -362,7 +362,7 @@
-     except KeyError:
-         raise EasyID3KeyError(key)
-     else:
--        return [u"%f" % frame.peak]
-+        return ["%f" % frame.peak]
- def peak_set(id3, key, value):
-     if len(value) != 1:
-@@ -423,7 +423,7 @@
-     "TSOT": "titlesort",
-     "TSRC": "isrc",
-     "TSST": "discsubtitle",
--    }.iteritems():
-+    }.items():
-     EasyID3.RegisterTextKey(key, frameid)
- EasyID3.RegisterKey("genre", genre_get, genre_set, genre_delete)
-@@ -444,20 +444,20 @@
- # http://bugs.musicbrainz.org/ticket/1383
- # http://musicbrainz.org/doc/MusicBrainzTag
- for desc, key in {
--    u"MusicBrainz Artist Id": "musicbrainz_artistid",
--    u"MusicBrainz Album Id": "musicbrainz_albumid",
--    u"MusicBrainz Album Artist Id": "musicbrainz_albumartistid",
--    u"MusicBrainz TRM Id": "musicbrainz_trmid",
--    u"MusicIP PUID": "musicip_puid",
--    u"MusicMagic Fingerprint": "musicip_fingerprint",
--    u"MusicBrainz Album Status": "musicbrainz_albumstatus",
--    u"MusicBrainz Album Type": "musicbrainz_albumtype",
--    u"MusicBrainz Album Release Country": "releasecountry",
--    u"MusicBrainz Disc Id": "musicbrainz_discid",
--    u"ASIN": "asin",
--    u"ALBUMARTISTSORT": "albumartistsort",
--    u"BARCODE": "barcode",
--    }.iteritems():
-+    "MusicBrainz Artist Id": "musicbrainz_artistid",
-+    "MusicBrainz Album Id": "musicbrainz_albumid",
-+    "MusicBrainz Album Artist Id": "musicbrainz_albumartistid",
-+    "MusicBrainz TRM Id": "musicbrainz_trmid",
-+    "MusicIP PUID": "musicip_puid",
-+    "MusicMagic Fingerprint": "musicip_fingerprint",
-+    "MusicBrainz Album Status": "musicbrainz_albumstatus",
-+    "MusicBrainz Album Type": "musicbrainz_albumtype",
-+    "MusicBrainz Album Release Country": "releasecountry",
-+    "MusicBrainz Disc Id": "musicbrainz_discid",
-+    "ASIN": "asin",
-+    "ALBUMARTISTSORT": "albumartistsort",
-+    "BARCODE": "barcode",
-+    }.items():
-     EasyID3.RegisterTXXXKey(key, desc)
- class EasyID3FileType(ID3FileType):
-diff -Naur client175_0.7-original/mutagen/easymp4.py client175_0.7/mutagen/easymp4.py
---- client175_0.7-original/mutagen/easymp4.py  2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/easymp4.py   2021-08-03 14:54:50.039718986 +0200
-@@ -86,11 +86,11 @@
-         """
-         def getter(tags, key):
--            return map(unicode, tags[atomid])
-+            return list(map(str, tags[atomid]))
-         def setter(tags, key, value):
-             clamp = lambda x: int(min(max(min_value, x), max_value))
--            tags[atomid] = map(clamp, map(int, value))
-+            tags[atomid] = list(map(clamp, list(map(int, value))))
-         def deleter(tags, key):
-             del(tags[atomid])
-@@ -103,9 +103,9 @@
-             ret = []
-             for (track, total) in tags[atomid]:
-                 if total:
--                    ret.append(u"%d/%d" % (track, total))
-+                    ret.append("%d/%d" % (track, total))
-                 else:
--                    ret.append(unicode(track))
-+                    ret.append(str(track))
-             return ret
-         def setter(tags, key, value):
-@@ -143,7 +143,7 @@
-             return [s.decode("utf-8", "replace") for s in tags[atomid]]
-         def setter(tags, key, value):
--            tags[atomid] = map(utf8, value)
-+            tags[atomid] = list(map(utf8, value))
-         def deleter(tags, key):
-             del(tags[atomid])
-@@ -161,7 +161,7 @@
-     def __setitem__(self, key, value):
-         key = key.lower()
--        if isinstance(value, basestring):
-+        if isinstance(value, str):
-             value = [value]
-         func = dict_match(self.Set, key)
-         if func is not None:
-@@ -179,7 +179,7 @@
-     def keys(self):
-         keys = []
--        for key in self.Get.keys():
-+        for key in list(self.Get.keys()):
-             if key in self.List:
-                 keys.extend(self.List[key](self.__mp4, key))
-             elif key in self:
-@@ -195,7 +195,7 @@
-                 strings.append("%s=%s" % (key, value))
-         return "\n".join(strings)
--for atomid, key in {
-+for atomid, key in list({
-     '\xa9nam': 'title',
-     '\xa9alb': 'album',
-     '\xa9ART': 'artist',
-@@ -211,10 +211,10 @@
-     'soar': 'artistsort',
-     'sonm': 'titlesort',
-     'soco': 'composersort',
--    }.items():
-+    }.items()):
-     EasyMP4Tags.RegisterTextKey(key, atomid)
--for name, key in {
-+for name, key in list({
-     'MusicBrainz Artist Id': 'musicbrainz_artistid',
-     'MusicBrainz Track Id': 'musicbrainz_trackid',
-     'MusicBrainz Album Id': 'musicbrainz_albumid',
-@@ -223,18 +223,18 @@
-     'MusicBrainz Album Status': 'musicbrainz_albumstatus',
-     'MusicBrainz Album Type': 'musicbrainz_albumtype',
-     'MusicBrainz Release Country': 'releasecountry',
--    }.items():
-+    }.items()):
-     EasyMP4Tags.RegisterFreeformKey(key, name)
--for name, key in {
-+for name, key in list({
-     "tmpo": "bpm",
--    }.items():
-+    }.items()):
-     EasyMP4Tags.RegisterIntKey(key, name)
--for name, key in {
-+for name, key in list({
-     "trkn": "tracknumber",
-     "disk": "discnumber",
--    }.items():
-+    }.items()):
-     EasyMP4Tags.RegisterIntPairKey(key, name)
- class EasyMP4(MP4):
-diff -Naur client175_0.7-original/mutagen/flac.py client175_0.7/mutagen/flac.py
---- client175_0.7-original/mutagen/flac.py     2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/flac.py      2021-08-03 14:54:56.799621843 +0200
-@@ -22,11 +22,12 @@
- __all__ = ["FLAC", "Open", "delete"]
- import struct
--from cStringIO import StringIO
--from _vorbis import VCommentDict
-+from io import StringIO
-+from ._vorbis import VCommentDict
- from mutagen import FileType
- from mutagen._util import insert_bytes
- from mutagen.id3 import BitPaddedInt
-+from functools import reduce
- class error(IOError): pass
- class FLACNoHeaderError(error): pass
-@@ -35,7 +36,7 @@
- def to_int_be(string):
-     """Convert an arbitrarily-long string to a long using big-endian
-     byte order."""
--    return reduce(lambda a, b: (a << 8) + ord(b), string, 0L)
-+    return reduce(lambda a, b: (a << 8) + ord(b), string, 0)
- class MetadataBlock(object):
-     """A generic block of FLAC metadata.
-@@ -79,8 +80,8 @@
-         The overall size of the rendered blocks does not change, so
-         this adds several bytes of padding for each merged block."""
--        paddings = filter(lambda x: isinstance(x, Padding), blocks)
--        map(blocks.remove, paddings)
-+        paddings = [x for x in blocks if isinstance(x, Padding)]
-+        list(map(blocks.remove, paddings))
-         padding = Padding()
-         # total padding size is the sum of padding sizes plus 4 bytes
-         # per removed header.
-@@ -137,7 +138,7 @@
-         bps_tail = bps_total >> 36
-         bps_head = (sample_channels_bps & 1) << 4
-         self.bits_per_sample = int(bps_head + bps_tail + 1)
--        self.total_samples = bps_total & 0xFFFFFFFFFL
-+        self.total_samples = bps_total & 0xFFFFFFFFF
-         self.length = self.total_samples / float(self.sample_rate)
-         self.md5_signature = to_int_be(data.read(16))
-@@ -161,12 +162,12 @@
-         byte += (self.total_samples >> 32) & 0xF
-         f.write(chr(byte))
-         # last 32 of sample count
--        f.write(struct.pack(">I", self.total_samples & 0xFFFFFFFFL))
-+        f.write(struct.pack(">I", self.total_samples & 0xFFFFFFFF))
-         # MD5 signature
-         sig = self.md5_signature
-         f.write(struct.pack(
--            ">4I", (sig >> 96) & 0xFFFFFFFFL, (sig >> 64) & 0xFFFFFFFFL,
--            (sig >> 32) & 0xFFFFFFFFL, sig & 0xFFFFFFFFL))
-+            ">4I", (sig >> 96) & 0xFFFFFFFF, (sig >> 64) & 0xFFFFFFFF,
-+            (sig >> 32) & 0xFFFFFFFF, sig & 0xFFFFFFFF))
-         return f.getvalue()
-     def pprint(self):
-@@ -425,8 +426,8 @@
-     def __init__(self, data=None):
-         self.type = 0
--        self.mime = u''
--        self.desc = u''
-+        self.mime = ''
-+        self.desc = ''
-         self.width = 0
-         self.height = 0
-         self.depth = 0
-@@ -601,11 +602,10 @@
-     def clear_pictures(self):
-         """Delete all pictures from the file."""
--        self.metadata_blocks = filter(lambda b: b.code != Picture.code,
--                                      self.metadata_blocks)
-+        self.metadata_blocks = [b for b in self.metadata_blocks if b.code != Picture.code]
-     def __get_pictures(self):
--        return filter(lambda b: b.code == Picture.code, self.metadata_blocks)
-+        return [b for b in self.metadata_blocks if b.code == Picture.code]
-     pictures = property(__get_pictures, doc="List of embedded pictures")
-     def save(self, filename=None, deleteid3=False):
-diff -Naur client175_0.7-original/mutagen/id3.py client175_0.7/mutagen/id3.py
---- client175_0.7-original/mutagen/id3.py      2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/id3.py       2021-08-03 14:55:05.431497226 +0200
-@@ -79,7 +79,7 @@
-                 raise ValueError('Requested bytes (%s) less than zero' % size)
-             if size > self.__filesize:
-                 raise EOFError('Requested %#x of %#x (%s)' % 
--                        (long(size), long(self.__filesize), self.filename))
-+                        (int(size), int(self.__filesize), self.filename))
-         except AttributeError: pass
-         data = self.__fileobj.read(size)
-         if len(data) != size: raise EOFError
-@@ -115,18 +115,18 @@
-                 self.size = 0
-                 raise ID3NoHeaderError("%s: too small (%d bytes)" %(
-                     filename, self.__filesize))
--            except (ID3NoHeaderError, ID3UnsupportedVersionError), err:
-+            except (ID3NoHeaderError, ID3UnsupportedVersionError) as err:
-                 self.size = 0
-                 import sys
-                 stack = sys.exc_info()[2]
-                 try: self.__fileobj.seek(-128, 2)
--                except EnvironmentError: raise err, None, stack
-+                except EnvironmentError: raise err.with_traceback(stack)
-                 else:
-                     frames = ParseID3v1(self.__fileobj.read(128))
-                     if frames is not None:
-                         self.version = (1, 1)
--                        map(self.add, frames.values())
--                    else: raise err, None, stack
-+                        list(map(self.add, list(frames.values())))
-+                    else: raise err.with_traceback(stack)
-             else:
-                 frames = self.__known_frames
-                 if frames is None:
-@@ -159,14 +159,14 @@
-         if key in self: return [self[key]]
-         else:
-             key = key + ":"
--            return [v for s,v in self.items() if s.startswith(key)]
-+            return [v for s,v in list(self.items()) if s.startswith(key)]
-     def delall(self, key):
-         """Delete all tags of a given kind; see getall."""
-         if key in self: del(self[key])
-         else:
-             key = key + ":"
--            for k in filter(lambda s: s.startswith(key), self.keys()):
-+            for k in [s for s in list(self.keys()) if s.startswith(key)]:
-                 del(self[k])
-     def setall(self, key, values):
-@@ -184,7 +184,7 @@
-         However, ID3 frames can have multiple keys:
-             POPM=user@example.org=3 128/255
-         """
--        return "\n".join(map(Frame.pprint, self.values()))
-+        return "\n".join(map(Frame.pprint, list(self.values())))
-     def loaded_frame(self, tag):
-         """Deprecated; use the add method."""
-@@ -343,9 +343,9 @@
-         # Sort frames by 'importance'
-         order = ["TIT2", "TPE1", "TRCK", "TALB", "TPOS", "TDRC", "TCON"]
--        order = dict(zip(order, range(len(order))))
-+        order = dict(list(zip(order, list(range(len(order))))))
-         last = len(order)
--        frames = self.items()
-+        frames = list(self.items())
-         frames.sort(lambda a, b: cmp(order.get(a[0][:4], last),
-                                      order.get(b[0][:4], last)))
-@@ -355,7 +355,7 @@
-         if not framedata:
-             try:
-                 self.delete(filename)
--            except EnvironmentError, err:
-+            except EnvironmentError as err:
-                 from errno import ENOENT
-                 if err.errno != ENOENT: raise
-             return
-@@ -365,7 +365,7 @@
-         if filename is None: filename = self.filename
-         try: f = open(filename, 'rb+')
--        except IOError, err:
-+        except IOError as err:
-             from errno import ENOENT
-             if err.errno != ENOENT: raise
-             f = open(filename, 'ab') # create, then reopen
-@@ -393,7 +393,7 @@
-             try:
-                 f.seek(-128, 2)
--            except IOError, err:
-+            except IOError as err:
-                 from errno import EINVAL
-                 if err.errno != EINVAL: raise
-                 f.seek(0, 2) # ensure read won't get "TAG"
-@@ -540,7 +540,7 @@
-     def __new__(cls, value, bits=7, bigendian=True):
-         "Strips 8-bits bits out of every byte"
-         mask = (1<<(bits))-1
--        if isinstance(value, (int, long)):
-+        if isinstance(value, int):
-             bytes = []
-             while value:
-                 bytes.append(value & ((1<<bits)-1))
-@@ -549,10 +549,10 @@
-             bytes = [ord(byte) & mask for byte in value]
-             if bigendian: bytes.reverse()
-         numeric_value = 0
--        for shift, byte in zip(range(0, len(bytes)*bits, bits), bytes):
-+        for shift, byte in zip(list(range(0, len(bytes)*bits, bits)), bytes):
-             numeric_value += byte << shift
--        if isinstance(numeric_value, long):
--            self = long.__new__(BitPaddedLong, numeric_value)
-+        if isinstance(numeric_value, int):
-+            self = int.__new__(BitPaddedLong, numeric_value)
-         else:
-             self = int.__new__(BitPaddedInt, numeric_value)
-         self.bits = bits
-@@ -571,7 +571,7 @@
-         # PCNT and POPM use growing integers of at least 4 bytes as counters.
-         if width == -1: width = max(4, len(bytes))
-         if len(bytes) > width:
--            raise ValueError, 'Value too wide (%d bytes)' % len(bytes)
-+            raise ValueError('Value too wide (%d bytes)' % len(bytes))
-         else: bytes.extend([0] * (width-len(bytes)))
-         if bigendian: bytes.reverse()
-         return ''.join(map(chr, bytes))
-@@ -654,7 +654,7 @@
-     def validate(self, frame, value):
-         if 0 <= value <= 3: return value
-         if value is None: return None
--        raise ValueError, 'Invalid Encoding: %r' % value
-+        raise ValueError('Invalid Encoding: %r' % value)
- class StringSpec(Spec):
-     def __init__(self, name, length):
-@@ -666,8 +666,8 @@
-         else: return (str(value) + '\x00' * s.len)[:s.len]
-     def validate(s, frame, value):
-         if value is None: return None
--        if isinstance(value, basestring) and len(value) == s.len: return value
--        raise ValueError, 'Invalid StringSpec[%d] data: %r' % (s.len, value)
-+        if isinstance(value, str) and len(value) == s.len: return value
-+        raise ValueError('Invalid StringSpec[%d] data: %r' % (s.len, value))
- class BinaryDataSpec(Spec):
-     def read(self, frame, data): return data, ''
-@@ -696,14 +696,14 @@
-                     data, ret = data[0:offset], data[offset+2:]; break
-             except ValueError: pass
--        if len(data) < len(term): return u'', ret
-+        if len(data) < len(term): return '', ret
-         return data.decode(enc), ret
-     def write(self, frame, value):
-         enc, term = self._encodings[frame.encoding]
-         return value.encode(enc) + term
--    def validate(self, frame, value): return unicode(value)
-+    def validate(self, frame, value): return str(value)
- class MultiSpec(Spec):
-     def __init__(self, name, *specs, **kw):
-@@ -735,7 +735,7 @@
-     def validate(self, frame, value):
-         if value is None: return []
--        if self.sep and isinstance(value, basestring):
-+        if self.sep and isinstance(value, str):
-             value = value.split(self.sep)
-         if isinstance(value, list):
-             if len(self.specs) == 1:
-@@ -744,7 +744,7 @@
-                 return [ 
-                     [s.validate(frame, v) for (v,s) in zip(val, self.specs)]
-                     for val in value ]
--        raise ValueError, 'Invalid MultiSpec data: %r' % value
-+        raise ValueError('Invalid MultiSpec data: %r' % value)
- class EncodedNumericTextSpec(EncodedTextSpec): pass
- class EncodedNumericPartTextSpec(EncodedTextSpec): pass
-@@ -758,7 +758,7 @@
-     def write(self, data, value):
-         return value.encode('latin1') + '\x00'
--    def validate(self, frame, value): return unicode(value)
-+    def validate(self, frame, value): return str(value)
- class ID3TimeStamp(object):
-     """A time stamp in ID3v2 format.
-@@ -782,9 +782,9 @@
-         parts = [self.year, self.month, self.day,
-                 self.hour, self.minute, self.second]
-         pieces = []
--        for i, part in enumerate(iter(iter(parts).next, None)):
-+        for i, part in enumerate(iter(iter(parts).__next__, None)):
-             pieces.append(self.__formats[i]%part + self.__seps[i])
--        return u''.join(pieces)[:-1]
-+        return ''.join(pieces)[:-1]
-     def set_text(self, text, splitre=re.compile('[-T:/.]|\s+')):
-         year, month, day, hour, minute, second = \
-@@ -812,11 +812,11 @@
-     def validate(self, frame, value):
-         try: return ID3TimeStamp(value)
--        except TypeError: raise ValueError, "Invalid ID3TimeStamp: %r" % value
-+        except TypeError: raise ValueError("Invalid ID3TimeStamp: %r" % value)
- class ChannelSpec(ByteSpec):
-     (OTHER, MASTER, FRONTRIGHT, FRONTLEFT, BACKRIGHT, BACKLEFT, FRONTCENTRE,
--     BACKCENTRE, SUBWOOFER) = range(9)
-+     BACKCENTRE, SUBWOOFER) = list(range(9))
- class VolumeAdjustmentSpec(Spec):
-     def read(self, frame, data):
-@@ -900,7 +900,7 @@
-             freq /= 2.0
-             adj /= 512.0
-             adjustments[freq] = adj
--        adjustments = adjustments.items()
-+        adjustments = list(adjustments.items())
-         adjustments.sort()
-         return adjustments, data
-@@ -1033,21 +1033,21 @@
-                 data = data[4:]
-             if tflags & Frame.FLAG24_UNSYNCH or id3.f_unsynch:
-                 try: data = unsynch.decode(data)
--                except ValueError, err:
-+                except ValueError as err:
-                     if id3.PEDANTIC:
--                        raise ID3BadUnsynchData, '%s: %r' % (err, data)
-+                        raise ID3BadUnsynchData('%s: %r' % (err, data))
-             if tflags & Frame.FLAG24_ENCRYPT:
-                 raise ID3EncryptionUnsupportedError
-             if tflags & Frame.FLAG24_COMPRESS:
-                 try: data = data.decode('zlib')
--                except zlibError, err:
-+                except zlibError as err:
-                     # the initial mutagen that went out with QL 0.12 did not
-                     # write the 4 bytes of uncompressed size. Compensate.
-                     data = datalen_bytes + data
-                     try: data = data.decode('zlib')
--                    except zlibError, err:
-+                    except zlibError as err:
-                         if id3.PEDANTIC:
--                            raise ID3BadCompressedData, '%s: %r' % (err, data)
-+                            raise ID3BadCompressedData('%s: %r' % (err, data))
-         elif (2,3,0) <= id3.version:
-             if tflags & Frame.FLAG23_COMPRESS:
-@@ -1057,9 +1057,9 @@
-                 raise ID3EncryptionUnsupportedError
-             if tflags & Frame.FLAG23_COMPRESS:
-                 try: data = data.decode('zlib')
--                except zlibError, err:
-+                except zlibError as err:
-                     if id3.PEDANTIC:
--                        raise ID3BadCompressedData, '%s: %r' % (err, data)
-+                        raise ID3BadCompressedData('%s: %r' % (err, data))
-         frame = cls()
-         frame._rawdata = data
-@@ -1138,12 +1138,12 @@
-     """
-     _framespec = [ EncodingSpec('encoding'),
--        MultiSpec('text', EncodedTextSpec('text'), sep=u'\u0000') ]
-+        MultiSpec('text', EncodedTextSpec('text'), sep='\u0000') ]
-     def __str__(self): return self.__unicode__().encode('utf-8')
--    def __unicode__(self): return u'\u0000'.join(self.text)
-+    def __unicode__(self): return '\u0000'.join(self.text)
-     def __eq__(self, other):
-         if isinstance(other, str): return str(self) == other
--        elif isinstance(other, unicode): return unicode(self) == other
-+        elif isinstance(other, str): return str(self) == other
-         return self.text == other
-     def __getitem__(self, item): return self.text[item]
-     def __iter__(self): return iter(self.text)
-@@ -1160,7 +1160,7 @@
-     """
-     _framespec = [ EncodingSpec('encoding'),
--        MultiSpec('text', EncodedNumericTextSpec('text'), sep=u'\u0000') ]
-+        MultiSpec('text', EncodedNumericTextSpec('text'), sep='\u0000') ]
-     def __pos__(self):
-         """Return the numerical value of the string."""
-@@ -1176,7 +1176,7 @@
-     """
-     _framespec = [ EncodingSpec('encoding'),
--        MultiSpec('text', EncodedNumericPartTextSpec('text'), sep=u'\u0000') ]
-+        MultiSpec('text', EncodedNumericPartTextSpec('text'), sep='\u0000') ]
-     def __pos__(self):
-         return int(self.text[0].split("/")[0])
-@@ -1188,7 +1188,7 @@
-     """
-     _framespec = [ EncodingSpec('encoding'),
--        MultiSpec('text', TimeStampSpec('stamp'), sep=u',') ]
-+        MultiSpec('text', TimeStampSpec('stamp'), sep=',') ]
-     def __str__(self): return self.__unicode__().encode('utf-8')
-     def __unicode__(self): return ','.join([stamp.text for stamp in self.text])
-     def _pprint(self):
-@@ -1235,9 +1235,9 @@
-         for value in self.text:
-             if value.isdigit():
-                 try: genres.append(self.GENRES[int(value)])
--                except IndexError: genres.append(u"Unknown")
--            elif value == "CR": genres.append(u"Cover")
--            elif value == "RX": genres.append(u"Remix")
-+                except IndexError: genres.append("Unknown")
-+            elif value == "CR": genres.append("Cover")
-+            elif value == "RX": genres.append("Remix")
-             elif value:
-                 newgenres = []
-                 genreid, dummy, genrename = genre_re.match(value).groups()
-@@ -1245,11 +1245,11 @@
-                 if genreid:
-                     for gid in genreid[1:-1].split(")("):
-                         if gid.isdigit() and int(gid) < len(self.GENRES):
--                            gid = unicode(self.GENRES[int(gid)])
-+                            gid = str(self.GENRES[int(gid)])
-                             newgenres.append(gid)
--                        elif gid == "CR": newgenres.append(u"Cover")
--                        elif gid == "RX": newgenres.append(u"Remix")
--                        else: newgenres.append(u"Unknown")
-+                        elif gid == "CR": newgenres.append("Cover")
-+                        elif gid == "RX": newgenres.append("Remix")
-+                        else: newgenres.append("Unknown")
-                 if genrename:
-                     # "Unescaping" the first parenthesis
-@@ -1261,8 +1261,8 @@
-         return genres
-     def __set_genres(self, genres):
--        if isinstance(genres, basestring): genres = [genres]
--        self.text = map(self.__decode, genres)
-+        if isinstance(genres, str): genres = [genres]
-+        self.text = list(map(self.__decode, genres))
-     def __decode(self, value):
-         if isinstance(value, str):
-@@ -1333,7 +1333,7 @@
-     the same). Many taggers use this frame to store freeform keys.
-     """
-     _framespec = [ EncodingSpec('encoding'), EncodedTextSpec('desc'),
--        MultiSpec('text', EncodedTextSpec('text'), sep=u'\u0000') ]
-+        MultiSpec('text', EncodedTextSpec('text'), sep='\u0000') ]
-     HashKey = property(lambda s: '%s:%s' % (s.FrameID, s.desc))
-     def _pprint(self): return "%s=%s" % (self.desc, " / ".join(self.text))
-@@ -1448,7 +1448,7 @@
-     """
-     _framespec = [ EncodingSpec('encoding'), StringSpec('lang', 3),
-         EncodedTextSpec('desc'),
--        MultiSpec('text', EncodedTextSpec('text'), sep=u'\u0000') ]
-+        MultiSpec('text', EncodedTextSpec('text'), sep='\u0000') ]
-     HashKey = property(lambda s: '%s:%s:%r' % (s.FrameID, s.desc, s.lang))
-     def _pprint(self): return "%s=%r=%s" % (
-         self.desc, self.lang, " / ".join(self.text))
-@@ -1545,7 +1545,7 @@
-     def __eq__(self, other): return self.count == other
-     def __pos__(self): return self.count
--    def _pprint(self): return unicode(self.count)
-+    def _pprint(self): return str(self.count)
- class POPM(FrameOpt):
-     """Popularimeter.
-@@ -1774,7 +1774,7 @@
-                    ASPIIndexSpec("Fi") ]
-     def __eq__(self, other): return self.Fi == other
--Frames = dict([(k,v) for (k,v) in globals().items()
-+Frames = dict([(k,v) for (k,v) in list(globals().items())
-         if len(k)==4 and isinstance(v, type) and issubclass(v, Frame)])
- """All supported ID3v2 frames, keyed by frame name."""
- del(k); del(v)
-@@ -1867,7 +1867,7 @@
-     _framespec = [ StringSpec('frameid', 3), Latin1TextSpec('url') ]
-     _optionalspec = [ BinaryDataSpec('data') ]
--Frames_2_2 = dict([(k,v) for (k,v) in globals().items()
-+Frames_2_2 = dict([(k,v) for (k,v) in list(globals().items())
-         if len(k)==3 and isinstance(v, type) and issubclass(v, Frame)])
- # support open(filename) as interface
-@@ -1886,8 +1886,8 @@
-     if tag != "TAG": return None
-     def fix(string):
-         return string.split("\x00")[0].strip().decode('latin1')
--    title, artist, album, year, comment = map(
--        fix, [title, artist, album, year, comment])
-+    title, artist, album, year, comment = list(map(
-+        fix, [title, artist, album, year, comment]))
-     if title: frames["TIT2"] = TIT2(encoding=0, text=title)
-     if artist: frames["TPE1"] = TPE1(encoding=0, text=[artist])
-@@ -1907,8 +1907,8 @@
-     v1 = {}
--    for v2id, name in {"TIT2": "title", "TPE1": "artist",
--                       "TALB": "album"}.items():
-+    for v2id, name in list({"TIT2": "title", "TPE1": "artist",
-+                       "TALB": "album"}.items()):
-         if v2id in id3:
-             text = id3[v2id].text[0].encode('latin1', 'replace')[:30]
-         else:
-diff -Naur client175_0.7-original/mutagen/__init__.py client175_0.7/mutagen/__init__.py
---- client175_0.7-original/mutagen/__init__.py 2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/__init__.py  2021-08-03 14:55:18.899303101 +0200
-@@ -82,7 +82,7 @@
-         If the file has no tags at all, a KeyError is raised.
-         """
--        if self.tags is None: raise KeyError, key
-+        if self.tags is None: raise KeyError(key)
-         else: return self.tags[key]
-     def __setitem__(self, key, value):
-@@ -100,7 +100,7 @@
-         If the file has no tags at all, a KeyError is raised.
-         """
--        if self.tags is None: raise KeyError, key
-+        if self.tags is None: raise KeyError(key)
-         else: del(self.tags[key])
-     def keys(self):
-@@ -109,7 +109,7 @@
-         If the file has no tags at all, an empty list is returned.
-         """
-         if self.tags is None: return []
--        else: return self.tags.keys()
-+        else: return list(self.tags.keys())
-     def delete(self, filename=None):
-         """Remove tags from a file."""
-@@ -210,7 +210,7 @@
-                    for Kind in options]
-     finally:
-         fileobj.close()
--    results = zip(results, options)
-+    results = list(zip(results, options))
-     results.sort()
-     (score, name), Kind = results[-1]
-     if score > 0: return Kind(filename)
-diff -Naur client175_0.7-original/mutagen/m4a.py client175_0.7/mutagen/m4a.py
---- client175_0.7-original/mutagen/m4a.py      2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/m4a.py       2021-08-03 14:55:39.954998104 +0200
-@@ -25,7 +25,7 @@
- import struct
- import sys
--from cStringIO import StringIO
-+from io import StringIO
- from mutagen import FileType, Metadata
- from mutagen._constants import GENRES
-@@ -119,7 +119,7 @@
-             if child.name == remaining[0]:
-                 return child[remaining[1:]]
-         else:
--            raise KeyError, "%r not found" % remaining[0]
-+            raise KeyError("%r not found" % remaining[0])
-     def __repr__(self):
-         klass = self.__class__.__name__
-@@ -166,13 +166,13 @@
-         'names' may be a list of atoms (['moov', 'udta']) or a string
-         specifying the complete path ('moov.udta').
-         """
--        if isinstance(names, basestring):
-+        if isinstance(names, str):
-             names = names.split(".")
-         for child in self.atoms:
-             if child.name == names[0]:
-                 return child[names[1:]]
-         else:
--            raise KeyError, "%s not found" % names[0]
-+            raise KeyError("%s not found" % names[0])
-     def __repr__(self):
-         return "\n".join([repr(child) for child in self.atoms])
-@@ -202,7 +202,7 @@
-     def load(self, atoms, fileobj):
-         try: ilst = atoms["moov.udta.meta.ilst"]
--        except KeyError, key:
-+        except KeyError as key:
-             raise M4AMetadataError(key)
-         for atom in ilst.children:
-             fileobj.seek(atom.offset + 8)
-@@ -210,14 +210,16 @@
-             parse = self.__atoms.get(atom.name, (M4ATags.__parse_text,))[0]
-             parse(self, atom, data)
--    def __key_sort((key1, v1), (key2, v2)):
-+    def __key_sort(xxx_todo_changeme, xxx_todo_changeme1):
-         # iTunes always writes the tags in order of "relevance", try
-         # to copy it as closely as possible.
-+        (key1, v1) = xxx_todo_changeme
-+        (key2, v2) = xxx_todo_changeme1
-         order = ["\xa9nam", "\xa9ART", "\xa9wrt", "\xa9alb",
-                  "\xa9gen", "gnre", "trkn", "disk",
-                  "\xa9day", "cpil", "tmpo", "\xa9too",
-                  "----", "covr", "\xa9lyr"]
--        order = dict(zip(order, range(len(order))))
-+        order = dict(list(zip(order, list(range(len(order))))))
-         last = len(order)
-         # If there's no key-based way to distinguish, order by length.
-         # If there's still no way, go by string comparison on the
-@@ -229,7 +231,7 @@
-     def save(self, filename):
-         """Save the metadata to the given filename."""
-         values = []
--        items = self.items()
-+        items = list(self.items())
-         items.sort(self.__key_sort)
-         for key, value in items:
-             render = self.__atoms.get(
-@@ -411,7 +413,7 @@
-     def pprint(self):
-         values = []
--        for key, value in self.iteritems():
-+        for key, value in self.items():
-             key = key.decode('latin1')
-             try: values.append("%s=%s" % (key, value))
-             except UnicodeDecodeError:
-@@ -475,13 +477,13 @@
-         try:
-             atoms = Atoms(fileobj)
-             try: self.info = M4AInfo(atoms, fileobj)
--            except StandardError, err:
--                raise M4AStreamInfoError, err, sys.exc_info()[2]
-+            except Exception as err:
-+                raise M4AStreamInfoError(err).with_traceback(sys.exc_info()[2])
-             try: self.tags = M4ATags(atoms, fileobj)
-             except M4AMetadataError:
-                 self.tags = None
--            except StandardError, err:
--                raise M4AMetadataError, err, sys.exc_info()[2]
-+            except Exception as err:
-+                raise M4AMetadataError(err).with_traceback(sys.exc_info()[2])
-         finally:
-             fileobj.close()
-diff -Naur client175_0.7-original/mutagen/mp3.py client175_0.7/mutagen/mp3.py
---- client175_0.7-original/mutagen/mp3.py      2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/mp3.py       2021-08-03 14:55:59.774710002 +0200
-@@ -19,7 +19,7 @@
- class InvalidMPEGHeader(error, IOError): pass
- # Mode values.
--STEREO, JOINTSTEREO, DUALCHANNEL, MONO = range(4)
-+STEREO, JOINTSTEREO, DUALCHANNEL, MONO = list(range(4))
- class MPEGInfo(object):
-     """MPEG audio stream information
-@@ -46,7 +46,7 @@
-     # Map (version, layer) tuples to bitrates.
-     __BITRATE = {
--        (1, 1): range(0, 480, 32),
-+        (1, 1): list(range(0, 480, 32)),
-         (1, 2): [0, 32, 48, 56, 64, 80, 96, 112,128,160,192,224,256,320,384],
-         (1, 3): [0, 32, 40, 48, 56, 64, 80, 96, 112,128,160,192,224,256,320],
-         (2, 1): [0, 32, 48, 56, 64, 80, 96, 112,128,144,160,176,192,224,256],
-@@ -95,7 +95,7 @@
-         # and 90% through the file.
-         for i in [offset, 0.3 * size, 0.6 * size, 0.9 * size]:
-             try: self.__try(fileobj, int(i), size - offset)
--            except error, e: pass
-+            except error as e: pass
-             else: break
-         # If we can't find any two consecutive frames, try to find just
-         # one frame back at the original offset given.
-diff -Naur client175_0.7-original/mutagen/mp4.py client175_0.7/mutagen/mp4.py
---- client175_0.7-original/mutagen/mp4.py      2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/mp4.py       2021-08-03 14:56:05.542625890 +0200
-@@ -121,7 +121,7 @@
-             if child.name == remaining[0]:
-                 return child[remaining[1:]]
-         else:
--            raise KeyError, "%r not found" % remaining[0]
-+            raise KeyError("%r not found" % remaining[0])
-     def __repr__(self):
-         klass = self.__class__.__name__
-@@ -168,13 +168,13 @@
-         'names' may be a list of atoms (['moov', 'udta']) or a string
-         specifying the complete path ('moov.udta').
-         """
--        if isinstance(names, basestring):
-+        if isinstance(names, str):
-             names = names.split(".")
-         for child in self.atoms:
-             if child.name == names[0]:
-                 return child[names[1:]]
-         else:
--            raise KeyError, "%s not found" % names[0]
-+            raise KeyError("%s not found" % names[0])
-     def __repr__(self):
-         return "\n".join([repr(child) for child in self.atoms])
-@@ -242,7 +242,7 @@
-     def load(self, atoms, fileobj):
-         try: ilst = atoms["moov.udta.meta.ilst"]
--        except KeyError, key:
-+        except KeyError as key:
-             raise MP4MetadataError(key)
-         for atom in ilst.children:
-             fileobj.seek(atom.offset + 8)
-@@ -250,14 +250,16 @@
-             info = self.__atoms.get(atom.name, (type(self).__parse_text, None))
-             info[0](self, atom, data, *info[2:])
--    def __key_sort((key1, v1), (key2, v2)):
-+    def __key_sort(xxx_todo_changeme, xxx_todo_changeme1):
-         # iTunes always writes the tags in order of "relevance", try
-         # to copy it as closely as possible.
-+        (key1, v1) = xxx_todo_changeme
-+        (key2, v2) = xxx_todo_changeme1
-         order = ["\xa9nam", "\xa9ART", "\xa9wrt", "\xa9alb",
-                  "\xa9gen", "gnre", "trkn", "disk",
-                  "\xa9day", "cpil", "pgap", "pcst", "tmpo",
-                  "\xa9too", "----", "covr", "\xa9lyr"]
--        order = dict(zip(order, range(len(order))))
-+        order = dict(list(zip(order, list(range(len(order))))))
-         last = len(order)
-         # If there's no key-based way to distinguish, order by length.
-         # If there's still no way, go by string comparison on the
-@@ -269,14 +271,14 @@
-     def save(self, filename):
-         """Save the metadata to the given filename."""
-         values = []
--        items = self.items()
-+        items = list(self.items())
-         items.sort(self.__key_sort)
-         for key, value in items:
-             info = self.__atoms.get(key[:4], (None, type(self).__render_text))
-             try:
-                 values.append(info[1](self, key, value, *info[2:]))
--            except (TypeError, ValueError), s:
--                raise MP4MetadataValueError, s, sys.exc_info()[2]
-+            except (TypeError, ValueError) as s:
-+                raise MP4MetadataValueError(s).with_traceback(sys.exc_info()[2])
-         data = Atom.render("ilst", "".join(values))
-         # Find the old atoms.
-@@ -440,7 +442,7 @@
-         dummy, mean, name = key.split(":", 2)
-         mean = struct.pack(">I4sI", len(mean) + 12, "mean", 0) + mean
-         name = struct.pack(">I4sI", len(name) + 12, "name", 0) + name
--        if isinstance(value, basestring):
-+        if isinstance(value, str):
-             value = [value]
-         return Atom.render("----", mean + name + "".join([
-             struct.pack(">I4s2I", len(data) + 16, "data", 1, 0) + data
-@@ -492,7 +494,7 @@
-             raise MP4MetadataValueError(
-                 "tmpo must be a list of 16 bit integers")
--        values = map(cdata.to_ushort_be, value)
-+        values = list(map(cdata.to_ushort_be, value))
-         return self.__render_data(key, 0x15, values)
-     def __parse_bool(self, atom, data):
-@@ -531,10 +533,10 @@
-         if value:
-             self[atom.name] = value
-     def __render_text(self, key, value, flags=1):
--        if isinstance(value, basestring):
-+        if isinstance(value, str):
-             value = [value]
-         return self.__render_data(
--            key, flags, map(utf8, value))
-+            key, flags, list(map(utf8, value)))
-     def delete(self, filename):
-         self.clear()
-@@ -556,13 +558,13 @@
-     def pprint(self):
-         values = []
--        for key, value in self.iteritems():
-+        for key, value in self.items():
-             key = key.decode('latin1')
-             if key == "covr":
-                 values.append("%s=%s" % (key, ", ".join(
-                     ["[%d bytes of data]" % len(data) for data in value])))
-             elif isinstance(value, list):
--                values.append("%s=%s" % (key, " / ".join(map(unicode, value))))
-+                values.append("%s=%s" % (key, " / ".join(map(str, value))))
-             else:
-                 values.append("%s=%s" % (key, value))
-         return "\n".join(values)
-@@ -658,13 +660,13 @@
-         try:
-             atoms = Atoms(fileobj)
-             try: self.info = MP4Info(atoms, fileobj)
--            except StandardError, err:
--                raise MP4StreamInfoError, err, sys.exc_info()[2]
-+            except Exception as err:
-+                raise MP4StreamInfoError(err).with_traceback(sys.exc_info()[2])
-             try: self.tags = self.MP4Tags(atoms, fileobj)
-             except MP4MetadataError:
-                 self.tags = None
--            except StandardError, err:
--                raise MP4MetadataError, err, sys.exc_info()[2]
-+            except Exception as err:
-+                raise MP4MetadataError(err).with_traceback(sys.exc_info()[2])
-         finally:
-             fileobj.close()
-diff -Naur client175_0.7-original/mutagen/oggflac.py client175_0.7/mutagen/oggflac.py
---- client175_0.7-original/mutagen/oggflac.py  2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/oggflac.py   2021-08-03 14:56:29.786272048 +0200
-@@ -21,7 +21,7 @@
- import struct
--from cStringIO import StringIO
-+from io import StringIO
- from mutagen.flac import StreamInfo, VCFLACDict
- from mutagen.ogg import OggPage, OggFileType, error as OggError
-diff -Naur client175_0.7-original/mutagen/ogg.py client175_0.7/mutagen/ogg.py
---- client175_0.7-original/mutagen/ogg.py      2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/ogg.py       2021-08-03 14:56:22.722375298 +0200
-@@ -20,7 +20,7 @@
- import sys
- import zlib
--from cStringIO import StringIO
-+from io import StringIO
- from mutagen import FileType
- from mutagen._util import cdata, insert_bytes, delete_bytes
-@@ -57,7 +57,7 @@
-     version = 0
-     __type_flags = 0
--    position = 0L
-+    position = 0
-     serial = 0
-     sequence = 0
-     offset = None
-@@ -103,8 +103,8 @@
-             lacings.append(total)
-             self.complete = False
--        self.packets = map(fileobj.read, lacings)
--        if map(len, self.packets) != lacings:
-+        self.packets = list(map(fileobj.read, lacings))
-+        if list(map(len, self.packets)) != lacings:
-             raise error("unable to read full data")
-     def __eq__(self, other):
-@@ -301,7 +301,7 @@
-                     if page.packets[-1]:
-                         page.complete = False
-                         if len(page.packets) == 1:
--                            page.position = -1L
-+                            page.position = -1
-                     else:
-                         page.packets.pop(-1)
-                     pages.append(page)
-@@ -334,7 +334,7 @@
-         # Number the new pages starting from the first old page.
-         first = old_pages[0].sequence
--        for page, seq in zip(new_pages, range(first, first + len(new_pages))):
-+        for page, seq in zip(new_pages, list(range(first, first + len(new_pages)))):
-             page.sequence = seq
-             page.serial = old_pages[0].serial
-@@ -346,7 +346,7 @@
-         new_pages[-1].last = old_pages[-1].last
-         new_pages[-1].complete = old_pages[-1].complete
-         if not new_pages[-1].complete and len(new_pages[-1].packets) == 1:
--            new_pages[-1].position = -1L
-+            new_pages[-1].position = -1
-         new_data = "".join(map(klass.write, new_pages))
-@@ -454,10 +454,10 @@
-                     denom = self.info.fps
-                 self.info.length = samples / float(denom)
--            except error, e:
--                raise self._Error, e, sys.exc_info()[2]
-+            except error as e:
-+                raise self._Error(e).with_traceback(sys.exc_info()[2])
-             except EOFError:
--                raise self._Error, "no appropriate stream found"
-+                raise self._Error("no appropriate stream found")
-         finally:
-             fileobj.close()
-@@ -473,10 +473,10 @@
-         fileobj = file(filename, "rb+")
-         try:
-             try: self.tags._inject(fileobj)
--            except error, e:
--                raise self._Error, e, sys.exc_info()[2]
-+            except error as e:
-+                raise self._Error(e).with_traceback(sys.exc_info()[2])
-             except EOFError:
--                raise self._Error, "no appropriate stream found"
-+                raise self._Error("no appropriate stream found")
-         finally:
-             fileobj.close()
-@@ -490,9 +490,9 @@
-         fileobj = file(filename, "rb+")
-         try:
-             try: self.tags._inject(fileobj)
--            except error, e:
--                raise self._Error, e, sys.exc_info()[2]
-+            except error as e:
-+                raise self._Error(e).with_traceback(sys.exc_info()[2])
-             except EOFError:
--                raise self._Error, "no appropriate stream found"
-+                raise self._Error("no appropriate stream found")
-         finally:
-             fileobj.close()
-diff -Naur client175_0.7-original/mutagen/_util.py client175_0.7/mutagen/_util.py
---- client175_0.7-original/mutagen/_util.py    2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/_util.py     2021-08-03 14:57:18.085567569 +0200
-@@ -32,7 +32,7 @@
-     """
-     def __iter__(self):
--        return iter(self.keys())
-+        return iter(list(self.keys()))
-     def has_key(self, key):
-         try: self[key]
-@@ -40,18 +40,18 @@
-         else: return True
-     __contains__ = has_key
--    iterkeys = lambda self: iter(self.keys())
-+    iterkeys = lambda self: iter(list(self.keys()))
-     def values(self):
--        return map(self.__getitem__, self.keys())
--    itervalues = lambda self: iter(self.values())
-+        return list(map(self.__getitem__, list(self.keys())))
-+    itervalues = lambda self: iter(list(self.values()))
-     def items(self):
--        return zip(self.keys(), self.values())
--    iteritems = lambda s: iter(s.items())
-+        return list(zip(list(self.keys()), list(self.values())))
-+    iteritems = lambda s: iter(list(s.items()))
-     def clear(self):
--        map(self.__delitem__, self.keys())
-+        list(map(self.__delitem__, list(self.keys())))
-     def pop(self, key, *args):
-         if len(args) > 1:
-@@ -65,7 +65,7 @@
-     def popitem(self):
-         try:
--            key = self.keys()[0]
-+            key = list(self.keys())[0]
-             return key, self.pop(key)
-         except IndexError: raise KeyError("dictionary is empty")
-@@ -74,7 +74,7 @@
-             self.update(kwargs)
-             other = {}
--        try: map(self.__setitem__, other.keys(), other.values())
-+        try: list(map(self.__setitem__, list(other.keys()), list(other.values())))
-         except AttributeError:
-             for key, value in other:
-                 self[key] = value
-@@ -90,14 +90,14 @@
-         except KeyError: return default
-     def __repr__(self):
--        return repr(dict(self.items()))
-+        return repr(dict(list(self.items())))
-     def __cmp__(self, other):
-         if other is None: return 1
--        else: return cmp(dict(self.items()), other)
-+        else: return cmp(dict(list(self.items())), other)
-     def __len__(self):
--        return len(self.keys())
-+        return len(list(self.keys()))
- class DictProxy(DictMixin):
-     def __init__(self, *args, **kwargs):
-@@ -114,7 +114,7 @@
-         del(self.__dict[key])
-     def keys(self):
--        return self.__dict.keys()
-+        return list(self.__dict.keys())
- class cdata(object):
-     """C character buffer to Python numeric type conversions."""
-@@ -300,7 +300,7 @@
-     """Convert a basestring to a valid UTF-8 str."""
-     if isinstance(data, str):
-         return data.decode("utf-8", "replace").encode("utf-8")
--    elif isinstance(data, unicode):
-+    elif isinstance(data, str):
-         return data.encode("utf-8")
-     else: raise TypeError("only unicode/str types can be converted to UTF-8")
-@@ -308,7 +308,7 @@
-     try:
-         return d[key]
-     except KeyError:
--        for pattern, value in d.iteritems():
-+        for pattern, value in d.items():
-             if fnmatchcase(key, pattern):
-                 return value
-     return default
-diff -Naur client175_0.7-original/mutagen/_vorbis.py client175_0.7/mutagen/_vorbis.py
---- client175_0.7-original/mutagen/_vorbis.py  2010-05-15 00:42:14.000000000 +0200
-+++ client175_0.7/mutagen/_vorbis.py   2021-08-03 14:57:26.321443147 +0200
-@@ -16,7 +16,7 @@
- import sys
--from cStringIO import StringIO
-+from io import StringIO
- import mutagen
- from mutagen._util import DictMixin, cdata
-@@ -54,7 +54,7 @@
-     vendor -- the stream 'vendor' (i.e. writer); default 'Mutagen'
-     """
--    vendor = u"Mutagen " + mutagen.version_string
-+    vendor = "Mutagen " + mutagen.version_string
-     def __init__(self, data=None, *args, **kwargs):
-         # Collect the args to pass to load, this lets child classes
-@@ -90,16 +90,16 @@
-                 except (OverflowError, MemoryError):
-                     raise error("cannot read %d bytes, too large" % length)
-                 try: tag, value = string.split('=', 1)
--                except ValueError, err:
-+                except ValueError as err:
-                     if errors == "ignore":
-                         continue
-                     elif errors == "replace":
--                        tag, value = u"unknown%d" % i, string
-+                        tag, value = "unknown%d" % i, string
-                     else:
--                        raise VorbisEncodingError, str(err), sys.exc_info()[2]
-+                        raise VorbisEncodingError(str(err)).with_traceback(sys.exc_info()[2])
-                 try: tag = tag.encode('ascii', errors)
-                 except UnicodeEncodeError:
--                    raise VorbisEncodingError, "invalid tag name %r" % tag
-+                    raise VorbisEncodingError("invalid tag name %r" % tag)
-                 else:
-                     if is_valid_key(tag): self.append((tag, value))
-             if framing and not ord(fileobj.read(1)) & 0x01:
-@@ -115,7 +115,7 @@
-         any invalid keys or values are found, a ValueError is raised.
-         """
--        if not isinstance(self.vendor, unicode):
-+        if not isinstance(self.vendor, str):
-             try: self.vendor.decode('utf-8')
-             except UnicodeDecodeError: raise ValueError
-@@ -123,7 +123,7 @@
-             try:
-                 if not is_valid_key(key): raise ValueError
-             except: raise ValueError("%r is not a valid key" % key)
--            if not isinstance(value, unicode):
-+            if not isinstance(value, str):
-                 try: value.encode("utf-8")
-                 except: raise ValueError("%r is not a valid value" % value)
-         else: return True
-@@ -181,15 +181,15 @@
-         """
-         key = key.lower().encode('ascii')
-         values = [value for (k, value) in self if k.lower() == key]
--        if not values: raise KeyError, key
-+        if not values: raise KeyError(key)
-         else: return values
-     def __delitem__(self, key):
-         """Delete all values associated with the key."""
-         key = key.lower().encode('ascii')
--        to_delete = filter(lambda x: x[0].lower() == key, self)
--        if not to_delete:raise KeyError, key
--        else: map(self.remove, to_delete)
-+        to_delete = [x for x in self if x[0].lower() == key]
-+        if not to_delete:raise KeyError(key)
-+        else: list(map(self.remove, to_delete))
-     def __contains__(self, key):
-         """Return true if the key has any values."""
-@@ -220,4 +220,4 @@
-     def as_dict(self):
-         """Return a copy of the comment data in a real dict."""
--        return dict((key, self[key]) for key in self.keys())
-+        return dict((key, self[key]) for key in list(self.keys()))
-diff -Naur client175_0.7-original/server.py client175_0.7/server.py
---- client175_0.7-original/server.py   2011-04-06 13:18:04.000000000 +0200
-+++ client175_0.7/server.py    2021-08-03 14:40:26.132681859 +0200
-@@ -20,7 +20,7 @@
- #       MA 02110-1301, USA.
--import cherrypy, json, os, pwd, urllib, urllib2, sys
-+import cherrypy, json, os, pwd, urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse, sys
- from BeautifulSoup import BeautifulSoup
- from time import sleep
- from datetime import datetime, timedelta
-@@ -55,7 +55,7 @@
- PASSWORD = None
- RUN_AS = pwd.getpwuid(os.getuid())[0]
--if os.environ.has_key("MPD_HOST"):
-+if "MPD_HOST" in os.environ:
-     mpd_host = str(os.environ["MPD_HOST"])
-     if "@" in mpd_host:
-         mpd_host = mpd_host.split("@")
-@@ -64,7 +64,7 @@
-     else:
-         HOST = mpd_host
--if os.environ.has_key("MPD_PORT"):
-+if "MPD_PORT" in os.environ:
-     PORT = int(os.environ["MPD_PORT"])
- HOST = cherrypy.config.get('mpd_host', HOST)
-@@ -124,7 +124,7 @@
-     def add(self, *args, **kwargs):
-         if len(kwargs) > 0:
--            args = list(args) + kwargs.values()
-+            args = list(args) + list(kwargs.values())
-         if len(args) == 2:
-             if args[0] in ('file', 'directory'):
-                 d = args[1]
-@@ -146,7 +146,7 @@
-                 if ext in ['mp3', 'pgg', 'wav', 'flac', 'aac', 'mod', 'wma']:
-                     mpd.add(d)
-                 else:
--                    sock = urllib2.urlopen(d)
-+                    sock = urllib.request.urlopen(d)
-                     data = sock.read()
-                     info = sock.info()
-                     mime = info.gettype()
-@@ -201,13 +201,13 @@
-         """
-         if len(kwargs) > 0:
--            args = list(args) + kwargs.values()
-+            args = list(args) + list(kwargs.values())
-         try:
-             if len(args) == 1:
-                 args = args[0]
--            print args
-+            print(args)
-             result = mpd.execute(args)
--        except MPDError, e:
-+        except MPDError as e:
-             raise cherrypy.HTTPError(501, message=str(e))
-         return json.dumps(result)
-     default.exposed = True
-@@ -231,7 +231,7 @@
-             return "WAV editing not supported."
-         tags = {}
--        for tag, val in kwargs.items():
-+        for tag, val in list(kwargs.items()):
-             tag = tag.lower()
-             if tag == 'track':
-                 tags['tracknumber'] = val
-@@ -239,7 +239,7 @@
-                 tags['discnumber'] = val
-             else:
-                 tags[tag] = val
--            print '%s[%s] = "%s"' % (id, tag, val)
-+            print('%s[%s] = "%s"' % (id, tag, val))
-         f = metadata.get_format(loc)
-         f.write_tags(tags)
-@@ -249,7 +249,7 @@
-             try:
-                 mpd.update(id)
-                 updating = True
--            except MPDError, e:
-+            except MPDError as e:
-                 if str(e) == "[54@0] {update} already updating":
-                     sleep(0.01)
-                 else:
-@@ -327,7 +327,7 @@
-         d = []
-         skip = ('type', 'time', 'ptime', 'songs')
-         for item in data:
--            for key, val in item.items():
-+            for key, val in list(item.items()):
-                 if key not in skip:
-                     if filter in str(val).lower():
-                         d.append(item)
-@@ -444,7 +444,7 @@
-         """
-         try:
-             return mpd.raw(cmd)
--        except MPDError, e:
-+        except MPDError as e:
-             raise cherrypy.HTTPError(501, message=str(e))
-     protocol.exposed = True
-@@ -514,9 +514,9 @@
-             return json.dumps(s)
-         n = 0
-         while n < 50:
--            if mpd.state.get('uptime', '') <> client_uptime:
-+            if mpd.state.get('uptime', '') != client_uptime:
-                 return json.dumps(mpd.state)
--            if mpd.state.get('updating_db', '') <> client_updating_db:
-+            if mpd.state.get('updating_db', '') != client_updating_db:
-                 return json.dumps(mpd.state)
-             sleep(0.1)
-             n += 1
-@@ -649,11 +649,11 @@
-     if sport is None:
-         sport = "8080"
--    print ""
--    print "=" * 60
--    print "Server Ready."
--    print "Client175 is available at:  http://%s%s:%s" % (shost, sport, SERVER_ROOT)
--    print "=" * 60
--    print ""
-+    print("")
-+    print("=" * 60)
-+    print("Server Ready.")
-+    print("Client175 is available at:  http://%s%s:%s" % (shost, sport, SERVER_ROOT))
-+    print("=" * 60)
-+    print("")
-     serve()