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
Fix comparison of KeyReferenceToPersistent
with a proxied one.
#6
base: master
Are you sure you want to change the base?
Conversation
…enceToPersistent`. This got broken somewhere between 3.6.2 an 4.x: KeyReferenceToPersistent.object is not part of the interface so it cannot be accessed on a security proxied object. The interface suggests to use __call__ to get the object.
I have so many questions 😄 First, I can't find any evidence that security-proxied objects were ever supported in comparisons. The very first revision of persistent.py in this repository (2009's 3.5.0 of zope.app.keyreference) doesn't use So when and how did this ever actually work? Was there some change in (My apologies if I'm missing something obvious here and those questions are gibberish. I rarely-to-never use security-proxied objects, so I'm going off the description in this PR of what the problem is.) If we can figure out that it did work at one point, but has been not-working for the past ~8 years, is fixing it actually worthwhile at this point? (I assume it's causing you problems or else you wouldn't have this PR 😄 ) Maybe people have come to depend (unknowingly?) on the current behaviour? If so, is it worth adding a hard dependency on Last (finally!), can there be a test case that shows it was broken and is now fixed? Some of these seem like important enough questions that it might be good to get some more eyeballs from other contributors ( @tseaver ?). Not that I'm trying to pass the buck 😉 |
@jamadden Thank you for looking so deep into this PR. A bit about what I do and where the code breaks so I wrote this PR. I call zope.intid.IntIds.getId() using a security proxied object. Inside it adapts the object to Until Since When I remove Conclusion: There seems to be a special handling of Unfortunately I cannot debug this deeper because the pure python variant of the security proxy does not run with my code. It breaks somewhere in @tseaver Do you see a chance to "fix" the proxy of |
If you were running into zopefoundation/zope.proxy#16 that's now fixed in master. CPython's comparison logic is complex to follow. Internally it basically still wants to use the Objects implemented in Python automatically have the A C proxy manually fills in the C The guts of static PyObject *
try_rich_compare(KeyReference *self, Proxy *other, int op)
{
richcmpfunc f;
PyObject *res;
if (self->ob_type != other->ob_type && /* True */
PyType_IsSubtype(other->ob_type, self->ob_type) /* False */ &&
...) {
/* Doesn't matter, not taken */
}
if ((f = RICHCOMPARE(self->ob_type)) != NULL) { /* True */
/* f will be slot_tp_richcompare, which will delegate to the
Python method __eq__ KeyReference has __eq__, so we call it. */
res = (*f)(v, w, op); /* BOOM: Raise exception, return NULL, propagate exception */
if (res != Py_NotImplemented)
return res; /* And we're out */
Py_DECREF(res);
}
if (...) /* not important, not taken */
} Ok, so what about the case where we don't have static PyObject *
try_rich_compare(KeyReference *self, Proxy *other, int op)
{
richcmpfunc f;
PyObject *res;
if (self->ob_type != other->ob_type && /* True */
PyType_IsSubtype(other->ob_type, self->ob_type) /* False */ &&
...) {
/* Doesn't matter, not taken */
}
if ((f = RICHCOMPARE(self->ob_type)) != NULL) { /* True */
/* f will be slot_tp_richcompare, which will delegate to the
Python method __eq__. We DONT have __eq__,
and neither does the C proxy (because __eq__ is only
looked up on the type, bypassing __getattribute__),
so it returns Py_NotImplemented */
res = (*f)(v, w, op); /* Py_NotImplemented */
Py_DECREF(res);
}
if ((f = RICHCOMPARE(other->ob_type)) != NULL) {
/* Proxy has rich compare so we call it with swapped arguments: wrap_richcompare */
return (*f)(w, v, _Py_SwappedOp[op]);
}
} We can see that if we (self) don't have An important side note would be what happens if the Python versions of the proxies are in use: They do provide an Ok, that said, I think the fact that adding |
The test cases for zope.app.catalog produce this now 😦 |
@jamadden Thank you for your excellent in depth analysis. Sorry for picking up this after so much time, but this problem occurred in a hobby project which I eventually want to migrate to Python 3. Did you find a solution for tests in |
I have released a package, which can be installed to apply the changes of this PR in an actual project, if really needed. |
This got broken somewhere between 3.6.2 an 4.x: KeyReferenceToPersistent.object
is not part of the interface so it cannot be accessed on a security proxied
object. The interface suggests to use
__call__()
to get the object.