summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2018-09-22 16:51:47 -0500
committerJason Madden <jamadden@gmail.com>2018-09-22 16:55:47 -0500
commite554ce3c6ebca03f9f809e197f8d44d35f7fb39f (patch)
tree8ae0ee149e907c9053206df9396cc1a0ec926dbb
parentd78c57c3c9ab8608fda1c1213c64d1666db770e2 (diff)
downloadzope-schema-e554ce3c6ebca03f9f809e197f8d44d35f7fb39f.tar.gz
Make NativeString[Line] into distinct types.issue74
This facilitates documentation and interactive exploration. Fixes #74.
-rw-r--r--CHANGES.rst7
-rw-r--r--docs/api.rst8
-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
-rw-r--r--version.txt2
6 files changed, 131 insertions, 24 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index a861314..01773e1 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -2,10 +2,13 @@
Changes
=========
-4.8.1 (unreleased)
+4.9.0 (unreleased)
==================
-- Nothing changed yet.
+- Make ``NativeString`` and ``NativeStringLine`` distinct types that
+ implement the newly-distinct interfaces ``INativeString`` and
+ ``INativeStringLine``. Previously these were just aliases for either
+ ``Text`` (on Python 3) or ``Bytes`` (on Python 2).
4.8.0 (2018-09-19)
diff --git a/docs/api.rst b/docs/api.rst
index 9344e44..bea7c69 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -37,6 +37,8 @@ Strings
.. autointerface:: zope.schema.interfaces.ITextLine
.. autointerface:: zope.schema.interfaces.IASCII
.. autointerface:: zope.schema.interfaces.IASCIILine
+.. autointerface:: zope.schema.interfaces.INativeString
+.. autointerface:: zope.schema.interfaces.INativeStringLine
.. autointerface:: zope.schema.interfaces.IPassword
.. autointerface:: zope.schema.interfaces.IURI
@@ -199,8 +201,6 @@ Field Implementations
.. autoclass:: zope.schema.MutableMapping
.. autoclass:: zope.schema.MutableSequence
.. autoclass:: zope.schema.MinMaxLen
-.. autoclass:: zope.schema.NativeString
-.. autoclass:: zope.schema.NativeStringLine
.. autoclass:: zope.schema.Object
:no-show-inheritance:
.. autoclass:: zope.schema.Orderable
@@ -232,6 +232,10 @@ Strings
:no-show-inheritance:
.. autoclass:: zope.schema.TextLine
:no-show-inheritance:
+.. autoclass:: zope.schema.NativeString
+ :no-show-inheritance:
+.. autoclass:: zope.schema.NativeStringLine
+ :no-show-inheritance:
Numbers
-------
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
diff --git a/version.txt b/version.txt
index 40aa077..4cc29d9 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-4.8.1.dev0
+4.9.0.dev0