Commit e64a066d authored by Stephan Richter's avatar Stephan Richter

Added support for Python 3.3 and tests pass. TOX does not run yet,

because porting zope.publisher is not done, but with buildout I am able
to run against the branch. Same for ZODB.
parent 3d95da90
......@@ -2,9 +2,11 @@
CHANGES
=======
4.0.0 (unreleased)
4.0.0a1 (unreleased)
-------------------
- Added support for Python 3.3.
- Made ``Folder`` class inherit from ``BTreeContainer`` class, so that the
IContainer interface does not need to be re-implemented. Added a ``data``
attribute for BBB.
......
This diff is collapsed.
[buildout]
develop = .
parts = test graph coverage-test coverage-report
../zope.publisher
../ZODB
parts = test
# graph coverage-test coverage-report
[test]
recipe = zc.recipe.testrunner
......
......@@ -24,8 +24,23 @@ from setuptools import setup, find_packages, Extension
def read(*rnames):
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
def alltests():
import os
import sys
import unittest
# use the zope.testrunner machinery to find all the
# test suites we've put under ourselves
import zope.testrunner.find
import zope.testrunner.options
here = os.path.abspath(os.path.join(os.path.dirname(__file__), 'src'))
args = sys.argv[:]
defaults = ["--test-path", here]
options = zope.testrunner.options.get_options(args, defaults)
suites = list(zope.testrunner.find.find_suites(options))
return unittest.TestSuite(suites)
setup(name='zope.container',
version = '4.0.0dev',
version = '4.0.0a1.dev0',
author='Zope Foundation and Contributors',
author_email='zope-dev@zope.org',
description='Zope Container',
......@@ -64,16 +79,17 @@ setup(name='zope.container',
], include_dirs=['include']),
],
extras_require=dict(
test=['zope.testing',
test=['zope.testing', 'zope.testrunner'
],
zcml=[
'zope.component[zcml]',
'zope.configuration',
'zope.security[zcml]>=3.8',
'zope.security[zcml]>=4.0.0a3',
],
zodb=['ZODB>=3.10',
]),
install_requires=['setuptools',
'six',
'zope.interface',
'zope.dottedname',
'zope.schema',
......@@ -85,11 +101,15 @@ setup(name='zope.container',
'zope.i18nmessageid',
'zope.filerepresentation',
'zope.size',
'zope.traversing',
'zope.traversing>=4.0.0a1',
'zope.publisher',
'persistent',
'BTrees'
],
tests_require = [
'zope.testing',
'zope.testrunner'],
test_suite = '__main__.alltests',
include_package_data = True,
zip_safe = False,
)
......@@ -309,18 +309,18 @@ class ItemTypePrecondition(_TypesBased):
>>> try:
... precondition(None, 'foo', ob)
... except InvalidItemType, v:
... print v[0], (v[1] is ob), (v[2] == (I1, I2))
... except InvalidItemType as v:
... print(v.args[0], (v.args[1] is ob), (v.args[2] == (I1, I2)))
... else:
... print 'Should have failed'
... print('Should have failed')
None True True
>>> try:
... precondition.factory(None, 'foo', factory)
... except InvalidItemType, v:
... print v[0], (v[1] is factory), (v[2] == (I1, I2))
... except InvalidItemType as v:
... print(v.args[0], (v.args[1] is factory), (v.args[2] == (I1, I2)))
... else:
... print 'Should have failed'
... print('Should have failed')
None True True
>>> zope.interface.classImplements(Ob, I2)
......@@ -415,10 +415,10 @@ class ContainerTypesConstraint(_TypesBased):
>>> constraint = ContainerTypesConstraint(I1, I2)
>>> try:
... constraint(ob)
... except InvalidContainerType, v:
... print (v[0] is ob), (v[1] == (I1, I2))
... except InvalidContainerType as v:
... print((v.args[0] is ob), (v.args[1] == (I1, I2)))
... else:
... print 'Should have failed'
... print('Should have failed')
True True
>>> zope.interface.classImplements(Ob, I2)
......
......@@ -13,8 +13,6 @@
##############################################################################
"""Classes to support implementing `IContained`
"""
__docformat__ = 'restructuredtext'
import zope.component
import zope.interface.declarations
from zope.interface import providedBy, Interface
......@@ -43,6 +41,11 @@ except ImportError:
class IBroken(Interface):
pass
try:
unicode
except NameError:
# Py3: Define unicode type.
unicode = str
@zope.interface.implementer(IContained)
class Contained(object):
......@@ -130,8 +133,7 @@ def dispatchToSublocations(object, event):
Now, we should have seen all of the subobjects:
>>> seenreprs = map(repr, seen)
>>> seenreprs.sort()
>>> seenreprs = sorted(map(repr, seen))
>>> seenreprs
['(C(11), C(1))', '(C(12), C(1))', '(L(111), C(1))',""" \
""" '(L(112), C(1))', '(L(121), C(1))', '(L(122), C(1))',""" \
......@@ -519,7 +521,7 @@ def setitem(container, setitemf, name, object):
...
TypeError: name not unicode or ascii string
>>> setitem(container, container.__setitem__, 'hello ' + chr(200), item)
>>> setitem(container, container.__setitem__, b'hello ' + bytes([200]), item)
Traceback (most recent call last):
...
TypeError: name not unicode or ascii string
......@@ -538,9 +540,9 @@ def setitem(container, setitemf, name, object):
"""
# Do basic name check:
if isinstance(name, str):
if isinstance(name, bytes):
try:
name = unicode(name)
name = name.decode('ascii')
except UnicodeError:
raise TypeError("name not unicode or ascii string")
elif not isinstance(name, unicode):
......@@ -746,8 +748,8 @@ class NameChooser(object):
NameReserved: reserved
"""
if isinstance(name, str):
name = unicode(name)
if isinstance(name, bytes):
name = name.decode()
elif not isinstance(name, unicode):
raise TypeError("Invalid name type", type(name))
......@@ -789,7 +791,10 @@ class NameChooser(object):
the suggested name is converted to unicode:
>>> NameChooser(container).chooseName('foobar', object())
>>> NameChooser(container).chooseName(u'foobar', object())
u'foobar'
>>> NameChooser(container).chooseName(b'foobar', object())
u'foobar'
If it already exists, a number is appended but keeps the same extension:
......@@ -812,14 +817,19 @@ class NameChooser(object):
container = self.context
# convert to unicode and remove characters that checkName does not allow
try:
name = unicode(name)
except:
name = u''
if isinstance(name, bytes):
name = name.decode()
if not isinstance(name, unicode):
try:
name = unicode(name)
except:
name = u''
name = name.replace('/', '-').lstrip('+@')
if not name:
name = unicode(object.__class__.__name__)
name = object.__class__.__name__
if isinstance(name, bytes):
name = name.decode()
# for an existing name, append a number.
# We should keep client's os.path.extsep (not ours), we assume it's '.'
......@@ -834,7 +844,7 @@ class NameChooser(object):
i = 1
while n in container:
i += 1
n = name + u'-' + unicode(i) + suffix
n = name + u'-' + str(i) + suffix
# Make sure the name is valid. We may have started with something bad.
self.checkName(n, object)
......
......@@ -28,6 +28,8 @@ from zope.interface import implementer
from zope.component.interfaces import ISite
from zope.security.proxy import removeSecurityProxy
import zope.filerepresentation.interfaces
from six.moves import map
from six.moves import zip
MARKER = object()
......
......@@ -16,8 +16,8 @@
__docformat__ = 'restructuredtext'
from zope.interface import implementer
from interfaces import IFind, IIdFindFilter, IObjectFindFilter
from interfaces import IReadContainer
from .interfaces import IFind, IIdFindFilter, IObjectFindFilter
from .interfaces import IReadContainer
@implementer(IFind)
class FindAdapter(object):
......
......@@ -23,10 +23,10 @@ class Folder(btree.BTreeContainer):
# BBB: The data attribute used to be exposed. This should make it also
# compatible with old pickles.
@apply
def data():
def getter(self):
return self._SampleContainer__data
def setter(self, value):
self._SampleContainer__data = value
return property(getter, setter)
@property
def data(self):
return self._SampleContainer__data
@data.setter
def data(self, value):
self._SampleContainer__data = value
......@@ -14,13 +14,12 @@
"""Ordered container implementation.
"""
__docformat__ = 'restructuredtext'
from zope.container.interfaces import IOrderedContainer
from zope.interface import implementer
import six
from persistent import Persistent
from persistent.dict import PersistentDict
from persistent.list import PersistentList
from types import StringTypes, TupleType, ListType
from zope.container.interfaces import IOrderedContainer
from zope.interface import implementer
from zope.container.contained import Contained, setitem, uncontained
from zope.container.contained import notifyContainerModified
......@@ -157,7 +156,7 @@ class OrderedContainer(Persistent, Contained):
0
"""
return self._data.has_key(key)
return key in self._data
has_key = __contains__
......@@ -184,12 +183,15 @@ class OrderedContainer(Persistent, Contained):
['foo', 'baz']
"""
existed = self._data.has_key(key)
existed = key in self._data
bad = False
if isinstance(key, StringTypes):
if isinstance(key, six.string_types):
try:
unicode(key)
key.decode()
except AttributeError:
# Py3 str cannot decode.
pass
except UnicodeError:
bad = True
else:
......@@ -288,8 +290,8 @@ class OrderedContainer(Persistent, Contained):
0
"""
if not isinstance(order, ListType) and \
not isinstance(order, TupleType):
if not isinstance(order, list) and \
not isinstance(order, tuple):
raise TypeError('order must be a tuple or a list.')
if len(order) != len(self._order):
......
......@@ -75,7 +75,7 @@ class SampleContainer(Contained):
def __contains__(self, key):
'''See interface `IReadContainer`'''
return self.__data.has_key(key)
return key in self.__data
has_key = __contains__
......
......@@ -13,13 +13,15 @@
##############################################################################
"""Unit test logic for setting up and tearing down basic infrastructure
"""
import re
import zope.interface
import zope.traversing.testing
from zope import component
from zope.component.testing import PlacelessSetup as CAPlacelessSetup
from zope.component.eventtesting import PlacelessSetup as EventPlacelessSetup
from zope.traversing.interfaces import ITraversable, IContainmentRoot
import zope.traversing.testing
import zope.interface
from zope.testing import renormalizing
from zope.container.interfaces import IWriteContainer, INameChooser
from zope.container.contained import NameChooser
......@@ -27,6 +29,29 @@ from zope.container.interfaces import ISimpleReadContainer
from zope.container.traversal import ContainerTraversable
from zope.container.sample import SampleContainer
checker = renormalizing.RENormalizing([
# Python 3 unicode removed the "u".
(re.compile("u('.*?')"),
r"\1"),
(re.compile('u(".*?")'),
r"\1"),
# Python 3 renamed type to class.
(re.compile('<type '),
r"<class "),
# Python 3 adds module name to exceptions.
(re.compile("zope.interface.exceptions.Invalid"),
r"Invalid"),
(re.compile("zope.container.interfaces.InvalidContainerType"),
r"InvalidContainerType"),
(re.compile("zope.container.interfaces.InvalidItemType"),
r"InvalidItemType"),
(re.compile("zope.container.interfaces.NameReserved"),
r"NameReserved"),
(re.compile("zope.schema._bootstrapinterfaces.ConstraintNotSatisfied"),
r"ConstraintNotSatisfied"),
])
# XXX we would like to swap the names of the *PlacelessSetup classes
# in here as that would seem to follow the convention better, but
# unfortunately that would break compatibility with zope.app.testing
......
......@@ -21,11 +21,11 @@ The new folder isn't a site manager and doesn't have any entries:
KeyError: 'test'
>>> list(fs_folder.__iter__())
[]
>>> fs_folder.values()
>>> list(fs_folder.values())
[]
>>> len(fs_folder)
0
>>> fs_folder.items()
>>> list(fs_folder.items())
[]
>>> 'test' in fs_folder
False
......@@ -37,7 +37,7 @@ access non-existing entries:
>>> from zope.security.checker import NamesChecker
>>> proxied_folder = ProxyFactory(fs_folder, NamesChecker(('get',)))
>>> proxied_fs_folder = ReadDirectory(proxied_folder)
>>> print proxied_fs_folder['i dont exist']
>>> print(proxied_fs_folder['i dont exist'])
Traceback (most recent call last):
KeyError: 'i dont exist'
......@@ -40,7 +40,7 @@ class TestBTreeSpecials(TestCase):
bc = BTreeContainer()
self.assertEqual(bc.__dict__['_BTreeContainer__len'](), 0)
del bc.__dict__['_BTreeContainer__len']
self.failIf(bc.__dict__.has_key('_BTreeContainer__len'))
self.failIf('_BTreeContainer__len' in bc.__dict__)
bc['1'] = 1
self.assertEqual(len(bc), 1)
self.assertEqual(bc.__dict__['_BTreeContainer__len'](), 1)
......@@ -154,12 +154,11 @@ class TestBTreeSpecials(TestCase):
def checkIterable(self, iterable):
it = iter(iterable)
self.assert_(callable(it.next))
self.assert_(callable(it.__iter__))
self.assert_(iter(it) is it)
# Exhaust the iterator:
first_time = list(it)
self.assertRaises(StopIteration, it.next)
self.assertRaises(StopIteration, next, it)
# Subsequent iterations will return the same values:
self.assertEqual(list(iterable), first_time)
self.assertEqual(list(iterable), first_time)
......
......@@ -13,10 +13,10 @@
##############################################################################
"""Container constraint tests
"""
import doctest
import unittest
from zope.testing import module
from zope.container import testing
def setUp(test):
module.setUp(test, 'zope.container.constraints_txt')
......@@ -26,9 +26,11 @@ def tearDown(test):
def test_suite():
return unittest.TestSuite((
doctest.DocTestSuite('zope.container.constraints'),
doctest.DocFileSuite('../constraints.txt',
setUp=setUp, tearDown=tearDown),
doctest.DocTestSuite(
'zope.container.constraints', checker=testing.checker),
doctest.DocFileSuite(
'../constraints.txt',
setUp=setUp, tearDown=tearDown, checker=testing.checker),
))
if __name__ == '__main__': unittest.main()
......@@ -130,10 +130,10 @@ def test_ContainedProxy_instances_have_no_instance_dictionaries():
>>> p.__dict__
{'x': 1}
>>> p.y = 3
>>> p.__dict__
{'y': 3, 'x': 1}
>>> c.__dict__
{'y': 3, 'x': 1}
>>> sorted(p.__dict__.items())
[('x', 1), ('y', 3)]
>>> sorted(c.__dict__.items())
[('x', 1), ('y', 3)]
>>> p.__dict__ is c.__dict__
True
......@@ -219,17 +219,21 @@ class TestNameChooser(unittest.TestCase):
class BadBoy:
def __unicode__(self):
raise Exception
# Py3: Support
__str__ = __unicode__
self.assertEqual(nc.chooseName(BadBoy(), set()), u'set')
def test_suite():
return unittest.TestSuite((
doctest.DocTestSuite('zope.container.contained',
setUp=testing.setUp,
tearDown=testing.tearDown),
doctest.DocTestSuite(optionflags=doctest.NORMALIZE_WHITESPACE),
unittest.makeSuite(TestNameChooser),
))
if __name__ == '__main__': unittest.main()
suite = unittest.TestSuite((
unittest.makeSuite(TestNameChooser),
))
suite.addTest(doctest.DocTestSuite(
'zope.container.contained',
setUp=testing.setUp, tearDown=testing.tearDown,
checker=testing.checker))
suite.addTest(doctest.DocTestSuite(
optionflags=doctest.NORMALIZE_WHITESPACE,
checker=testing.checker))
return suite
......@@ -224,9 +224,6 @@ def test_suite():
if not HAVE_ZODB:
return unittest.TestSuite([])
return unittest.TestSuite((
doctest.DocTestSuite('zope.container.contained',
setUp=testing.setUp,
tearDown=testing.tearDown),
doctest.DocTestSuite(optionflags=doctest.NORMALIZE_WHITESPACE),
))
......
......@@ -20,16 +20,17 @@ from zope.traversing.interfaces import TraversalError
from zope.container.traversal import ContainerTraversable
from zope.container.interfaces import IContainer
import six
@implementer(IContainer)
class Container(object):
def __init__(self, attrs={}, objs={}):
for attr,value in attrs.iteritems():
for attr,value in six.iteritems(attrs):
setattr(self, attr, value)
self.__objs = {}
for name,value in objs.iteritems():
for name,value in six.iteritems(objs):
self.__objs[name] = value
......@@ -40,7 +41,7 @@ class Container(object):
return self.__objs.get(name, default)
def __contains__(self, name):
return self.__objs.has_key(name)
return name in self.__objs
class Test(CleanUp, unittest.TestCase):
......
......@@ -89,7 +89,7 @@ class BaseTestIContainer(testing.ContainerPlacelessSetup):
values.remove(v)
except ValueError:
self.fail('Value not in list')
self.assertEqual(values, [])
def test_len(self):
......@@ -174,11 +174,11 @@ class BaseTestIContainer(testing.ContainerPlacelessSetup):
folder[name] = foo
self.assertEquals(len(folder.keys()), 1)
self.assertEquals(folder.keys()[0], name)
self.assertEquals(list(folder.keys())[0], name)
self.assertEquals(len(folder.values()), 1)
self.assertEquals(folder.values()[0], foo)
self.assertEquals(list(folder.values())[0], foo)
self.assertEquals(len(folder.items()), 1)
self.assertEquals(folder.items()[0], (name, foo))
self.assertEquals(list(folder.items())[0], (name, foo))
self.assertEquals(len(folder), 1)
self.failUnless(name in folder)
......@@ -192,7 +192,7 @@ class BaseTestIContainer(testing.ContainerPlacelessSetup):
self.assertRaises(KeyError, folder.__getitem__, data[6][0])
foo2 = data[1][1]
name2 = data[1][0]
folder[name2] = foo2
......@@ -308,7 +308,7 @@ class TestSampleContainer(BaseTestIContainer, TestCase):
return '10'
def getBadKeyTypes(self):
return [None, ['foo'], 1, '\xf3abc']
return [None, [b'foo'], 1, b'\xf3abc']
def test_suite():
return makeSuite(TestSampleContainer)
......
......@@ -61,10 +61,10 @@ def test_order_events():
def test_all_items_available_at_object_added_event():
"""
Prepare the setup::
>>> from zope.container.sample import SampleContainer
>>> root = SampleContainer()
Now register an event subscriber to object added events.
>>> import zope.component
......@@ -73,11 +73,11 @@ def test_all_items_available_at_object_added_event():
>>> @zope.component.adapter(IObjectAddedEvent)
... def printContainerKeys(event):
... print event.newParent.keys()
... print(event.newParent.keys())
>>> zope.component.provideHandler(printContainerKeys)
Now we are adding an object to the container.
Now we are adding an object to the container.
>>> from zope.container.ordered import OrderedContainer
>>> oc = OrderedContainer()
......@@ -138,7 +138,7 @@ def test_adding_none():
[None]
>>> oc.items()
[('foo', None)]
>>> print oc['foo']
>>> print(oc['foo'])
None
"""
......@@ -147,10 +147,12 @@ def test_suite():
suite = unittest.TestSuite()
suite.addTest(DocTestSuite("zope.container.ordered",
setUp=testing.setUp,
tearDown=testing.tearDown))
tearDown=testing.tearDown,
checker=testing.checker))
suite.addTest(DocTestSuite(
setUp=testing.ContainerPlacefulSetup().setUp,
tearDown=testing.ContainerPlacefulSetup().tearDown))
tearDown=testing.ContainerPlacefulSetup().tearDown,
checker=testing.checker))
return suite
if __name__ == '__main__':
......
......@@ -25,6 +25,8 @@ from zope.publisher.interfaces import IDefaultViewName, NotFound
from zope.container.interfaces import ISimpleReadContainer, IItemContainer
from zope.container.interfaces import IReadContainer
from six.moves import map
from six.moves import zip
# Note that the next two classes are included here because they
# can be used for multiple view types.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment