git: a5811e8b9106 - main - www/py-formencode: Fix build with setuptools 58.0.0+

From: Po-Chuan Hsieh <sunpoet_at_FreeBSD.org>
Date: Mon, 07 Mar 2022 18:27:28 UTC
The branch main has been updated by sunpoet:

URL: https://cgit.FreeBSD.org/ports/commit/?id=a5811e8b910638e4fd6f24e801a25e7f7dbc1df0

commit a5811e8b910638e4fd6f24e801a25e7f7dbc1df0
Author:     Po-Chuan Hsieh <sunpoet@FreeBSD.org>
AuthorDate: 2022-03-07 18:08:01 +0000
Commit:     Po-Chuan Hsieh <sunpoet@FreeBSD.org>
CommitDate: 2022-03-07 18:13:21 +0000

    www/py-formencode: Fix build with setuptools 58.0.0+
    
    - While I'm here, add USES=dos2unix
    
    With  hat:      python
---
 www/py-formencode/Makefile             |    2 +-
 www/py-formencode/files/patch-2to3     | 1528 ++++++++++++++++++++++++++++++++
 www/py-formencode/files/patch-setup.py |   28 +-
 3 files changed, 1543 insertions(+), 15 deletions(-)

diff --git a/www/py-formencode/Makefile b/www/py-formencode/Makefile
index d91df677d4a2..079af408c3f2 100644
--- a/www/py-formencode/Makefile
+++ b/www/py-formencode/Makefile
@@ -14,7 +14,7 @@ LICENSE=	PSFL
 
 OPTIONS_DEFINE=	DOCS
 
-USES=		gettext-runtime:run python:3.6+ zip
+USES=		dos2unix gettext-runtime:run python:3.6+ zip
 USE_PYTHON=	distutils autoplist
 
 DOCSDIR=	${PREFIX}/share/doc/py-${PORTNAME}
