Skip to content

add __fspath__ support to os.path #2053

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

Merged
merged 2 commits into from
May 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 102 additions & 32 deletions stdlib/2/os/path.pyi
Original file line number Diff line number Diff line change
@@ -1,51 +1,106 @@
# Stubs for os.path
# Ron Murawski <[email protected]>

# based on http://docs.python.org/3.2/library/os.path.html
# adapted for 2.7 by Michal Pokorny
from posix import stat_result
import sys
from typing import (
overload, List, Any, AnyStr, Sequence, Tuple, BinaryIO, TextIO,
TypeVar, Union, Text, Callable
TypeVar, Union, Text, Callable, Optional
)

_T = TypeVar('_T')

if sys.version_info >= (3, 6):
from builtins import _PathLike
_PathType = Union[bytes, Text, _PathLike]
_StrPath = Union[Text, _PathLike[Text]]
_BytesPath = Union[bytes, _PathLike[bytes]]
else:
_PathType = Union[bytes, Text]
_StrPath = Text
_BytesPath = bytes

# ----- os.path variables -----
supports_unicode_filenames = False
supports_unicode_filenames: bool
# aliases (also in os)
curdir = ... # type: str
pardir = ... # type: str
sep = ... # type: str
altsep = ... # type: str
extsep = ... # type: str
pathsep = ... # type: str
defpath = ... # type: str
devnull = ... # type: str
curdir: str
pardir: str
sep: str
altsep: str
extsep: str
pathsep: str
defpath: str
devnull: str

