summaryrefslogtreecommitdiff
path: root/Lib/os.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/os.py')
-rw-r--r--Lib/os.py207
1 files changed, 131 insertions, 76 deletions
diff --git a/Lib/os.py b/Lib/os.py
index b4c651d24d..7379dad41a 100644
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -1,9 +1,9 @@
r"""OS routines for NT or Posix depending on what system we're on.
This exports:
- - all functions from posix, nt or ce, e.g. unlink, stat, etc.
+ - all functions from posix or nt, e.g. unlink, stat, etc.
- os.path is either posixpath or ntpath
- - os.name is either 'posix', 'nt' or 'ce'.
+ - os.name is either 'posix' or 'nt'
- os.curdir is a string representing the current directory ('.' or ':')
- os.pardir is a string representing the parent directory ('..' or '::')
- os.sep is the (or a most common) pathname separator ('/' or ':' or '\\')
@@ -22,7 +22,7 @@ and opendir), and leave all pathname manipulation to os.path
"""
#'
-
+import abc
import sys, errno
import stat as st
@@ -85,27 +85,6 @@ elif 'nt' in _names:
except ImportError:
pass
-elif 'ce' in _names:
- name = 'ce'
- linesep = '\r\n'
- from ce import *
- try:
- from ce import _exit
- __all__.append('_exit')
- except ImportError:
- pass
- # We can use the standard Windows path.
- import ntpath as path
-
- import ce
- __all__.extend(_get_exports_list(ce))
- del ce
-
- try:
- from ce import _have_functions
- except ImportError:
- pass
-
else:
raise ImportError('no os specific module found')
@@ -353,9 +332,10 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
dirs.remove('CVS') # don't visit CVS directories
"""
-
+ top = fspath(top)
dirs = []
nondirs = []
+ walk_dirs = []
# We may not have read permission for top, in which case we can't
# get a list of the files the directory contains. os.walk
@@ -369,42 +349,52 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
# Note that scandir is global in this module due
# to earlier import-*.
scandir_it = scandir(top)
- entries = list(scandir_it)
except OSError as error:
if onerror is not None:
onerror(error)
return
- for entry in entries:
- try:
- is_dir = entry.is_dir()
- except OSError:
- # If is_dir() raises an OSError, consider that the entry is not
- # a directory, same behaviour than os.path.isdir().
- is_dir = False
-
- if is_dir:
- dirs.append(entry.name)
- else:
- nondirs.append(entry.name)
+ with scandir_it:
+ while True:
+ try:
+ try:
+ entry = next(scandir_it)
+ except StopIteration:
+ break
+ except OSError as error:
+ if onerror is not None:
+ onerror(error)
+ return
- if not topdown and is_dir:
- # Bottom-up: recurse into sub-directory, but exclude symlinks to
- # directories if followlinks is False
- if followlinks:
- walk_into = True
+ try:
+ is_dir = entry.is_dir()
+ except OSError:
+ # If is_dir() raises an OSError, consider that the entry is not
+ # a directory, same behaviour than os.path.isdir().
+ is_dir = False
+
+ if is_dir:
+ dirs.append(entry.name)
else:
- try:
- is_symlink = entry.is_symlink()
- except OSError:
- # If is_symlink() raises an OSError, consider that the
- # entry is not a symbolic link, same behaviour than
- # os.path.islink().
- is_symlink = False
- walk_into = not is_symlink
+ nondirs.append(entry.name)
- if walk_into:
- yield from walk(entry.path, topdown, onerror, followlinks)
+ if not topdown and is_dir:
+ # Bottom-up: recurse into sub-directory, but exclude symlinks to
+ # directories if followlinks is False
+ if followlinks:
+ walk_into = True
+ else:
+ try:
+ is_symlink = entry.is_symlink()
+ except OSError:
+ # If is_symlink() raises an OSError, consider that the
+ # entry is not a symbolic link, same behaviour than
+ # os.path.islink().
+ is_symlink = False
+ walk_into = not is_symlink
+
+ if walk_into:
+ walk_dirs.append(entry.path)
# Yield before recursion if going top down
if topdown:
@@ -421,6 +411,9 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
if followlinks or not islink(new_path):
yield from walk(new_path, topdown, onerror, followlinks)
else:
+ # Recurse into sub-directories
+ for new_path in walk_dirs:
+ yield from walk(new_path, topdown, onerror, followlinks)
# Yield after recursion if going bottom up
yield top, dirs, nondirs
@@ -467,10 +460,23 @@ class _DummyDirEntry:
stat = self.stat(follow_symlinks=False)
return st.S_ISLNK(stat.st_mode)
-def _dummy_scandir(dir):
+class _dummy_scandir:
# listdir-based implementation for bytes patches on Windows
- for name in listdir(dir):
- yield _DummyDirEntry(dir, name)
+ def __init__(self, dir):
+ self.dir = dir
+ self.it = iter(listdir(dir))
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ return _DummyDirEntry(self.dir, next(self.it))
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.it = iter(())
__all__.append("walk")
@@ -509,6 +515,8 @@ if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd:
if 'CVS' in dirs:
dirs.remove('CVS') # don't visit CVS directories
"""
+ if not isinstance(top, int) or not hasattr(top, '__index__'):
+ top = fspath(top)
# Note: To guard against symlink races, we use the standard
# lstat()/open()/fstat() trick.
orig_st = stat(top, follow_symlinks=False, dir_fd=dir_fd)
@@ -843,36 +851,31 @@ if supports_bytes_environ:
def _fscodec():
encoding = sys.getfilesystemencoding()
- if encoding == 'mbcs':
- errors = 'strict'
- else:
- errors = 'surrogateescape'
+ errors = sys.getfilesystemencodeerrors()
def fsencode(filename):
+ """Encode filename (an os.PathLike, bytes, or str) to the filesystem
+ encoding with 'surrogateescape' error handler, return bytes unchanged.
+ On Windows, use 'strict' error handler if the file system encoding is
+ 'mbcs' (which is the default encoding).
"""
- Encode filename to the filesystem encoding with 'surrogateescape' error
- handler, return bytes unchanged. On Windows, use 'strict' error handler if
- the file system encoding is 'mbcs' (which is the default encoding).
- """
- if isinstance(filename, bytes):
- return filename
- elif isinstance(filename, str):
+ filename = fspath(filename) # Does type-checking of `filename`.
+ if isinstance(filename, str):
return filename.encode(encoding, errors)
else:
- raise TypeError("expect bytes or str, not %s" % type(filename).__name__)
+ return filename
def fsdecode(filename):
+ """Decode filename (an os.PathLike, bytes, or str) from the filesystem
+ encoding with 'surrogateescape' error handler, return str unchanged. On
+ Windows, use 'strict' error handler if the file system encoding is
+ 'mbcs' (which is the default encoding).
"""
- Decode filename from the filesystem encoding with 'surrogateescape' error
- handler, return str unchanged. On Windows, use 'strict' error handler if
- the file system encoding is 'mbcs' (which is the default encoding).
- """
- if isinstance(filename, str):
- return filename
- elif isinstance(filename, bytes):
+ filename = fspath(filename) # Does type-checking of `filename`.
+ if isinstance(filename, bytes):
return filename.decode(encoding, errors)
else:
- raise TypeError("expect bytes or str, not %s" % type(filename).__name__)
+ return filename
return fsencode, fsdecode
@@ -1070,3 +1073,55 @@ def fdopen(fd, *args, **kwargs):
raise TypeError("invalid fd type (%s, expected integer)" % type(fd))
import io
return io.open(fd, *args, **kwargs)
+
+
+# For testing purposes, make sure the function is available when the C
+# implementation exists.
+def _fspath(path):
+ """Return the path representation of a path-like object.
+
+ If str or bytes is passed in, it is returned unchanged. Otherwise the
+ os.PathLike interface is used to get the path representation. If the
+ path representation is not str or bytes, TypeError is raised. If the
+ provided path is not str, bytes, or os.PathLike, TypeError is raised.
+ """
+ if isinstance(path, (str, bytes)):
+ return path
+
+ # Work from the object's type to match method resolution of other magic
+ # methods.
+ path_type = type(path)
+ try:
+ path_repr = path_type.__fspath__(path)
+ except AttributeError:
+ if hasattr(path_type, '__fspath__'):
+ raise
+ else:
+ raise TypeError("expected str, bytes or os.PathLike object, "
+ "not " + path_type.__name__)
+ if isinstance(path_repr, (str, bytes)):
+ return path_repr
+ else:
+ raise TypeError("expected {}.__fspath__() to return str or bytes, "
+ "not {}".format(path_type.__name__,
+ type(path_repr).__name__))
+
+# If there is no C implementation, make the pure Python version the
+# implementation as transparently as possible.
+if not _exists('fspath'):
+ fspath = _fspath
+ fspath.__name__ = "fspath"
+
+
+class PathLike(abc.ABC):
+
+ """Abstract base class for implementing the file system path protocol."""
+
+ @abc.abstractmethod
+ def __fspath__(self):
+ """Return the file system path representation of the object."""
+ raise NotImplementedError
+
+ @classmethod
+ def __subclasshook__(cls, subclass):
+ return hasattr(subclass, '__fspath__')