Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lines property in Zope 5.3: new one has text, old one has bytes #987

Open
mauritsvanrees opened this issue Aug 6, 2021 · 7 comments
Open

Comments

@mauritsvanrees
Copy link
Member

Zope 5.3 changed the lines property from storing bytes to storing text. See issue #962 and PR #965.

What now happens:

  • Create a Zope 5.2.1 site, add a Folder A, and in the ZMI add a lines property on it. It contains a tuple of bytes.
  • Update to Zope 5.3. (I simply switch between git tags.)
  • Create folder B and add a lines property. It contains a tuple of texts (strings).
  • The lines property in folder A still contains a tuple of bytes.
  • Save the properties form in folder A, and it is converted to a tuple of strings.

So depending on when you created or edited the lines property, getProperty in Zope 5.3 may return bytes or string.
I don't really know if this is a problem or not.

But there are already some test failures in GenericSetup, which is why I started looking. Apart from some expectations in tests which need to be updated, GenericSetup needs a few fixes in the part of its code where it is adding or updating properties, so the affected code is limited. See PR. The most difficult about that PR, is that there is code in GenericSetup which supports non-utf8 encodings in profile files, so the changes are more convoluted than they should have been. Other packages which do similar things, would need to update their code as well.

If we think this should be fixed in Zope, I can see two solutions:

  1. In getProperty call the converter of the field and return the result. But this means extra code gets run whenever you call getProperty. Not nice for performance.
  2. Add a function that goes over all properties (or all lines properties) of an object, calls the converters, and saves changes. Integrators could apply this with zopeFindAndApply. Plone could choose to do this in a GenericSetup upgrade step, although this may take long when your Data.fs is 100 GB.

Additionally, a function could be useful to go over all deprecated properties, for example turning ulines into lines. The two functions could be combined.

Does someone already have code like this? Even when Zope itself does not call the function anywhere, having it available in Zope would be useful.
cc @jugmac00


O, let me share some output from debugging. I started with Zope 5.2.1 and several properties containing non-ascii: äh. In the ZMI this looks fine in all fields, except for the lines field, where it looks wrong: äh. With bin/instance debug:

>>> app.testfolder.getProperty("lines")
(b'foo', b'\xc3\xa4h')
>>> app.testfolder.getProperty("lines")[1].decode("utf-8")
'äh'

Save it a couple of times, and the ZMI show äh:

>>> app.testfolder.getProperty("lines")
(b'foo', b'\xc3\x83\xc6\x92\xc3\x86\xe2\x80\x99\xc3\x83\xe2\x80\x9a\xc3\x82\xc2\xa4h')

So a lines field with non-ascii in older Zopes was already wrong: you should have used a ulines field. So probably the real problems are only there when you already had a problem in the earlier Zopes.

Start in Zope 5.3 and it it still looks wrong, because the original data is bad. Saving it once (or multiple times), changes the bytes to string, but it is still looking wrong:

>>> app.testfolder.getProperty("lines")
('foo', 'äh')

Manually insert the proper äh in the ZMI and save, and then it is fine:

>>> app.testfolder.getProperty("lines")
('foo', 'äh')
@jugmac00
Copy link
Member

jugmac00 commented Aug 6, 2021

Hi @mauritsvanrees,

thanks for investigating and sorry that my change caused you so much work.

