Files
sphinx/sphinx/util/pycompat.py
T

151 lines
4.2 KiB
Python

# -*- coding: utf-8 -*-
"""
sphinx.util.pycompat
~~~~~~~~~~~~~~~~~~~~
Stuff for Python version compatibility.
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
import codecs
from six import PY3, text_type, exec_
if False:
# For type annotation
from typing import Any, Callable, Generator # NOQA
NoneType = type(None)
# ------------------------------------------------------------------------------
# Python 2/3 compatibility
# prefix for Unicode strings
if PY3:
u = ''
else:
u = 'u'
# TextIOWrapper
if PY3:
from io import TextIOWrapper
else:
def TextIOWrapper(stream, encoding):
# type: (file, str) -> unicode
return codecs.lookup(encoding or 'ascii')[2](stream)
# sys_encoding: some kind of default system encoding; should be used with
# a lenient error handler
if PY3:
sys_encoding = sys.getdefaultencoding()
else:
sys_encoding = __import__('locale').getpreferredencoding()
# terminal_safe(): safely encode a string for printing to the terminal
if PY3:
def terminal_safe(s):
# type: (unicode) -> unicode
return s.encode('ascii', 'backslashreplace').decode('ascii')
else:
def terminal_safe(s):
# type: (unicode) -> unicode
return s.encode('ascii', 'backslashreplace')
# convert_with_2to3():
if PY3:
# support for running 2to3 over config files
def convert_with_2to3(filepath):
# type: (unicode) -> unicode
from lib2to3.refactor import RefactoringTool, get_fixers_from_package
from lib2to3.pgen2.parse import ParseError
fixers = get_fixers_from_package('lib2to3.fixes')
refactoring_tool = RefactoringTool(fixers)
source = refactoring_tool._read_python_source(filepath)[0]
try:
tree = refactoring_tool.refactor_string(source, 'conf.py')
except ParseError as err:
# do not propagate lib2to3 exceptions
lineno, offset = err.context[1]
# try to match ParseError details with SyntaxError details
raise SyntaxError(err.msg, (filepath, lineno, offset, err.value))
return text_type(tree)
else:
# no need to refactor on 2.x versions
convert_with_2to3 = None
# htmlescape()
if PY3:
from html import escape as htmlescape
else:
from cgi import escape as htmlescape # NOQA
# UnicodeMixin
if PY3:
class UnicodeMixin(object):
"""Mixin class to handle defining the proper __str__/__unicode__
methods in Python 2 or 3."""
def __str__(self):
return self.__unicode__()
else:
class UnicodeMixin(object):
"""Mixin class to handle defining the proper __str__/__unicode__
methods in Python 2 or 3."""
def __str__(self):
# type: () -> str
return self.__unicode__().encode('utf8') # type: ignore
# indent()
if PY3:
from textwrap import indent
else:
# backport from python3
def indent(text, prefix, predicate=None):
# type: (unicode, unicode, Callable) -> unicode
if predicate is None:
def predicate(line):
# type: (unicode) -> unicode
return line.strip()
def prefixed_lines():
# type: () -> Generator
for line in text.splitlines(True):
yield (prefix + line if predicate(line) else line)
return ''.join(prefixed_lines())
def execfile_(filepath, _globals, open=open):
# type: (unicode, Any, Callable) -> None
from sphinx.util.osutil import fs_encoding
# get config source -- 'b' is a no-op under 2.x, while 'U' is
# ignored under 3.x (but 3.x compile() accepts \r\n newlines)
mode = 'rb' if PY3 else 'rbU'
with open(filepath, mode) as f:
source = f.read()
# compile to a code object, handle syntax errors
filepath_enc = filepath.encode(fs_encoding)
try:
code = compile(source, filepath_enc, 'exec')
except SyntaxError:
if convert_with_2to3:
# maybe the file uses 2.x syntax; try to refactor to
# 3.x syntax using 2to3
source = convert_with_2to3(filepath)
code = compile(source, filepath_enc, 'exec')
else:
raise
exec_(code, _globals)