sphinx/sphinx/util/osutil.py

203 lines
5.4 KiB
Python
Raw Normal View History

2010-01-17 10:35:12 -06:00
# -*- coding: utf-8 -*-
"""
sphinx.util.osutil
~~~~~~~~~~~~~~~~~~
2010-01-17 10:35:12 -06:00
Operating system-related utility functions for Sphinx.
2015-01-03 14:36:32 -06:00
:copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
2010-01-17 10:35:12 -06:00
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
2010-01-17 10:35:12 -06:00
import os
import re
2010-07-28 11:43:40 -05:00
import sys
2010-01-17 10:35:12 -06:00
import time
import errno
import locale
2010-01-17 10:35:12 -06:00
import shutil
from os import path
2014-09-25 11:46:29 -05:00
import contextlib
2010-01-17 10:35:12 -06:00
from six import PY2, text_type
2010-01-17 10:35:12 -06:00
# Errnos that we need.
EEXIST = getattr(errno, 'EEXIST', 0)
ENOENT = getattr(errno, 'ENOENT', 0)
EPIPE = getattr(errno, 'EPIPE', 0)
EINVAL = getattr(errno, 'EINVAL', 0)
2010-01-17 10:35:12 -06:00
# SEP separates path elements in the canonical file names
#
# Define SEP as a manifest constant, not so much because we expect it to change
# in the future as to avoid the suspicion that a stray "/" in the code is a
# hangover from more *nix-oriented origins.
SEP = "/"
2015-03-08 10:55:34 -05:00
2010-01-17 10:35:12 -06:00
def os_path(canonicalpath):
return canonicalpath.replace(SEP, path.sep)
def relative_uri(base, to):
"""Return a relative URL from ``base`` to ``to``."""
if to.startswith(SEP):
return to
b2 = base.split(SEP)
t2 = to.split(SEP)
2012-04-28 14:03:30 -05:00
# remove common segments (except the last segment)
for x, y in zip(b2[:-1], t2[:-1]):
2010-01-17 10:35:12 -06:00
if x != y:
break
b2.pop(0)
t2.pop(0)
2012-04-28 14:03:30 -05:00
if b2 == t2:
# Special case: relative_uri('f/index.html','f/index.html')
# returns '', not 'index.html'
return ''
if len(b2) == 1 and t2 == ['']:
# Special case: relative_uri('f/index.html','f/') should
2012-04-28 14:03:30 -05:00
# return './', not ''
2015-03-08 10:55:34 -05:00
return '.' + SEP
2010-01-17 10:35:12 -06:00
return ('..' + SEP) * (len(b2)-1) + SEP.join(t2)
def ensuredir(path):
"""Ensure that a path exists."""
try:
os.makedirs(path)
except OSError as err:
2010-01-17 10:35:12 -06:00
# 0 for Jython/Win32
if err.errno not in [0, EEXIST]:
raise
# This function is same as os.walk of Python2.6, 2.7, 3.2, 3.3 except a
# customization that check UnicodeError.
# The customization obstacle to replace the function with the os.walk.
2010-01-17 10:35:12 -06:00
def walk(top, topdown=True, followlinks=False):
2010-08-22 04:36:08 -05:00
"""Backport of os.walk from 2.6, where the *followlinks* argument was
added.
2010-01-17 10:35:12 -06:00
"""
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):
2010-01-17 10:35:12 -06:00
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
def mtimes_of_files(dirnames, suffix):
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:
pass
def movefile(source, dest):
"""Move a file, removing the destination if it exists."""
if os.path.exists(dest):
try:
os.unlink(dest)
except OSError:
pass
os.rename(source, dest)
def copytimes(source, dest):
"""Copy a file's modification times."""
st = os.stat(source)
if hasattr(os, 'utime'):
os.utime(dest, (st.st_atime, st.st_mtime))
def copyfile(source, dest):
"""Copy a file and its modification times, if possible."""
shutil.copyfile(source, dest)
try:
# don't do full copystat because the source may be read-only
copytimes(source, dest)
except OSError:
pass
no_fn_re = re.compile(r'[^a-zA-Z0-9_-]')
2015-03-08 10:55:34 -05:00
2010-01-17 10:35:12 -06:00
def make_filename(string):
return no_fn_re.sub('', string) or 'sphinx'
2010-01-17 10:35:12 -06:00
if PY2:
# strftime for unicode strings
2010-05-24 12:41:02 -05:00
def ustrftime(format, *args):
# 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
def ustrftime(format, *args):
# On Windows, time.strftime() and Unicode characters will raise UnicodeEncodeError.
# http://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 safe_relpath(path, start=None):
try:
return os.path.relpath(path, start)
except ValueError:
return path
2015-03-08 10:55:34 -05:00
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
def abspath(pathdir):
pathdir = path.abspath(pathdir)
if isinstance(pathdir, bytes):
pathdir = pathdir.decode(fs_encoding)
return pathdir
def getcwd():
if hasattr(os, 'getcwdu'):
return os.getcwdu()
return os.getcwd()
2014-09-25 12:11:51 -05:00
2014-09-25 11:46:29 -05:00
@contextlib.contextmanager
def cd(target_dir):
cwd = getcwd()
try:
os.chdir(target_dir)
yield
finally:
os.chdir(cwd)