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

Borked interface resolution order for boolean field #80

Closed
ale-rt opened this issue Oct 2, 2018 · 6 comments · Fixed by #92
Closed

Borked interface resolution order for boolean field #80

ale-rt opened this issue Oct 2, 2018 · 6 comments · Fixed by #92
Labels
Projects

Comments

@ale-rt
Copy link
Member

ale-rt commented Oct 2, 2018

If I take a boolean field I would expect IBool to be the first interface,compare the output of the field __iro__ with other fields.

In [23]: ITestContent['_bool'].__provides__.__iro__
Out[23]: 
(<InterfaceClass zope.schema._bootstrapinterfaces.IFromUnicode>,
 <InterfaceClass zope.schema._bootstrapinterfaces.IFromBytes>,
 <InterfaceClass zope.schema.interfaces.IBool>,
 <InterfaceClass zope.schema.interfaces.IField>,
 <InterfaceClass zope.schema._bootstrapinterfaces.IValidatable>,
 <InterfaceClass zope.interface.interfaces.IAttribute>,
 <InterfaceClass zope.interface.interfaces.IElement>,
 <InterfaceClass zope.interface.Interface>)

In [24]: ITestContent['_decimal'].__provides__.__iro__
Out[24]: 
(<InterfaceClass zope.schema.interfaces.IDecimal>,
 <InterfaceClass zope.schema._bootstrapinterfaces.IFromUnicode>,
 <InterfaceClass zope.schema._bootstrapinterfaces.IFromBytes>,
 <InterfaceClass zope.schema.interfaces.INumber>,
 <InterfaceClass zope.schema.interfaces.IMinMax>,
 <InterfaceClass zope.schema.interfaces.IOrderable>,
 <InterfaceClass zope.schema.interfaces.IField>,
 <InterfaceClass zope.schema._bootstrapinterfaces.IValidatable>,
 <InterfaceClass zope.interface.interfaces.IAttribute>,
 <InterfaceClass zope.interface.interfaces.IElement>,
 <InterfaceClass zope.interface.Interface>)

In [25]: ITestContent['_float'].__provides__.__iro__
Out[25]: 
(<InterfaceClass zope.schema.interfaces.IFloat>,
 <InterfaceClass zope.schema.interfaces.IReal>,
 <InterfaceClass zope.schema.interfaces.IComplex>,
 <InterfaceClass zope.schema._bootstrapinterfaces.IFromUnicode>,
 <InterfaceClass zope.schema._bootstrapinterfaces.IFromBytes>,
 <InterfaceClass zope.schema.interfaces.INumber>,
 <InterfaceClass zope.schema.interfaces.IMinMax>,
 <InterfaceClass zope.schema.interfaces.IOrderable>,
 <InterfaceClass zope.schema.interfaces.IField>,
 <InterfaceClass zope.schema._bootstrapinterfaces.IValidatable>,
 <InterfaceClass zope.interface.interfaces.IAttribute>,
 <InterfaceClass zope.interface.interfaces.IElement>,
 <InterfaceClass zope.interface.Interface>)

This prevents to register specific adaters for the IBool interface, see https://github.com/plone/plone.rfc822/pull/6/files#diff-13c585cfb8d0755a94087390ae8b22e4R30

@ale-rt
Copy link
Member Author

ale-rt commented Oct 2, 2018

@jamadden I think you probably have a good idea on how to fix this given your recent work on the IFrom* interface implementation.

@icemac icemac added the bug label Oct 2, 2018
@icemac icemac added this to To do in Zope 4 final release via automation Oct 2, 2018
@jamadden
Copy link
Member

jamadden commented Oct 2, 2018

This is the case for many of the fields, including Text, TextLine and Int, due to the way they are implemented in _bootstrapfield.py and then later "assigned" their final interfaces in _field.py:

classImplements(Text, IText)
classImplements(TextLine, ITextLine)
classImplements(Password, IPassword)
classImplements(Bool, IBool)
classImplements(Iterable, IIterable)
classImplements(Container, IContainer)