I have never used properties, but when I have a look at our documentation ( https://zope.readthedocs.io/en/latest/zdgbook/Products.html#ofs-propertymanager ) there is no mention of bytes at all.

I also lack imagination, why I ever want to store bytes on an object. Though I never used properties, I could imagine they are used for e.g. dates, delivery numbers or whatever...

Usually it is a good idea to convert from bytes to text at the outer most border of a system. But maybe I oversee some use-cases.

As of your suggested fixes - I have a hard time to imagine what people used a list of bytes for.

I do not like the idea to have a getter (getProperty) which does not only get but changes the underlying data - just when reading or even permanent, so an upgrade step which converts the data would be preferable.

You write

Other packages which do similar things, would need to update their code as well.

Are you aware which other packages use the converters?

I just grepped the zopefoundation repos, and it looks like there is only one more repository Products.CMFCore:

❯ all-repos-grep -C zope.json type_converters '*.py'
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/MemberDataTool.py:from ZPublisher.Converters import type_converters
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/MemberDataTool.py:                        if proptype in type_converters:
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/MemberDataTool.py:                            value = type_converters[proptype](value)
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:from ZPublisher.Converters import type_converters
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:                if prop_type in type_converters:
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:                    # The type_converters use the ZPublisher default_encoding
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:                        prop_value = type_converters[prop_type](prop_value)
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:                        prop_value = type_converters[prop_type](prop_value)
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:from ZPublisher.Converters import type_converters
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:            if proptype in type_converters:
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:                value = type_converters[proptype](value)
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:        if type in type_converters:
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:            value = type_converters[type](value)
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:from ZPublisher.Converters import type_converters
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:            if proptype in type_converters:
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:                value = type_converters[proptype](value)
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:        if type in type_converters:
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:            value = type_converters[type](value)
output_zope/zopefoundation/Zope:src/ZPublisher/BaseRequest.py:from ZPublisher.Converters import type_converters
output_zope/zopefoundation/Zope:src/ZPublisher/BaseRequest.py:        return list(type_converters.keys())
output_zope/zopefoundation/Zope:src/ZPublisher/Converters.py:type_converters = {
output_zope/zopefoundation/Zope:src/ZPublisher/Converters.py:get_converter = type_converters.get
output_zope/zopefoundation/Zope:src/ZPublisher/tests/testHTTPRequest.py:        from ZPublisher.Converters import type_converters
output_zope/zopefoundation/Zope:src/ZPublisher/tests/testHTTPRequest.py:        for type, convert in list(type_converters.items()):

I am short on time today, but I will have a closer look on the weekend.

@mauritsvanrees
Copy link
Member Author

thanks for investigating and sorry that my change caused you so much work.

The change itself seems good to me, don't worry. :-)
And I am not using Zope 5.3 yet. I am just trying to update Plone 6 to use it, but Plone 6 does not even have an alpha release yet. (I want an alpha release within two weeks though.)

I have a hard time to imagine what people used a list of bytes for.

On Python 3: sure.
But on a project that started in Python 2: you use it for everything, because bytes is the native string type on Python 2. If you want to store a list of for example urls, ids, portal types, css classes, then the natural choice is to put it in a lines field. On Python 2 this gives you bytes. Most of the data you put in there will be ascii anyway, so bytes is fine.
For lots of data you only want ascii characters, so you are not going to store it in a ulines property.

And then you switch the project to Python 3, and bytes is no longer the native string type, and then you suddenly need to think about what kind of data is already in the lines and what kind of data you add to it.

Where it goes wrong in the GenericSetup case, is that lines is ('foo', 'bar') and some import code checks if b'foo' is in there, which it is not, so it adds it and the lines field becomes ('foo', 'bar', b'foo'). So there is one duplicate element, and if you want to sort the tuple, it fails with TypeError: '<' not supported between instances of 'bytes' and 'str'.

The other way around could also happen: new code written for a Zope 5.3 project see a lines property, concludes that it must be text so it can safely add more text. But the property was created earlier and still contains bytes, so the end result is again a tuple of bytes and text.

Are you aware which other packages use the converters?

It's not just code that directly uses the converters. It is also code that calls getProperty and _updateProperty, especially when it combines the two, as GenericSetup does: add an element to the tuple if it is not yet there.

@jugmac00
Copy link
Member

jugmac00 commented Aug 6, 2021

👀

❯ all-repos-grep -C zope.json _updateProperty '*.py'
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_memberdata.py:            mdtool._updateProperty('email', 'value1')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_memberdata.py:            mdtool._updateProperty('portal_skin', 'value2')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_memberdata.py:            mdtool._updateProperty('listed', 'True')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_memberdata.py:            mdtool._updateProperty('login_time', '2010/01/01')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_memberdata.py:            mdtool._updateProperty('last_login_time', '2010/01/01')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_memberdata.py:            mdtool._updateProperty('fullname', 'value3')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:            site._updateProperty('foo', 'Foo')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:            site._updateProperty('bar', ('Bar',))
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        site._updateProperty('title', NONASCII)
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        site._updateProperty('title', '')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/formlib/schema.py:            inst.context._updateProperty(self._get_name, value)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to21.py:            ti._updateProperty('product', '')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to21.py:            ti._updateProperty('factory', _FACTORIES[key])
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to22.py:                ti._updateProperty('icon_expr', icon_expr)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to22.py:            ti._updateProperty('add_view_expr',
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:        mdtool._updateProperty('listed', '')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:        mdtool._updateProperty('login_time', '2000/01/01')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:        mdtool._updateProperty('last_login_time', '2000/01/01')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:        portal._updateProperty('enable_actionicons', enable_actionicons)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:                ti._updateProperty('icon_expr', icon_expr)
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/content.py:                context._updateProperty(option, value)
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_content.py:        site._updateProperty('title', 'test_export_empty_site_with_setup_tool')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_boolean', 'True')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_date', '2000/01/01 00:00:00 UTC')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_float', '1.1')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_int', '1')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:            obj._updateProperty('foo_lines',
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:            obj._updateProperty('foo_lines',
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_long', '1')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_string', 'Foo String')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_text', 'Foo\nText')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_tokens', ('Foo', 'Tokens'))
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_selection', 'Foo')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_mselection', ('Foo', 'Baz'))
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_date_naive', '2000/01/01 00:00:00')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_ro', 'RO')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_boolean_nodel', 'True')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_date_nodel', '2000/01/01 00:00:00 UTC')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_float_nodel', '3.1415')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        obj._updateProperty('foo_int_nodel', '1789')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:                self.context._updateProperty(prop_id, prop_value)
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:            obj._updateProperty('i18n_domain', i18n_domain)
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:                # if we pass a *string* to _updateProperty, all other values
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:            obj._updateProperty(prop_id, prop_value)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/plugins/DynamicGroupsPlugin.py:    def _updateProperty(self, id, value):
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/plugins/DynamicGroupsPlugin.py:            PropertyManager._updateProperty(self, id, value)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/plugins/tests/test_DynamicGroupsPlugin.py:        dpg.willing._updateProperty('willing', 1)
output_zope/zopefoundation/ZServer:src/webdav/PropertySheets.py:    def _updateProperty(self, id, value):
output_zope/zopefoundation/ZServer:src/webdav/davcmds.py:                        propset._updateProperty(name, val, meta=md)
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:    def _updateProperty(self, id, value):
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:                self._updateProperty(name, value)
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:                self._updateProperty(name, value)
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:    def _updateProperty(self, id, value, meta=None):
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:                self._updateProperty(name, value)
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:                self._updateProperty(name, value)
output_zope/zopefoundation/Zope:src/OFS/interfaces.py:    def _updateProperty(id, value):
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        inst._updateProperty('prop', ['xxx', 'yyy'])
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:    def test_updateProperty_transforms(self):
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        pm._updateProperty('test_lines', 'foo\nbar')
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        pm._updateProperty('test_lines', b'bar\nbaz')
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        inst._updateProperty('prop', ['xxx', 'yyy'])
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:    def test_updateProperty_transforms(self):
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        ps._updateProperty('test_lines', 'foo\nbar')
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        ps._updateProperty('test_lines', b'bar\nbaz')
output_zope/zopefoundation/Zope:src/webdav/PropertySheets.py:    def _updateProperty(self, id, value):
output_zope/zopefoundation/Zope:src/webdav/davcmds.py:                        propset._updateProperty(name, val, meta=md)
❯ all-repos-grep -C zope.json getProperty '*.py'
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/MemberDataTool.py:                memberProperty = u.getProperty
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/MemberDataTool.py:                memberProperty = user_wrapper.getProperty
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/MemberDataTool.py:                        proptype = tool.getPropertyType(id) or 'string'
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/MemberDataTool.py:    def getProperty(self, id, default=_marker):
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/MemberDataTool.py:        tool_value = self._tool.getProperty(id, _marker)
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/SkinsTool.py:        if hasattr(aq_base(member), 'getProperty'):
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/SkinsTool.py:            mskin = member.getProperty('portal_skin')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/content.py:        self._encoding = self.context.getProperty('default_charset', 'utf-8')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/properties.py:        self._encoding = self.context.getProperty(
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        self.assertEqual(site.getProperty('foo'), 'Foo')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        self.assertEqual(site.getProperty('bar'), ('Bar',))
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        self.assertEqual(site.getProperty('foo'), 'Foo')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        self.assertEqual(site.getProperty('bar'), ('Bar',))
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        self.assertEqual(site.getProperty('foo'), 'Foo')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        self.assertEqual(site.getProperty('bar'), ('Bar',))
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        self.assertEqual(site.getProperty('foo'), 'Foo')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        self.assertEqual(site.getProperty('bar'), ('Bar',))
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        self.assertEqual(site.getProperty('foo'), 'Foo')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/exportimport/tests/test_properties.py:        self.assertEqual(site.getProperty('bar'), (b'Bar',))
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/base/dummy.py:    def getProperty(self, id, default=None):
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_DirectoryView.py:        self.assertEqual(testfolder.getProperty('title'),
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_FSPropertiesObject.py:        self.assertEqual(fspo.getProperty('title'), 'Test properties')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_FSPropertiesObject.py:        self.assertEqual(fspo.getProperty('value1'), 'one')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_FSPropertiesObject.py:        self.assertEqual(fspo.getProperty('value2'), 'two')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_FSPropertiesObject.py:        self.assertEqual(fspo.getProperty('an_int'), 42)
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_FSPropertiesObject.py:        self.assertEqual(fspo.getProperty('a_float'), 3.1415926)
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_FSPropertiesObject.py:        self.assertEqual(fspo.getProperty('a_boolean'), False)
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_FSPropertiesObject.py:        self.assertEqual(fspo.getProperty('a_long'), 40000000000)
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_FSPropertiesObject.py:        self.assertEqual(fspo.getProperty('a_date'), DateTime('01/01/2001'))
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_FSPropertiesObject.py:        self.assertEqual(fspo.getProperty('a_tokens'),
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_FSPropertiesObject.py:            self.assertEqual(target.getProperty(prop_id),
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_FSPropertiesObject.py:                             fspo.getProperty(prop_id))
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_MemberDataTool.py:        self.assertEqual(tool.getProperty('email'), '')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_MemberDataTool.py:        self.assertEqual(tool.getProperty('portal_skin'), '')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_MemberDataTool.py:        self.assertEqual(tool.getProperty('listed'), False)
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_MemberDataTool.py:        self.assertEqual(tool.getProperty('login_time'),
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_MemberDataTool.py:        self.assertEqual(tool.getProperty('last_login_time'),
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_MemberDataTool.py:        self.assertEqual(member.getProperty('email'), '')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_MemberDataTool.py:        self.assertEqual(member.getProperty('login_time'),
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_MemberDataTool.py:        self.assertEqual(member.getProperty('email'), 'BOB@EXAMPLE.ORG')
output_zope/zopefoundation/Products.CMFCore:src/Products/CMFCore/tests/test_MemberDataTool.py:        self.assertEqual(member.getProperty('login_time'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/MembershipTool.py:            listed = member.getProperty('listed')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/RegistrationTool.py:        email = member.getProperty('email')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/RegistrationTool.py:            existing = member.getProperty('email')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/admin/config.py:            value = self.context.getProperty(name)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/authentication.py:        last_login = member.getProperty('login_time')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/authentication.py:        if never_logged_in and ptool.getProperty('validate_email'):
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/authentication.py:        return ptool.getProperty('email_from_address')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/join.py:        return ptool.getProperty('validate_email')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/members.py:        last_login = member.getProperty('login_time')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/members.py:        self.fullname = member.getProperty('fullname').decode(charset)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/members.py:        self.email = member.getProperty('email')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/notify.py:            self.ptool.getProperty('email_from_name'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/notify.py:            self.ptool.getProperty('email_from_address')))
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/notify.py:        return self.ptool.getProperty('description')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/notify.py:        return self.ptool.getProperty('email_from_name')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/notify.py:        self.email = member and member.getProperty('email') \
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/password.py:        default_charset = ptool.getProperty('default_charset', None)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/password.py:        return self.context.getProperty('last_login_time')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/preferences.py:        return self.context.getProperty(name, default='')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/preferences.py:        encoding = ptool.getProperty('default_charset', None)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/preferences.py:        return self.context.getProperty('fullname').decode(encoding)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/preferences.py:        encoding = ptool.getProperty('default_charset', None)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/membership/tests/test_members.py:    def getProperty(self, attr):
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/search/interfaces.py:        login_time = member.getProperty('last_login_time')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/search/tests/test_search.py:    def getProperty(self, id, d=None):
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/skins/icons.py:        show = ptool.getProperty('enable_actionicons', False)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/skins/tests/test_ursa.py:        member.getProperty = lambda x: 'John Smith'
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/skins/tests/test_ursa.py:        member.getProperty = lambda x: ''
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/skins/tests/test_ursa.py:    def getProperty(self, id, d=None):
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/skins/ursa.py:            default_charset = self.ptool.getProperty('default_charset', None)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/skins/ursa.py:        return self.isAnon and 'Guest' or (self.member.getProperty('fullname')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/browser/utils.py:        return ptool.getProperty('default_charset', None)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/formlib/schema.py:        self.encoding = ptool.getProperty('default_charset', None)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_control/change_password.py:if member.getProperty('last_login_time') == DateTime('1999/01/01'):
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_control/members_add_control.py:    if ptool.getProperty('validate_email') or send_password:
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_control/validatePassword.py:if ptool.getProperty('validate_email'):
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/getMainGlobals.py:    default_charset = ptool.getProperty('default_charset', None)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/getMainGlobals.py:           'membername': isAnon and 'Guest' or (member.getProperty('fullname')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/getMainGlobals.py:           'show_actionicons': ptool.getProperty('enable_actionicons'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/join_form.py:validate_email = ptool.getProperty('validate_email')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/logged_in.py:    options['admin_email'] = ptool.getProperty('email_from_address')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/logged_in.py:    last_login = member.getProperty('login_time')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/logged_in.py:    if never_logged_in and ptool.getProperty('validate_email'):
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/members_delete_form.py:    members.append('%s (%s)' % (member.getProperty('fullname'), member_id))
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/members_manage_form.py:    fullname = member.getProperty('fullname')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/members_manage_form.py:    last_login = member.getProperty('login_time')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/members_manage_form.py:                   'email': member.getProperty('email'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/password_email.py:headers['From'] = '%s <%s>' % (ptool.getProperty('email_from_name'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/password_email.py:                               ptool.getProperty('email_from_address'))
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/password_email.py:headers['To'] = '<%s>' % (member and member.getProperty('email') or
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/password_form.py:is_first_login = (member.getProperty('last_login_time') == DateTime('1999/01/01'))
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/password_form.py:    options['portal_title'] = ptool.getProperty('title')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/reconfig_form.py:                    'email_from_name': ptool.getProperty('email_from_name'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/reconfig_form.py:                                      ptool.getProperty('email_from_address'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/reconfig_form.py:                    'description': ptool.getProperty('description'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/reconfig_form.py:                    'validate_email': ptool.getProperty('validate_email'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/reconfig_form.py:                    'default_charset': ptool.getProperty('default_charset'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/reconfig_form.py:                    'email_charset': ptool.getProperty('email_charset'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/reconfig_form.py:                                      ptool.getProperty('enable_actionicons'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/reconfig_form.py:                    'enable_permalink': ptool.getProperty('enable_permalink'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/registered_email.py:options['portal_description'] = ptool.getProperty('description')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/registered_email.py:email_from_name = ptool.getProperty('email_from_name')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/registered_email.py:                               ptool.getProperty('email_from_address'))
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/skins/zpt_generic/search_form.py:    created.append({'value': member.getProperty('last_login_time'),
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/tests/test_PropertiesTool.py:        site_prop = self.site.getProperty
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/tests/test_PropertiesTool.py:        site_prop = self.site.getProperty
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/tests/test_join.py:        self.assertEqual(m.getProperty('email'), 'foo@bar.com')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to21.py:        key = '%s-%s' % (ti.getProperty('product'), ti.getProperty('factory'))
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to21.py:        key = '%s-%s' % (ti.getProperty('product'), ti.getProperty('factory'))
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to22.py:        if not ti.getProperty('add_view_expr') and \
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to22.py:                ti.getProperty('content_meta_type') != 'Discussion Item':
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to22.py:            if content_icon and not ti.getProperty('icon_expr'):
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to22.py:        if not ti.getProperty('add_view_expr') and \
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to22.py:                ti.getProperty('content_meta_type') != 'Discussion Item':
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:    listed = mdtool.getProperty('listed')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:    login_time = mdtool.getProperty('login_time')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:    last_login_time = mdtool.getProperty('last_login_time')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:    listed = mdtool.getProperty('listed')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:    login_time = mdtool.getProperty('login_time')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:    last_login_time = mdtool.getProperty('last_login_time')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:    enable_actionicons = portal.getProperty('enable_actionicons')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:    enable_actionicons = portal.getProperty('enable_actionicons')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:        immediate_view = ti.getProperty('immediate_view')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:            icon_expr = ti.getProperty('icon_expr')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:        immediate_view = ti.getProperty('immediate_view')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23.py:            icon_expr = ti.getProperty('icon_expr')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23views.py:        prop = obj.getProperty('url_expr')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/upgrade/to23views.py:        prop = obj.getProperty('url_expr')
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/utils.py:    default_charset = ptool.getProperty('default_charset', None)
output_zope/zopefoundation/Products.CMFDefault:Products/CMFDefault/utils.py:    email_charset = ptool.getProperty('email_charset', None) or 'utf-8'
output_zope/zopefoundation/Products.CMFUid:src/Products/CMFUid/UniqueIdAnnotationTool.py:                remove_on_add = anno_tool.getProperty('remove_on_add', False)
output_zope/zopefoundation/Products.CMFUid:src/Products/CMFUid/UniqueIdAnnotationTool.py:                remove_on_clone = anno_tool.getProperty('remove_on_clone',
output_zope/zopefoundation/Products.CMFUid:src/Products/CMFUid/UniqueIdAnnotationTool.py:                assign_on_add = anno_tool.getProperty('assign_on_add', False)
output_zope/zopefoundation/Products.CMFUid:src/Products/CMFUid/UniqueIdAnnotationTool.py:            remove_on_clone = anno_tool.getProperty('remove_on_clone', False)
output_zope/zopefoundation/Products.CMFUid:src/Products/CMFUid/UniqueIdAnnotationTool.py:            assign_on_clone = anno_tool.getProperty('assign_on_clone', False)
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/content.py:            prop_type = context.getPropertyType(option)
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        self.assertEqual(helpers.context.getProperty('i18n_domain'),
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        self.assertEqual(obj.getProperty('lines1'), (b'Foo', b'Bar'))
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        self.assertEqual(obj.getProperty('lines2'), (b'Foo', b'Bar'))
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        self.assertEqual(obj.getProperty('lines3'), (b'Gee', b'Foo', b'Bar'))
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        self.assertEqual(obj.getProperty('lines1'), (b'Foo', b'Bar'))
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        self.assertEqual(obj.getProperty('lines2'), (b'Foo', b'Bar'))
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        self.assertEqual(obj.getProperty('lines3'), (b'Gee', b'Foo', b'Bar'))
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        self.assertEqual(obj.getProperty('lines1'), (b'Gee', b'Bar'))
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        self.assertEqual(obj.getProperty('lines2'), (b'Gee',))
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/tests/test_utils.py:        self.assertEqual(obj.getProperty('line1'), 'Line 1')
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:            prop = self.context.getProperty(prop_id)
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:                prop = obj.getProperty(prop_id)
output_zope/zopefoundation/Products.GenericSetup:src/Products/GenericSetup/utils.py:                prop_type = obj.getPropertyType(prop_id) or 'string'
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/PropertiedUser.py:    def getPropertysheet(self, id):
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/PropertiedUser.py:    __getitem__ = getPropertysheet
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/UserPropertySheet.py:    def getProperty(self, id, default=None):
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/UserPropertySheet.py:    def getPropertyType(self, id):
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/interfaces/authservice.py:        o for each id in the list getPropertysheet(id)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/interfaces/authservice.py:    def getPropertysheet(id):
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/interfaces/authservice.py:          i.e. user.getPropertysheet(id) == user[ id ]
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/interfaces/propertysheets.py:    def getProperty(id, default=None):
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/interfaces/propertysheets.py:    def getPropertyType(id):
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_PropertiedUser.py:        self.assertRaises(KeyError, user.getPropertysheet, 'nonesuch')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_PropertiedUser.py:        sheet = user.getPropertysheet('one')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_PropertiedUser.py:        self.assertEqual(sheet.getPropertyType('a'), 'int')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_PropertiedUser.py:        self.assertEqual(sheet.getPropertyType('b'), 'string')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_PropertiedUser.py:        self.assertEqual(sheet.getPropertyType('a'), 'int')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_PropertiedUser.py:        self.assertEqual(sheet.getPropertyType('b'), 'string')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_PropertiedUser.py:        self.assertRaises(KeyError, user.getPropertysheet, 'another')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:        self.assertEqual(ups.getProperty('foo'), None)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:        self.assertEqual(ups.getPropertyType('foo'), None)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:        self.assertEqual(ups.getPropertyType('s'), 'string')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('s'), None)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('s'), self._STRING_VALUE)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:        self.assertEqual(ups.getPropertyType('i'), 'int')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('i'), None)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('i'), self._INT_VALUE)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:        self.assertEqual(ups.getPropertyType('f'), 'float')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('f'), None)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('f'), self._FLOAT_VALUE)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:        self.assertEqual(ups.getPropertyType('n'), self._LONG_TYPE)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('n'), None)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('n'), self._LONG_VALUE)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:        self.assertEqual(ups.getPropertyType('d'), 'date')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('d'), None)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('d'), self._DATE_VALUE)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:        self.assertEqual(ups.getPropertyType('b'), 'boolean')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('b'), None)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('b'), self._BOOL_VALUE)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:        self.assertEqual(ups.getPropertyType('l'), 'lines')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('l'), None)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            got = ups.getProperty('l')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:        self.assertEqual(ups.getPropertyType('t'), 'lines')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('t'), None)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            got = ups.getProperty('t')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:        self.assertEqual(ups.getPropertyType('img'), 'image')
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            self.assertEqual(ups.getProperty('img'), None)
output_zope/zopefoundation/Products.PluggableAuthService:src/Products/PluggableAuthService/tests/test_UserPropertySheet.py:            got = ups.getProperty('img')
output_zope/zopefoundation/Products.ZCatalog:src/Products/PluginIndexes/CompositeIndex/CompositeIndex.py:            skip = zc.getProperty('skip_compositeindex', False)
output_zope/zopefoundation/ZServer:src/webdav/PropertySheet.py:            value = self.getProperty(name)
output_zope/zopefoundation/ZServer:src/webdav/PropertySheet.py:            value = self.getProperty(name)
output_zope/zopefoundation/ZServer:src/webdav/PropertySheets.py:    def getProperty(self, id, default=None):
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:    def getProperty(self, id, d=None):
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:    def getPropertyType(self, id):
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:            proptype = self.getPropertyType(id) or 'string'
output_zope/zopefoundation/Zope:src/OFS/PropertyManager.py:            if ids == self.getProperty('ids', None):
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:    def getProperty(self, id, default=None):
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:    def getPropertyType(self, id):
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:        return [self.getProperty(i['id']) for i in self._propertyMap()]
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:        return [(i['id'], self.getProperty(i['id']))
output_zope/zopefoundation/Zope:src/OFS/PropertySheets.py:            if ids == self.getProperty('ids', None):
output_zope/zopefoundation/Zope:src/OFS/interfaces.py:    def getProperty(id, d=None):
output_zope/zopefoundation/Zope:src/OFS/interfaces.py:    def getPropertyType(id):
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        self.assertIsInstance(inst.getProperty('prop'), tuple)
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        self.assertIsInstance(inst.getProperty('prop'), tuple)
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        self.assertIsInstance(inst.getProperty('prop'), tuple)
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        self.assertIsInstance(inst.getProperty('prop2'), tuple)
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        self.assertEqual(pm.getProperty('test_lines'), ('foo', 'bar'))
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        self.assertEqual(pm.getProperty('test_lines'), ('bar', 'baz'))
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        self.assertIsInstance(inst.getProperty('prop'), tuple)
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        self.assertIsInstance(inst.getProperty('prop'), tuple)
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        self.assertIsInstance(inst.getProperty('prop2'), tuple)
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        self.assertEqual(ps.getProperty('test_lines'), ('foo', 'bar'))
output_zope/zopefoundation/Zope:src/OFS/tests/testProperties.py:        self.assertEqual(ps.getProperty('test_lines'), ('bar', 'baz'))
output_zope/zopefoundation/Zope:src/webdav/PropertySheet.py:            value = self.getProperty(name)
output_zope/zopefoundation/Zope:src/webdav/PropertySheet.py:            value = self.getProperty(name)
output_zope/zopefoundation/Zope:src/webdav/PropertySheets.py:    def getProperty(self, id, default=None):

@leorochael
Copy link
Contributor

I have a suggestion:

Having .getProperty() run extra code to protect against past behavior is sub-optimal for new instances, but necessary for old ones.

And it's always best if a system can behave itself in the absence of migrations, even if migrations are going to be performed later.

So I suggest having .getProperty() do the extra work if the zope configuration tells it to do so.

This could be implemented by switching the .getProperty() implementation at import time based on the configuration, so during runtime, no price is paid for deciding if .getProperty() should do extra conversions or not, and if disabled, .getProperty() works exactly like today.

@leorochael
Copy link
Contributor

Additionally, we could mark this conversion functionality as deprecated and remove it at the next Zope release.

@jugmac00
Copy link
Member

@leorochael's suggestion looks like a good compromise. Any objections or other opinions on this?

@mauritsvanrees
Copy link
Member Author

That could work.
There is zope.configuration for this, but an environment variable may be easier.
Default should probably be: assume the stored value is fine already, so do no extra work.

I have meanwhile merged my GenericSetup branch, so no changes are needed in Zope for that. But in other cases it can still be needed. Same for migration code.

mauritsvanrees added a commit that referenced this issue Dec 2, 2021
You can call this to fix lines properties to only contain strings, not bytes.
It also replaces the deprecated property types ulines, utext, utoken, and ustring with their non-unicode variants.
See #987 for why this is needed.

Note: the code is not called by Zope itself.
The idea is that integrators who think they need this in their database, can call it.
Sample use:

    app.ZopeFindAndApply(app, apply_func=fix_properties)

I intend to use this (or a temporary copy) in the Plone 6 upgrade code.
See plone/Products.CMFPlone#3305
mauritsvanrees added a commit that referenced this issue Dec 2, 2021
You can call this to fix lines properties to only contain strings, not bytes.
It also replaces the deprecated property types ulines, utext, utoken, and ustring with their non-unicode variants.
See #987 for why this is needed.

Note: the code is not called by Zope itself.
The idea is that integrators who think they need this in their database, can call it.
Sample use:

    app.ZopeFindAndApply(app, apply_func=fix_properties)

I intend to use this (or a temporary copy) in the Plone 6 upgrade code.
See plone/Products.CMFPlone#3305
mauritsvanrees added a commit that referenced this issue Dec 2, 2021
You can call this to fix lines properties to only contain strings, not bytes.
It also replaces the deprecated property types ulines, utext, utoken, and ustring with their non-unicode variants.
See #987 for why this is needed.

Note: the code is not called by Zope itself.
The idea is that integrators who think they need this in their database, can call it.
Sample use:

    app.ZopeFindAndApply(app, apply_func=fix_properties)

I intend to use this (or a temporary copy) in the Plone 6 upgrade code.
See plone/Products.CMFPlone#3305
mauritsvanrees added a commit that referenced this issue Dec 2, 2021
* Add function ``ZPublisher.utils.fix_properties``.

You can call this to fix lines properties to only contain strings, not bytes.
It also replaces the deprecated property types ulines, utext, utoken, and ustring with their non-unicode variants.
See #987 for why this is needed.

Note: the code is not called by Zope itself.
The idea is that integrators who think they need this in their database, can call it.
Sample use:

    app.ZopeFindAndApply(app, apply_func=fix_properties)

I intend to use this (or a temporary copy) in the Plone 6 upgrade code.
See plone/Products.CMFPlone#3305

* Update src/ZPublisher/utils.py

Co-authored-by: Jens Vagelpohl <jens@plyp.com>

* Update src/ZPublisher/utils.py

Co-authored-by: Jens Vagelpohl <jens@plyp.com>

* Fix unicode properties: mark object as changed when _properties is changed.

Co-authored-by: Jens Vagelpohl <jens@plyp.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants