summaryrefslogtreecommitdiff
path: root/src/zope/schema
diff options
context:
space:
mode:
Diffstat (limited to 'src/zope/schema')
-rw-r--r--src/zope/schema/_field.py57
-rw-r--r--src/zope/schema/interfaces.py24
-rw-r--r--src/zope/schema/tests/test__field.py57
3 files changed, 119 insertions, 19 deletions
diff --git a/src/zope/schema/_field.py b/src/zope/schema/_field.py
index 1414b47..0444656 100644
--- a/src/zope/schema/_field.py
+++ b/src/zope/schema/_field.py
@@ -65,6 +65,8 @@ from zope.schema.interfaces import IMinMaxLen
from zope.schema.interfaces import IMapping
from zope.schema.interfaces import IMutableMapping
from zope.schema.interfaces import IMutableSequence
+from zope.schema.interfaces import INativeString
+from zope.schema.interfaces import INativeStringLine
from zope.schema.interfaces import IObject
from zope.schema.interfaces import INumber
from zope.schema.interfaces import IPassword
@@ -170,11 +172,27 @@ class Bytes(MinMaxLen, Field):
self.validate(value)
return value
-# for things which are of the str type on both Python 2 and 3
-if PY3: # pragma: no cover
- NativeString = Text
-else: # pragma: no cover
- NativeString = Bytes
+
+@implementer(INativeString, IFromUnicode, IFromBytes)
+class NativeString(Text if PY3 else Bytes):
+ """
+ A native string is always the type `str`.
+
+ In addition to :class:`~zope.schema.interfaces.INativeString`,
+ this implements :class:`~zope.schema.interfaces.IFromUnicode` and
+ :class:`~zope.schema.interfaces.IFromBytes`.
+
+ .. versionchanged:: 4.9.0
+ This is now a distinct type instead of an alias for either `Text` or `Bytes`,
+ depending on the platform.
+ """
+ _type = str
+
+ if PY3:
+ def fromBytes(self, value):
+ value = value.decode('utf-8')
+ self.validate(value)
+ return value
@implementer(IASCII)
@@ -191,17 +209,34 @@ class ASCII(NativeString):
@implementer(IBytesLine)
class BytesLine(Bytes):
- """A Text field with no newlines."""
+ """A `Bytes` field with no newlines."""
def constraint(self, value):
# TODO: we should probably use a more general definition of newlines
return b'\n' not in value
-# for things which are of the str type on both Python 2 and 3
-if PY3: # pragma: no cover
- NativeStringLine = TextLine
-else: # pragma: no cover
- NativeStringLine = BytesLine
+
+@implementer(INativeStringLine, IFromUnicode, IFromBytes)
+class NativeStringLine(TextLine if PY3 else BytesLine):
+ """
+ A native string is always the type `str`; this field excludes
+ newlines.
+
+ In addition to :class:`~zope.schema.interfaces.INativeStringLine`,
+ this implements :class:`~zope.schema.interfaces.IFromUnicode` and
+ :class:`~zope.schema.interfaces.IFromBytes`.
+
+ .. versionchanged:: 4.9.0
+ This is now a distinct type instead of an alias for either `TextLine`
+ or `BytesLine`, depending on the platform.
+ """
+ _type = str
+
+ if PY3:
+ def fromBytes(self, value):
+ value = value.decode('utf-8')
+ self.validate(value)
+ return value
@implementer(IASCIILine)
diff --git a/src/zope/schema/interfaces.py b/src/zope/schema/interfaces.py
index c0ac93b..c6c7aea 100644
--- a/src/zope/schema/interfaces.py
+++ b/src/zope/schema/interfaces.py
@@ -416,10 +416,14 @@ class IText(IMinMaxLen, IIterable, IField):
# for things which are of the str type on both Python 2 and 3
-if PY3: # pragma: no cover
- INativeString = IText
-else: # pragma: no cover
- INativeString = IBytes
+class INativeString(IText if PY3 else IBytes):
+ """
+ A field that always contains the native `str` type.
+
+ .. versionchanged:: 4.9.0
+ This is now a distinct type instead of an alias for either `IText`
+ or `IBytes`, depending on the platform.
+ """
class IASCII(INativeString):
@@ -446,10 +450,14 @@ class ITextLine(IText):
"""Field containing a unicode string without newlines."""
-if PY3: # pragma: no cover
- INativeStringLine = ITextLine
-else: # pragma: no cover
- INativeStringLine = IBytesLine
+class INativeStringLine(ITextLine if PY3 else IBytesLine):
+ """
+ A field that always contains the native `str` type, without any newlines.
+
+ .. versionchanged:: 4.9.0
+ This is now a distinct type instead of an alias for either `ITextLine`
+ or `IBytesLine`, depending on the platform.
+ """
class IPassword(ITextLine):
diff --git a/src/zope/schema/tests/test__field.py b/src/zope/schema/tests/test__field.py
index 0eb02ff..4f99d69 100644
--- a/src/zope/schema/tests/test__field.py
+++ b/src/zope/schema/tests/test__field.py
@@ -1702,6 +1702,63 @@ class DictTests(MutableMappingTests):
with self.assertRaises(WrongType):
super(DictTests, self).test_mutable_mapping()
+class NativeStringTests(EqualityTestsMixin,
+ WrongTypeTestsMixin,
+ unittest.TestCase):
+
+ def _getTargetClass(self):
+ from zope.schema._field import NativeString
+ return NativeString
+
+ def _getTargetInterface(self):
+ from zope.schema.interfaces import INativeString
+ return INativeString
+
+ def _getTargetInterfaces(self):
+ from zope.schema.interfaces import IFromUnicode
+ from zope.schema.interfaces import IFromBytes
+ return [self._getTargetInterface(), IFromUnicode, IFromBytes]
+
+ def test_fromBytes(self):
+ field = self._makeOne()
+ self.assertEqual(field.fromBytes(b''), '')
+ self.assertEqual(field.fromBytes(b'DEADBEEF'), 'DEADBEEF')
+
+ def test_fromUnicode(self):
+ field = self._makeOne()
+ self.assertIsInstance(field.fromUnicode(u''), str)
+ self.assertEqual(field.fromUnicode(u''), '')
+ self.assertEqual(field.fromUnicode(u'DEADBEEF'), 'DEADBEEF')
+
+
+class NativeStringLineTests(EqualityTestsMixin,
+ WrongTypeTestsMixin,
+ unittest.TestCase):
+
+ def _getTargetClass(self):
+ from zope.schema._field import NativeStringLine
+ return NativeStringLine
+
+ def _getTargetInterface(self):
+ from zope.schema.interfaces import INativeStringLine
+ return INativeStringLine
+
+ def _getTargetInterfaces(self):
+ from zope.schema.interfaces import IFromUnicode
+ from zope.schema.interfaces import IFromBytes
+ return [self._getTargetInterface(), IFromUnicode, IFromBytes]
+
+ def test_fromBytes(self):
+ field = self._makeOne()
+ self.assertEqual(field.fromBytes(b''), '')
+ self.assertEqual(field.fromBytes(b'DEADBEEF'), 'DEADBEEF')
+
+ def test_fromUnicode(self):
+ field = self._makeOne()
+ self.assertIsInstance(field.fromUnicode(u''), str)
+ self.assertEqual(field.fromUnicode(u''), '')
+ self.assertEqual(field.fromUnicode(u'DEADBEEF'), 'DEADBEEF')
+
def _makeSampleVocabulary():
from zope.interface import implementer