You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
The hash and equals methods of code objects are large, complex and unnecessary. #101346
Technically, they are immutable values but realistically, there is only on code object per source unit.
Attempting to maintain value semantics when the underlying code object is internally mutable (due to specialization and instrumentation) is cumbersome and probably buggy, and will only get worse with more optimizations.
We should just delete the hash and comparison methods and have identity comparison. It will be faster too.
It's a good idea.
What's stopping you from implementing this?
How i understand, we need to delete code_hash and code_richcompare from Objects/codeobject.c(and ofc, set tp_hash and tp_richcompare to 0 in PyCode_Type
Does it breaks backwards compatibility?
So do we need a SC ruling on whether this needs a deprecation process? I guess technically we could raise a DeprecationWarning anytime an __eq__ comparison returns True for different code objects.
I put up a pull request for this, and looking at the resulting test failures in the CPython test suite alone has changed my mind about whether this is a good idea (or at the very least, whether we can do it without a deprecation process.)
Dynamically compiling Python source strings to code objects is not uncommon, and in that scenario it is a) quite likely that the same input will result in the "same" output code object, position information and all, and b) quite likely that the test suite for such dynamic-compilation code may be using code object equality. And in fact this case does occur in our own test suite (e.g. in test_codeop and elsewhere), which makes me think it's reasonably likely to be occurring in third-party test suites also.
I didn't see any reasonable way to fix test_codeop other than to reimplement the entire current definition of code object equality as an assertion method in the codeop tests. This is actually quite difficult to do given the unavailability of _PyCode_ConstantKey to managed code and the fact that code objects can occur nested arbitrarily deep in co_consts. And the fact that we would need to do it at all suggests that the current definition of code object equality is in fact sometimes useful.
Indeed, I hoped initially it was not as bad as it appeared! But when I dug into trying to fix test_codeop, it did lead me to question whether we should do this, for the reasons I mentioned above. Happy to hear counter-arguments, though! Ideally specific proposals for what we should do with test_codeop, and what we would tell any third-party libraries that do dynamic compilation if they have similar code in their test suite.
What is test_codeop supposed to be testing? I'm pretty sure it isn't the notion of equality of code objects.
Whatever notion of equivalence it has should be spelled, rather than using == on code objects.
I suspect that testing that the bytecode, names and constants were the same would be enough.
any third-party libraries that do dynamic compilation
Given the bytecode changes from one version to the next, I suspect a change in code object equality will be the least of their problems. We have changed the behavior of code object equality several times in the past.
I think in principle what test_codeop wants to test is "would any consumer of this code object observe any behavior difference from this other code object," which does sound a lot like "code object equality."
We have changed the behavior of code object equality several times in the past.
Has it ever been defined in a way that would mean that compile("some code") == compile("some code") would ever not have been true? This is the scenario that I think (in somewhat more complex disguise) may not be uncommon in test suites for libraries doing dynamic compilation.
I suspect that testing that the bytecode, names and constants were the same would be enough.
Constants make this non-trivial, since you have to check the types of constants and, if they are code objects, recursively use whatever definition of pseudo-code-object equality you have chosen on those nested code objects too, instead of just comparing them. self.assertEqual(c1.co_consts, c2.co_consts) doesn't work. This is of course doable in any particular case, but it seems to me like it may be a pretty noticeable burden to impose on anyone who wants to ask "is method 1 of compiling this code leading to the 'same' results as method 2."
Activity
markshannon commentedon Jan 26, 2023
This will involves a slight change in semantics:
Currently:
With this change:
Eclips4 commentedon Feb 2, 2023
It's a good idea.
What's stopping you from implementing this?
How i understand, we need to delete
code_hashandcode_richcomparefromObjects/codeobject.c(and ofc, settp_hashandtp_richcompareto 0 inPyCode_TypeDoes it breaks backwards compatibility?
carljm commentedon Mar 18, 2023
I assume this meant to say "by identity."
So do we need a SC ruling on whether this needs a deprecation process? I guess technically we could raise a
DeprecationWarninganytime an__eq__comparison returnsTruefor different code objects.pythongh-101346: compare and hash code objects by identity
carljm commentedon Apr 6, 2023
I put up a pull request for this, and looking at the resulting test failures in the CPython test suite alone has changed my mind about whether this is a good idea (or at the very least, whether we can do it without a deprecation process.)
Dynamically compiling Python source strings to code objects is not uncommon, and in that scenario it is a) quite likely that the same input will result in the "same" output code object, position information and all, and b) quite likely that the test suite for such dynamic-compilation code may be using code object equality. And in fact this case does occur in our own test suite (e.g. in
test_codeopand elsewhere), which makes me think it's reasonably likely to be occurring in third-party test suites also.I didn't see any reasonable way to fix
test_codeopother than to reimplement the entire current definition of code object equality as an assertion method in the codeop tests. This is actually quite difficult to do given the unavailability of_PyCode_ConstantKeyto managed code and the fact that code objects can occur nested arbitrarily deep inco_consts. And the fact that we would need to do it at all suggests that the current definition of code object equality is in fact sometimes useful.markshannon commentedon Apr 6, 2023
We have lots of tests that test artifacts of the implementation, rather than any spec.
It may not be as bad as it appears.
carljm commentedon Apr 6, 2023
Indeed, I hoped initially it was not as bad as it appeared! But when I dug into trying to fix
test_codeop, it did lead me to question whether we should do this, for the reasons I mentioned above. Happy to hear counter-arguments, though! Ideally specific proposals for what we should do withtest_codeop, and what we would tell any third-party libraries that do dynamic compilation if they have similar code in their test suite.markshannon commentedon Apr 6, 2023
What is
test_codeopsupposed to be testing? I'm pretty sure it isn't the notion of equality of code objects.Whatever notion of equivalence it has should be spelled, rather than using
==on code objects.I suspect that testing that the bytecode, names and constants were the same would be enough.
markshannon commentedon Apr 6, 2023
Given the bytecode changes from one version to the next, I suspect a change in code object equality will be the least of their problems. We have changed the behavior of code object equality several times in the past.
carljm commentedon Apr 6, 2023
I think in principle what
test_codeopwants to test is "would any consumer of this code object observe any behavior difference from this other code object," which does sound a lot like "code object equality."Has it ever been defined in a way that would mean that
compile("some code") == compile("some code")would ever not have been true? This is the scenario that I think (in somewhat more complex disguise) may not be uncommon in test suites for libraries doing dynamic compilation.Constants make this non-trivial, since you have to check the types of constants and, if they are code objects, recursively use whatever definition of pseudo-code-object equality you have chosen on those nested code objects too, instead of just comparing them.
self.assertEqual(c1.co_consts, c2.co_consts)doesn't work. This is of course doable in any particular case, but it seems to me like it may be a pretty noticeable burden to impose on anyone who wants to ask "is method 1 of compiling this code leading to the 'same' results as method 2."