diff options
Diffstat (limited to 'src/zope/schema')
| -rw-r--r-- | src/zope/schema/_field.py | 57 | ||||
| -rw-r--r-- | src/zope/schema/interfaces.py | 24 | ||||
| -rw-r--r-- | src/zope/schema/tests/test__field.py | 57 |
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 |