diff --git a/www/py-formencode/files/patch-2to3 b/www/py-formencode/files/patch-2to3
new file mode 100644
index 000000000000..b3e63d5fe513
--- /dev/null
+++ b/www/py-formencode/files/patch-2to3
@@ -0,0 +1,1528 @@
+--- formencode/api.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/api.py
+@@ -150,15 +150,15 @@ class Invalid(Exception):
+         val = self.msg
+         return val
+ 
+-    if unicode is not str:  # Python 2
++    if str is not str:  # Python 2
+ 
+         def __unicode__(self):
+-            if isinstance(self.msg, unicode):
++            if isinstance(self.msg, str):
+                 return self.msg
+             elif isinstance(self.msg, str):
+                 return self.msg.decode('utf8')
+             else:
+-                return unicode(self.msg)
++                return str(self.msg)
+ 
+     def unpack_errors(self, encode_variables=False, dict_char='.',
+                       list_char='-'):
+@@ -177,15 +177,15 @@ class Invalid(Exception):
+                 for item in self.error_list]
+         if self.error_dict:
+             result = {}
+-            for name, item in self.error_dict.iteritems():
++            for name, item in self.error_dict.items():
+                 result[name] = item if isinstance(
+-                    item, basestring) else item.unpack_errors()
++                    item, str) else item.unpack_errors()
+             if encode_variables:
+                 from . import variabledecode
+                 result = variabledecode.variable_encode(
+                     result, add_repetitions=False,
+                     dict_char=dict_char, list_char=list_char)
+-                for key in result.keys():
++                for key in list(result.keys()):
+                     if not result[key]:
+                         del result[key]
+             return result
+@@ -245,8 +245,8 @@ class Validator(declarative.Declarative):
+         except AttributeError:
+             try:
+                 if self.use_builtins_gettext:
+-                    import __builtin__
+-                    trans = __builtin__._
++                    import builtins
++                    trans = builtins._
+                 else:
+                     trans = _stdtrans
+             except AttributeError:
+@@ -311,7 +311,7 @@ class Validator(declarative.Declarative):
+         """
+         doc = cls.__doc__ or ''
+         doc = [textwrap.dedent(doc).rstrip()]
+-        messages = sorted(cls._messages.iteritems())
++        messages = sorted(cls._messages.items())
+         doc.append('\n\n**Messages**\n\n')
+         for name, default in messages:
+             default = re.sub(r'(%\(.*?\)[rsifcx])', r'``\1``', default)
+@@ -456,7 +456,7 @@ class FancyValidator(Validator):
+ 
+     def to_python(self, value, state=None):
+         try:
+-            if self.strip and isinstance(value, basestring):
++            if self.strip and isinstance(value, str):
+                 value = value.strip()
+             elif hasattr(value, 'mixed'):
+                 # Support Paste's MultiDict
+@@ -484,7 +484,7 @@ class FancyValidator(Validator):
+ 
+     def from_python(self, value, state=None):
+         try:
+-            if self.strip and isinstance(value, basestring):
++            if self.strip and isinstance(value, str):
+                 value = value.strip()
+             if not self.accept_python:
+                 if self.is_empty(value):
+@@ -520,7 +520,7 @@ class FancyValidator(Validator):
+         return None
+ 
+     def assert_string(self, value, state):
+-        if not isinstance(value, basestring):
++        if not isinstance(value, str):
+             raise Invalid(self.message('badType', state,
+                                        type=type(value), value=value),
+                           value, state)
+--- formencode/compound.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/compound.py
+@@ -39,7 +39,7 @@ class CompoundValidator(FancyValidator):
+     def __classinit__(cls, new_attrs):
+         FancyValidator.__classinit__(cls, new_attrs)
+         toAdd = []
+-        for name, value in new_attrs.iteritems():
++        for name, value in new_attrs.items():
+             if is_validator(value) and value is not Identity:
+                 toAdd.append((name, value))
+                 # @@: Should we really delete too?
+@@ -200,7 +200,7 @@ class All(CompoundValidator):
+         filtering out None and trying to keep `All` validators from
+         being nested (which isn't needed).
+         """
+-        validators = filter(lambda v: v and v is not Identity, validators)
++        validators = [v for v in validators if v and v is not Identity]
+         if not validators:
+             return Identity
+         if len(validators) == 1:
+--- formencode/context.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/context.py
+@@ -96,7 +96,7 @@ class Context(object):
+             "You can only write attribute on context object with the .set() method")
+ 
+     def set(self, **kw):
+-        state_id = _restore_ids.next()
++        state_id = next(_restore_ids)
+         try:
+             stack = self._local.stack
+         except AttributeError:
+--- formencode/declarative.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/declarative.py
+@@ -55,9 +55,9 @@ class _methodwrapper(object):
+     def __repr__(self):
+         if self.obj is None:
+             return ('<bound class method %s.%s>'
+-                    % (self.cls.__name__, self.func.func_name))
++                    % (self.cls.__name__, self.func.__name__))
+         return ('<bound method %s.%s of %r>'
+-                % (self.cls.__name__, self.func.func_name, self.obj))
++                % (self.cls.__name__, self.func.__name__, self.obj))
+ 
+ 
+ class DeclarativeMeta(type):
+@@ -66,11 +66,11 @@ class DeclarativeMeta(type):
+         cls = type.__new__(meta, class_name, bases, new_attrs)
+         for name in cls.__mutableattributes__:
+             setattr(cls, name, copy.copy(getattr(cls, name)))
+-        cls.declarative_count = cls.counter.next()
++        cls.declarative_count = next(cls.counter)
+         if ('__classinit__' in new_attrs and not isinstance(cls.__classinit__,
+                 (staticmethod, types.FunctionType))):
+             setattr(cls, '__classinit__',
+-                    staticmethod(cls.__classinit__.im_func))
++                    staticmethod(cls.__classinit__.__func__))
+         cls.__classinit__(cls, new_attrs)
+         names = getattr(cls, '__singletonmethods__', None)
+         if names:
+@@ -97,14 +97,12 @@ class singletonmethod(object):
+         return types.MethodType(self.func, obj)
+ 
+ 
+-class Declarative(object):
++class Declarative(object, metaclass=DeclarativeMeta):
+ 
+     __unpackargs__ = ()
+ 
+     __mutableattributes__ = ()
+ 
+-    __metaclass__ = DeclarativeMeta
+-
+     __singletonmethods__ = ()
+ 
+     counter = count()
+@@ -143,10 +141,10 @@ class Declarative(object):
+         for name in self.__mutableattributes__:
+             if name not in kw:
+                 setattr(self, name, copy.copy(getattr(self, name)))
+-        for name, value in kw.iteritems():
++        for name, value in kw.items():
+             setattr(self, name, value)
+         if 'declarative_count' not in kw:
+-            self.declarative_count = self.counter.next()
++            self.declarative_count = next(self.counter)
+         self.__initargs__(kw)
+ 
+     def __initargs__(self, new_attrs):
+@@ -179,7 +177,7 @@ class Declarative(object):
+                 and self.__unpackargs__[1] in vals):
+             v = vals[self.__unpackargs__[1]]
+             if isinstance(v, (list, int)):
+-                args.extend(map(source.makeRepr, v))
++                args.extend(list(map(source.makeRepr, v)))
+                 del v[self.__unpackargs__[1]]
+         for name in self.__unpackargs__:
+             if name in vals:
+@@ -188,7 +186,7 @@ class Declarative(object):
+             else:
+                 break
+         args.extend('%s=%s' % (name, source.makeRepr(value))
+-                     for (name, value) in vals.iteritems())
++                     for (name, value) in vals.items())
+         return '%s(%s)' % (self.__class__.__name__,
+                            ', '.join(args))
+ 
+--- formencode/doctest_xml_compare.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/doctest_xml_compare.py
+@@ -11,7 +11,7 @@ RealOutputChecker = doctest.OutputChecker
+ 
+ def debug(*msg):
+     import sys
+-    print >> sys.stderr, ' '.join(map(str, msg))
++    print(' '.join(map(str, msg)), file=sys.stderr)
+ 
+ 
+ class HTMLOutputChecker(RealOutputChecker):
+@@ -69,7 +69,7 @@ def xml_compare(x1, x2, reporter=None):
+         if reporter:
+             reporter('Tags do not match: %s and %s' % (x1.tag, x2.tag))
+         return False
+-    for name, value in x1.attrib.iteritems():
++    for name, value in x1.attrib.items():
+         if x2.attrib.get(name) != value:
+             if reporter:
+                 reporter('Attributes do not match: %s=%r, %s=%r'
+@@ -120,7 +120,7 @@ def make_xml(s):
+ 
+ 
+ def make_string(xml):
+-    if isinstance(xml, basestring):
++    if isinstance(xml, str):
+         xml = make_xml(xml)
+     s = ET.tostring(xml)
+     if s == '<xml />':
+--- formencode/foreach.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/foreach.py
+@@ -83,7 +83,7 @@ class ForEach(CompoundValidator):
+                     new_list = set(new_list)
+                 return new_list
+             else:
+-                raise Invalid('Errors:\n%s' % '\n'.join(unicode(e)
++                raise Invalid('Errors:\n%s' % '\n'.join(str(e)
+                     for e in errors if e), value, state, error_list=errors)
+         finally:
+             if state is not None:
+@@ -125,7 +125,7 @@ class ForEach(CompoundValidator):
+     del _IfMissing
+ 
+     def _convert_to_list(self, value):
+-        if isinstance(value, basestring):
++        if isinstance(value, str):
+             return [value]
+         elif value is None:
+             return []
+--- formencode/htmlfill.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/htmlfill.py
+@@ -226,7 +226,7 @@ class FillingParser(RewritingParser):
+         self.in_select = None
+         self.skip_next = False
+         self.errors = errors or {}
+-        if isinstance(self.errors, basestring):
++        if isinstance(self.errors, str):
+             self.errors = {None: self.errors}
+         self.in_error = None
+         self.skip_error = False
+@@ -253,14 +253,14 @@ class FillingParser(RewritingParser):
+         Compare the two objects as strings (coercing to strings if necessary).
+         Also uses encoding to compare the strings.
+         """
+-        if not isinstance(str1, basestring):
++        if not isinstance(str1, str):
+             if hasattr(str1, '__unicode__'):
+-                str1 = unicode(str1)
++                str1 = str(str1)
+             else:
+                 str1 = str(str1)
+         if type(str1) == type(str2):
+             return str1 == str2
+-        if isinstance(str1, unicode):
++        if isinstance(str1, str):
+             str1 = str1.encode(self.encoding or self.default_encoding)
+         else:
+             str2 = str2.encode(self.encoding or self.default_encoding)
+@@ -274,7 +274,7 @@ class FillingParser(RewritingParser):
+             if key in unused_errors:
+                 del unused_errors[key]
+         if self.auto_error_formatter:
+-            for key, value in unused_errors.iteritems():
++            for key, value in unused_errors.items():
+                 error_message = self.auto_error_formatter(value)
+                 error_message = '<!-- for: %s -->\n%s' % (key, error_message)
+                 self.insert_at_marker(
+@@ -297,7 +297,7 @@ class FillingParser(RewritingParser):
+         if self.encoding is not None:
+             new_content = []
+             for item in self._content:
+-                if (unicode is not str  # Python 2
++                if (str is not str  # Python 2
+                         and isinstance(item, str)):
+                     item = item.decode(self.encoding)
+                 new_content.append(item)
+@@ -390,11 +390,11 @@ class FillingParser(RewritingParser):
+         if self.prefix_error:
+             self.write_marker(name)
+         value = self.defaults.get(name)
+-        if (unicode is not str  # Python 2
+-                and isinstance(name, unicode) and isinstance(value, str)):
++        if (str is not str  # Python 2
++                and isinstance(name, str) and isinstance(value, str)):
+             value = value.decode(self.encoding or self.default_encoding)
+         if name in self.add_attributes:
+-            for attr_name, attr_value in self.add_attributes[name].iteritems():
++            for attr_name, attr_value in self.add_attributes[name].items():
+                 if attr_name.startswith('+'):
+                     attr_name = attr_name[1:]
+                     self.set_attr(attrs, attr_name,
+@@ -540,7 +540,7 @@ class FillingParser(RewritingParser):
+         """
+         if obj is None:
+             return False
+-        if isinstance(obj, basestring):
++        if isinstance(obj, str):
+             return obj == value
+         if hasattr(obj, '__contains__'):
+             if value in obj:
+--- formencode/htmlgen.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/htmlgen.py
+@@ -90,12 +90,12 @@ class _HTML:
+     def quote(self, arg):
+         if arg is None:
+             return ''
+-        if unicode is not str:  # Python 2
+-            arg = unicode(arg).encode(default_encoding)
++        if str is not str:  # Python 2
++            arg = str(arg).encode(default_encoding)
+         return escape(arg, True)
+ 
+     def str(self, arg, encoding=None):
+-        if isinstance(arg, basestring):
++        if isinstance(arg, str):
+             if not isinstance(arg, str):
+                 arg = arg.encode(default_encoding)
+             return arg
+@@ -106,7 +106,7 @@ class _HTML:
+         elif isinstance(arg, Element):
+             return str(arg)
+         else:
+-            arg = unicode(arg)
++            arg = str(arg)
+             if not isinstance(arg, str):  # Python 2
+                 arg = arg.encode(default_encoding)
+             return arg
+@@ -127,11 +127,11 @@ class Element(ET.Element
+             args = kw.pop('c')
+             if not isinstance(args, (list, tuple)):
+                 args = (args,)
+-        for name, value in kw.items():
++        for name, value in list(kw.items()):
+             if value is None:
+                 del kw[name]
+                 continue
+-            kw[name] = unicode(value)
++            kw[name] = str(value)
+             if name.endswith('_'):
+                 kw[name[:-1]] = value
+                 del kw[name]
+@@ -151,20 +151,20 @@ class Element(ET.Element
+             if not ET.iselement(arg):
+                 if last is None:
+                     if el.text is None:
+-                        el.text = unicode(arg)
++                        el.text = str(arg)
+                     else:
+-                        el.text += unicode(arg)
++                        el.text += str(arg)
+                 else:
+                     if last.tail is None:
+-                        last.tail = unicode(arg)
++                        last.tail = str(arg)
+                     else:
+-                        last.tail += unicode(arg)
++                        last.tail += str(arg)
+             else:
+                 last = arg
+                 el.append(last)
+         return el
+ 
+-    if unicode is str:  # Python 3
++    if str is str:  # Python 3
+ 
+         def __str__(self):
+             return ET.tostring(
+--- formencode/national.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/national.py
+@@ -53,7 +53,7 @@ if tgformat:
+         else:
+             d = dict(country_additions)
+             d.update(dict(c2))
+-        ret = d.items() + fuzzy_countrynames
++        ret = list(d.items()) + fuzzy_countrynames
+         return ret
+ 
+     def get_country(code):
+@@ -65,7 +65,7 @@ if tgformat:
+         if len(c1) > len(c2):
+             d = dict(c1)
+             d.update(dict(c2))
+-            return d.items()
++            return list(d.items())
+         else:
+             return c2
+ 
+@@ -154,7 +154,7 @@ class DelimitedDigitsPostalCode(Regex):
+ 
+     def __init__(self, partition_lengths, delimiter=None,
+                  *args, **kw):
+-        if isinstance(partition_lengths, (int, long)):
++        if isinstance(partition_lengths, int):
+             partition_lengths = [partition_lengths]
+         if not delimiter:
+             delimiter = ''
+@@ -675,7 +675,7 @@ class InternationalPhoneNumber(FancyValidator):
+             value = value.encode('ascii', 'strict')
+         except UnicodeEncodeError:
+             raise Invalid(self.message('phoneFormat', state), value, state)
+-        if unicode is str:  # Python 3
++        if str is str:  # Python 3
+             value = value.decode('ascii')
+         value = self._mark_chars_re.sub('-', value)
+         for f, t in [('  ', ' '),
+@@ -765,7 +765,7 @@ class LanguageValidator(FancyValidator):
+ 
+ def validators():
+     """Return the names of all validators in this module."""
+-    return [name for name, value in globals().items()
++    return [name for name, value in list(globals().items())
+         if isinstance(value, type) and issubclass(value, FancyValidator)]
+ 
+ __all__ = ['Invalid'] + validators()
+--- formencode/rewritingparser.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/rewritingparser.py
+@@ -1,5 +1,5 @@
+ 
+-import HTMLParser
++import html.parser
+ import re
+ 
+ try:
+@@ -7,7 +7,7 @@ try:
+ except ImportError:  # Python < 3.2
+     from cgi import escape
+ 
+-from htmlentitydefs import name2codepoint
++from html.entities import name2codepoint
+ 
+ 
+ def html_quote(v):
+@@ -15,23 +15,23 @@ def html_quote(v):
+         return ''
+     if hasattr(v, '__html__'):
+         return v.__html__()
+-    if isinstance(v, basestring):
++    if isinstance(v, str):
+         return escape(v, True)
+     if hasattr(v, '__unicode__'):
+-        v = unicode(v)
++        v = str(v)
+     else:
+         v = str(v)
+     return escape(v, True)
+ 
+ 
+-class RewritingParser(HTMLParser.HTMLParser):
++class RewritingParser(html.parser.HTMLParser):
+ 
+     listener = None
+     skip_next = False
+ 
+     def __init__(self):
+         self._content = []
+-        HTMLParser.HTMLParser.__init__(self)
++        html.parser.HTMLParser.__init__(self)
+ 
+     def feed(self, data):
+         self.data_is_str = isinstance(data, str)
+@@ -40,7 +40,7 @@ class RewritingParser(HTMLParser.HTMLParser):
+         self.source_pos = 1, 0
+         if self.listener:
+             self.listener.reset()
+-        HTMLParser.HTMLParser.feed(self, data)
++        html.parser.HTMLParser.feed(self, data)
+ 
+     _entityref_re = re.compile('&([a-zA-Z][-.a-zA-Z\d]*);')
+     _charref_re = re.compile('&#(\d+|[xX][a-fA-F\d]+);')
+@@ -56,7 +56,7 @@ class RewritingParser(HTMLParser.HTMLParser):
+             # If we don't recognize it, pass it through as though it
+             # wasn't an entity ref at all
+             return match.group(0)
+-        return unichr(name2codepoint[name])
++        return chr(name2codepoint[name])
+ 
+     def _sub_charref(self, match):
+         num = match.group(1)
+@@ -64,7 +64,7 @@ class RewritingParser(HTMLParser.HTMLParser):
+             num = int(num[1:], 16)
+         else:
+             num = int(num)
+-        return unichr(num)
++        return chr(num)
+ 
+     def handle_misc(self, whatever):
+         self.write_pos()
+--- formencode/schema.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/schema.py
+@@ -81,7 +81,7 @@ class Schema(FancyValidator):
+         # Scan through the class variables we've defined *just*
+         # for this subclass, looking for validators (both classes
+         # and instances):
+-        for key, value in new_attrs.iteritems():
++        for key, value in new_attrs.items():
+             if key in ('pre_validators', 'chained_validators'):
+                 if is_validator(value):
+                     msg = "Any validator with the name %s will be ignored." % \
+@@ -96,12 +96,12 @@ class Schema(FancyValidator):
+             elif key in cls.fields:
+                 del cls.fields[key]
+ 
+-        for name, value in cls.fields.iteritems():
++        for name, value in cls.fields.items():
+             cls.add_field(name, value)
+ 
+     def __initargs__(self, new_attrs):
+         self.fields = self.fields.copy()
+-        for key, value in new_attrs.iteritems():
++        for key, value in new_attrs.items():
+             if key in ('pre_validators', 'chained_validators'):
+                 if is_validator(value):
+                     msg = "Any validator with the name %s will be ignored." % \
+@@ -139,13 +139,13 @@ class Schema(FancyValidator):
+ 
+         new = {}
+         errors = {}
+-        unused = self.fields.keys()
++        unused = list(self.fields.keys())
+         if state is not None:
+             previous_key = getattr(state, 'key', None)
+             previous_full_dict = getattr(state, 'full_dict', None)
+             state.full_dict = value_dict
+         try:
+-            for name, value in value_dict.items():
++            for name, value in list(value_dict.items()):
+                 try:
+                     unused.remove(name)
+                 except ValueError:
+@@ -239,14 +239,14 @@ class Schema(FancyValidator):
+         self.assert_dict(value_dict, state)
+         new = {}
+         errors = {}
+-        unused = self.fields.keys()
++        unused = list(self.fields.keys())
+         if state is not None:
+             previous_key = getattr(state, 'key', None)
+             previous_full_dict = getattr(state, 'full_dict', None)
+             state.full_dict = value_dict
+         try:
+             __traceback_info__ = None
+-            for name, value in value_dict.iteritems():
++            for name, value in value_dict.items():
+                 __traceback_info__ = 'for_python in %s' % name
+                 try:
+                     unused.remove(name)
+@@ -327,7 +327,7 @@ class Schema(FancyValidator):
+         result = []
+         result.extend(self.pre_validators)
+         result.extend(self.chained_validators)
+-        result.extend(self.fields.itervalues())
++        result.extend(iter(self.fields.values()))
+         return result
+ 
+     def is_empty(self, value):
+@@ -338,7 +338,7 @@ class Schema(FancyValidator):
+         return {}
+ 
+     def _value_is_iterator(self, value):
+-        if isinstance(value, basestring):
++        if isinstance(value, str):
+             return False
+         elif isinstance(value, (list, tuple)):
+             return True
+@@ -361,16 +361,16 @@ def format_compound_error(v, indent=0):
+             # version if possible, and unicode() if necessary, because
+             # testing for the presence of a __unicode__ method isn't
+             # enough
+-            return unicode(v)
++            return str(v)
+     elif isinstance(v, dict):
+         return ('%s\n' % (' ' * indent)).join(
+             '%s: %s' % (k, format_compound_error(value, indent=len(k) + 2))
+-            for k, value in sorted(v.iteritems()) if value is not None)
++            for k, value in sorted(v.items()) if value is not None)
+     elif isinstance(v, list):
+         return ('%s\n' % (' ' * indent)).join(
+             '%s' % (format_compound_error(value, indent=indent))
+             for value in v if value is not None)
+-    elif isinstance(v, basestring):
++    elif isinstance(v, str):
+         return v
+     else:
+         assert False, "I didn't expect something like %s" % repr(v)
+@@ -383,7 +383,7 @@ def merge_dicts(d1, d2):
+ 
+ 
+ def merge_values(v1, v2):
+-    if isinstance(v1, basestring) and isinstance(v2, basestring):
++    if isinstance(v1, str) and isinstance(v2, str):
+         return v1 + '\n' + v2
+     elif isinstance(v1, (list, tuple)) and isinstance(v2, (list, tuple)):
+         return merge_lists(v1, v2)
+@@ -474,7 +474,7 @@ class SimpleFormValidator(FancyValidator):
+         errors = self.func(value_dict, state, self)
+         if not errors:
+             return value_dict
+-        if isinstance(errors, basestring):
++        if isinstance(errors, str):
+             raise Invalid(errors, value_dict, state)
+         elif isinstance(errors, dict):
+             raise Invalid(
+--- formencode/tests/test_doctests.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/tests/test_doctests.py
+@@ -27,7 +27,7 @@ base = os.path.dirname(os.path.dirname(os.path.dirname
+     os.path.abspath(__file__))))
+ 
+ 
+-if unicode is str:  # Python 3
++if str is str:  # Python 3
+ 
+     OutputChecker = doctest.OutputChecker
+ 
+--- formencode/tests/test_email.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/tests/test_email.py
+@@ -15,7 +15,7 @@ class TestEmail(unittest.TestCase):
+         try:
+             return self.validator.to_python(*args)
+         except Invalid as e:
+-            return unicode(e)
++            return str(e)
+ 
+     def message(self, message_name, username, domain):
+         email = '@'.join((username, domain))
+@@ -74,8 +74,8 @@ class TestUnicodeEmailWithResolveDomain(unittest.TestC
+ 
+     def test_unicode_ascii_subgroup(self):
+         self.assertEqual(self.validator.to_python(
+-            u'foo@yandex.com'), 'foo@yandex.com')
++            'foo@yandex.com'), 'foo@yandex.com')
+ 
+     def test_cyrillic_email(self):
+         self.assertEqual(self.validator.to_python(
+-            u'me@письмо.рф'), u'me@письмо.рф')
++            'me@письмо.рф'), 'me@письмо.рф')
+--- formencode/tests/test_htmlfill.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/tests/test_htmlfill.py
+@@ -10,7 +10,7 @@ try:
+ except AttributeError:  # Python < 2.7
+     from xml.parsers.expat import ExpatError as XMLParseError
+ 
+-from htmlentitydefs import name2codepoint
++from html.entities import name2codepoint
+ 
+ base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
+     os.path.abspath(__file__)))))
+@@ -44,7 +44,7 @@ def run_filename(filename):
+         data_content = ''
+     namespace = {}
+     if data_content:
+-        exec data_content in namespace
++        exec(data_content, namespace)
+     data = namespace.copy()
+     data['defaults'] = data.get('defaults', {})
+     if 'check' in data:
+@@ -52,7 +52,7 @@ def run_filename(filename):
+     else:
+         def checker(p, s):
+             pass
+-    for name in data.keys():
++    for name in list(data.keys()):
+         if name.startswith('_') or hasattr('__builtin__', name):
+             del data[name]
+     listener = htmlfill_schemabuilder.SchemaBuilder()
+@@ -62,7 +62,7 @@ def run_filename(filename):
+     output = p.text()
+ 
+     def reporter(v):
+-        print v
++        print(v)
+ 
+     try:
+         output_xml = ET.XML(output)
+@@ -72,10 +72,10 @@ def run_filename(filename):
+     else:
+         comp = xml_compare(output_xml, expected_xml, reporter)
+     if not comp:
+-        print '---- Output:   ----'
+-        print output
+-        print '---- Expected: ----'
+-        print expected
++        print('---- Output:   ----')
++        print(output)
++        print('---- Expected: ----')
++        print(expected)
+         assert False
+     checker(p, listener.schema())
+     checker(p, htmlfill_schemabuilder.parse_schema(template))
+@@ -87,14 +87,14 @@ def test_no_trailing_newline():
+ 
+ 
+ def test_escape_defaults():
+-    rarr = unichr(name2codepoint['rarr'])
++    rarr = chr(name2codepoint['rarr'])
+     assert (htmlfill.render('<input type="submit" value="next&gt;&rarr;">', {}, {})
+             == '<input type="submit" value="next&gt;%s">' % rarr)
+     assert (htmlfill.render('<input type="submit" value="1&amp;2">', {}, {})
+             == '<input type="submit" value="1&amp;2">')
+     assert (htmlfill.render('<input type="submit" value="Japan - &#x65E5;&#x672C; Nihon" />',
+                             {}, {}) ==
+-            u'<input type="submit" value="Japan - 日本 Nihon" />')
++            '<input type="submit" value="Japan - 日本 Nihon" />')
+ 
+ 
+ def test_xhtml():
+@@ -180,7 +180,7 @@ def test_checkbox():
+ 
+ 
+ def test_unicode():
+-    assert (htmlfill.render(u'<input type="checkbox" name="tags" value="2" />',
++    assert (htmlfill.render('<input type="checkbox" name="tags" value="2" />',
+                            dict(tags=[])) ==
+             '<input type="checkbox" name="tags" value="2" />')
+ 
+@@ -455,10 +455,10 @@ def test_error_class_textarea():
+ 
+ def test_mix_str_and_unicode():
+     html = '<input type="text" name="cheese">'
+-    uhtml = unicode(html)
++    uhtml = str(html)
+     cheese = dict(cheese='Käse')
+-    ucheese = dict(cheese=u'Käse')
+-    expected = u'<input type="text" name="cheese" value="Käse">'
++    ucheese = dict(cheese='Käse')
++    expected = '<input type="text" name="cheese" value="Käse">'
+     rendered = htmlfill.render(html, defaults=cheese, encoding='utf-8')
+     assert expected == rendered
+     rendered = htmlfill.render(html, defaults=ucheese, encoding='utf-8')
+--- formencode/tests/test_htmlgen.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/tests/test_htmlgen.py
+@@ -4,8 +4,8 @@ import doctest
+ from formencode.htmlgen import html
+ 
+ # A test value that can't be encoded as ascii:
+-uni_value = u'\xff'
+-str_value = uni_value if str is unicode else uni_value.encode('utf-8')
++uni_value = '\xff'
++str_value = uni_value if str is str else uni_value.encode('utf-8')
+ 
+ 
+ def test_basic():
+@@ -37,7 +37,7 @@ def test_unicode():
+         assert False, (
+             "We need something that can't be ASCII-encoded: %r (%r)"
+             % (uni_value, uni_value.encode('ascii')))
+-    assert unicode(html.b(uni_value)) == u'<b>%s</b>' % uni_value
++    assert str(html.b(uni_value)) == '<b>%s</b>' % uni_value
+ 
+ 
+ def test_quote():
+@@ -76,10 +76,10 @@ def test_namespace():
+ 
+ if __name__ == '__main__':
+     # It's like a super-mini py.test...
+-    for name, value in globals().iteritems():
++    for name, value in globals().items():
+         if name.startswith('test'):
+-            print name
++            print(name)
+             value()
+     from formencode import htmlgen
+     doctest.testmod(htmlgen)
+-    print 'doctest'
++    print('doctest')
+--- formencode/tests/test_i18n.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/tests/test_i18n.py
+@@ -8,15 +8,15 @@ ne = formencode.validators.NotEmpty()
+ def _test_builtins(func):
+     def dummy(s):
+         return "builtins dummy"
+-    import __builtin__
+-    __builtin__._ = dummy
++    import builtins
++    builtins._ = dummy
+ 
+     try:
+         ne.to_python("")
+     except formencode.api.Invalid as e:
+         func(e)
+ 
+-    del __builtin__._
++    del builtins._
+ 
+ 
+ def test_builtins():
+@@ -52,90 +52,90 @@ def _test_lang(language, notemptytext):
+     try:
+         ne.to_python("")
+     except formencode.api.Invalid as e:
+-        assert unicode(e) == notemptytext
++        assert str(e) == notemptytext
+ 
+     formencode.api.set_stdtranslation()  # set back to defaults
+ 
+ 
+ def test_de():
+-    _test_lang("de", u"Bitte einen Wert eingeben")
++    _test_lang("de", "Bitte einen Wert eingeben")
+ 
+ 
+ def test_es():
+-    _test_lang("es", u"Por favor introduzca un valor")
++    _test_lang("es", "Por favor introduzca un valor")
+ 
+ 
+ def test_pt_BR():
+-    _test_lang("pt_BR", u"Por favor digite um valor")
++    _test_lang("pt_BR", "Por favor digite um valor")
+ 
+ 
+ def test_zh_TW():
+-    _test_lang("zh_TW", u"請輸入值")
++    _test_lang("zh_TW", "請輸入值")
+ 
+ 
+ def test_sk():
+-    _test_lang("sk", u"Zadajte hodnotu, prosím")
++    _test_lang("sk", "Zadajte hodnotu, prosím")
+ 
+ 
+ def test_ru():
+-    _test_lang("ru", u"Необходимо ввести значение")
++    _test_lang("ru", "Необходимо ввести значение")
+ 
+ 
+ def test_sl():
+-    _test_lang("sl", u"Prosim, izpolnite polje")
++    _test_lang("sl", "Prosim, izpolnite polje")
+ 
+ 
+ def test_pt_PT():
+-    _test_lang("pt_PT", u"Por favor insira um valor")
++    _test_lang("pt_PT", "Por favor insira um valor")
+ 
+ 
+ def test_fr():
+-    _test_lang("fr", u"Saisissez une valeur")
++    _test_lang("fr", "Saisissez une valeur")
+ 
+ 
+ def test_nl():
+-    _test_lang("nl", u"Voer een waarde in")
++    _test_lang("nl", "Voer een waarde in")
+ 
+ 
+ def test_pl():
+-    _test_lang("pl", u"Proszę podać wartość")
++    _test_lang("pl", "Proszę podać wartość")
+ 
+ 
+ def test_el():
+-    _test_lang("el", u"Παρακαλούμε εισάγετε μια τιμή")
++    _test_lang("el", "Παρακαλούμε εισάγετε μια τιμή")
+ 
+ 
+ def test_zh_CN():
+-    _test_lang("zh_CN", u"请输入一个值")
++    _test_lang("zh_CN", "请输入一个值")
+ 
+ 
+ def test_cs():
+-    _test_lang("cs", u"Prosím zadejte hodnotu")
++    _test_lang("cs", "Prosím zadejte hodnotu")
+ 
+ 
+ def test_fi():
+-    _test_lang("fi", u"Anna arvo")
++    _test_lang("fi", "Anna arvo")
+ 
+ 
+ def test_nb_NO():
+-    _test_lang("nb_NO", u"Venligst fyll inn en verdi")
++    _test_lang("nb_NO", "Venligst fyll inn en verdi")
+ 
+ 
+ def test_it():
+-    _test_lang("it", u"Inserire un dato")
++    _test_lang("it", "Inserire un dato")
+ 
+ 
+ def test_et():
+-    _test_lang("et", u"Palun sisestada väärtus")
++    _test_lang("et", "Palun sisestada väärtus")
+ 
+ 
+ def test_lt():
+-    _test_lang("lt", u"Prašome įvesti reikšmę")
++    _test_lang("lt", "Prašome įvesti reikšmę")
+ 
+ 
+ def test_ja():
+-    _test_lang("ja", u"入力してください")
++    _test_lang("ja", "入力してください")
+ 
+ 
+ def test_tr():
+-    _test_lang("tr", u"Lütfen bir değer giriniz")
++    _test_lang("tr", "Lütfen bir değer giriniz")
+--- formencode/tests/test_schema.py.orig	2022-03-04 13:31:11 UTC
++++ formencode/tests/test_schema.py
+@@ -1,6 +1,6 @@
+ import unittest
+ 
+-from urlparse import parse_qsl
++from urllib.parse import parse_qsl
+ 
+ from formencode import Invalid, Validator, compound, foreach, validators
+ from formencode.schema import Schema, merge_dicts, SimpleFormValidator
+@@ -14,14 +14,14 @@ def _notranslation(s):
+ def setup_module(module):
+     """Disable i18n translation"""
+ 
+-    import __builtin__
+-    __builtin__._ = _notranslation
++    import builtins
++    builtins._ = _notranslation
+ 
+ 
+ def teardown_module(module):
+     """Remove translation function"""
+-    import __builtin__
+-    del __builtin__._
++    import builtins
++    del builtins._
+ 
+ 
+ def d(**kw):
+@@ -56,9 +56,9 @@ class DecodeCase(object):
+         all_cases.append(self)
+ 
+     def test(self):
+-        print 'input', repr(self.input)
++        print('input', repr(self.input))
+         actual = self.schema.to_python(self.input)
*** 617 LINES SKIPPED ***