Skip to content

Commit

Permalink
Make NativeString[Line] into distinct types.
Browse files Browse the repository at this point in the history
This facilitates documentation and interactive exploration.

Fixes #74.
  • Loading branch information
jamadden committed Sep 22, 2018
1 parent d78c57c commit 29bb041
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 24 deletions.
7 changes: 5 additions & 2 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 6 additions & 2 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
-------
Expand Down
57 changes: 46 additions & 11 deletions src/zope/schema/_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
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
Expand Down Expand Up @@ -170,11 +172,27 @@ def fromBytes(self, value):
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)
Expand All @@ -191,17 +209,34 @@ def _validate(self, value):

@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)
Expand Down
24 changes: 16 additions & 8 deletions src/zope/schema/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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):
Expand Down
45 changes: 45 additions & 0 deletions src/zope/schema/tests/test__field.py
Original file line number Diff line number Diff line change
Expand Up @@ -1702,6 +1702,51 @@ def test_mutable_mapping(self):
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')


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 _makeSampleVocabulary():
from zope.interface import implementer
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.8.1.dev0
4.9.0.dev0

0 comments on commit 29bb041

Please sign in to comment.