Commits

Georg Brandl  committed 46be1ca

Run 2to3 on config files which contain Python 2.x unicode literals.

  • Participants
  • Parent commits c34a538

Comments (0)

Files changed (3)

File sphinx/config.py

 import re
 import sys
 from os import path
+try:
+    from distutils.util import run_2to3
+except ImportError:
+    run_2to3 = None
 
 from sphinx.errors import ConfigError
 from sphinx.util.osutil import make_filename
-from sphinx.util.pycompat import bytes, b
+from sphinx.util.pycompat import bytes, b, should_run_2to3, run_2to3
 
 nonascii_re = re.compile(b(r'[\x80-\xff]'))
 
+CONFIG_SYNTAX_ERROR = "There is a syntax error in your configuration file: %s"
+if sys.version_info >= (3, 0):
+    CONFIG_SYNTAX_ERROR += "\nDid you change the syntax from 2.x to 3.x?"
 
 class Config(object):
     """Configuration file abstraction."""
             try:
                 try:
                     os.chdir(dirname)
-                    f = open(config_file, 'rb')
-                    try:
-                        code = compile(f.read(), config_file, 'exec')
-                    finally:
-                        f.close()
-                    exec code in config
+                    if should_run_2to3(config_file):
+                        code = run_2to3(config_file)
+                    else:
+                        f = open(config_file, 'rb')
+                        try:
+                            code = f.read()
+                        finally:
+                            f.close()
+                    exec compile(code, config_file, 'exec') in config
                 except SyntaxError, err:
-                    raise ConfigError('There is a syntax error in your '
-                                      'configuration file: ' + str(err))
+                    raise ConfigError(CONFIG_SYNTAX_ERROR % err)
             finally:
                 os.chdir(olddir)
 

File sphinx/quickstart.py

 
 PROMPT_PREFIX = '> '
 
-QUICKSTART_CONF = '''\
+if sys.version_info >= (3, 0):
+    # prevents that the file is checked for being written in Python 2.x syntax
+    QUICKSTART_CONF = '#!/usr/bin/env python3\n'
+else:
+    QUICKSTART_CONF = ''
+
+QUICKSTART_CONF += '''\
 # -*- coding: utf-8 -*-
 #
 # %(project)s documentation build configuration file, created by

File sphinx/util/pycompat.py

 import sys
 import codecs
 import encodings
+import re
 
 try:
     from types import ClassType
     # Python 3
     class_types = (type,)
 
-try:
-    base_exception = BaseException
-except NameError:
-    base_exception = Exception
-
 
 # the ubiquitous "bytes" helper function
 if sys.version_info >= (3, 0):
     b = str
 
 
+encoding_re = re.compile(b(r'coding[=:]\s*([-\w.]+)'))
+unicode_literal_re = re.compile(ur"""
+(?:
+    "(?:[^"\]]*(?:\\.[^"\\]*)*)"|
+    '(?:[^'\]]*(?:\\.[^'\\]*)*)'
+)
+""", re.VERBOSE)
+
+
+try:
+    from lib2to3.refactor import RefactoringTool, get_fixers_from_package
+except ImportError:
+    _run_2to3 = None
+    def should_run_2to3(filepath):
+        return False
+else:
+    def should_run_2to3(filepath):
+        # th default source code encoding for python 2.x
+        encoding = 'ascii'
+        # only the first match of the encoding cookie counts
+        encoding_set = False
+        f = open(filepath, 'rb')
+        try:
+            for i, line in enumerate(f):
+                if line.startswith(b('#')):
+                    if i == 0 and b('python3') in line:
+                        return False
+                    if not encoding_set:
+                        encoding_match = encoding_re.match(line)
+                        if encoding_match:
+                            encoding = encoding_match.group(1)
+                            encodin_set = True
+                elif line.strip():
+                    try:
+                        line = line.decode(encoding)
+                    except UnicodeDecodeError:
+                        # I'm not sure this will work but let's try it anyway
+                        return True
+                    if unicode_literal_re.search(line) is not None:
+                        return True
+        finally:
+            f.close()
+        return False
+
+    def run_2to3(filepath):
+        sys.path.append('..')
+        fixers = get_fixers_from_package('lib2to3.fixes')
+        fixers.extend(get_fixers_from_package('custom_fixers'))
+        refactoring_tool = RefactoringTool(fixers)
+        source = refactoring_tool._read_python_source(filepath)[0]
+        ast = refactoring_tool.refactor_string(source, 'conf.py')
+        return unicode(ast)
+
+
+try:
+    base_exception = BaseException
+except NameError:
+    base_exception = Exception
+
+
 try:
     next = next
 except NameError:
     def next(iterator):
         return iterator.next()
 
+
 try:
     bytes = bytes
 except NameError: