Skip to content

Commit 0b75d71

Browse files
AlexWaygoodAkuli
andauthored
Add a structseq class to _typeshed (#6560)
Co-authored-by: Akuli <akuviljanen17@gmail.com>
1 parent 183a43a commit 0b75d71

16 files changed

+300
-253
lines changed

stdlib/_thread.pyi

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import sys
2+
from _typeshed import structseq
23
from threading import Thread
34
from types import TracebackType
45
from typing import Any, Callable, NoReturn, Optional, Tuple, Type
@@ -32,7 +33,9 @@ TIMEOUT_MAX: float
3233
if sys.version_info >= (3, 8):
3334
def get_native_id() -> int: ... # only available on some platforms
3435
@final
35-
class _ExceptHookArgs(Tuple[Type[BaseException], Optional[BaseException], Optional[TracebackType], Optional[Thread]]):
36+
class _ExceptHookArgs(
37+
structseq[Any], Tuple[Type[BaseException], Optional[BaseException], Optional[TracebackType], Optional[Thread]]
38+
):
3639
@property
3740
def exc_type(self) -> Type[BaseException]: ...
3841
@property

stdlib/_typeshed/__init__.pyi

+18-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import ctypes
77
import mmap
88
import sys
99
from os import PathLike
10-
from typing import AbstractSet, Any, Awaitable, Container, Iterable, Protocol, TypeVar, Union
10+
from typing import AbstractSet, Any, Awaitable, ClassVar, Container, Generic, Iterable, Protocol, Type, TypeVar, Union
1111
from typing_extensions import Literal, final
1212

1313
_KT = TypeVar("_KT")
@@ -199,3 +199,20 @@ else:
199199
@final
200200
class NoneType:
201201
def __bool__(self) -> Literal[False]: ...
202+
203+
# This is an internal CPython type that is like, but subtly different from, a NamedTuple
204+
# Subclasses of this type are found in multiple modules.
205+
# In typeshed, `structseq` is only ever used as a mixin in combination with a fixed-length `Tuple`
206+
# See discussion at #6546 & #6560
207+
# `structseq` classes are unsubclassable, so are all decorated with `@final`.
208+
class structseq(Generic[_T_co]):
209+
n_fields: ClassVar[int]
210+
n_unnamed_fields: ClassVar[int]
211+
n_sequence_fields: ClassVar[int]
212+
# The first parameter will generally only take an iterable of a specific length.
213+
# E.g. `os.uname_result` takes any iterable of length exactly 5.
214+
#
215+
# The second parameter will accept a dict of any kind without raising an exception,
216+
# but only has any meaning if you supply it a dict where the keys are strings.
217+
# https://github.com/python/typeshed/pull/6560#discussion_r767149830
218+
def __new__(cls: Type[_T], sequence: Iterable[_T_co], dict: dict[str, Any] = ...) -> _T: ...

stdlib/grp.pyi

+13-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
from typing import NamedTuple
1+
from _typeshed import structseq
2+
from typing import Any, List, Optional, Tuple
3+
from typing_extensions import final
24

3-
class struct_group(NamedTuple):
4-
gr_name: str
5-
gr_passwd: str | None
6-
gr_gid: int
7-
gr_mem: list[str]
5+
@final
6+
class struct_group(structseq[Any], Tuple[str, Optional[str], int, List[str]]):
7+
@property
8+
def gr_name(self) -> str: ...
9+
@property
10+
def gr_passwd(self) -> str | None: ...
11+
@property
12+
def gr_gid(self) -> int: ...
13+
@property
14+
def gr_mem(self) -> list[str]: ...
815

916
def getgrall() -> list[struct_group]: ...
1017
def getgrgid(id: int) -> struct_group: ...

stdlib/os/__init__.pyi

+158-105
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ from _typeshed import (
99
Self,
1010
StrOrBytesPath,
1111
StrPath,
12+
structseq,
1213
)
1314
from builtins import OSError
1415
from contextlib import AbstractContextManager
@@ -26,7 +27,6 @@ from typing import (
2627
List,
2728
Mapping,
2829
MutableMapping,
29-
NamedTuple,
3030
NoReturn,
3131
Protocol,
3232
Sequence,
@@ -284,49 +284,71 @@ TMP_MAX: int # Undocumented, but used by tempfile
284284

285285
# ----- os classes (structures) -----
286286
@final
287-
class stat_result:
288-
# For backward compatibility, the return value of stat() is also
289-
# accessible as a tuple of at least 10 integers giving the most important
290-
# (and portable) members of the stat structure, in the order st_mode,
291-
# st_ino, st_dev, st_nlink, st_uid, st_gid, st_size, st_atime, st_mtime,
292-
# st_ctime. More items may be added at the end by some implementations.
293-
294-
st_mode: int # protection bits,
295-
st_ino: int # inode number,
296-
st_dev: int # device,
297-
st_nlink: int # number of hard links,
298-
st_uid: int # user id of owner,
299-
st_gid: int # group id of owner,
300-
st_size: int # size of file, in bytes,
301-
st_atime: float # time of most recent access,
302-
st_mtime: float # time of most recent content modification,
303-
st_ctime: float # platform dependent (time of most recent metadata change on Unix, or the time of creation on Windows)
304-
st_atime_ns: int # time of most recent access, in nanoseconds
305-
st_mtime_ns: int # time of most recent content modification in nanoseconds
306-
st_ctime_ns: int # platform dependent (time of most recent metadata change on Unix, or the time of creation on Windows) in nanoseconds
307-
if sys.version_info >= (3, 8) and sys.platform == "win32":
308-
st_reparse_tag: int
287+
class stat_result(structseq[float], Tuple[int, int, int, int, int, int, int, float, float, float]):
288+
# The constructor of this class takes an iterable of variable length (though it must be at least 10).
289+
#
290+
# However, this class behaves like a tuple of 10 elements,
291+
# no matter how long the iterable supplied to the constructor is.
292+
# https://github.com/python/typeshed/pull/6560#discussion_r767162532
293+
#
294+
# The 10 elements always present are st_mode, st_ino, st_dev, st_nlink,
295+
# st_uid, st_gid, st_size, st_atime, st_mtime, st_ctime.
296+
#
297+
# More items may be added at the end by some implementations.
298+
@property
299+
def st_mode(self) -> int: ... # protection bits,
300+
@property
301+
def st_ino(self) -> int: ... # inode number,
302+
@property
303+
def st_dev(self) -> int: ... # device,
304+
@property
305+
def st_nlink(self) -> int: ... # number of hard links,
306+
@property
307+
def st_uid(self) -> int: ... # user id of owner,
308+
@property
309+
def st_gid(self) -> int: ... # group id of owner,
310+
@property
311+
def st_size(self) -> int: ... # size of file, in bytes,
312+
@property
313+
def st_atime(self) -> float: ... # time of most recent access,
314+
@property
315+
def st_mtime(self) -> float: ... # time of most recent content modification,
316+
# platform dependent (time of most recent metadata change on Unix, or the time of creation on Windows)
317+
@property
318+
def st_ctime(self) -> float: ...
319+
@property
320+
def st_atime_ns(self) -> int: ... # time of most recent access, in nanoseconds
321+
@property
322+
def st_mtime_ns(self) -> int: ... # time of most recent content modification in nanoseconds
323+
# platform dependent (time of most recent metadata change on Unix, or the time of creation on Windows) in nanoseconds
324+
@property
325+
def st_ctime_ns(self) -> int: ...
309326
if sys.platform == "win32":
310-
st_file_attributes: int
311-
def __getitem__(self, i: int) -> int: ...
312-
# not documented
313-
def __init__(self, tuple: Tuple[int, ...]) -> None: ...
314-
# On some Unix systems (such as Linux), the following attributes may also
315-
# be available:
316-
st_blocks: int # number of blocks allocated for file
317-
st_blksize: int # filesystem blocksize
318-
st_rdev: int # type of device if an inode device
319-
st_flags: int # user defined flags for file
320-
321-
# On other Unix systems (such as FreeBSD), the following attributes may be
322-
# available (but may be only filled out if root tries to use them):
323-
st_gen: int # file generation number
324-
st_birthtime: int # time of file creation
325-
326-
# On Mac OS systems, the following attributes may also be available:
327-
st_rsize: int
328-
st_creator: int
329-
st_type: int
327+
@property
328+
def st_file_attributes(self) -> int: ...
329+
if sys.version_info >= (3, 8):
330+
@property
331+
def st_reparse_tag(self) -> int: ...
332+
else:
333+
@property
334+
def st_blocks(self) -> int: ... # number of blocks allocated for file
335+
@property
336+
def st_blksize(self) -> int: ... # filesystem blocksize
337+
@property
338+
def st_rdev(self) -> int: ... # type of device if an inode device
339+
if sys.platform != "linux":
340+
# These properties are available on MacOS, but not on Windows or Ubuntu.
341+
# On other Unix systems (such as FreeBSD), the following attributes may be
342+
# available (but may be only filled out if root tries to use them):
343+
@property
344+
def st_gen(self) -> int: ... # file generation number
345+
@property
346+
def st_birthtime(self) -> int: ... # time of file creation
347+
if sys.platform == "darwin":
348+
@property
349+
def st_flags(self) -> int: ... # user defined flags for file
350+
# Atributes documented as sometimes appearing, but deliberately omitted from the stub: `st_creator`, `st_rsize`, `st_type`.
351+
# See https://github.com/python/typeshed/pull/6560#issuecomment-991253327
330352

331353
@runtime_checkable
332354
class PathLike(Protocol[_AnyStr_co]):
@@ -359,45 +381,55 @@ class DirEntry(Generic[AnyStr]):
359381
if sys.version_info >= (3, 9):
360382
def __class_getitem__(cls, item: Any) -> GenericAlias: ...
361383

362-
if sys.platform != "win32":
363-
_Tuple10Int = Tuple[int, int, int, int, int, int, int, int, int, int]
364-
_Tuple11Int = Tuple[int, int, int, int, int, int, int, int, int, int, int]
365-
if sys.version_info >= (3, 7):
366-
# f_fsid was added in https://github.com/python/cpython/pull/4571
367-
@final
368-
class statvfs_result(_Tuple10Int): # Unix only
369-
def __new__(cls, seq: _Tuple10Int | _Tuple11Int, dict: dict[str, int] = ...) -> statvfs_result: ...
370-
n_fields: int
371-
n_sequence_fields: int
372-
n_unnamed_fields: int
373-
374-
f_bsize: int
375-
f_frsize: int
376-
f_blocks: int
377-
f_bfree: int
378-
f_bavail: int
379-
f_files: int
380-
f_ffree: int
381-
f_favail: int
382-
f_flag: int
383-
f_namemax: int
384-
f_fsid: int
385-
else:
386-
class statvfs_result(_Tuple10Int): # Unix only
387-
n_fields: int
388-
n_sequence_fields: int
389-
n_unnamed_fields: int
390-
391-
f_bsize: int
392-
f_frsize: int
393-
f_blocks: int
394-
f_bfree: int
395-
f_bavail: int
396-
f_files: int
397-
f_ffree: int
398-
f_favail: int
399-
f_flag: int
400-
f_namemax: int
384+
if sys.version_info >= (3, 7):
385+
@final
386+
class statvfs_result(structseq[int], Tuple[int, int, int, int, int, int, int, int, int, int, int]):
387+
@property
388+
def f_bsize(self) -> int: ...
389+
@property
390+
def f_frsize(self) -> int: ...
391+
@property
392+
def f_blocks(self) -> int: ...
393+
@property
394+
def f_bfree(self) -> int: ...
395+
@property
396+
def f_bavail(self) -> int: ...
397+
@property
398+
def f_files(self) -> int: ...
399+
@property
400+
def f_ffree(self) -> int: ...
401+
@property
402+
def f_favail(self) -> int: ...
403+
@property
404+
def f_flag(self) -> int: ...
405+
@property
406+
def f_namemax(self) -> int: ...
407+
@property
408+
def f_fsid(self) -> int: ...
409+
410+
else:
411+
@final
412+
class statvfs_result(structseq[int], Tuple[int, int, int, int, int, int, int, int, int, int]):
413+
@property
414+
def f_bsize(self) -> int: ...
415+
@property
416+
def f_frsize(self) -> int: ...
417+
@property
418+
def f_blocks(self) -> int: ...
419+
@property
420+
def f_bfree(self) -> int: ...
421+
@property
422+
def f_bavail(self) -> int: ...
423+
@property
424+
def f_files(self) -> int: ...
425+
@property
426+
def f_ffree(self) -> int: ...
427+
@property
428+
def f_favail(self) -> int: ...
429+
@property
430+
def f_flag(self) -> int: ...
431+
@property
432+
def f_namemax(self) -> int: ...
401433

402434
# ----- os function stubs -----
403435
def fsencode(filename: StrOrBytesPath) -> bytes: ...
@@ -415,12 +447,17 @@ def getppid() -> int: ...
415447
def strerror(__code: int) -> str: ...
416448
def umask(__mask: int) -> int: ...
417449
@final
418-
class uname_result(NamedTuple):
419-
sysname: str
420-
nodename: str
421-
release: str
422-
version: str
423-
machine: str
450+
class uname_result(structseq[str], Tuple[str, str, str, str, str]):
451+
@property
452+
def sysname(self) -> str: ...
453+
@property
454+
def nodename(self) -> str: ...
455+
@property
456+
def release(self) -> str: ...
457+
@property
458+
def version(self) -> str: ...
459+
@property
460+
def machine(self) -> str: ...
424461

425462
if sys.platform != "win32":
426463
def ctermid() -> str: ...
@@ -602,9 +639,11 @@ if sys.platform != "win32":
602639
def writev(__fd: int, __buffers: Sequence[bytes]) -> int: ...
603640

604641
@final
605-
class terminal_size(Tuple[int, int]):
606-
columns: int
607-
lines: int
642+
class terminal_size(structseq[int], Tuple[int, int]):
643+
@property
644+
def columns(self) -> int: ...
645+
@property
646+
def lines(self) -> int: ...
608647

609648
def get_terminal_size(fd: int = ...) -> terminal_size: ...
610649
def get_inheritable(__fd: int) -> bool: ...
@@ -819,12 +858,17 @@ else:
819858

820859
def system(command: StrOrBytesPath) -> int: ...
821860
@final
822-
class times_result(NamedTuple):
823-
user: float
824-
system: float
825-
children_user: float
826-
children_system: float
827-
elapsed: float
861+
class times_result(structseq[float], Tuple[float, float, float, float, float]):
862+
@property
863+
def user(self) -> float: ...
864+
@property
865+
def system(self) -> float: ...
866+
@property
867+
def children_user(self) -> float: ...
868+
@property
869+
def children_system(self) -> float: ...
870+
@property
871+
def elapsed(self) -> float: ...
828872

829873
def times() -> times_result: ...
830874
def waitpid(__pid: int, __options: int) -> tuple[int, int]: ...
@@ -839,12 +883,18 @@ else:
839883
def spawnvpe(mode: int, file: StrOrBytesPath, args: _ExecVArgs, env: _ExecEnv) -> int: ...
840884
def wait() -> tuple[int, int]: ... # Unix only
841885
if sys.platform != "darwin":
842-
class waitid_result(NamedTuple):
843-
si_pid: int
844-
si_uid: int
845-
si_signo: int
846-
si_status: int
847-
si_code: int
886+
@final
887+
class waitid_result(structseq[int], Tuple[int, int, int, int, int]):
888+
@property
889+
def si_pid(self) -> int: ...
890+
@property
891+
def si_uid(self) -> int: ...
892+
@property
893+
def si_signo(self) -> int: ...
894+
@property
895+
def si_status(self) -> int: ...
896+
@property
897+
def si_code(self) -> int: ...
848898
def waitid(idtype: int, ident: int, options: int) -> waitid_result: ...
849899
def wait3(options: int) -> tuple[int, int, Any]: ...
850900
def wait4(pid: int, options: int) -> tuple[int, int, Any]: ...
@@ -885,8 +935,11 @@ else:
885935
) -> int: ...
886936

887937
if sys.platform != "win32":
888-
class sched_param(NamedTuple):
889-
sched_priority: int
938+
@final
939+
class sched_param(structseq[int], Tuple[int]):
940+
def __new__(cls, sched_priority: int) -> sched_param: ...
941+
@property
942+
def sched_priority(self) -> int: ...
890943
def sched_get_priority_min(policy: int) -> int: ... # some flavors of Unix
891944
def sched_get_priority_max(policy: int) -> int: ... # some flavors of Unix
892945
def sched_yield() -> None: ... # some flavors of Unix

0 commit comments

Comments
 (0)