# ----- os.path function stubs -----
def abspath(path: AnyStr) -> AnyStr: ...
def basename(path: AnyStr) -> AnyStr: ...
if sys.version_info >= (3, 6):
# Overloads are necessary to work around python/mypy#3644.
@overload
def abspath(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def abspath(path: AnyStr) -> AnyStr: ...
@overload
def basename(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def basename(path: AnyStr) -> AnyStr: ...
@overload
def dirname(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def dirname(path: AnyStr) -> AnyStr: ...
@overload
def expanduser(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def expanduser(path: AnyStr) -> AnyStr: ...
@overload
def expandvars(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def expandvars(path: AnyStr) -> AnyStr: ...
@overload
def normcase(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def normcase(path: AnyStr) -> AnyStr: ...
@overload
def normpath(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def normpath(path: AnyStr) -> AnyStr: ...
if sys.platform == 'win32':
@overload
def realpath(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def realpath(path: AnyStr) -> AnyStr: ...
else:
@overload
def realpath(filename: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def realpath(filename: AnyStr) -> AnyStr: ...

if sys.version_info >= (3, 5):
else:
def abspath(path: AnyStr) -> AnyStr: ...
def basename(path: AnyStr) -> AnyStr: ...
def dirname(path: AnyStr) -> AnyStr: ...
def expanduser(path: AnyStr) -> AnyStr: ...
def expandvars(path: AnyStr) -> AnyStr: ...
def normcase(path: AnyStr) -> AnyStr: ...
def normpath(path: AnyStr) -> AnyStr: ...
if sys.platform == 'win32':
def realpath(path: AnyStr) -> AnyStr: ...
else:
def realpath(filename: AnyStr) -> AnyStr: ...

if sys.version_info >= (3, 6):
# In reality it returns str for sequences of _StrPath and bytes for sequences
# of _BytesPath, but mypy does not accept such a signature.
def commonpath(paths: Sequence[_PathType]) -> Any: ...
elif sys.version_info >= (3, 5):
def commonpath(paths: Sequence[AnyStr]) -> AnyStr: ...

# NOTE: Empty lists results in '' (str) regardless of contained type.
# Also, in Python 2 mixed sequences of Text and bytes results in either Text or bytes
# So, fall back to Any
def commonprefix(list: Sequence[AnyStr]) -> Any: ...
def commonprefix(list: Sequence[_PathType]) -> Any: ...

def dirname(path: AnyStr) -> AnyStr: ...
def exists(path: _PathType) -> bool: ...
def lexists(path: _PathType) -> bool: ...
def expanduser(path: AnyStr) -> AnyStr: ...
def expandvars(path: AnyStr) -> AnyStr: ...

# These return float if os.stat_float_times() == True,
# but int is a subclass of float.
Expand Down Expand Up @@ -75,26 +130,41 @@ if sys.version_info < (3, 0):
def join(__p1: bytes, __p2: bytes, __p3: Text, *p: _PathType) -> Text: ...
@overload
def join(__p1: bytes, __p2: bytes, __p3: bytes, __p4: Text, *p: _PathType) -> Text: ...
elif sys.version_info >= (3, 6):
# Mypy complains that the signatures overlap (same for relpath below), but things seem to behave correctly anyway.
@overload
def join(path: _StrPath, *paths: _StrPath) -> Text: ... # type: ignore
@overload
def join(path: _BytesPath, *paths: _BytesPath) -> bytes: ...
else:
def join(path: AnyStr, *paths: AnyStr) -> AnyStr: ...

def normcase(path: AnyStr) -> AnyStr: ...
def normpath(path: AnyStr) -> AnyStr: ...
if sys.platform == 'win32':
def realpath(path: AnyStr) -> AnyStr: ...
else:
def realpath(filename: AnyStr) -> AnyStr: ...
def relpath(path: AnyStr, start: _PathType = ...) -> AnyStr: ...
@overload
def relpath(path: _StrPath, start: Optional[_StrPath] = ...) -> Text: ... # type: ignore
@overload
def relpath(path: _BytesPath, start: _BytesPath) -> bytes: ...

def samefile(path1: _PathType, path2: _PathType) -> bool: ...
def sameopenfile(fp1: int, fp2: int) -> bool: ...
# TODO
# def samestat(stat1: stat_result,
# stat2: stat_result) -> bool: ... # Unix only
def samestat(stat1: stat_result, stat2: stat_result) -> bool: ...

def split(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
def splitdrive(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
def splitext(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
if sys.version_info >= (3, 6):
@overload
def split(path: _PathLike[AnyStr]) -> Tuple[AnyStr, AnyStr]: ...
@overload
def split(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
@overload
def splitdrive(path: _PathLike[AnyStr]) -> Tuple[AnyStr, AnyStr]: ...
@overload
def splitdrive(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
@overload
def splitext(path: _PathLike[AnyStr]) -> Tuple[AnyStr, AnyStr]: ...
@overload
def splitext(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
else:
def split(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
def splitdrive(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
def splitext(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...

def splitunc(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ... # Windows only, deprecated

Expand Down
131 changes: 99 additions & 32 deletions stdlib/3/os/path.pyi
Original file line number Diff line number Diff line change
@@ -1,51 +1,106 @@
# Stubs for os.path
# Ron Murawski <[email protected]>

# based on http://docs.python.org/3.2/library/os.path.html
# adapted for 2.7 by Michal Pokorny
from posix import stat_result
import sys
from typing import (
overload, List, Any, AnyStr, Sequence, Tuple, BinaryIO, TextIO,
TypeVar, Union, Text, Callable
TypeVar, Union, Text, Callable, Optional
)

_T = TypeVar('_T')

if sys.version_info >= (3, 6):
from builtins import _PathLike
_PathType = Union[bytes, Text, _PathLike]
_StrPath = Union[Text, _PathLike[Text]]
_BytesPath = Union[bytes, _PathLike[bytes]]
else:
_PathType = Union[bytes, Text]
_StrPath = Text
_BytesPath = bytes

# ----- os.path variables -----
supports_unicode_filenames = False
supports_unicode_filenames: bool
# aliases (also in os)
curdir = ... # type: str
pardir = ... # type: str
sep = ... # type: str
altsep = ... # type: str
extsep = ... # type: str
pathsep = ... # type: str
defpath = ... # type: str
devnull = ... # type: str
curdir: str
pardir: str
sep: str
altsep: str
extsep: str
pathsep: str
defpath: str
devnull: str

# ----- os.path function stubs -----
def abspath(path: AnyStr) -> AnyStr: ...
def basename(path: AnyStr) -> AnyStr: ...
if sys.version_info >= (3, 6):
# Overloads are necessary to work around python/mypy#3644.
@overload
def abspath(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def abspath(path: AnyStr) -> AnyStr: ...
@overload
def basename(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def basename(path: AnyStr) -> AnyStr: ...
@overload
def dirname(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def dirname(path: AnyStr) -> AnyStr: ...
@overload
def expanduser(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def expanduser(path: AnyStr) -> AnyStr: ...
@overload
def expandvars(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def expandvars(path: AnyStr) -> AnyStr: ...
@overload
def normcase(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def normcase(path: AnyStr) -> AnyStr: ...
@overload
def normpath(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def normpath(path: AnyStr) -> AnyStr: ...
if sys.platform == 'win32':
@overload
def realpath(path: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def realpath(path: AnyStr) -> AnyStr: ...
else:
@overload
def realpath(filename: _PathLike[AnyStr]) -> AnyStr: ...
@overload
def realpath(filename: AnyStr) -> AnyStr: ...

else:
def abspath(path: AnyStr) -> AnyStr: ...
def basename(path: AnyStr) -> AnyStr: ...
def dirname(path: AnyStr) -> AnyStr: ...
def expanduser(path: AnyStr) -> AnyStr: ...
def expandvars(path: AnyStr) -> AnyStr: ...
def normcase(path: AnyStr) -> AnyStr: ...
def normpath(path: AnyStr) -> AnyStr: ...
if sys.platform == 'win32':
def realpath(path: AnyStr) -> AnyStr: ...
else:
def realpath(filename: AnyStr) -> AnyStr: ...

if sys.version_info >= (3, 5):
if sys.version_info >= (3, 6):
# In reality it returns str for sequences of _StrPath and bytes for sequences
# of _BytesPath, but mypy does not accept such a signature.
def commonpath(paths: Sequence[_PathType]) -> Any: ...
elif sys.version_info >= (3, 5):
def commonpath(paths: Sequence[AnyStr]) -> AnyStr: ...

# NOTE: Empty lists results in '' (str) regardless of contained type.
# Also, in Python 2 mixed sequences of Text and bytes results in either Text or bytes
# So, fall back to Any
def commonprefix(list: Sequence[AnyStr]) -> Any: ...
def commonprefix(list: Sequence[_PathType]) -> Any: ...

def dirname(path: AnyStr) -> AnyStr: ...
def exists(path: _PathType) -> bool: ...
def lexists(path: _PathType) -> bool: ...
def expanduser(path: AnyStr) -> AnyStr: ...
def expandvars(path: AnyStr) -> AnyStr: ...

# These return float if os.stat_float_times() == True,
# but int is a subclass of float.
Expand Down Expand Up @@ -75,29 +130,41 @@ if sys.version_info < (3, 0):
def join(__p1: bytes, __p2: bytes, __p3: Text, *p: _PathType) -> Text: ...
@overload
def join(__p1: bytes, __p2: bytes, __p3: bytes, __p4: Text, *p: _PathType) -> Text: ...
elif sys.version_info >= (3, 6):
# Mypy complains that the signatures overlap (same for relpath below), but things seem to behave correctly anyway.
@overload
def join(path: _StrPath, *paths: _StrPath) -> Text: ... # type: ignore
@overload
def join(path: _BytesPath, *paths: _BytesPath) -> bytes: ...
else:
def join(path: AnyStr, *paths: AnyStr) -> AnyStr: ...

def normcase(path: AnyStr) -> AnyStr: ...
def normpath(path: AnyStr) -> AnyStr: ...
if sys.platform == 'win32':
def realpath(path: AnyStr) -> AnyStr: ...
else:
def realpath(filename: AnyStr) -> AnyStr: ...
def relpath(path: AnyStr, start: _PathType = ...) -> AnyStr: ...
@overload
def relpath(path: _StrPath, start: Optional[_StrPath] = ...) -> Text: ... # type: ignore
@overload
def relpath(path: _BytesPath, start: _BytesPath) -> bytes: ...
Copy link
Contributor

@FichteFoll FichteFoll Apr 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

relpath isn't guarded by a 3.6 version check and there is no AnyStr fallback.

Also, the bytes variant is missing Optional.

(Applies to the 2 version as well.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is intentional. _StrPath is just Text below 3.6, so this implementation is correct there.

If start is None, the code uses os.curdir, which is Text, so the path also has to be Text. Although come to think of it, we may need something more complicated in Python 2, where you can mix bytes and Text paths.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the current code already works in Python 2 (modulo a mypy bug where some overloads silently fall back to Any).


def samefile(path1: _PathType, path2: _PathType) -> bool: ...
def sameopenfile(fp1: int, fp2: int) -> bool: ...
# TODO
# def samestat(stat1: stat_result,
# stat2: stat_result) -> bool: ... # Unix only
def samestat(stat1: stat_result, stat2: stat_result) -> bool: ...

if sys.version_info >= (3, 6):
def split(path: Union[AnyStr, _PathLike[AnyStr]]) -> Tuple[AnyStr, AnyStr]: ...
@overload
def split(path: _PathLike[AnyStr]) -> Tuple[AnyStr, AnyStr]: ...
@overload
def split(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
@overload
def splitdrive(path: _PathLike[AnyStr]) -> Tuple[AnyStr, AnyStr]: ...
@overload
def splitdrive(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
@overload
def splitext(path: _PathLike[AnyStr]) -> Tuple[AnyStr, AnyStr]: ...
@overload
def splitext(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
else:
def split(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
def splitdrive(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
def splitext(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
def splitdrive(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...
def splitext(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ...

def splitunc(path: AnyStr) -> Tuple[AnyStr, AnyStr]: ... # Windows only, deprecated

Expand Down