diff --git a/stdlib/@tests/test_cases/check_platform.py b/stdlib/@tests/test_cases/check_platform.py new file mode 100644 index 000000000..efcbd992c --- /dev/null +++ b/stdlib/@tests/test_cases/check_platform.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +import platform +import sys +from typing_extensions import assert_type + +# platform.uname_result emulates a 6 field named tuple, but on 3.9+ the processor +# field is lazily evaluated, which results in it being a little funky. +uname = platform.uname() +if sys.version_info >= (3, 9): + myuname = platform.uname_result("Darwin", "local", "22.5.0", "Darwin Kernel Version 22.5.0", "arm64") +else: + myuname = platform.uname_result("Darwin", "local", "22.5.0", "Darwin Kernel Version 22.5.0", "arm64", "arm") + +assert_type(uname, platform.uname_result) +assert_type(myuname, platform.uname_result) + +assert_type(uname[5], str) +assert_type(myuname[5], str) diff --git a/stdlib/platform.pyi b/stdlib/platform.pyi index fcb5b0056..73393eada 100644 --- a/stdlib/platform.pyi +++ b/stdlib/platform.pyi @@ -1,5 +1,6 @@ import sys from typing import NamedTuple, type_check_only +from typing_extensions import Self def libc_ver(executable: str | None = None, lib: str = "", version: str = "", chunksize: int = 16384) -> tuple[str, str]: ... def win32_ver(release: str = "", version: str = "", csd: str = "", ptype: str = "") -> tuple[str, str, str, str]: ... @@ -15,6 +16,8 @@ def system_alias(system: str, release: str, version: str) -> tuple[str, str, str def architecture(executable: str = sys.executable, bits: str = "", linkage: str = "") -> tuple[str, str]: ... if sys.version_info >= (3, 9): + # This class is not exposed. It calls itself platform.uname_result_base. + # At runtime it only has 5 fields. @type_check_only class _uname_result_base(NamedTuple): system: str @@ -22,12 +25,23 @@ if sys.version_info >= (3, 9): release: str version: str machine: str + # This base class doesn't have this field at runtime, but claiming it + # does is the least bad way to handle the situation. Nobody really + # sees this class anyway. See #13068 + processor: str + # uname_result emulates a 6-field named tuple, but the processor field + # is lazily evaluated rather than being passed in to the constructor. class uname_result(_uname_result_base): + if sys.version_info >= (3, 10): + __match_args__ = ("system", "node", "release", "version", "machine") # pyright: ignore[reportAssignmentType] + + def __new__(_cls, system: str, node: str, release: str, version: str, machine: str) -> Self: ... @property def processor(self) -> str: ... else: + # On 3.8, uname_result is actually just a regular NamedTuple. class uname_result(NamedTuple): system: str node: str