-
Notifications
You must be signed in to change notification settings - Fork 288
Update specification for directives for sys.implementation and sys.platform checks.#2173
Update specification for directives for sys.implementation and sys.platform checks. #2173Josverl wants to merge 1 commit intopython:main from
Conversation
...atform checks. Signed-off-by: Jos Verlinde <Jos.Verlinde@Microsoft.com>
All commit authors signed the Contributor License Agreement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example doesn't make much sense, since it would never be true at runtime on any actual Python version.
A realistic example that might actually return True would need to look like sys.version_info == (3, 13, 1, "final", 0) -- but how useful is that in practice?
I would suggest not specifying that equality checks should be supported at all; I don't think they are usable for any realistic scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that the specific version should be adjusted, but I don't think that testing for equality should be dismissed, or truncated to an arbitrary number of nodes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what the possible use case is here. Can you elaborate?
To be clear, at runtime equality checks with sys.version_info will never return True unless you provide all five elements of the sys.version_info tuple. Every other equality check will always return False. So this is only usable if you check all five elements, and making that useful requires that type checkers allow specifying the Python version down to "releaselevel" and "serial" level in their configurations.
From a type checker implementer perspective, I feel pretty strongly that we would never implement support for this, because it is a bunch of work and it is not useful in any realistic case. I see this as a blocker for this proposal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the clear feedback,
Do I understand correctly that Major.Minor would be acceptable for comparison?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think that all sys.version_info checks should only handle major/minor. That implies that support for inequality/equality checks against all of sys.version_info must be removed from the proposal, because there is no way to perform a useful equality/inequality check against all of sys.version_info (as shown in this example) that only considers major/minor.
I'm fine with supporting sys.version_info[:2] == (3, 14)! So if we are explicit that equality/inequality checks are supported against only the first two elements, but not against the entire tuple, that resolves my objection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If all sys.version_info comparisons are only supported on the first two elements of the version tuple, the I think that therer is no need to support the explicit form sys.version_info[:2] either.
I have removed that option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think inequality checks should also not be supported, for the same reason discussed above for equality checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your point; removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for these cases (and even for the basic comparison case) it's also important to specify which elements of the tuple are supported. For example, are type checkers expected to support sys.version_info[2] == 1 (where sys.version_info[2] is the micro version). What about the fourth and fifth elements ("releaselevel" and "serial")?
I think we should be explicit that type checkers should only be expected to have special handling for the first two tuple elements (major and minor version); anything more than that adds a lot of complexity to type checker configuration for little to no gain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I think that is a useful restriction.
Patch level should mean no API level changes, thus no changes in typing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the named attributes? Should type checkers support e.g. sys.version_info.major >= 3?
(I don't think this is useful enough to require, but maybe we should be explicit that support for it is not expected.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a MicroPyton perspective I concur, as it does not support named attributes for this, but I don't necessarily want to force that on other implementations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Target version to what granularity?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point, I think; as you suggested; Major.Minor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding this has does have significant ecosystem costs (as outlined by @erictraut in https://discuss.python.org/t/proposal-to-improve-support-for-other-python-platforms-in-the-typing-specification/91877/3 ).
In the end I think it is probably worth it, because without it, type-checking for alternative implementations of Python seems quite difficult. The cost to type checkers themselves seems relatively low: one new config option, defaulting to "cpython".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ecosystem is larger than just one implementation.
But I know I can't properly assess the effort needed to update and maintain the tooling I hope to influence.
So I'll leave the weighing up to those who can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's important for the language to be very specific about the exact syntactical forms that are supported. Type checkers need to evaluate these expressions early in the analysis process, prior to type evaluation. That means they need to do exact tree matching of the AST. That means it's important to reduce the variations that are supported. For example, sys.version_info <= (3, 10) is fine, but version_info <= (3, 10) is not, nor is (3, 10) > version_info.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type checkers need to evaluate these expressions early in the analysis process, prior to type evaluation. That means they need to do exact tree matching of the AST. That means it's important to reduce the variations that are supported. For example,
sys.version_info <= (3, 10)is fine, butversion_info <= (3, 10)is not, nor is(3, 10) > version_info.
(None of this is true for how ty implements sys.version_info or sys.platform branches, FWIW)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that changes anything about what should be supported in the spec though, since it's true for all other type checkers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with Carl. Equality and inequality checks don't make sense. In think the only operators that make sense are < and >=.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adjusted accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not convinced that containment should be supported here. There's already a way to express this in a way that all tools today support. I understand the argument that this is less verbose, but it's not that common for checks to include more than one platform, so I don't think there's a compelling argument to force all tools to support this additional form.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For deciding on issues like this it would be helpful to have a little summary of what type checkers currently support (like what I did in https://discuss.python.org/t/spec-change-clarify-that-tuple-should-not-be-prohibited-as-an-argument-to-type/105590/7). I'll gather a few variants and summarize it.
I think part of this change will be codifying what is already supported universally, and another part will be adding support for more features. The first group should be uncontroversial, and in the second group we should only force work on type checker authors if there's a clear use case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If possible this would really help with creating readable and maintainable type stubs for MicroPython.
even with the proposed sys.implementation.name check we still see significant API differences due the underlying MCU vendor SDKs being significantly different.
Though MicroPython tries to abstract much of these differences away that is not entirly possible as the underlying SDK or hardware is simply different. This is a common source of errors when code is ported from one MCU architecture to another.
For instance Timers are available on all platforms, but with many different default values.
While the below would work
# machine.pyi class Timer(): """"Timer object""" if sys.implementation.name == "micropython" and (sys.platform == "esp32" or sys.platform == "mimxrt" or sys.platform == "rp2" or sys.platform == "samd" or sys.platform == "stm32" or sys.platform == "alif" or sys.platform == "webassembly"): @overload def __init__( self, id: int, /, *, mode: int = PERIODIC, period: int | None = None, callback: Callable[[Timer], None] | None = None, hard: bool | None = None, ):... elif sys.implementation.name == "micropython" and (sys.platform == "esp8266" or sys.platform == "unix" or sys.platform == "windows" or sys.platform == "zephyr"): @overload def __init__( self, id: int = -1, /, *, mode: int = PERIODIC, period: int | None = None, callback: Callable[[Timer], None] | None = None, ):...
the below is much simpler to understand and maintain.
class Timer(): """"Timer object""" if sys.implementation.name == "micropython" and (sys.platform in ("esp32", "mimxrt", "rp2", "samd", "stm32", "alif", "webassembly")): @overload def __init__( self, id: int, /, *, mode: int = PERIODIC, period: int | None = None, callback: Callable[[Timer], None] | None = None, hard: bool | None = None, ):... elif sys.implementation.name == "micropython" and (sys.platform in ("esp8266", "unix", "windows", "zephyr")): @overload def __init__( self, id: int = -1, /, *, mode: int = PERIODIC, period: int | None = None, callback: Callable[[Timer], None] | None = None, ):...
I think it would be reasonable to explicitly restrict this to "a tuple of literal strings",assuming that simplies the implementation.
Negative membership option could also be omitted , I think that would still be sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here also, I think it would be useful for the spec to be specific that sys.platform == "linux" is supported but other variants like platform == "linux" and "linux" == sys.platform are not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense.
I added a note to explicitly clarify that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here again, I don't think there's good justification for supporting a containment operator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I addedd this for consistency, but I'm OK to remove sys.implementation.name in ("pypy", "graalpy") from the spec to simplify the implementation.
Uh oh!
There was an error while loading. Please reload this page.
This PR adds additional detail to the specification for Version and Platform checking.
Specificially it aims to add support for typechers to add support for :
sys.implementation.nameReferences :