diff options
Diffstat (limited to 'sphinx/util/osutil.py')
-rw-r--r-- | sphinx/util/osutil.py | 166 |
1 files changed, 57 insertions, 109 deletions
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index d48968482..646a92f1b 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ sphinx.util.osutil ~~~~~~~~~~~~~~~~~~ @@ -8,37 +7,30 @@ :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ -from __future__ import print_function import contextlib import errno import filecmp -import locale import os import re import shutil import sys import time import warnings -from io import BytesIO, StringIO +from io import StringIO from os import path -from six import PY2, PY3, text_type - -from sphinx.deprecation import RemovedInSphinx30Warning +from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning if False: # For type annotation from typing import Any, Iterator, List, Tuple, Union # NOQA # Errnos that we need. -EEXIST = getattr(errno, 'EEXIST', 0) -ENOENT = getattr(errno, 'ENOENT', 0) -EPIPE = getattr(errno, 'EPIPE', 0) -EINVAL = getattr(errno, 'EINVAL', 0) - -if PY3: - unicode = str # special alias for static typing... +EEXIST = getattr(errno, 'EEXIST', 0) # RemovedInSphinx40Warning +ENOENT = getattr(errno, 'ENOENT', 0) # RemovedInSphinx40Warning +EPIPE = getattr(errno, 'EPIPE', 0) # RemovedInSphinx40Warning +EINVAL = getattr(errno, 'EINVAL', 0) # RemovedInSphinx40Warning # SEP separates path elements in the canonical file names # @@ -49,18 +41,18 @@ SEP = "/" def os_path(canonicalpath): - # type: (unicode) -> unicode + # type: (str) -> str return canonicalpath.replace(SEP, path.sep) def canon_path(nativepath): - # type: (unicode) -> unicode + # type: (str) -> str """Return path in OS-independent form""" return nativepath.replace(path.sep, SEP) def relative_uri(base, to): - # type: (unicode, unicode) -> unicode + # type: (str, str) -> str """Return a relative URL from ``base`` to ``to``.""" if to.startswith(SEP): return to @@ -84,66 +76,33 @@ def relative_uri(base, to): def ensuredir(path): - # type: (unicode) -> None + # type: (str) -> None """Ensure that a path exists.""" - try: - os.makedirs(path) - except OSError: - # If the path is already an existing directory (not a file!), - # that is OK. - if not os.path.isdir(path): - raise + os.makedirs(path, exist_ok=True) -# This function is same as os.walk of Python2.7 except a customization -# that check UnicodeError. -# The customization obstacle to replace the function with the os.walk. def walk(top, topdown=True, followlinks=False): - # type: (unicode, bool, bool) -> Iterator[Tuple[unicode, List[unicode], List[unicode]]] - """Backport of os.walk from 2.6, where the *followlinks* argument was - added. - """ - names = os.listdir(top) - - dirs, nondirs = [], [] - for name in names: - try: - fullpath = path.join(top, name) - except UnicodeError: - print('%s:: ERROR: non-ASCII filename not supported on this ' - 'filesystem encoding %r, skipped.' % (name, fs_encoding), - file=sys.stderr) - continue - if path.isdir(fullpath): - dirs.append(name) - else: - nondirs.append(name) - - if topdown: - yield top, dirs, nondirs - for name in dirs: - fullpath = path.join(top, name) - if followlinks or not path.islink(fullpath): - for x in walk(fullpath, topdown, followlinks): - yield x - if not topdown: - yield top, dirs, nondirs + # type: (str, bool, bool) -> Iterator[Tuple[str, List[str], List[str]]] + warnings.warn('sphinx.util.osutil.walk() is deprecated for removal. ' + 'Please use os.walk() instead.', + RemovedInSphinx40Warning) + return os.walk(top, topdown=topdown, followlinks=followlinks) def mtimes_of_files(dirnames, suffix): - # type: (List[unicode], unicode) -> Iterator[float] + # type: (List[str], str) -> Iterator[float] for dirname in dirnames: for root, dirs, files in os.walk(dirname): for sfile in files: if sfile.endswith(suffix): try: yield path.getmtime(path.join(root, sfile)) - except EnvironmentError: + except OSError: pass def movefile(source, dest): - # type: (unicode, unicode) -> None + # type: (str, str) -> None """Move a file, removing the destination if it exists.""" if os.path.exists(dest): try: @@ -154,7 +113,7 @@ def movefile(source, dest): def copytimes(source, dest): - # type: (unicode, unicode) -> None + # type: (str, str) -> None """Copy a file's modification times.""" st = os.stat(source) if hasattr(os, 'utime'): @@ -162,7 +121,7 @@ def copytimes(source, dest): def copyfile(source, dest): - # type: (unicode, unicode) -> None + # type: (str, str) -> None """Copy a file and its modification times, if possible. Note: ``copyfile`` skips copying if the file has not been changed""" @@ -176,15 +135,21 @@ def copyfile(source, dest): no_fn_re = re.compile(r'[^a-zA-Z0-9_-]') +project_suffix_re = re.compile(' Documentation$') def make_filename(string): - # type: (str) -> unicode + # type: (str) -> str return no_fn_re.sub('', string) or 'sphinx' +def make_filename_from_project(project): + # type: (str) -> str + return make_filename(project_suffix_re.sub('', project)).lower() + + def ustrftime(format, *args): - # type: (unicode, Any) -> unicode + # type: (str, Any) -> str """[DEPRECATED] strftime for unicode strings.""" warnings.warn('sphinx.util.osutil.ustrtime is deprecated for removal', RemovedInSphinx30Warning, stacklevel=2) @@ -196,23 +161,17 @@ def ustrftime(format, *args): if source_date_epoch is not None: time_struct = time.gmtime(float(source_date_epoch)) args = [time_struct] # type: ignore - if PY2: - # if a locale is set, the time strings are encoded in the encoding - # given by LC_TIME; if that is available, use it - enc = locale.getlocale(locale.LC_TIME)[1] or 'utf-8' - return time.strftime(text_type(format).encode(enc), *args).decode(enc) - else: # Py3 - # On Windows, time.strftime() and Unicode characters will raise UnicodeEncodeError. - # https://bugs.python.org/issue8304 - try: - return time.strftime(format, *args) - except UnicodeEncodeError: - r = time.strftime(format.encode('unicode-escape').decode(), *args) - return r.encode().decode('unicode-escape') + # On Windows, time.strftime() and Unicode characters will raise UnicodeEncodeError. + # https://bugs.python.org/issue8304 + try: + return time.strftime(format, *args) + except UnicodeEncodeError: + r = time.strftime(format.encode('unicode-escape').decode(), *args) + return r.encode().decode('unicode-escape') def relpath(path, start=os.curdir): - # type: (unicode, unicode) -> unicode + # type: (str, str) -> str """Return a relative filepath to *path* either from the current directory or from an optional *start* directory. @@ -226,33 +185,34 @@ def relpath(path, start=os.curdir): safe_relpath = relpath # for compatibility -fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() # type: unicode +fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() def abspath(pathdir): - # type: (unicode) -> unicode + # type: (str) -> str pathdir = path.abspath(pathdir) if isinstance(pathdir, bytes): try: pathdir = pathdir.decode(fs_encoding) except UnicodeDecodeError: - raise UnicodeDecodeError('multibyte filename not supported on ' # type: ignore + raise UnicodeDecodeError('multibyte filename not supported on ' 'this filesystem encoding ' '(%r)' % fs_encoding) return pathdir def getcwd(): - # type: () -> unicode - if hasattr(os, 'getcwdu'): - return os.getcwdu() + # type: () -> str + warnings.warn('sphinx.util.osutil.getcwd() is deprecated. ' + 'Please use os.getcwd() instead.', + RemovedInSphinx40Warning) return os.getcwd() @contextlib.contextmanager def cd(target_dir): - # type: (unicode) -> Iterator[None] - cwd = getcwd() + # type: (str) -> Iterator[None] + cwd = os.getcwd() try: os.chdir(target_dir) yield @@ -260,7 +220,7 @@ def cd(target_dir): os.chdir(cwd) -class FileAvoidWrite(object): +class FileAvoidWrite: """File-like object that buffers output and only writes if content changed. Use this class like when writing to a file to avoid touching the original @@ -273,19 +233,15 @@ class FileAvoidWrite(object): Objects can be used as context managers. """ def __init__(self, path): - # type: (unicode) -> None + # type: (str) -> None self._path = path - self._io = None # type: Union[StringIO, BytesIO] + self._io = None # type: StringIO def write(self, data): - # type: (Union[str, unicode]) -> None + # type: (str) -> None if not self._io: - if isinstance(data, text_type): - self._io = StringIO() - else: - self._io = BytesIO() - - self._io.write(data) # type: ignore + self._io = StringIO() + self._io.write(data) def close(self): # type: () -> None @@ -296,23 +252,15 @@ class FileAvoidWrite(object): buf = self.getvalue() self._io.close() - r_mode = 'r' - w_mode = 'w' - if isinstance(self._io, BytesIO): - r_mode = 'rb' - w_mode = 'wb' - - old_content = None - try: - with open(self._path, r_mode) as old_f: + with open(self._path) as old_f: old_content = old_f.read() if old_content == buf: return - except IOError: + except OSError: pass - with open(self._path, w_mode) as f: + with open(self._path, 'w') as f: f.write(buf) def __enter__(self): @@ -320,7 +268,7 @@ class FileAvoidWrite(object): return self def __exit__(self, type, value, traceback): - # type: (unicode, unicode, unicode) -> None + # type: (str, str, str) -> None self.close() def __getattr__(self, name): @@ -334,7 +282,7 @@ class FileAvoidWrite(object): def rmtree(path): - # type: (unicode) -> None + # type: (str) -> None if os.path.isdir(path): shutil.rmtree(path) else: |