classImplements(Number, INumber)
classImplements(Complex, IComplex)
classImplements(Real, IReal)
classImplements(Rational, IRational)
classImplements(Integral, IIntegral)
classImplements(Int, IInt)

classImplements(Object, IObject)

e.g., for Text:

>>> from zope.schema import Text
>>> list(providedBy(Text()).flattened())
[<InterfaceClass zope.schema._bootstrapinterfaces.IFromUnicode>,
 <InterfaceClass zope.schema.interfaces.IText>,
 <InterfaceClass zope.schema.interfaces.IMinMaxLen>,
 <InterfaceClass zope.schema.interfaces.ILen>,
 <InterfaceClass zope.schema.interfaces.IIterable>,
 <InterfaceClass zope.schema.interfaces.IField>,
 <InterfaceClass zope.schema._bootstrapinterfaces.IValidatable>,
 <InterfaceClass zope.interface.interfaces.IAttribute>,
 <InterfaceClass zope.interface.interfaces.IElement>,
 <InterfaceClass zope.interface.Interface>]

It has been done this way since at least 3.4.0. I'm not sure what else can be done.

@davisagli
Copy link
Member

Seems like a big problem to me -- it means that an adapter registered for IFromUnicode takes precedence over one registered for a specific field type.

@mgedmin
Copy link
Member

mgedmin commented Oct 2, 2018

Would it help if IText et al explicitly inherited IFromUnicode and similar "mixin" interfaces?

@jamadden
Copy link
Member

jamadden commented Oct 2, 2018

Would it help if IText et al explicitly inherited IFromUnicode and similar "mixin" interfaces?

It seems to me those are properties of the implementation of the field, not the contract of its interface. OK, I think IText might be a bit of a special case, IFromUnicode seems reasonable to expect of a IText field. But not necessarily for an IBool field---indeed, zope.configuration implements its Bool's fromUnicode method very differently than the Bool class here does, and maybe there's an IBool implementation that doesn't want to accept strings at all.

The other option I can think of is to remove all the @implementer decorators in the _bootstrapfields.py file, and then manually move all of those declarations to the classImplements block in _field.py. But that's tedious. Also, does classImplements actually make any guarantees about IRO on the final object?

@icemac icemac added this to To do in Zope 5.0 via automation May 14, 2019
@icemac icemac removed this from To do in Zope 4 final release May 14, 2019
@jamadden
Copy link
Member

But that's tedious. Also, does classImplements actually make any guarantees about IRO on the final object?

No, not really. It just sticks whatever you give it on the end of the list of things already directly implemented. That can lead to really messed up IROs in the case of inheritance (I had to tweak it for that reason in zopefoundation/zope.interface#182, but that doesn't solve the issue here) . Arguably it should put what you're giving it at the front of the list, or at the very least you should get to choose (probably a separate function).

I mean, you can go back in and manually fix it by re-arranging implementedBy(Bool).__bases__, but that's cheating. Still, it is an option. If zopefoundation/zope.interface#182 gets merged, I'll need to do a bit of cleanups on about 5 of the IROs here anyway (all having to do with strings, IIRC).

jamadden added a commit that referenced this issue Mar 20, 2020
And test this. Fixes #80.

Add Python 3.8, drop Python 3.4.
jamadden added a commit that referenced this issue Mar 20, 2020
And test this. Fixes #80.

Add Python 3.8, drop Python 3.4.
jamadden added a commit that referenced this issue Mar 20, 2020
And test this. Fixes #80.

Add Python 3.8, drop Python 3.4.
jamadden added a commit that referenced this issue Mar 20, 2020
And test this. Fixes #80.

Add Python 3.8, drop Python 3.4.
Zope 5.0 automation moved this from To do to Done Mar 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
No open projects
Zope 5.0
  
Done
Development

Successfully merging a pull request may close this issue.

5 participants