diff --git a/docs/source/conf.py b/docs/source/conf.py
index 159b168..983a943 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -61,28 +61,3 @@
],
"genindex": ["links.html", "sourcelink.html", "searchbox.html"],
}
-
-# Avoid issues like below when running under python 3.x:
-#
-# Expected:
-# [u'hello', u'world']
-#
-# Got:
-# ['hello', 'world']
-
-import re
-import sys
-import doctest
-
-OrigOutputChecker = doctest.OutputChecker
-
-
-class Py23OutputChecker(OrigOutputChecker):
- def check_output(self, want, got, optionflags):
- if sys.version_info[0] > 2:
- want = re.sub("u'(.*?)'", "'\\1'", want)
- want = re.sub('u"(.*?)"', '"\\1"', want)
- return OrigOutputChecker.check_output(self, want, got, optionflags)
-
-
-doctest.OutputChecker = Py23OutputChecker
diff --git a/docs/source/intro.rst b/docs/source/intro.rst
index 7d41c52..c295b15 100644
--- a/docs/source/intro.rst
+++ b/docs/source/intro.rst
@@ -65,7 +65,7 @@ blank, using default values, or with values taken from your objects.
form = SignInForm.from_flat(request.POST)
if form.validate():
- logging.info(u"sign-in: %s" % form['username'].value)
+ logging.info("sign-in: %s" % form['username'].value)
redirect('/app/')
else:
render('login.html', form=form)
@@ -81,6 +81,6 @@ used as-is in output templates for form layout, redisplay and error reporting.
>>> isinstance(as_regular_python_data, dict)
True
>>> as_regular_python_data['username']
- u'jek'
+ 'jek'
>>> form2 = SignInForm(as_regular_python_data)
>>> assert form['username'].value == form2['username'].value
diff --git a/docs/source/markup.rst b/docs/source/markup.rst
index d1e0a3b..fc0fa57 100644
--- a/docs/source/markup.rst
+++ b/docs/source/markup.rst
@@ -178,7 +178,7 @@ participate in ``tabindex=`` generation.
>>> print(html.textarea(auto_tabindex=True))
>>> html.set(auto_tabindex=True)
- u''
+ ''
>>> print(html.textarea())
diff --git a/docs/source/schema/lists.rst b/docs/source/schema/lists.rst
index d96fc7e..bcee0d8 100644
--- a/docs/source/schema/lists.rst
+++ b/docs/source/schema/lists.rst
@@ -46,15 +46,15 @@ Example:
>>> from flatland import List, String
>>> Names = List.named('names').of(String.named('name'))
- >>> names = Names([u'a', u'b'])
+ >>> names = Names(['a', 'b'])
>>> names.value
- [u'a', u'b']
+ ['a', 'b']
>>> names.flatten()
- [(u'names_0_name', u'a'), (u'names_1_name', u'b')]
+ [('names_0_name', 'a'), ('names_1_name', 'b')]
>>> names[1].value
- u'b'
+ 'b'
>>> names.find_one('1').value
- u'b'
+ 'b'
Validation
----------
diff --git a/docs/source/schema/schema.rst b/docs/source/schema/schema.rst
index 3d92694..d46e5d9 100644
--- a/docs/source/schema/schema.rst
+++ b/docs/source/schema/schema.rst
@@ -15,7 +15,7 @@ types:
.. testcode:: fso
from flatland import Dict, String
- SearchSchema = Dict.named('search').of(String.named(u'keywords'))
+ SearchSchema = Dict.named('search').of(String.named('keywords'))
.. TODO:: FIXME UPDATE:
@@ -29,9 +29,9 @@ types:
.. doctest:: fso
:options: +ELLIPSIS
- >>> form = SearchSchema({u'keywords': u'foo bar baz'})
+ >>> form = SearchSchema({'keywords': 'foo bar baz'})
>>> form.value
- {u'keywords': u'foo bar baz'}
+ {'keywords': 'foo bar baz'}
.. TODO:: FIXME UPDATE:
@@ -42,10 +42,10 @@ types:
>>> from flatland import List
>>> ComposedSchema = Dict.of(SearchSchema,
- ... List.named(u'many_searches').of(SearchSchema))
+ ... List.named('many_searches').of(SearchSchema))
>>> form = ComposedSchema()
>>> sorted(form.value.keys())
- [u'many_searches', u'search']
+ ['many_searches', 'search']
.. TODO:: FIXME UPDATE:
diff --git a/docs/source/schema/traversal.rst b/docs/source/schema/traversal.rst
index 4ddd52f..684ebc4 100644
--- a/docs/source/schema/traversal.rst
+++ b/docs/source/schema/traversal.rst
@@ -25,7 +25,7 @@ has a bit of variety in its structure.
'location': {'x': 10, 'y': 20},
}
- ann1 = Annotation(sample_data, name=u'ann1')
+ ann1 = Annotation(sample_data, name='ann1')
Going Raw
@@ -38,7 +38,7 @@ application. An element's :attr:`~base.Element.value` is a full & recursive
.. doctest::
>>> ann1['title'] # ann1 is a flatland structure
-
+
>>> isinstance(ann1.value, dict) # but its .value is not
True
>>> ann1.value == sample_data
@@ -59,12 +59,12 @@ For example, ``Form`` and ``Dict`` can be indexed and used like ``dict``:
.. doctest::
>>> ann1['title'].value
- u'Interesting Spot'
+ 'Interesting Spot'
>>> ann1['location']['x'].value
10
>>> sorted(ann1['location'].items())
- [(u'x', ), (u'y', )]
- >>> u'title' in ann1
+ [('x', ), ('y', )]
+ >>> 'title' in ann1
True
And ``List`` and similar types can be used like lists:
@@ -108,9 +108,9 @@ to related elements: :attr:`~base.Element.root`,
>>> list(ann1['title'].children) # title is a String and has no children
[]
>>> sorted(el.name for el in ann1.all_children if el.name)
- [u'flags', u'location', u'title', u'x', u'y']
+ ['flags', 'location', 'title', 'x', 'y']
>>> [el.name for el in ann1['location']['x'].parents]
- [u'location', u'ann1']
+ ['location', 'ann1']
Each of these properties (excepting ``root``) returns an iterator of
elements.
@@ -128,28 +128,28 @@ use when authoring flexible & reusable validators.
.. doctest::
>>> ann1.find('title') # find 'ann1's child named 'title'
- []
+ []
Paths are evaluated relative to the element:
.. doctest::
>>> ann1['location'].find('x')
- []
+ []
Referencing parents is possible with ``..``:
.. doctest::
>>> ann1['location']['x'].find('../../title')
- []
+ []
Absolute paths begin with a ``/``.
.. doctest::
>>> ann1['location']['x'].find('/title')
- []
+ []
Members of sequences can be selected like any other child (their index number
is their name), or you can use Python-like slicing:
@@ -181,7 +181,7 @@ needed to illustrate this:
>>> p = Points([[dict(x=1, y=1), dict(x=2, y=2)],
... [dict(x=3, y=3)]])
>>> p.find('[:][:]/x')
- [, , ]
+ [, , ]
The equivalent straight Python to select the same set of elements is quite a
bit more wordy.
diff --git a/src/flatland/_compat.py b/src/flatland/_compat.py
deleted file mode 100644
index 89fc060..0000000
--- a/src/flatland/_compat.py
+++ /dev/null
@@ -1,98 +0,0 @@
-import sys
-
-__all__ = [
- "PY2",
- "builtins",
- "bytestring_type",
- "getattr_py2",
- "hasattr_py2",
- "identifier_transform",
- "iterkeys",
- "iteritems",
- "itervalues",
- "long_type",
- "setattr_py2",
- "string_types",
- "text_type",
- "with_metaclass",
- "xrange",
-]
-
-
-PY2 = sys.version_info[0] == 2
-
-if PY2:
- import __builtin__ as builtins
-
- text_type = unicode
- bytestring_type = str
- long_type = long
-
- def identifier_transform(identifier):
- if isinstance(identifier, unicode):
- return identifier.encode("ascii")
- else:
- return identifier
-
- # for simple, purposeful conversions ala ``unicode(2)`` that
- # should be allowed by the text suite's unicode coercion detector
- def text_transform(object):
- if isinstance(object, unicode):
- return object
- elif hasattr(object, "__unicode__"):
- return object.__unicode__()
- else:
- return str(object).decode("ascii", "strict")
-
- def hasattr_py2(obj, attr):
- attr = identifier_transform(attr)
- return hasattr(obj, attr)
-
- def getattr_py2(obj, attr, *default):
- # quietly downgrade u'attribute' to b'attribute'
- attr = identifier_transform(attr)
- if default:
- return getattr(obj, attr, default[0])
- else:
- return getattr(obj, attr)
-
- def setattr_py2(obj, attr, value):
- attr = identifier_transform(attr)
- setattr(obj, attr, value)
-
- iterkeys = lambda d: d.iterkeys()
- itervalues = lambda d: d.itervalues()
- iteritems = lambda d: d.iteritems()
- xrange = xrange
-else:
- import builtins
-
- text_type = str
- bytestring_type = bytes
- long_type = int
- identifier_transform = lambda i: i
- text_transform = str
- getattr_py2 = getattr
- hasattr_py2 = hasattr
- setattr_py2 = setattr
- iterkeys = lambda d: iter(d.keys())
- itervalues = lambda d: iter(d.values())
- iteritems = lambda d: iter(d.items())
- xrange = range
-
-string_types = (bytestring_type, text_type)
-
-
-def with_metaclass(meta, *bases):
- # From flask, MIT License
- # https://github.com/mitsuhiko/flask/blob/master/flask/_compat.py
- class metaclass(meta):
- __call__ = type.__call__
- __init__ = type.__init__
-
- def __new__(cls, name, this_bases, d):
- if this_bases is None:
- return type.__new__(cls, name, (), d)
- return meta(name, bases, d)
-
- return metaclass("temporary_class", None, {})
diff --git a/src/flatland/out/generic.py b/src/flatland/out/generic.py
index e6b4e40..bd64a99 100644
--- a/src/flatland/out/generic.py
+++ b/src/flatland/out/generic.py
@@ -1,6 +1,5 @@
import re
-from flatland._compat import PY2, bytestring_type, iteritems, text_type
from flatland.out.util import parse_trool
from flatland.schema import Array, Boolean
from flatland.util import Maybe, to_pairs
@@ -48,9 +47,7 @@ def __getitem__(self, key):
def __setitem__(self, key, value):
if key not in self:
- raise KeyError(
- f"{key!r} not permitted in this {self.__class__.__name__}"
- )
+ raise KeyError(f"{key!r} not permitted in this {self.__class__.__name__}")
self._frames[-1][key] = value
def __contains__(self, key):
@@ -65,16 +62,14 @@ def update(self, *iterable, **kwargs):
source = to_pairs(iterable[0])
for key, value in source:
self[key] = value
- for key, value in iteritems(kwargs):
- if PY2:
- key = key.decode("ascii")
+ for key, value in kwargs.items():
self[key] = value
def __repr__(self):
return f"{self.__class__.__name__}({self._frames[-1]!r})"
-class Markup(text_type):
+class Markup(str):
"""A string of HTML markup that should not be escaped in output."""
__slots__ = ()
@@ -162,7 +157,7 @@ def transform_value(tagname, attributes, contents, context, bind):
current = attributes.get("value")
if current is not None:
value = current
- elif isinstance(contents, text_type):
+ elif isinstance(contents, str):
value = contents.strip()
elif contents is None:
value = ""
@@ -231,7 +226,7 @@ def transform_tabindex(tagname, attributes, contents, context, bind):
current = attributes.get("tabindex")
if forced or current is None and tagname in _auto_tags["tabindex"]:
- attributes["tabindex"] = text_type(str(tabindex))
+ attributes["tabindex"] = str(tabindex)
if tabindex > 0:
context["tabindex"] = tabindex + 1
return contents
@@ -310,9 +305,9 @@ def _sanitize_domid_suffix(string):
def _unpack(html_string):
"""Extract HTML from a __html__() interface."""
unpacked = html_string.__html__()
- if unpacked.__class__ is text_type:
+ if unpacked.__class__ is str:
return unpacked
- return text_type(unpacked)
+ return str(unpacked)
def _markup_escape(string):
diff --git a/src/flatland/out/genshi.py b/src/flatland/out/genshi.py
index 852aee5..7847df7 100644
--- a/src/flatland/out/genshi.py
+++ b/src/flatland/out/genshi.py
@@ -12,7 +12,6 @@
from genshi.template.directives import Directive
from genshi.template.interpolation import interpolate
-from flatland._compat import bytestring_type, iteritems, text_type
from flatland.out.generic import _unpack, transform, Context
__all__ = ("setup",)
@@ -125,7 +124,7 @@ def process(self, stream, directives, ctxt, vars):
def inject(self, mapping, ctxt, vars):
"""Inject the translated key and interpolated value into *mapping*."""
raw = self.raw_value
- if raw.__class__ is text_type:
+ if raw.__class__ is str:
final_value = raw
else:
parts = []
@@ -134,7 +133,7 @@ def inject(self, mapping, ctxt, vars):
parts.append(value)
else:
value = _eval_expr(value, ctxt, vars)
- parts.append(text_type(value))
+ parts.append(str(value))
final_value = "".join(parts)
mapping[_to_context.get(self._name, self._name)] = final_value
@@ -280,7 +279,7 @@ def _rewrite_stream(stream, directives, ctxt, vars, bind):
existing_attributes = {}
for qname, value in attrs:
if qname.namespace is None:
- if not isinstance(value, text_type):
+ if not isinstance(value, str):
value = _simplify_stream(value, ctxt, vars)
attrs |= ((qname, value),)
existing_attributes[qname.localname] = qname
@@ -297,10 +296,10 @@ def _rewrite_stream(stream, directives, ctxt, vars, bind):
if new_contents is None:
new_contents = ()
- elif isinstance(new_contents, text_type):
+ elif isinstance(new_contents, str):
new_contents = [(TEXT, new_contents, (None, -1, -1))]
- pairs = sorted(iteritems(mutable_attrs), key=_attribute_sort_key)
+ pairs = sorted(mutable_attrs.items(), key=_attribute_sort_key)
for attribute_name, value in pairs:
if attribute_name in existing_attributes:
qname = existing_attributes.pop(attribute_name)
@@ -377,15 +376,15 @@ def _simplify_stream(stream, ctxt, vars):
while hasattr(value, "__next__") or hasattr(value, "next"):
value = list(value)
value = _simplify_stream(value, ctxt, vars)
- if not isinstance(value, text_type):
+ if not isinstance(value, str):
stream[idx : idx + 1] = value
return stream
else:
stream[idx] = (TEXT, value, pos)
- elif isinstance(value, bytestring_type):
+ elif isinstance(value, bytes):
value = value.decode("utf8", "replace")
- elif not isinstance(value, text_type):
- value = text_type(value)
+ elif not isinstance(value, str):
+ value = str(value)
parts.append(value)
else:
return stream
diff --git a/src/flatland/out/markup.py b/src/flatland/out/markup.py
index 398770a..ac10dd4 100644
--- a/src/flatland/out/markup.py
+++ b/src/flatland/out/markup.py
@@ -1,6 +1,5 @@
from collections import defaultdict
-from flatland._compat import PY2, bytestring_type, iteritems, text_type
from flatland.out.generic import Context, transform, _unpack
from flatland.out.util import parse_trool
@@ -67,8 +66,6 @@ def set(self, **settings):
"""
for key, value in settings.items():
- if PY2:
- key = key.decode("ascii")
if key not in self:
raise TypeError("%r is not a valid argument." % key)
if key.startswith("auto_"):
@@ -194,8 +191,8 @@ def tag(self, tagname, bind=None, **attributes):
example, ``tag('input')`` is equivalent to ``input()``.
"""
- if isinstance(tagname, bytestring_type): # pragma: nocover
- tagname = text_type(tagname)
+ if isinstance(tagname, bytes): # pragma: nocover
+ tagname = str(tagname)
tagname = tagname.lower()
if bind is None and not attributes:
return self._tag(tagname)
@@ -269,7 +266,7 @@ def _open(self, bind, kwargs):
if self._context["ordered_attributes"]:
pairs = sorted(attributes.items(), key=_attribute_sort_key)
else:
- pairs = iteritems(attributes)
+ pairs = attributes.items()
guts = " ".join(f'{k}="{_attribute_escape(v)}"' for k, v in pairs)
if guts:
return "<" + tagname + " " + guts
@@ -317,8 +314,6 @@ def _attribute_escape(string):
def _transform_keys(d):
rekeyed = {}
for key, value in d.items():
- if PY2:
- key = key.decode("ascii")
key = key.rstrip("_")
rekeyed[key] = value
return rekeyed
diff --git a/src/flatland/out/util.py b/src/flatland/out/util.py
index e48f07b..4c1c18e 100644
--- a/src/flatland/out/util.py
+++ b/src/flatland/out/util.py
@@ -1,4 +1,3 @@
-from flatland._compat import bytestring_type
from flatland.util import Maybe
YES = ("1", "true", "True", "t", "on", "yes")
@@ -9,7 +8,7 @@
def parse_trool(value):
if value is True or value is False or value is Maybe:
return value
- if isinstance(value, bytestring_type):
+ if isinstance(value, bytes):
value = value.decode("utf8", "replace")
value = value.lower()
if value in YES:
diff --git a/src/flatland/schema/base.py b/src/flatland/schema/base.py
index 1ec2bb4..6ce7822 100644
--- a/src/flatland/schema/base.py
+++ b/src/flatland/schema/base.py
@@ -2,7 +2,6 @@
import itertools
import operator
-from flatland._compat import PY2, bytestring_type, iteritems, text_type
from flatland.schema.paths import pathexpr
from flatland.schema.properties import Properties
from flatland.signals import validator_validated
@@ -168,8 +167,8 @@ def named(cls, name):
:returns: a new class
"""
- if not isinstance(name, (text_type, NoneType)):
- name = text_type(name)
+ if not isinstance(name, (str, NoneType)):
+ name = str(name)
cls.name = name
return cls
@@ -192,7 +191,7 @@ def using(cls, **overrides):
if not isinstance(overrides["properties"], Properties):
overrides["properties"] = Properties(overrides["properties"])
- for attribute, value in iteritems(overrides):
+ for attribute, value in overrides.items():
# TODO: must make better
if callable(value):
value = staticmethod(value)
@@ -396,17 +395,17 @@ def fq_name(self):
:attr:`Element.root` (``/``) down to the element.
>>> from flatland import Dict, Integer
- >>> Point = Dict.named(u'point').of(Integer.named(u'x'),
- ... Integer.named(u'y'))
+ >>> Point = Dict.named('point').of(Integer.named('x'),
+ ... Integer.named('y'))
>>> p = Point(dict(x=10, y=20))
>>> p.name
- u'point'
+ 'point'
>>> p.fq_name()
- u'/'
+ '/'
>>> p['x'].name
- u'x'
+ 'x'
>>> p['x'].fq_name()
- u'/x'
+ '/x'
The index used in a path may not be the :attr:`.name` of the
element. For example, sequence members are referenced by their
@@ -414,15 +413,15 @@ def fq_name(self):
>>> from flatland import List, String
>>> Addresses = List.named('addresses').of(String.named('address'))
- >>> form = Addresses([u'uptown', u'downtown'])
+ >>> form = Addresses(['uptown', 'downtown'])
>>> form.name
- u'addresses'
+ 'addresses'
>>> form.fq_name()
- u'/'
+ '/'
>>> form[0].name
- u'address'
+ 'address'
>>> form[0].fq_name()
- u'/0'
+ '/0'
"""
if self.parent is None:
@@ -489,9 +488,9 @@ class Profile(Schema):
>>> cities = form.find('/contact/addresses[:]/city')
>>> [el.value for el in cities]
- [u'Kingsport', u'Dunwich']
+ ['Kingsport', 'Dunwich']
>>> form.find('/contact/name', single=True)
-
+
"""
expr = pathexpr(path)
@@ -501,10 +500,8 @@ class Profile(Schema):
elif not results:
return None
elif len(results) > 1 and strict:
- display_path = repr(path).lstrip("u")
raise LookupError(
- "Path %s matched multiple elements; single "
- "result expected." % display_path
+ "Path %r matched multiple elements; single result expected." % path
)
else:
return results[0]
@@ -544,11 +541,11 @@ def flattened_name(self, sep="_"):
>>> form = flatland.List('addresses',
... flatland.String('address'))
>>> element = form()
- >>> element.set([u'uptown', u'downtown'])
+ >>> element.set(['uptown', 'downtown'])
>>> element[0].value
- u'uptown'
+ 'uptown'
>>> element['0'].flattened_name()
- u'addresses_0_address'
+ 'addresses_0_address'
"""
return sep.join(parent.name for parent in self.path if parent.name is not None)
@@ -572,25 +569,25 @@ def flatten(self, sep="_", value=operator.attrgetter("u")):
>>> from flatland import Schema, Dict, String
>>> class Nested(Schema):
- ... contact = Dict.of(String.named(u'name'),
- ... Dict.named(u'address').
- ... of(String.named(u'email')))
+ ... contact = Dict.of(String.named('name'),
+ ... Dict.named('address').
+ ... of(String.named('email')))
...
>>> element = Nested()
>>> element.flatten()
- [(u'contact_name', u''), (u'contact_address_email', u'')]
+ [('contact_name', ''), ('contact_address_email', '')]
The value of each pair can be customized with the *value* callable::
>>> element.flatten(value=operator.attrgetter('u'))
- [(u'contact_name', u''), (u'contact_address_email', u'')]
+ [('contact_name', ''), ('contact_address_email', '')]
>>> element.flatten(value=lambda el: el.value)
- [(u'contact_name', None), (u'contact_address_email', None)]
+ [('contact_name', None), ('contact_address_email', None)]
Solo elements will return a sequence containing a single pair::
>>> element['name'].flatten()
- [(u'contact_name', u'')]
+ [('contact_name', '')]
"""
if self.flattenable:
@@ -617,32 +614,32 @@ def set(self, obj):
native value of None will be represented as u'' in :attr:`u`.
If adaptation fails, :attr:`value` will be ``None`` and :attr:`u` will
- contain ``str(value)`` (or unicode), or ``u''`` for None.
+ contain ``str(value)``, or ``''`` for None.
>>> from flatland import Integer
>>> el = Integer()
>>> el.u, el.value
- (u'', None)
+ ('', None)
>>> el.set('123')
True
>>> el.u, el.value
- (u'123', 123)
+ ('123', 123)
>>> el.set(456)
True
>>> el.u, el.value
- (u'456', 456)
+ ('456', 456)
>>> el.set('abc')
False
>>> el.u, el.value
- (u'abc', None)
+ ('abc', None)
>>> el.set(None)
True
>>> el.u, el.value
- (u'', None)
+ ('', None)
"""
raise NotImplementedError()
diff --git a/src/flatland/schema/compound.py b/src/flatland/schema/compound.py
index 006ec41..4656a98 100644
--- a/src/flatland/schema/compound.py
+++ b/src/flatland/schema/compound.py
@@ -1,12 +1,6 @@
from functools import wraps, reduce
import operator
-from flatland._compat import (
- PY2,
- identifier_transform,
- string_types,
- with_metaclass,
-)
from flatland.exc import AdaptationError
from flatland.signals import element_set
from flatland.util import (
@@ -77,10 +71,7 @@ def __call__(cls, value=Unspecified, **kw):
def _wrap_compound_init(fn):
"""Decorate __compound_init__ with a status setter & classmethod."""
if isinstance(fn, classmethod):
- if PY2:
- fn = fn.__get__(str).__func__ # type doesn't matter here
- else:
- fn = fn.__func__
+ fn = fn.__func__
@wraps(fn)
def __compound_init__(cls):
@@ -91,7 +82,7 @@ def __compound_init__(cls):
return classmethod(__compound_init__)
-class Compound(with_metaclass(_MetaCompound, Mapping, Scalar)):
+class Compound(Mapping, Scalar, metaclass=_MetaCompound):
"""A mapping container that acts like a scalar value.
Compound fields are dictionary-like fields that can assemble a
@@ -256,9 +247,7 @@ def explode(self, value):
value = Date.adapt(self, value)
for attrib, child_schema in zip(self.used, self.field_schema):
- self[child_schema.name].set(
- getattr(value, identifier_transform(attrib))
- )
+ self[child_schema.name].set(getattr(value, attrib))
except (AdaptationError, TypeError):
for child_schema in self.field_schema:
self[child_schema.name].set(None)
@@ -274,12 +263,12 @@ class JoinedString(Array, String):
>>> from flatland import JoinedString
>>> el = JoinedString(['x', 'y', 'z'])
>>> el.value
- u'x,y,z'
+ 'x,y,z'
>>> el2 = JoinedString('foo,bar')
>>> el2[1].value
- u'bar'
+ 'bar'
>>> el2.value
- u'foo,bar'
+ 'foo,bar'
Only the joined representation is considered when flattening or restoring
with :meth:`set_flat`. JoinedStrings run validation after their children.
@@ -303,7 +292,7 @@ class JoinedString(Array, String):
#: ... separator_regex=re.compile('\s*,\s*'))
#: ...
#: >>> schema('a , b,c,d').value
- #: u'a, b, c, d'
+ #: 'a, b, c, d'
separator_regex = None
#: The default child type is :class:`~flatland.schema.scalars.String`,
@@ -318,7 +307,7 @@ def set(self, value):
self.raw = value
if isinstance(value, (list, tuple)):
values = value
- elif not isinstance(value, string_types):
+ elif not isinstance(value, (str, bytes)):
values = list(value)
elif self.separator_regex:
# a text regexp separator
diff --git a/src/flatland/schema/containers.py b/src/flatland/schema/containers.py
index b502e82..d5649a8 100644
--- a/src/flatland/schema/containers.py
+++ b/src/flatland/schema/containers.py
@@ -1,16 +1,6 @@
from collections import defaultdict
import re
-from flatland._compat import (
- PY2,
- identifier_transform,
- iteritems,
- iterkeys,
- itervalues,
- bytestring_type,
- text_type,
- xrange,
-)
from flatland.util import (
Unspecified,
assignable_class_property,
@@ -19,7 +9,6 @@
keyslice_pairs,
re_uescape,
to_pairs,
- decode_repr,
)
from flatland.signals import element_set
from .base import Element, Unevaluated, Slot, validate_element
@@ -178,7 +167,7 @@ def of(cls, *schema):
>>> el = Names(['Bob', 'Biff'])
>>> el
- [, ]
+ [, ]
If more than one `~flatland.schema.base.Element` is specified in
*\*schema*, an anonymous :class:`Dict` is created to hold them.
@@ -192,9 +181,9 @@ def of(cls, *schema):
>>> el = Points([dict(x=1, y=2)])
>>> point = el[0]
>>> point['x']
-
+
>>> point['y']
-
+
"""
for field in schema:
@@ -379,7 +368,7 @@ def value(self):
@property
def u(self):
return "[%s]" % ", ".join(
- element.u if isinstance(element, Container) else decode_repr(element.u)
+ element.u if isinstance(element, Container) else repr(element.u)
for element in self.children
)
@@ -451,11 +440,9 @@ def _as_element(self, value):
def _new_slot(self, value=Unspecified):
"""Wrap *value* in a Slot named as the element's index in the list."""
- # avoid direct text_type() here so that test suite unicode coercion
- # detector isn't triggered
new_idx = len(self)
name = str(new_idx)
- if not isinstance(name, text_type):
+ if not isinstance(name, str):
name = name.decode("ascii")
return self.slot_type(name=name, parent=self, element=self._as_element(value))
@@ -529,11 +516,7 @@ def reverse(self):
def _renumber(self):
for idx, slot in enumerate(self._slots):
- # don't trigger naive unicode coercion (for test suite)
- name = str(idx)
- if not isinstance(name, text_type):
- name = name.decode("ascii")
- slot.name = name
+ slot.name = str(idx)
@property
def children(self):
@@ -590,7 +573,7 @@ def _set_flat(self, pairs, sep):
# schema-configured maximum. flat + python indexes match.
else:
max_index = min(max(indexes) + 1, self.maximum_set_flat_members)
- for index in xrange(0, max_index):
+ for index in range(0, max_index):
slot = self._new_slot()
list.append(self, slot)
flat = indexes.get(index, None)
@@ -617,7 +600,7 @@ def set_default(self):
del self[:]
if isinstance(default, int):
- for _ in xrange(0, default):
+ for _ in range(0, default):
slot = self._new_slot()
list.append(self, slot)
slot.element.set_default()
@@ -796,7 +779,7 @@ def update(self, *dictish, **kwargs):
elif dictish:
for key, value in to_pairs(dictish[0]):
self[key] = value
- for key, value in iteritems(kwargs):
+ for key, value in kwargs.items():
self[key] = value
def setdefault(self, key, default=None):
@@ -816,7 +799,7 @@ def get(self, key, default=None):
@property
def children(self):
# order not guaranteed
- return itervalues(self)
+ return self.values()
def set(self, value):
""".. TODO:: doc set()"""
@@ -835,7 +818,7 @@ def set(self, value):
)
converted &= self[key].set(value)
seen.add(key)
- required = set(iterkeys(self))
+ required = set(self.keys())
if seen != required:
missing = required - seen
raise TypeError(
@@ -891,15 +874,15 @@ def _index(self, name):
def u(self):
"""A string repr of the element."""
pairs = (
- (key, value.u if isinstance(value, Container) else decode_repr(value.u))
- for key, value in iteritems(self)
+ (key, value.u if isinstance(value, Container) else repr(value.u))
+ for key, value in self.items()
)
- return "{%s}" % ", ".join(f"{decode_repr(k)}: {v}" for k, v in pairs)
+ return "{%s}" % ", ".join(f"{repr(k)}: {v}" for k, v in pairs)
@property
def value(self):
"""The element as a regular Python dictionary."""
- return {key: value.value for key, value in iteritems(self)}
+ return {key: value.value for key, value in self.items()}
@property
def is_empty(self):
@@ -1002,9 +985,7 @@ def set(self, value, policy=None):
if policy is None:
policy = self.policy
if policy not in ("strict", "subset", "duck", None):
- raise RuntimeError(
- f"Unknown {self.__class__.__name__} policy {policy!r}"
- )
+ raise RuntimeError(f"Unknown {self.__class__.__name__} policy {policy!r}")
if policy == "strict":
missing, extra = _evaluate_dict_strict_policy(self, pairs)
@@ -1039,8 +1020,6 @@ def set(self, value, policy=None):
fields = self.field_schema_mapping
converted = True
for key, value in pairs:
- if PY2 and isinstance(key, bytestring_type):
- key = key.decode("ascii", "replace")
if key not in fields:
continue
if dict.__contains__(self, key):
@@ -1097,18 +1076,18 @@ def __init__(self, login=None, password=None):
>>> form = UserForm()
>>> form.set_by_object(user)
>>> form['login'].value
- u'biff'
- >>> form['password'] = u'new-password'
+ 'biff'
+ >>> form['password'] = 'new-password'
>>> form.update_object(user, omit=['verify_password'])
>>> user.password
- u'new-password'
+ 'new-password'
>>> user_keywords = form.slice(omit=['verify_password'], key=str)
>>> sorted(user_keywords.keys())
['login', 'password']
>>> new_user = User(**user_keywords)
"""
- fields = set(iterkeys(self))
+ fields = set(self.keys())
attributes = fields.copy()
if rename:
rename = list(to_pairs(rename))
@@ -1127,9 +1106,7 @@ def __init__(self, login=None, password=None):
final = {key: value for key, value in sliced if key in fields}
self.set(final)
- def update_object(
- self, obj, include=None, omit=None, rename=None, key=identifier_transform
- ):
+ def update_object(self, obj, include=None, omit=None, rename=None, key=lambda x: x):
"""Update an object's attributes using the element's values.
Produces a :meth:`slice` using *include*, *omit*, *rename* and
@@ -1140,12 +1117,12 @@ def update_object(
"""
data = self.slice(include=include, omit=omit, rename=rename, key=key)
- for attribute, value in iteritems(data):
+ for attribute, value in data.items():
setattr(obj, attribute, value)
def slice(self, include=None, omit=None, rename=None, key=None):
"""Return a ``dict`` containing a subset of the element's values."""
- pairs = ((key, element.value) for key, element in sorted(iteritems(self)))
+ pairs = ((key, element.value) for key, element in sorted(self.items()))
sliced = keyslice_pairs(
pairs, include=include, omit=omit, rename=rename, key=key
)
@@ -1250,7 +1227,9 @@ def pop(self, key):
def setdefault(self, key, default=None):
if not self.may_contain(key):
raise TypeError(
- "Key {!r} not allowed in {} {!r}".format(key, type(self).__name__, self.name)
+ "Key {!r} not allowed in {} {!r}".format(
+ key, type(self).__name__, self.name
+ )
)
if key in self:
@@ -1264,7 +1243,7 @@ def setdefault(self, key, default=None):
@property
def is_empty(self):
- for _ in iterkeys(self):
+ for _ in self:
return False
return True
@@ -1289,8 +1268,6 @@ def set_default(self):
def _textset(iterable):
values = set()
for value in iterable:
- if PY2 and isinstance(value, bytestring_type):
- value = value.decode("ascii", "replace")
values.add(value)
return values
diff --git a/src/flatland/schema/declarative.py b/src/flatland/schema/declarative.py
index c62a8d5..7204aa4 100644
--- a/src/flatland/schema/declarative.py
+++ b/src/flatland/schema/declarative.py
@@ -1,6 +1,5 @@
"""Class attribute-style declarative schema construction."""
-from flatland._compat import PY2, with_metaclass
from .base import Element
from .containers import Dict, SparseDict
@@ -38,13 +37,9 @@ def __new__(self, class_name, bases, members):
# add / replace fields declared as attributes on this class
declared_fields = []
for name, value in list(members.items()):
- if PY2:
- text_name = name.decode("ascii")
- else:
- text_name = name
if isinstance(value, type) and issubclass(value, Element):
- if text_name != value.name:
- value = value.named(text_name)
+ if name != value.name:
+ value = value.named(name)
declared_fields.append(value)
del members[name]
fields.add_and_overwrite(declared_fields)
@@ -81,7 +76,7 @@ def add_and_overwrite(self, iterable):
self.elements.append(field)
-class Schema(with_metaclass(_MetaSchema, Dict)):
+class Schema(Dict, metaclass=_MetaSchema):
"""A declarative collection of named elements.
Schemas behave like |Dict|, but are defined with Python class syntax:
@@ -106,7 +101,7 @@ class Schema(with_metaclass(_MetaSchema, Dict)):
...
>>> helloworld = HelloSchema()
>>> sorted(helloworld.keys())
- [u'hello', u'world']
+ ['hello', 'world']
Schemas may embed other container fields and other schemas:
@@ -138,14 +133,14 @@ class attributes. They are removed from the class dictionary and placed
>>> hasattr(HelloSchema, 'hello')
False
>>> sorted([field.name for field in HelloSchema.field_schema])
- [u'hello', u'world']
+ ['hello', 'world']
The order of ``field_schema`` is undefined.
"""
-class SparseSchema(with_metaclass(_MetaSchema, SparseDict)):
+class SparseSchema(SparseDict, metaclass=_MetaSchema):
"""A sparse variant of `~flatland.schema.declarative.Schema`.
Exactly as ``Schema``, but based upon
@@ -154,5 +149,5 @@ class SparseSchema(with_metaclass(_MetaSchema, SparseDict)):
"""
-class Form(with_metaclass(_MetaSchema, Dict)):
+class Form(Dict, metaclass=_MetaSchema):
"""An alias for Schema, for older flatland version compatibility."""
diff --git a/src/flatland/schema/paths.py b/src/flatland/schema/paths.py
index b5cab97..6878570 100644
--- a/src/flatland/schema/paths.py
+++ b/src/flatland/schema/paths.py
@@ -1,7 +1,6 @@
import re
-from flatland._compat import PY2, bytestring_type, text_type, xrange
-from flatland.util import decode_repr, symbol
+from flatland.util import symbol
__all__ = ["pathexpr"]
@@ -41,9 +40,7 @@
def pathexpr(expr):
if isinstance(expr, PathExpression):
return expr
- elif PY2 and isinstance(expr, bytestring_type):
- expr = text_type(expr)
- elif isinstance(expr, text_type):
+ elif isinstance(expr, str):
pass
elif hasattr(expr, "__iter__"):
expr = "/".join(expr)
@@ -68,7 +65,7 @@ def __call__(self, element, strict=False):
contexts = [(self.ops, element)]
for _ops, el in contexts:
- for idx in xrange(len(_ops)):
+ for idx in range(len(_ops)):
op, data = _ops[idx]
if op is TOP:
el = el.root
@@ -84,13 +81,13 @@ def __call__(self, element, strict=False):
if strict:
if el.name:
type_ = "{} element {}".format(
- el.__class__.__name__, decode_repr(el.name)
+ el.__class__.__name__, repr(el.name)
)
else:
type_ = "Unnamed element %s" % (el.__class__.__name__)
raise LookupError(
"{} has no child {} in expression {}".format(
- type_, decode_repr(data), decode_repr(self.expr)
+ type_, repr(data), repr(self.expr)
)
)
break
@@ -103,8 +100,6 @@ def __call__(self, element, strict=False):
return found
def __str__(self):
- if PY2:
- return self.expr.encode("utf8")
return self.expr
def __unicode__(self):
diff --git a/src/flatland/schema/properties.py b/src/flatland/schema/properties.py
index 66af70a..c9f1589 100644
--- a/src/flatland/schema/properties.py
+++ b/src/flatland/schema/properties.py
@@ -1,6 +1,5 @@
from weakref import WeakKeyDictionary
-from flatland._compat import iteritems
from flatland.util import symbol
Deleted = symbol("deleted")
@@ -8,23 +7,14 @@
class DictLike:
- def iteritems(self): # pragma: nocover
+ def items(self): # pragma: nocover
raise NotImplementedError
- def items(self):
- return list(self.iteritems())
-
- def iterkeys(self):
- return (item[0] for item in self.iteritems())
-
def keys(self):
- return list(self.iterkeys())
-
- def itervalues(self):
- return (item[1] for item in self.iteritems())
+ return (item[0] for item in self.items())
def values(self):
- return list(self.itervalues())
+ return (item[1] for item in self.items())
def get(self, key, default=None):
try:
@@ -33,13 +23,13 @@ def get(self, key, default=None):
return default
def copy(self):
- return dict(self.iteritems())
+ return dict(self.items())
def popitem(self):
raise NotImplementedError
def __contains__(self, key):
- return key in self.iterkeys()
+ return key in self.keys()
def __bool__(self):
return bool(self.copy())
@@ -85,7 +75,7 @@ def __delitem__(self, key):
def clear(self):
frame = self._base_frame
- for key in self.iterkeys():
+ for key in self.keys():
frame[key] = Deleted
def pop(self, key, *default):
@@ -105,10 +95,10 @@ def update(self, *iterable, **values):
simplified = dict(*iterable, **values)
self._base_frame.update(simplified)
- def iteritems(self):
+ def items(self):
seen = set()
for frame in self._frames():
- for key, value in iteritems(frame):
+ for key, value in frame.items():
if key not in seen:
seen.add(key)
if value is not Deleted:
@@ -195,13 +185,13 @@ def update(self, *iterable, **values):
simplified = dict(*iterable, **values)
self.local.update(simplified)
- def iteritems(self):
+ def items(self):
seen = set()
- for key, value in iteritems(self.local):
+ for key, value in self.local.items():
seen.add(key)
if value is not Deleted:
yield key, value
- for key, value in self.class_lookup.iteritems():
+ for key, value in self.class_lookup.items():
if key not in seen:
seen.add(key)
if value is not Deleted: # pragma: nocover (coverage bug)
diff --git a/src/flatland/schema/scalars.py b/src/flatland/schema/scalars.py
index 5757cda..147b012 100644
--- a/src/flatland/schema/scalars.py
+++ b/src/flatland/schema/scalars.py
@@ -2,13 +2,6 @@
import decimal
import re
-from flatland._compat import (
- PY2,
- long_type,
- string_types,
- text_type,
- text_transform,
-)
from flatland.exc import AdaptationError
from flatland.signals import element_set
from flatland.util import (
@@ -71,7 +64,7 @@ def set(self, obj):
of ``None`` will be represented as ``u''`` in ``.u``.
If adaptation fails, ``.value`` will be ``None`` and ``.u`` will
- contain ``str(obj)`` (or unicode), or ``u''`` for none.
+ contain ``str(obj)``, or ``''`` for none.
"""
self.raw = obj
@@ -83,15 +76,15 @@ def set(self, obj):
# but, still try to textify it
if obj is None:
self.u = ""
- elif isinstance(obj, text_type):
+ elif isinstance(obj, str):
self.u = obj
else:
try:
- self.u = text_transform(obj)
+ self.u = str(obj)
except TypeError:
self.u = ""
except UnicodeDecodeError:
- self.u = text_type(obj, errors="replace")
+ self.u = str(obj, errors="replace")
element_set.send(self, adapted=False)
return False
@@ -123,10 +116,10 @@ def serialize(self, obj):
compatible type.
This semi-abstract method is called by :meth:`set`. The base
- implementation returns ``str(obj)`` (or unicode).
+ implementation returns ``str(obj)``.
"""
- return text_transform(obj)
+ return str(obj)
def _index(self, name):
raise IndexError(name)
@@ -148,8 +141,6 @@ def __bool__(self):
__nonzero__ = __bool__
def __str__(self):
- if PY2:
- return self.u.encode("utf8", "replace")
return self.u
def __unicode__(self):
@@ -178,8 +169,8 @@ def adapt(self, value):
"""
if value is None:
return None
- if not isinstance(value, text_type):
- value = text_transform(value)
+ if not isinstance(value, str):
+ value = str(value)
if self.strip:
return value.strip()
@@ -197,8 +188,8 @@ def serialize(self, value):
"""
if value is None:
return ""
- if not isinstance(value, text_type):
- value = text_transform(value)
+ if not isinstance(value, str):
+ value = str(value)
if self.strip:
return value.strip()
@@ -238,7 +229,7 @@ def adapt(self, value):
"""
if value is None:
return None
- if isinstance(value, string_types):
+ if isinstance(value, (str, bytes)):
value = value.strip() # decimal.Decimal doesn't like whitespace
try:
native = self.type_(value)
@@ -253,9 +244,8 @@ def adapt(self, value):
def serialize(self, value):
"""Generic numeric serialization.
- :returns: Unicode text formatted with :attr:`format` or the
- ``str()`` (or unicode) of *value* if *value* is not of
- :attr:`type_`
+ :returns: Text formatted with :attr:`format` or the
+ ``str()`` of *value* if *value* is not of :attr:`type_`
Converts *value* to a string using Python's string formatting function
and the :attr:`format` as the template. The *value* is provided to
@@ -264,7 +254,7 @@ def serialize(self, value):
"""
if type(value) is self.type_:
return self.format % value
- return text_transform(value)
+ return str(value)
class Integer(Number):
@@ -274,17 +264,17 @@ class Integer(Number):
"""``int``"""
format = "%i"
- """``u'%i'``"""
+ """``'%i'``"""
class Long(Number):
- """Element type for Python's long."""
+ """Element type for Python's integer."""
- type_ = long_type
- """``long``, or ``int`` on Python 3."""
+ type_ = int
+ """``int``"""
format = "%i"
- """``u'%i'``"""
+ """``'%i'``"""
class Float(Number):
@@ -294,7 +284,7 @@ class Float(Number):
"""``float``"""
format = "%f"
- """``u'%f'``"""
+ """``'%f'``"""
class Decimal(Number):
@@ -304,28 +294,28 @@ class Decimal(Number):
"""``decimal.Decimal``"""
format = "%f"
- """``u'%f'``"""
+ """``'%f'``"""
class Boolean(Scalar):
"""Element type for Python's ``bool``."""
true = "1"
- """The text serialization for ``True``: ``u'1'``."""
+ """The text serialization for ``True``: ``'1'``."""
true_synonyms = ("on", "true", "True", "1")
"""A sequence of acceptable string equivalents for True.
- Defaults to ``(u'on', u'true', u'True', u'1')``
+ Defaults to ``('on', 'true', 'True', '1')``
"""
false = ""
- """The text serialization for ``False``: ``u''``."""
+ """The text serialization for ``False``: ``''``."""
false_synonyms = ("off", "false", "False", "0", "")
"""A sequence of acceptable string equivalents for False.
- Defaults to ``(u'off', u'false', u'False', u'0', u'')``
+ Defaults to ``('off', 'false', 'False', '0', '')``
"""
def adapt(self, value):
@@ -340,7 +330,7 @@ def adapt(self, value):
For non-text values, equivalent to ``bool(value)``.
"""
- if not isinstance(value, text_type):
+ if not isinstance(value, str):
return bool(value)
elif value == self.true or value in self.true_synonyms:
return True
@@ -379,11 +369,11 @@ class Constrained(Scalar):
...
>>> schema = Constrained.using(child_type=Integer, valid_value=is_valid)
>>> element = schema()
- >>> element.set(u'2')
+ >>> element.set('2')
True
>>> element.value
2
- >>> element.set(u'5')
+ >>> element.set('5')
False
>>> element.value is None
True
@@ -481,7 +471,7 @@ def adapt(self, value):
return value
elif isinstance(value, self.type_):
return value
- elif isinstance(value, string_types):
+ elif isinstance(value, (str, bytes)):
if self.strip:
value = value.strip()
match = self.regex.match(value)
@@ -499,14 +489,13 @@ def serialize(self, value):
"""Serializes value to string.
If *value* is an instance of :attr:`type`, formats it as described in
- the attribute documentation. Otherwise returns ``str(value)`` (or
- unicode).
+ the attribute documentation. Otherwise returns ``str(value)``.
"""
if isinstance(value, self.type_):
return self.format % as_mapping(value)
else:
- return text_transform(value)
+ return str(value)
class DateTime(Temporal):
diff --git a/src/flatland/schema/util.py b/src/flatland/schema/util.py
index 75be7da..721477b 100644
--- a/src/flatland/schema/util.py
+++ b/src/flatland/schema/util.py
@@ -1,6 +1,6 @@
import itertools
-from flatland._compat import builtins
+import builtins
def element_ancestry(element):
diff --git a/src/flatland/util.py b/src/flatland/util.py
index fad2bd1..5b9a2df 100644
--- a/src/flatland/util.py
+++ b/src/flatland/util.py
@@ -7,19 +7,6 @@
except ImportError: # pragma:nocover
import dummy_threading as threading
-from flatland._compat import PY2, text_type
-
-
-def decode_repr(x):
- """create a unicode string representation (as a unicode string)
- for py2 and py3 that looks the same: u'example'
- """
- r = repr(x)
- if PY2:
- return r.decode("raw_unicode_escape")
- else:
- return "u" + r
-
# derived from ASPN Cookbook (#36302)
class lazy_property:
@@ -183,18 +170,11 @@ def __init__(self, target):
def __getitem__(self, item):
try:
- if PY2 and isinstance(item, text_type):
- return getattr(self.target, item.encode("ascii"))
return getattr(self.target, item)
except (AttributeError, UnicodeError):
raise KeyError(item)
def __contains__(self, item):
- if PY2 and isinstance(item, text_type):
- try:
- return hasattr(self.target, item.encode("ascii"))
- except UnicodeError:
- return False
return hasattr(self.target, item)
def __iter__(self):
@@ -216,9 +196,7 @@ def re_ucompile(pattern, flags=0):
return re.compile(pattern, flags | re.UNICODE)
-_alphanum = set(
- text_type(string.digits + string.ascii_lowercase + string.ascii_uppercase)
-)
+_alphanum = set(str(string.digits + string.ascii_lowercase + string.ascii_uppercase))
def re_uescape(pattern):
@@ -240,12 +218,12 @@ def to_pairs(dictlike):
"dictlike".
"""
- if hasattr(dictlike, "iteritems"):
- return dictlike.iteritems()
+ if hasattr(dictlike, "items"):
+ return dictlike.items()
elif hasattr(dictlike, "keys"):
return ((key, dictlike[key]) for key in dictlike.keys())
elif hasattr(dictlike, "_asdict"): # namedtuple interface
- return dictlike._asdict().iteritems()
+ return dictlike._asdict().items()
else:
return ((key, value) for key, value in dictlike)
@@ -269,8 +247,8 @@ def keyslice_pairs(pairs, include=None, omit=None, rename=None, key=None):
:param key: optional, a function of one argument that is used to extract a
comparison key from the first item of each pair. Similar to the
- ``key`` parameter to Python's ``sort`` and ``sorted``. Useful for
- transforming unicode keys to bytestrings with ```key=str``, adding or
+ ``key`` parameter to Python's ``sort`` and ``sorted``.
+ Useful for transforming keys to other types ```key=type``, adding or
removing prefixes en masse, etc.
:returns: yields ``(key, value)`` pairs.
diff --git a/src/flatland/validation/base.py b/src/flatland/validation/base.py
index 58cdf7f..243d053 100644
--- a/src/flatland/validation/base.py
+++ b/src/flatland/validation/base.py
@@ -2,7 +2,6 @@
from operator import attrgetter
-from flatland._compat import getattr_py2, hasattr_py2, iteritems, setattr_py2
from flatland.schema.util import find_i18n_function
N_ = lambda translatable: translatable
@@ -21,9 +20,9 @@ def __init__(self, **kw):
"""
cls = type(self)
- for attr, value in iteritems(kw):
- if hasattr_py2(cls, attr):
- setattr_py2(self, attr, value)
+ for attr, value in kw.items():
+ if hasattr(cls, attr):
+ setattr(self, attr, value)
else:
raise TypeError(
"{} has no attribute {!r}, can not override.".format(
@@ -108,7 +107,7 @@ def validate(self, element, state):
assert el.errors == ['Oh noes!']
"""
- message = message or getattr_py2(self, key)
+ message = message or getattr(self, key)
if message:
element.add_error(self.expand_message(element, state, message, **info))
return False
@@ -143,7 +142,7 @@ def note_warning(self, element, state, key=None, message=None, **info):
Always returns False.
"""
- message = message or getattr_py2(self, key)
+ message = message or getattr(self, key)
if message:
element.add_warning(self.expand_message(element, state, message, **info))
return False
@@ -182,8 +181,8 @@ def find_transformer(self, type, element, state, message):
5. Otherwise return ``None``.
"""
- if hasattr_py2(state, type):
- return getattr_py2(state, type)
+ if hasattr(state, type):
+ return getattr(state, type)
if hasattr(state, "__getitem__"):
try:
return state[type]
@@ -280,8 +279,6 @@ def __init__(self, *targets, **kw):
raise TypeError("unexpected keyword argument")
def __getitem__(self, item):
- __hopeless_morass_of_unicode_hell__ = True
-
for target in self.targets:
# try target[item] first
if hasattr(target, "__getitem__"):
@@ -292,7 +289,7 @@ def __getitem__(self, item):
pass
# then target.item
try:
- value = getattr_py2(target, item)
+ value = getattr(target, item)
break
except AttributeError:
pass
diff --git a/src/flatland/validation/network.py b/src/flatland/validation/network.py
index 5811b3f..04964c1 100644
--- a/src/flatland/validation/network.py
+++ b/src/flatland/validation/network.py
@@ -1,14 +1,9 @@
"""Network address and URL validation."""
-from flatland._compat import PY2
import re
-if PY2:
- import urlparse
-else:
- from urllib import parse as urlparse
+from urllib import parse as urlparse
-from flatland._compat import identifier_transform, text_transform
from .base import N_, Validator
@@ -173,11 +168,10 @@ def validate(self, element, state):
except Exception:
return self.note_error(element, state, "bad_format")
- scheme_name = identifier_transform(url.scheme)
- if scheme_name == "":
+ if url.scheme == "":
return self.note_error(element, state, "blocked_scheme")
elif self.allowed_schemes != ("*",):
- if scheme_name not in self.allowed_schemes:
+ if url.scheme not in self.allowed_schemes:
return self.note_error(element, state, "blocked_scheme")
for part in _url_parts:
@@ -271,7 +265,7 @@ def validate(self, element, state):
try:
value = getattr(parsed, part)
if part == "port":
- value = None if value is None else text_transform(value)
+ value = None if value is None else str(value)
except ValueError:
return self.note_error(element, state, "bad_format")
required = self.required_parts.get(part)
diff --git a/tests/__init__.py b/tests/__init__.py
index ce973a4..8b13789 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,5 +1 @@
-# TODO: switch this on with an environ variable or something.
-# and document.
-# def setup_package():
-# import tests._util
-# tests._util.enable_coercion_blocker()
+
diff --git a/tests/_util.py b/tests/_util.py
deleted file mode 100644
index dcbae81..0000000
--- a/tests/_util.py
+++ /dev/null
@@ -1,174 +0,0 @@
-"""Test suite helpers."""
-
-import codecs
-from contextlib import contextmanager
-from functools import wraps
-from inspect import stack
-import sys
-
-from flatland._compat import long_type, text_type
-
-__all__ = [
- "asciistr",
- "fails",
- "requires_unicode_coercion",
- "udict",
- "unicode_coercion_available",
-]
-
-
-# acts like 'str', but safe to use when tests are running with
-# sys.getdefaultencoding() == 'nocoercion'.
-_ascii_codec = codecs.getencoder("ascii")
-asciistr = lambda s: _ascii_codec(s)[0]
-# acts like naive unicode() on simple types like int
-textstr = lambda o: text_type(str(o))
-
-_coercion_override = None
-
-
-def fails(reason):
- """Mark a test case as expected to fail for *reason*."""
-
- def decorator(fn):
- @wraps(fn)
- def decorated(*args, **kw):
- try:
- fn(*args, **kw)
- except (SystemExit, KeyboardInterrupt):
- raise
- except:
- pass # ok
- else:
- raise AssertionError("Unexpected success.")
-
- return decorated
-
- return decorator
-
-
-def udict(*dictionary, **kwargs):
- "Return a dict with unicode keys. A stand-in for the dict constructor."
- kwargs = {text_type(k): v for k, v in kwargs.items()}
- if dictionary:
- base = {text_type(k): v for k, v in dictionary[0].items()}
- base.update(kwargs)
- return base
- else:
- return kwargs
-
-
-def unicode_coercion_available():
- return sys.getdefaultencoding() != "nocoercion"
-
-
-def requires_unicode_coercion(fn):
- @wraps(fn)
- def decorated(*args, **kw):
- if sys.getdefaultencoding() != "nocoercion":
- return fn(*args, **kw)
- try:
- fn(*args, **kw)
- except UnicodeError:
- # failure is success!
- return
- else:
- raise AssertionError("Test did not trigger expected UnicodeError.")
-
- return decorated
-
-
-@contextmanager
-def unicode_coercion_allowed():
- global _coercion_override
- initial_value = _coercion_override
- try:
- _coercion_override = True
- yield
- finally:
- _coercion_override = initial_value
-
-
-def _allowed_coercion(input):
- if _coercion_override:
- return True
- # TODO: this isn't hit anymore (buffer comes in). did it ever work?
- if isinstance(input, (int, float, long_type, type(None))):
- return True
-
- try:
- caller = stack()[2]
- if "__hopeless_morass_of_unicode_hell__" in caller[0].f_locals:
- return True
-
- calling_path = caller[1]
- if "/" in calling_path:
- calling_file = calling_path.rsplit("/", 1)[1]
- else:
- calling_file = calling_path
-
- if calling_file in ("sre_parse.py", "decimal.py", "urlparse.py"):
- return True
- elif "/_pytest/" in calling_path:
- return True
- elif "genshi" in calling_path and "out/genshi" not in calling_path:
- # OMG slow on genshi 0.5.2
- return True
- # this does lots of expected '%s' formatting e.g. unicode(2)
- elif "flatland/validation" in calling_path and caller[3] == "expand_message":
- return True
- return False
- finally:
- del caller
-
-
-class NoCoercionCodec(codecs.Codec):
-
- def encode(self, input, errors="string"):
- if _allowed_coercion(input):
- return codecs.ascii_encode(input, errors)
- raise UnicodeError("encoding coercion blocked")
-
- def decode(self, input, errors="strict"):
- if _allowed_coercion(input):
- return codecs.ascii_decode(input, errors)
- raise UnicodeError("encoding coercion blocked")
-
-
-class _IncrementalEncoder(codecs.IncrementalEncoder):
-
- def encode(self, input, final=False):
- if _allowed_coercion(input):
- return codecs.ascii_encode(input, self.errors)[0]
- raise UnicodeError("encoding coercion blocked")
-
-
-class _IncrementalDecoder(codecs.IncrementalDecoder):
-
- def decode(self, input, final=False):
- if _allowed_coercion(input):
- return codecs.ascii_decode(input, self.errors)[0]
- raise UnicodeError("encoding coercion blocked")
-
-
-class _StreamWriter(NoCoercionCodec, codecs.StreamWriter):
- pass
-
-
-class _StreamReader(NoCoercionCodec, codecs.StreamReader):
- pass
-
-
-def enable_coercion_blocker():
- registration = lambda name: codecs.CodecInfo(
- name="nocoercion",
- encode=NoCoercionCodec().encode,
- decode=NoCoercionCodec().decode,
- incrementalencoder=_IncrementalEncoder,
- incrementaldecoder=_IncrementalDecoder,
- streamwriter=_StreamWriter,
- streamreader=_StreamReader,
- )
- codecs.register(registration)
- reload(sys)
- sys.setdefaultencoding("nocoercion")
diff --git a/tests/markup/_util.py b/tests/markup/_util.py
index ff229f0..5291180 100644
--- a/tests/markup/_util.py
+++ b/tests/markup/_util.py
@@ -3,8 +3,6 @@
import pytest
-from flatland._compat import bytestring_type
-
class Capabilities(dict):
@@ -70,7 +68,7 @@ def __init__(self, language, schema, **kw):
def __call__(self, fn):
self.expected = inspect.cleandoc(fn.__doc__).strip()
- if isinstance(self.expected, bytestring_type):
+ if isinstance(self.expected, bytes):
self.expected = self.expected.decode("utf8")
self.alternate_expectations = getattr(fn, "alternates", {})
return self
diff --git a/tests/markup/test_transforms.py b/tests/markup/test_transforms.py
index 3cd330d..7f493c4 100644
--- a/tests/markup/test_transforms.py
+++ b/tests/markup/test_transforms.py
@@ -2,8 +2,6 @@
from flatland.out import generic
from flatland.out.generic import Context
-from tests._util import unicode_coercion_allowed, textstr
-
Unspecified = object()
Unique = object()
schema = Integer.named("number")
@@ -477,12 +475,9 @@ def test_value_option():
)
# This matches value = contents via a sentinel object
- with unicode_coercion_allowed():
- given = {}
- expected = {}
- assert_transform(
- generic.transform_value, "option", given, expected, bind=bind
- )
+ given = {}
+ expected = {}
+ assert_transform(generic.transform_value, "option", given, expected, bind=bind)
contents = expected_contents = "123"
given = {
@@ -638,7 +633,7 @@ def test_tabindex_stop_numbers():
for stop_num in -1, -2:
context = Context()
context["tabindex"] = stop_num
- expected = {"tabindex": textstr(stop_num)}
+ expected = {"tabindex": str(stop_num)}
assert_bound_transform(
generic.transform_tabindex, "input", given, expected, context=context
)
diff --git a/tests/schema/test_arrays.py b/tests/schema/test_arrays.py
index b19b3b0..ca9a92c 100644
--- a/tests/schema/test_arrays.py
+++ b/tests/schema/test_arrays.py
@@ -7,7 +7,6 @@
)
import pytest
-from tests._util import udict
def test_set_flat_pruned():
@@ -160,7 +159,7 @@ def test_mutation():
el.pop()
assert el.value == ["f"]
assert el[0].u == "f"
- assert el.u == "[u'f']"
+ assert el.u == "['f']"
del el[:]
assert list(el) == []
diff --git a/tests/schema/test_base.py b/tests/schema/test_base.py
index 44d172d..8cf4a55 100644
--- a/tests/schema/test_base.py
+++ b/tests/schema/test_base.py
@@ -5,10 +5,8 @@
SkipAllFalse,
Unevaluated,
)
-from flatland._compat import xrange
import pytest
-from tests._util import requires_unicode_coercion
def test_cloning():
@@ -18,7 +16,6 @@ def test_cloning():
assert "test_base" in new_element.__module__
-@requires_unicode_coercion
def test_naming():
for arg in ("unicode", "sysencoding", None):
schema = Element.named(arg)
@@ -40,11 +37,11 @@ def test_validators():
el = Element(validators=(123, 456))
assert el.validators == [123, 456]
- el = Element(validators=xrange(3))
- assert el.validators == list(xrange(3))
+ el = Element(validators=range(3))
+ assert el.validators == list(range(3))
- schema = Element.using(validators=xrange(3))
- assert schema.validators == list(xrange(3))
+ schema = Element.using(validators=range(3))
+ assert schema.validators == list(range(3))
def test_dsl_validated_by():
diff --git a/tests/schema/test_constrained.py b/tests/schema/test_constrained.py
index 949cf6c..74299e4 100644
--- a/tests/schema/test_constrained.py
+++ b/tests/schema/test_constrained.py
@@ -3,7 +3,6 @@
Enum,
Integer,
)
-from flatland._compat import text_transform
def test_constrained_no_default_validity():
@@ -102,9 +101,9 @@ def test_typed_enum():
for good_val in good_values:
el = schema()
- assert el.set(text_transform(good_val))
+ assert el.set(str(good_val))
assert el.value == good_val
- assert el.u == text_transform(good_val)
+ assert el.u == str(good_val)
assert not el.errors
el = schema()
diff --git a/tests/schema/test_declarative.py b/tests/schema/test_declarative.py
index d59e076..6e85681 100644
--- a/tests/schema/test_declarative.py
+++ b/tests/schema/test_declarative.py
@@ -4,10 +4,7 @@
String,
)
-from tests._util import requires_unicode_coercion
-
-@requires_unicode_coercion
def test_from_object():
class Obj:
@@ -66,7 +63,6 @@ class Outer(Schema):
assert el.value == wanted
-@requires_unicode_coercion
def test_inheritance_straight():
class Base(Schema):
@@ -85,7 +81,6 @@ class Sub(Base):
assert set(Sub().keys()) == {"base_member", "added_member"}
-@requires_unicode_coercion
def test_inheritance_diamond():
class A(Schema):
diff --git a/tests/schema/test_dicts.py b/tests/schema/test_dicts.py
index 8d3f4ca..0ffb553 100644
--- a/tests/schema/test_dicts.py
+++ b/tests/schema/test_dicts.py
@@ -6,15 +6,9 @@
Unset,
element_set,
)
-from flatland._compat import PY2, iteritems
from flatland.util import Unspecified, keyslice_pairs
import pytest
-from tests._util import (
- asciistr,
- udict,
- unicode_coercion_available,
-)
def test_dict():
@@ -77,41 +71,30 @@ def test_dict_update():
el = schema()
def value_dict(element):
- return {k: v.value for k, v in iteritems(element)}
+ return {k: v.value for k, v in element.items()}
- try:
- el.update(x=20, y=30)
- except UnicodeError:
- assert not unicode_coercion_available()
- el.update(udict(x=20, y=30))
- assert udict(x=20, y=30) == el.value
+ el.update(x=20, y=30)
+ assert {"x": 20, "y": 30} == el.value
el.update({"y": 40})
- assert udict(x=20, y=40) == el.value
+ assert {"x": 20, "y": 40} == el.value
el.update()
- assert udict(x=20, y=40) == el.value
+ assert {"x": 20, "y": 40} == el.value
el.update((_, 100) for _ in "xy")
- assert udict(x=100, y=100) == el.value
-
- try:
- el.update([("x", 1)], y=2)
- assert udict(x=1, y=2) == el.value
- except UnicodeError:
- assert not unicode_coercion_available()
-
- try:
- el.update([("x", 10), ("y", 10)], x=20, y=20)
- assert udict(x=20, y=20) == el.value
- except UnicodeError:
- assert not unicode_coercion_available()
-
- if unicode_coercion_available():
- with pytest.raises(TypeError):
- el.update(z=1)
- with pytest.raises(TypeError):
- el.update(x=1, z=1)
+ assert {"x": 100, "y": 100} == el.value
+
+ el.update([("x", 1)], y=2)
+ assert {"x": 1, "y": 2} == el.value
+
+ el.update([("x", 10), ("y", 10)], x=20, y=20)
+ assert {"x": 20, "y": 20} == el.value
+
+ with pytest.raises(TypeError):
+ el.update(z=1)
+ with pytest.raises(TypeError):
+ el.update(x=1, z=1)
with pytest.raises(TypeError):
el.update({"z": 1})
with pytest.raises(TypeError):
@@ -198,7 +181,7 @@ def test_full_set(self):
assert el.value == wanted
el = self.new_element()
- el.set(udict(x=101, y=102))
+ el.set({"x": 101, "y": 102})
assert el.value == wanted
el = self.new_element()
@@ -372,7 +355,7 @@ def test_dict_as_unicode():
schema = Dict.of(Integer.named("x"), Integer.named("y"))
el = schema({"x": 1, "y": 2})
- assert el.u in ("{u'x': u'1', u'y': u'2'}", "{u'y': u'2', u'x': u'1'}")
+ assert el.u in ("{'x': '1', 'y': '2'}", "{'y': '2', 'x': '1'}")
def test_nested_dict_as_unicode():
@@ -380,7 +363,7 @@ def test_nested_dict_as_unicode():
el = schema.from_defaults()
assert el.value == {"d": {"x": 10}}
- assert el.u == "{u'd': {u'x': u'10'}}"
+ assert el.u == "{'d': {'x': '10'}}"
def test_nested_unicode_dict_as_unicode():
@@ -389,7 +372,7 @@ def test_nested_unicode_dict_as_unicode():
)
el = schema.from_defaults()
assert el.value == {"d": {"x": "\u2308\u2309"}}
- assert el.u == "{u'd': {u'x': u'\u2308\u2309'}}"
+ assert el.u == "{'d': {'x': '\u2308\u2309'}}"
def test_dict_find():
@@ -420,7 +403,7 @@ def __init__(self, **kw):
def updated_(obj_factory, initial_value, wanted=None, **update_kw):
el = schema(initial_value)
obj = obj_factory()
- keyfunc = lambda x: (asciistr(x) if PY2 else x)
+ keyfunc = lambda x: x
update_kw.setdefault("key", keyfunc)
el.update_object(obj, **update_kw)
if wanted is None:
@@ -449,7 +432,7 @@ def same_(source, kw):
assert {type(_) for _ in sliced.keys()} == {type(_) for _ in wanted.keys()}
same_({"x": "X", "y": "Y"}, {})
- same_({"x": "X", "y": "Y"}, dict(key=asciistr))
+ same_({"x": "X", "y": "Y"}, dict(key=lambda s: s.encode("ascii")))
same_({"x": "X", "y": "Y"}, dict(include=["x"]))
same_({"x": "X", "y": "Y"}, dict(omit=["x"]))
same_({"x": "X", "y": "Y"}, dict(omit=["x"], rename={"y": "z"}))
diff --git a/tests/schema/test_lists.py b/tests/schema/test_lists.py
index b9cd738..d8f0b83 100644
--- a/tests/schema/test_lists.py
+++ b/tests/schema/test_lists.py
@@ -5,7 +5,6 @@
Unset,
element_set,
)
-from flatland._compat import xrange, text_type
from flatland.schema.base import Unspecified
@@ -272,7 +271,7 @@ def test_set():
assert el.value == [0, 1, 2]
el = schema()
- assert el.set(xrange(3))
+ assert el.set(range(3))
assert el.value == [0, 1, 2]
el = schema([0, 1, 2])
@@ -373,7 +372,7 @@ def test_mutation():
def order_ok():
slot_names = list(_.name for _ in el._slots)
for idx, name in enumerate(slot_names):
- assert name == text_type(str(idx))
+ assert name == str(idx)
assert not el
order_ok()
@@ -476,7 +475,7 @@ def test_slots():
def test_u():
schema = List.of(String)
el = schema(["x", "x"])
- assert el.u == "[u'x', u'x']"
+ assert el.u == "['x', 'x']"
def test_value():
diff --git a/tests/schema/test_paths.py b/tests/schema/test_paths.py
index 38f959d..66fd9a6 100644
--- a/tests/schema/test_paths.py
+++ b/tests/schema/test_paths.py
@@ -201,15 +201,15 @@ def test_find_strict_messaging():
el = Mixed.from_defaults()
message = _find_message(el, "bogus")
- expected = "Unnamed element Mixed has no child u'bogus' " "in expression u'bogus'"
+ expected = "Unnamed element Mixed has no child 'bogus' " "in expression 'bogus'"
assert expected in message
message = _find_message(el, "d1/bogus")
- expected = "Dict element u'd1' has no child u'bogus' " "in expression u'd1/bogus'"
+ expected = "Dict element 'd1' has no child 'bogus' " "in expression 'd1/bogus'"
assert expected in message
message = _find_message(el, "l1[5]")
- expected = "List element u'l1' has no child u'5' " "in expression u'l1[5]'"
+ expected = "List element 'l1' has no child '5' " "in expression 'l1[5]'"
assert expected in message
diff --git a/tests/schema/test_properties.py b/tests/schema/test_properties.py
index bd0995f..9782c8f 100644
--- a/tests/schema/test_properties.py
+++ b/tests/schema/test_properties.py
@@ -1,5 +1,4 @@
from flatland import String
-from flatland._compat import PY2, iterkeys, itervalues
from flatland.schema.properties import Properties
import pytest
@@ -42,10 +41,8 @@ class Base:
assert sorted(props.items()) == [("abc", 123), ("def", 456)]
assert sorted(props.keys()) == ["abc", "def"]
- assert sorted(iterkeys(props)) == ["abc", "def"]
assert sorted(props.values()) == [123, 456]
- assert sorted(itervalues(props)) == [123, 456]
assert props.get("abc") == 123
assert props.get("abc", "blah") == 123
@@ -284,21 +281,6 @@ class Broken:
assert Broken.properties == "something else"
-# python3 immediately raises an exception if there is such a name clash
-if PY2:
-
- def test_perverse_slots():
-
- class Base:
- __slots__ = ("properties",)
- properties = Properties()
-
- b = Base()
- with pytest.raises(AttributeError):
- # noinspection PyStatementEffect
- b.properties["abc"]
-
-
def test_dsl():
Sub = String.with_properties(abc=123)
diff --git a/tests/schema/test_scalars.py b/tests/schema/test_scalars.py
index 75b4b25..0d4388c 100644
--- a/tests/schema/test_scalars.py
+++ b/tests/schema/test_scalars.py
@@ -15,10 +15,8 @@
Unset,
element_set,
)
-from flatland._compat import PY2, long_type
import pytest
-from tests._util import requires_unicode_coercion
def test_scalar_abstract():
@@ -112,9 +110,6 @@ def validate_element_set(type_, raw, value, uni, schema_opts={}, set_return=None
assert element.__bool__() == bool(uni and value)
-coerced_validate_element_set = requires_unicode_coercion(validate_element_set)
-
-
def test_scalar_set():
# a variety of scalar set() failure cases, shoved through Integer
for spec in (
@@ -123,11 +118,6 @@ def test_scalar_set():
):
validate_element_set(Integer, *spec)
- if PY2:
- # TODO: test below fails on py3 and it is unclear what it is about.
- for spec in (("\xef\xf0", None, "\ufffd\ufffd"),):
- coerced_validate_element_set(Integer, *spec)
-
def test_scalar_set_signal():
data = []
@@ -168,7 +158,7 @@ def test_integer():
def test_long():
- L = long_type
+ L = int # Python 3
for spec in (
("123", L(123), "123"),
(" 123 ", L(123), "123"),
diff --git a/tests/test_i18n.py b/tests/test_i18n.py
index f9d837b..6ed0e69 100644
--- a/tests/test_i18n.py
+++ b/tests/test_i18n.py
@@ -98,7 +98,7 @@ def test_builtin_gettext():
try:
# translators can go into the builtins
- from flatland._compat import builtins
+ import builtins
builtins.ugettext = catalog.ugettext
builtins.ungettext = catalog.ungettext
diff --git a/tests/validation/test_containers.py b/tests/validation/test_containers.py
index b860eaa..46322f4 100644
--- a/tests/validation/test_containers.py
+++ b/tests/validation/test_containers.py
@@ -13,7 +13,6 @@
SetWithKnownFields,
)
-from tests._util import unicode_coercion_allowed
import pytest
@@ -54,23 +53,20 @@ def _test_no_duplicates(schema, a, b):
el = schema([a, b, a])
assert not el.validate()
assert valid_of_children(el) == [True, True, False]
- with unicode_coercion_allowed():
- assert el[2].errors == [f"{label} {3}"]
+ assert el[2].errors == [f"{label} {3}"]
el = schema([a, b, a, b])
assert not el.validate()
assert valid_of_children(el) == [True, True, False, False]
- with unicode_coercion_allowed():
- assert el[2].errors == [f"{label} {3}"]
- assert el[3].errors == [f"{label} {4}"]
+ assert el[2].errors == [f"{label} {3}"]
+ assert el[3].errors == [f"{label} {4}"]
el = schema([a, a, a, a])
assert not el.validate()
assert valid_of_children(el) == [True, False, False, False]
- with unicode_coercion_allowed():
- assert el[1].errors == [f"{label} {2}"]
- assert el[2].errors == [f"{label} {3}"]
- assert el[3].errors == [f"{label} {4}"]
+ assert el[1].errors == [f"{label} {2}"]
+ assert el[2].errors == [f"{label} {3}"]
+ assert el[3].errors == [f"{label} {4}"]
def test_no_duplicates_list_scalar():
diff --git a/tests/validation/test_network.py b/tests/validation/test_network.py
index 8235272..8c4c0d5 100644
--- a/tests/validation/test_network.py
+++ b/tests/validation/test_network.py
@@ -9,8 +9,6 @@
)
from flatland.validation.network import _url_parts
-from tests._util import unicode_coercion_allowed
-
def email(value):
return String(value, name="email", strip=False)
@@ -41,8 +39,7 @@ def test_email():
def test_email_idna():
- with unicode_coercion_allowed():
- assert_email_valid("bob@snow\u2603man.com")
+ assert_email_valid("bob@snow\u2603man.com")
def test_email_non_local():
diff --git a/tests/validation/test_scalars.py b/tests/validation/test_scalars.py
index 74a3d7e..a13f182 100644
--- a/tests/validation/test_scalars.py
+++ b/tests/validation/test_scalars.py
@@ -22,8 +22,6 @@
ValuesEqual,
)
-from tests._util import unicode_coercion_allowed
-
def form(value):
@@ -88,8 +86,7 @@ def test_value_less_than():
assert V(date.today() + timedelta(days=2)).validate(d, None)
two_days_ago = date.today() - timedelta(days=2)
assert not V(two_days_ago).validate(d, None)
- with unicode_coercion_allowed():
- assert d.errors == ["test must be less than %s." % two_days_ago]
+ assert d.errors == ["test must be less than %s." % two_days_ago]
def test_value_at_most():