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

Content type schema has no fields based on FTI ._p_mtime #165

Open
frapell opened this issue Apr 19, 2022 · 0 comments · May be fixed by #166
Open

Content type schema has no fields based on FTI ._p_mtime #165

frapell opened this issue Apr 19, 2022 · 0 comments · May be fixed by #166

Comments

@frapell
Copy link
Member

frapell commented Apr 19, 2022

So, this was a fun bug to debug (NOT)

The model used to define fields for a content type is a generated schema, which uses the FTI ._p_mtime to generate its unique name. In addition to this, there is a string replacement that replaces . with _2_ and then joins parts using _0_.
The _p_mtime is a timestamp, and when you don't have milliseconds in your datetime object, it will always end in .0

So, this works fine

>>> from plone.dexterity.schema import SchemaNameEncoder
>>> from datetime import datetime
>>> enc = SchemaNameEncoder()
>>> now = datetime.now()
>>> now.timestamp()
1650381570.771721
>>> enc.join("Plone", "1650381570.771721", "Image")
'Plone_0_1650381570_2_771721_0_Image'
>>> enc.split('Plone_0_1650381570_2_771721_0_Image')
['Plone', '1650381570.771721', 'Image']

This doesn't

>>> from plone.dexterity.schema import SchemaNameEncoder
>>> from datetime import datetime
>>> enc = SchemaNameEncoder()
>>> now = datetime(2022,4,19,12,21,30)
>>> now.timestamp()
1650388890.0
>>> enc.join("Plone", "1650388890.0", "Image")
'Plone_0_1650388890_2_0_0_Image'
>>> enc.split('Plone_0_1650388890_2_0_0_Image')
['Plone', '1650388890_2', '0_Image']

I cannot figure out why, or under which circumstances, my Image FTI ends up with a timestamp from a datetime with no milliseconds

>>> fti = queryUtility(IDexterityFTI, name="Image")
>>> fti._p_mtime
1650326400.0

And the way this manifests, is that suddenly your Images have no image field

>>> api.content.create(container=site, type="Image", id='my-image')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/opt/plone/eggs/decorator-5.1.1-py3.8.egg/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/vagrant/src/plone.api/src/plone/api/validation.py", line 75, in wrapped
    return function(*args, **kwargs)
  File "/opt/plone/eggs/decorator-5.1.1-py3.8.egg/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/vagrant/src/plone.api/src/plone/api/validation.py", line 147, in wrapped
    return function(*args, **kwargs)
  File "/vagrant/src/plone.api/src/plone/api/content.py", line 71, in create
    container.invokeFactory(type, content_id, **kwargs)
  File "/opt/plone/eggs/plone.dexterity-3.0.0a2-py3.8.egg/plone/dexterity/content.py", line 830, in invokeFactory
    return super(Container, self).invokeFactory(
  File "/opt/plone/eggs/Products.CMFCore-2.5.4-py3.8.egg/Products/CMFCore/PortalFolder.py", line 299, in invokeFactory
    return ttool.constructContent(type_name, self, id, RESPONSE,
  File "/opt/plone/eggs/Products.CMFCore-2.5.4-py3.8.egg/Products/CMFCore/TypesTool.py", line 809, in constructContent
    ob = info.constructInstance(container, id, *args, **kw)
  File "/opt/plone/eggs/Products.CMFCore-2.5.4-py3.8.egg/Products/CMFCore/TypesTool.py", line 308, in constructInstance
    return self._constructInstance(container, id, *args, **kw)
  File "/opt/plone/eggs/Products.CMFCore-2.5.4-py3.8.egg/Products/CMFCore/TypesTool.py", line 569, in _constructInstance
    notify(ObjectCreatedEvent(obj))
  File "/opt/plone/eggs/zope.event-4.5.0-py3.8.egg/zope/event/__init__.py", line 32, in notify
    subscriber(event)
  File "/opt/plone/eggs/zope.component-5.0.1-py3.8.egg/zope/component/event.py", line 27, in dispatch
    component_subscribers(event, None)
  File "/opt/plone/eggs/zope.component-5.0.1-py3.8.egg/zope/component/_api.py", line 134, in subscribers
    return sitemanager.subscribers(objects, interface)
  File "/opt/plone/eggs/zope.interface-5.4.0-py3.8-linux-x86_64.egg/zope/interface/registry.py", line 448, in subscribers
    return self.adapters.subscribers(objects, provided)
  File "/opt/plone/eggs/zope.interface-5.4.0-py3.8-linux-x86_64.egg/zope/interface/adapter.py", line 899, in subscribers
    subscription(*objects)
  File "/opt/plone/eggs/zope.component-5.0.1-py3.8.egg/zope/component/event.py", line 36, in objectEventNotify
    component_subscribers((event.object, event), None)
  File "/opt/plone/eggs/zope.component-5.0.1-py3.8.egg/zope/component/_api.py", line 134, in subscribers
    return sitemanager.subscribers(objects, interface)
  File "/opt/plone/eggs/zope.interface-5.4.0-py3.8-linux-x86_64.egg/zope/interface/registry.py", line 448, in subscribers
    return self.adapters.subscribers(objects, provided)
  File "/opt/plone/eggs/zope.interface-5.4.0-py3.8-linux-x86_64.egg/zope/interface/adapter.py", line 899, in subscribers
    subscription(*objects)
  File "/vagrant/src/plone.app.contenttypes/plone/app/contenttypes/subscribers.py", line 14, in set_title_description
    datafield = obj.image
  File "/opt/plone/eggs/plone.dexterity-3.0.0a2-py3.8.egg/plone/dexterity/content.py", line 407, in __getattr__
    raise AttributeError(name)
AttributeError: image

When I modify the FTI, the problem is solved

>>> setattr(fti, 'dummy', 1)
>>> transaction.commit()
>>> fti._p_mtime
1650382489.4636827
>>> api.content.create(container=site, type="Image", id='my-image')
<Image at /Plone/my-image>

In order to fix this, I believe it should be enough to simply return the int() part of the timestamp in

def get_suffix(fti):
mtime = getattr(fti, "_p_mtime", None)
# Python 2 rounds floats when we use the str function on them.
# Python 2:
# >>> str(1637689348.9999528)
# '1637689349.0'
# Python 3:
# >>> str(1637689348.9999528)
# '1637689348.9999528'
# This was causing the schema names in Python 2 to take an unexpected format,
# causing errors.
# So, we need to use the repr function, which doesn't round floats.
if mtime:
return repr(mtime)
return ""

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant