-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Update and add stubs for os module #1645
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
754a1cf
to
07916eb
Compare
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 for making all of these fixes! This will make the os
stubs a lot better.
stdlib/3/os/__init__.pyi
Outdated
def getgroups() -> List[int]: ... # Unix only, behaves differently on Mac | ||
def initgroups(username: str, gid: int) -> None: ... # Unix only | ||
def getlogin() -> str: ... | ||
def getpgid(pid: int) -> int: ... # Unix only | ||
def getpgrp() -> int: ... # Unix only | ||
def getpid() -> int: ... | ||
def getppid() -> int: ... | ||
if sys.version_info >= (3, 3): | ||
def getpriority(which: int, who: int) -> int: ... # Unix only | ||
def setpriority(which: int, who: int, priority: int) -> int: ... # Unix only |
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.
Actually returns None.
stdlib/3/os/__init__.pyi
Outdated
def isatty(fd: int) -> bool: ... # Unix only | ||
if sys.version_info >= (3, 3): | ||
def lockf(fd: int, cmd: int, length: int) -> None: ... # Unix only |
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 code calls the second argument command
, but it's positional-only anyway.
stdlib/3/os/__init__.pyi
Outdated
def lseek(fd: int, pos: int, how: int) -> int: ... | ||
def open(file: _PathType, flags: int, mode: int = ...) -> int: ... | ||
if sys.version_info >= (3, 3): | ||
def open(file: _PathType, flags: int, mode: Optional[int] = ..., *, dir_fd: Optional[int] = ...) -> int: ... |
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 mode
argument is not Optional.
stdlib/3/os/__init__.pyi
Outdated
def read(fd: int, n: int) -> bytes: ... | ||
if sys.version_info >= (3, 3): | ||
# sendfile() has a different variation on FreeBSD and Mac OS X | ||
def sendfile(out_fd: int, in_fd: int, offset: Optional[int], count: int) -> int: ... # Unix only |
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 I'm reading the code right count
is also Optional on at least some variations. I think we should give the most permissive type.
You're also missing the arguments headers, trailers, and flags. headers and trailers are Sequences of bytes and flags is an int.
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 didn't have a Mac on which to test the variant supporting headers, trailers, and flags, so I left it out. But I downloaded a FreeBSD image and added an overloaded variant. When testing, I found that count
is not Optional on Linux or FreeBSD.
stdlib/3/os/__init__.pyi
Outdated
def writev(fd: int, buffers: Sequence[bytes]) -> int: ... # Unix only | ||
|
||
terminal_size = NamedTuple('terminal_size', [('columns', int), ('lines', int)]) | ||
def get_terminal_size(fd: Optional[int] = ...) -> terminal_size: ... |
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.
In [9]: os.get_terminal_size(None)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-9-46d26e904443> in <module>()
----> 1 os.get_terminal_size(None)
TypeError: an integer is required (got type NoneType)
So it's not Optional.
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.
Another place where I was overzealous with Optionals. I think I fixed them all.
stdlib/3/os/__init__.pyi
Outdated
def chdir(path: _PathType) -> None: ... | ||
if sys.version_info >= (3, 3): | ||
def access(path: _FdOrPathType, mode: int, *, dir_fd: Optional[int] = ..., | ||
effective_ids: Optional[bool] = ..., follow_symlinks: Optional[bool] = ...) -> bool: ... |
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 these two are supposed to be just bool, not Optional[bool]
, though in reality it will accept anything and call bool()
on it.
stdlib/3/os/__init__.pyi
Outdated
def fchdir(fd: int) -> None: ... | ||
def getcwd() -> str: ... | ||
def getcwdb() -> bytes: ... | ||
def chflags(path: _PathType, flags: int) -> None: ... # Unix only | ||
if sys.version_info >= (3, 3): | ||
def chflags(path: _PathType, flags: int, *, follow_symlinks: Optional[bool] = ...) -> None: ... # Unix only |
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 last arguments is not actually keyword-only (and I think should not be Optional). The other boolean arguments below should not be Optional either.
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.
Sure enough. I tried to test this one, but it isn't supported on Linux. My new FreeBSD VM image solved that for me.
def listdir(path: Optional[str] = ...) -> List[str]: ... | ||
@overload | ||
def listdir(path: bytes) -> List[bytes]: ... | ||
@overload |
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.
Can't you just define only this overload in a conditional block?
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.
That's what I thought, but mypy complains when I do that:
stdlib/3/os/__init__.pyi:432: error: Name 'listdir' already defined
stdlib/3/os/__init__.pyi:432: error: Single overload definition, multiple required
stdlib/3/os/__init__.pyi
Outdated
def mknod(filename: _PathType, mode: int = ..., device: int = ...) -> None: ... | ||
if sys.version_info >= (3, 3): | ||
def lstat(path: _PathType, *, dir_fd: Optional[int] = ...) -> stat_result: ... | ||
def mkdir(path: _PathType, mode: Optional[int] = ..., *, dir_fd: Optional[int] = ...) -> None: ... |
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.
These mode arguments are not Optional. More instances of the same below: mode arguments should be int, dir_fd is correct as Optional[int], and all the boolean args should just be bool
.
stdlib/3/os/__init__.pyi
Outdated
def unlink(path: _PathType) -> None: ... | ||
if sys.version_info >= (3, 0): | ||
def utime(path: _PathType, times: Optional[Union[Tuple[int, int], Tuple[float, float]]] = ..., | ||
if sys.version_info >= (3, 3): |
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.
Didn't read further down from here yet.
Also fixes other issue found during review.
Thanks for looking at this so thoroughly. I know it's a big change and I messed it up with the ideas that were stuck in my head regarding Optionals. |
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 time I made it all the way through. I noticed a few more issues; thanks for fixing the previous batch!
stdlib/3/os/__init__.pyi
Outdated
@@ -375,6 +531,16 @@ else: | |||
def walk(top: AnyStr, topdown: bool = ..., onerror: Optional[Callable[[OSError], Any]] = ..., | |||
followlinks: bool = ...) -> Iterator[Tuple[AnyStr, List[AnyStr], | |||
List[AnyStr]]]: ... | |||
if sys.version_info >= (3, 3): | |||
def fwalk(top: AnyStr = ..., topdown: bool = ..., |
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.
top
is also allowed to be an int
or a Path, or not given (in which case it's a str). I think we need some overloads for those cases.
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.
So it accepts a file descriptor, but I get an error:
>>> next(os.fwalk(fd))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.6/os.py", line 458, in fwalk
orig_st = stat(top, follow_symlinks=False, dir_fd=dir_fd)
ValueError: stat: cannot use fd and follow_symlinks together
I also don't see anything in the documentation suggesting that top
can be an int.
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.
Interesting, the implementation explicitly checks for isinstance(top, int)
. Maybe this is a bug in CPython.
stdlib/3/os/__init__.pyi
Outdated
onerror: Optional[Callable] = ..., *, follow_symlinks: bool = ..., | ||
dir_fd: Optional[int] = ...) -> Iterator[Tuple[AnyStr, List[AnyStr], | ||
List[AnyStr], int]]: ... # Unix only | ||
def getxattr(path: _FdOrPathType, attribute: AnyStr, *, follow_symlinks: bool = ...) -> bytes: ... # Linux only |
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.
AnyStr generally doesn't make sense if it's only used once, since it's meant to express that multiple values should be the same type (either str or bytes). Also, the argument clinic types suggest these "attributes" are paths, so maybe PathLike should also be allowed.
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.
Yeah... sorry. Interesting thing is that the documentation says that fwalk()
"behaves exactly like walk()
", but that's not the case. If walk()
is called with a bytes path, the results are also bytes, hence the use of AnyStr. However, fwalk()
returns strings regardless of the input path type.
stdlib/3/os/__init__.pyi
Outdated
def __init__(self, command: str, mode: str = ..., | ||
bufsize: int = ...) -> None: ... | ||
def close(self) -> Any: ... # may return int | ||
else: |
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.
Let's leave this in so I can finish my project to merge the Python 2 and 3 os
stubs.
@@ -10,3 +10,23 @@ from typing import NamedTuple | |||
if sys.version_info >= (3, 3): | |||
uname_result = NamedTuple('uname_result', [('sysname', str), ('nodename', str), | |||
('release', str), ('version', str), ('machine', str)]) | |||
|
|||
times_result = NamedTuple('times_result', [ |
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.
Not sure why these specifically are in this file; most of the functions also live in the posix
module. Eventually we should move most code into posix.pyi
and thus re-export it in os.pyi
.
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.
Sounds like a good idea. I was just keeping with the convention used with uname_result
.
Address specific code review comments.
There were many missing methods and many that were updated in Python 3.3 and later. This pull request should bring the os module up-to-date.