Repository navigation
Add tests to prevent regressions with the combination of ctypes and metaclasses. #125783
Description
Activity
- addedtype-featureA feature request or enhancementA feature request or enhancement
on Oct 21, 2024 Before calling something a “breaking change”, please check that you're not using internal API. Things like this are fragile:
- using an underscore-prefixed name
- calling
__new__but not the corresponding__init__ - instantiating an internal type (one with an underscore-prefixed name/module, even if it's also available as
type(something_public)) - Omitting
super().__new__in an overridden__new__, and the same for__init__
I appreciate that Python's backwards compatibility policy is unclear on some of these techniques; should we clarify it?
I also appreciate that what you're doing isn't possible with public API. I will try to not break your use case (and time permitting, to add supported API). But let's be clear that your warranty is void, so to say. Expect that with some CPython version will break
comtypesagain, and we'll need to work together to fix things. Ideally, test with alpha & beta releases so we can react as soon as possible -- IMO, that is the most effective way to prevent regressions here :)
All that said, it would definitely be good to add tests like this, to make sure the internals don't change unexpectedly -- especially if people are relying on the internals.
Even better: we could expose some public API for adding things to
_pointer_type_cache. Could we solve your use case with a publicregister_pointer_typefunction -- or even better, a hook likeregister_pointer_type_factory, which could create a pointer type on demand (including pointers to pointers, for any level of recursion)?As you pointed out, it is true that the implementation of
comtypesis much more fragile than packages that rely on other standard Python libraries.
Thomas Heller, the originator ofctypes, was also the originator ofcomtypes, so I speculate that he used privatectypesAPIs with a certain level of caution in the implementation ofcomtypes(although this is just speculation, as he has retired from CPython).I don't think we can clearly define a backward compatibility policy for Python here.
I believe we focus on improvingctypeswhile ensuring that projects depending on it do not break critically.I agree with transitioning from directly manipulating private elements to using the public API.
However, I would like to first backport this test to the bugfix status versions (3.12 and 3.13), ensuring that this implementation works compatibly with Python 3.14 and beyond.For now, I think it’s reasonable to continue using
_pointer_type_cache, and once the public API is implemented, we can refactor the test to use that instead.Reacted by Petr ViktorinWould defining a class with a metaclass be sufficient for the test?
For example, would adding assertions formro,isinstance, andissubclassincrease robustness without hindering improvements to the production code?I came up with the idea of adding assertions to confirm superclass relationships to prevent projects like
comtypesfrom breaking critically.
For example, ensuring thatPOINTER(IDispatch)is a subclass ofPOINTER(IUnknown)andPOINTER(Scripting.Dictionary)is a subclass ofCoClass.Even if the registration method for pointer types changes in the future, as long as these assertions don't fail, those projects should continue to work.
Reacted by Petr Viktorin- added a commit that references this issue
on Oct 25, 2024 Thank you! The backports are merged now.
Reacted by Jun Komoda- added a commit that references this issue
on Nov 1, 2024 The PRs to add tests that creates and registers pointer types within the
__new__and__init__methods of the metaclass has been merged.I believe there is nothing more to do in this issue, so I’ll close it.
There may be some areas that haven’t been tested in terms of using COM with
ctypes, and these will be addressed in separate issues, such as gh-126384.I’m also interested in adding a public API like
register_pointer_type, but I plan to first add tests for any untested parts of the publicctypesAPI currently used incomtypesbefore tackling that.
Feature or enhancement
Proposal:
There were breaking changes to
ctypesin Python 3.13.Projects like
comtypesandpyglet, which implement functionality by combiningctypesand metaclasses, no longer work unless their codebases are updated.We discussed what changes such projects need to make to their codebases in gh-124520.
I believe the easiest and most effective way to prevent regressions from happening again is to add tests on the
cpythonside.I wrote a simple test like the one below.
If no errors occur when this test is executed, we can assume that no regressions have occurred with the combination of
ctypesand metaclasses.However, I feel that this may not be enough. What should we specify as the target for the
assert?Has this already been discussed elsewhere?
No response given
Links to previous discussion of this feature:
#124520
Linked PRs
ctypesand metaclasses. #125881ctypesand metaclasses. (GH-125881) #125987ctypesand metaclasses. (GH-125881) #125988