summaryrefslogtreecommitdiff
path: root/sphinx/util/pycompat.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/util/pycompat.py')
-rw-r--r--sphinx/util/pycompat.py162
1 files changed, 58 insertions, 104 deletions
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index 8bcf7e4f8..d0b3443b5 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -9,130 +9,81 @@
:license: BSD, see LICENSE for details.
"""
-import codecs
import sys
+import warnings
+from html import escape as htmlescape # NOQA
+from io import TextIOWrapper # NOQA
+from textwrap import indent # NOQA
-from six import PY3, text_type, exec_
+from six import text_type
+
+from sphinx.deprecation import RemovedInSphinx40Warning
+from sphinx.locale import __
+from sphinx.util import logging
if False:
# For type annotation
from typing import Any, Callable, Generator # NOQA
+logger = logging.getLogger(__name__)
+
+
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) -> Any
- return codecs.lookup(encoding or 'ascii')[2](stream)
+u = '' # RemovedInSphinx40Warning
# 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()
+sys_encoding = sys.getdefaultencoding()
# 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')
+def terminal_safe(s):
+ # type: (str) -> str
+ return s.encode('ascii', 'backslashreplace').decode('ascii')
# 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())
+# support for running 2to3 over config files
+def convert_with_2to3(filepath):
+ # type: (str) -> str
+ 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)
+
+
+class UnicodeMixin:
+ """Mixin class to handle defining the proper __str__/__unicode__
+ methods in Python 2 or 3.
+
+ .. deprecated:: 2.0
+ """
+ def __str__(self):
+ warnings.warn('UnicodeMixin is deprecated',
+ RemovedInSphinx40Warning, stacklevel=2)
+ return self.__unicode__()
def execfile_(filepath, _globals, open=open):
- # type: (unicode, Any, Callable) -> None
+ # type: (str, 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:
+ with open(filepath, 'rb') as f:
source = f.read()
# compile to a code object, handle syntax errors
@@ -140,11 +91,14 @@ def execfile_(filepath, _globals, open=open):
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)
+ # 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')
+ # TODO: When support for evaluating Python 2 syntax is removed,
+ # deprecate convert_with_2to3().
+ logger.warning(__('Support for evaluating Python 2 syntax is deprecated '
+ 'and will be removed in Sphinx 4.0. '
+ 'Convert %s to Python 3 syntax.'),
+ filepath)
+ exec(code, _globals)