[Checkins] SVN: zope.app.container/branches/3.6/ bugfix backport from zope.container (#227617)
Christophe Combelles
ccomb at free.fr
Sat Apr 24 10:23:15 EDT 2010
Log message for revision 111362:
bugfix backport from zope.container (#227617)
Changed:
U zope.app.container/branches/3.6/CHANGES.txt
U zope.app.container/branches/3.6/setup.py
U zope.app.container/branches/3.6/src/zope/app/container/contained.py
U zope.app.container/branches/3.6/src/zope/app/container/tests/test_contained.py
-=-
Modified: zope.app.container/branches/3.6/CHANGES.txt
===================================================================
--- zope.app.container/branches/3.6/CHANGES.txt 2010-04-24 14:22:40 UTC (rev 111361)
+++ zope.app.container/branches/3.6/CHANGES.txt 2010-04-24 14:23:14 UTC (rev 111362)
@@ -2,6 +2,13 @@
CHANGES
=======
+3.6.3 (unreleased)
+------------------
+
+- never fail if the suggested name is in a wrong type (#227617)
+
+- checkName first checks the parameter type before the emptiness
+
3.6.2 (2008-10-21)
------------------
Modified: zope.app.container/branches/3.6/setup.py
===================================================================
--- zope.app.container/branches/3.6/setup.py 2010-04-24 14:22:40 UTC (rev 111361)
+++ zope.app.container/branches/3.6/setup.py 2010-04-24 14:23:14 UTC (rev 111362)
@@ -22,7 +22,7 @@
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
setup(name='zope.app.container',
- version = '3.6.3',
+ version = '3.6.3dev',
author='Zope Corporation and Contributors',
author_email='zope-dev at zope.org',
description='Zope Container',
Modified: zope.app.container/branches/3.6/src/zope/app/container/contained.py
===================================================================
--- zope.app.container/branches/3.6/src/zope/app/container/contained.py 2010-04-24 14:22:40 UTC (rev 111361)
+++ zope.app.container/branches/3.6/src/zope/app/container/contained.py 2010-04-24 14:23:14 UTC (rev 111362)
@@ -714,47 +714,44 @@
>>> container['foo'] = 'bar'
>>> from zope.app.container.contained import NameChooser
- All these names are invalid:
+ An invalid name raises a UserError:
>>> NameChooser(container).checkName('+foo', object())
Traceback (most recent call last):
...
UserError: Names cannot begin with '+' or '@' or contain '/'
- >>> NameChooser(container).checkName('@foo', object())
- Traceback (most recent call last):
- ...
- UserError: Names cannot begin with '+' or '@' or contain '/'
- >>> NameChooser(container).checkName('f/oo', object())
- Traceback (most recent call last):
- ...
- UserError: Names cannot begin with '+' or '@' or contain '/'
+
+ A name that already exists raises a UserError:
+
>>> NameChooser(container).checkName('foo', object())
Traceback (most recent call last):
...
UserError: The given name is already being used
+
+ A name must be a string or unicode string:
+
>>> NameChooser(container).checkName(2, object())
Traceback (most recent call last):
...
TypeError: ('Invalid name type', <type 'int'>)
- This one is ok:
+ A correct name returns True:
>>> NameChooser(container).checkName('2', object())
True
-
"""
+ if isinstance(name, str):
+ name = unicode(name)
+ elif not isinstance(name, unicode):
+ raise TypeError("Invalid name type", type(name))
+
if not name:
raise UserError(
_("An empty name was provided. Names cannot be empty.")
)
- if isinstance(name, str):
- name = unicode(name)
- elif not isinstance(name, unicode):
- raise TypeError("Invalid name type", type(name))
-
if name[:1] in '+@' or '/' in name:
raise UserError(
_("Names cannot begin with '+' or '@' or contain '/'")
@@ -772,33 +769,51 @@
"""See zope.app.container.interfaces.INameChooser
The name chooser is expected to choose a name without error
-
+
We create and populate a dummy container
>>> from zope.app.container.sample import SampleContainer
>>> container = SampleContainer()
- >>> container['foo.old.rst'] = 'rst doc'
+ >>> container['foobar.old'] = 'rst doc'
>>> from zope.app.container.contained import NameChooser
- >>> NameChooser(container).chooseName('+ at +@foo.old.rst', object())
- u'foo.old-2.rst'
- >>> NameChooser(container).chooseName('+ at +@foo/foo', object())
+
+ the suggested name is converted to unicode:
+
+ >>> NameChooser(container).chooseName('foobar', object())
+ u'foobar'
+
+ If it already exists, a number is appended but keeps the same extension:
+
+ >>> NameChooser(container).chooseName('foobar.old', object())
+ u'foobar-2.old'
+
+ Bad characters are turned into dashes:
+
+ >>> NameChooser(container).chooseName('foo/foo', object())
u'foo-foo'
- >>> NameChooser(container).chooseName('', object())
- u'object'
- >>> NameChooser(container).chooseName('@+@', object())
- u'object'
+ If no name is suggested, it is based on the object type:
+
+ >>> NameChooser(container).chooseName('', [])
+ u'list'
+
"""
container = self.context
- # remove characters that checkName does not allow
- name = unicode(name.replace('/', '-').lstrip('+@'))
+ # convert to unicode and remove characters that checkName does not allow
+ try:
+ name = unicode(name)
+ except:
+ name = u''
+ name = name.replace('/', '-').lstrip('+@')
if not name:
name = unicode(object.__class__.__name__)
-
+
+ # for an existing name, append a number.
+ # We should keep client's os.path.extsep (not ours), we assume it's '.'
dot = name.rfind('.')
if dot >= 0:
suffix = name[dot:]
@@ -806,7 +821,6 @@
else:
suffix = ''
-
n = name + suffix
i = 1
while n in container:
Modified: zope.app.container/branches/3.6/src/zope/app/container/tests/test_contained.py
===================================================================
--- zope.app.container/branches/3.6/src/zope/app/container/tests/test_contained.py 2010-04-24 14:22:40 UTC (rev 111361)
+++ zope.app.container/branches/3.6/src/zope/app/container/tests/test_contained.py 2010-04-24 14:23:14 UTC (rev 111362)
@@ -23,10 +23,13 @@
from persistent import Persistent
import zope.interface
+import zope.component
from zope.testing import doctest
-from zope.app.container.contained import ContainedProxy
+from zope.app.container.contained import ContainedProxy, NameChooser
+from zope.app.container.sample import SampleContainer
from zope.app.testing import placelesssetup
+from zope.container.interfaces import NameReserved, IContainer
class MyOb(Persistent):
pass
@@ -313,15 +316,86 @@
>>> p.__dict__ is c.__dict__
True
-
+
"""
+
+class TestNameChooser(unittest.TestCase):
+ def test_checkName(self):
+ container = SampleContainer()
+ container['foo'] = 'bar'
+ checkName = NameChooser(container).checkName
+
+ # invalid type for the name
+ self.assertRaises(TypeError, checkName, 2, object())
+ self.assertRaises(TypeError, checkName, [], object())
+ self.assertRaises(TypeError, checkName, None, object())
+ self.assertRaises(TypeError, checkName, None, None)
+
+ # invalid names
+ self.assertRaises(ValueError, checkName, '+foo', object())
+ self.assertRaises(ValueError, checkName, '@foo', object())
+ self.assertRaises(ValueError, checkName, 'f/oo', object())
+ self.assertRaises(ValueError, checkName, '', object())
+
+ # existing names
+ self.assertRaises(KeyError, checkName, 'foo', object())
+ self.assertRaises(KeyError, checkName, u'foo', object())
+
+ # correct names
+ self.assertEqual(True, checkName('2', object()))
+ self.assertEqual(True, checkName(u'2', object()))
+ self.assertEqual(True, checkName('other', object()))
+ self.assertEqual(True, checkName(u'reserved', object()))
+ self.assertEqual(True, checkName(u'r\xe9served', object()))
+
+ def test_chooseName(self):
+ container = SampleContainer()
+ container['foo.old.rst'] = 'rst doc'
+ nc = NameChooser(container)
+
+ # correct name without changes
+ self.assertEqual(nc.chooseName('foobar.rst', None),
+ u'foobar.rst')
+ self.assertEqual(nc.chooseName(u'\xe9', None),
+ u'\xe9')
+
+ # automatically modified named
+ self.assertEqual(nc.chooseName('foo.old.rst', None),
+ u'foo.old-2.rst')
+ self.assertEqual(nc.chooseName('+ at +@foo.old.rst', None),
+ u'foo.old-2.rst')
+ self.assertEqual(nc.chooseName('+ at +@foo/foo+@', None),
+ u'foo-foo+@')
+
+ # empty name
+ self.assertEqual(nc.chooseName('', None), u'NoneType')
+ self.assertEqual(nc.chooseName('@+@', []), u'list')
+
+ # if the name is not a string it is converted
+ self.assertEqual(nc.chooseName(None, None), u'None')
+ self.assertEqual(nc.chooseName(2, None), u'2')
+ self.assertEqual(nc.chooseName([], None), u'[]')
+ container['None'] = 'something'
+ self.assertEqual(nc.chooseName(None, None), u'None-2')
+ container['None-2'] = 'something'
+ self.assertEqual(nc.chooseName(None, None), u'None-3')
+
+ # even if the given name cannot be converted to unicode
+ class BadBoy:
+ def __unicode__(self):
+ raise Exception
+
+ self.assertEqual(nc.chooseName(BadBoy(), set()), u'set')
+
+
def test_suite():
return unittest.TestSuite((
doctest.DocTestSuite('zope.app.container.contained',
setUp=placelesssetup.setUp,
tearDown=placelesssetup.tearDown),
doctest.DocTestSuite(optionflags=doctest.NORMALIZE_WHITESPACE),
+ unittest.makeSuite(TestNameChooser),
))
if __name__ == '__main__': unittest.main()
More information about the checkins
mailing list