From c705e3ae1f4d614ab7a806c0fa3f9484bf98a789 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sat, 24 Jul 2010 11:35:29 +0100
Subject: [PATCH 001/207] Trunk is now 1.1pre.
---
CHANGES | 4 ++++
sphinx/__init__.py | 4 ++--
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGES b/CHANGES
index 8465dae09..1bb0a276e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+Release 1.1 (in development)
+============================
+
+
Release 1.0.1 (in development)
==============================
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 8c1ebed56..31c61a86a 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -12,8 +12,8 @@
import sys
from os import path
-__version__ = '1.0+'
-__released__ = '1.0' # used when Sphinx builds its own docs
+__version__ = '1.1pre'
+__released__ = '1.1 (hg)' # used when Sphinx builds its own docs
package_dir = path.abspath(path.dirname(__file__))
From 12ecec3f5753881d7bd0509f057dd97353ae38cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Thu, 29 Apr 2010 18:06:22 +0200
Subject: [PATCH 002/207] Show python 3.x incompatibilities which cannot be
trivially fixed
---
Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 7057a7152..682f03666 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-PYTHON ?= python
+PYTHON ?= python -3
export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx
From e97122236d9e908ae6b9806858ad09c1d70cbcc1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Thu, 29 Apr 2010 18:08:44 +0200
Subject: [PATCH 003/207] Replace .has_key() calls with the in-operator
---
sphinx/writers/latex.py | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 5674b388c..360cf40c9 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -314,7 +314,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
# ... and all others are the appendices
self.body.append(u'\n\\appendix\n')
self.first_document = -1
- if node.has_key('docname'):
+ if 'docname' in node:
self.body.append(self.hypertarget(':doc'))
# "- 1" because the level is increased before the title is visited
self.sectionlevel = self.top_sectionlevel - 1
@@ -694,7 +694,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.table.rowcount += 1
def visit_entry(self, node):
- if node.has_key('morerows') or node.has_key('morecols'):
+ if 'morerows' in node or 'morecols' in node:
raise UnsupportedError('%s:%s: column or row spanning cells are '
'not yet implemented.' %
(self.curfilestack[-1], node.line or ''))
@@ -751,7 +751,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_term(self, node):
ctx = '}] \\leavevmode'
- if node.has_key('ids') and node['ids']:
+ if node.get('ids'):
ctx += self.hypertarget(node['ids'][0])
self.body.append('\\item[{')
self.context.append(ctx)
@@ -833,20 +833,20 @@ class LaTeXTranslator(nodes.NodeVisitor):
post = []
include_graphics_options = []
is_inline = self.is_inline(node)
- if attrs.has_key('scale'):
+ if 'scale' in attrs:
# Could also be done with ``scale`` option to
# ``\includegraphics``; doing it this way for consistency.
pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,))
post.append('}')
- if attrs.has_key('width'):
+ if 'width' in attrs:
w = self.latex_image_length(attrs['width'])
if w:
include_graphics_options.append('width=%s' % w)
- if attrs.has_key('height'):
+ if 'height' in attrs:
h = self.latex_image_length(attrs['height'])
if h:
include_graphics_options.append('height=%s' % h)
- if attrs.has_key('align'):
+ if 'align' in attrs:
align_prepost = {
# By default latex aligns the top of an image.
(1, 'top'): ('', ''),
@@ -887,13 +887,13 @@ class LaTeXTranslator(nodes.NodeVisitor):
pass
def visit_figure(self, node):
- if node.has_key('width') and node.get('align', '') in ('left', 'right'):
+ if 'width' in node and node.get('align', '') in ('left', 'right'):
self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' %
(node['align'] == 'right' and 'r' or 'l',
node['width']))
self.context.append('\\end{wrapfigure}\n')
else:
- if (not node.attributes.has_key('align') or
+ if (not 'align' in node.attributes or
node.attributes['align'] == 'center'):
# centering does not add vertical space like center.
align = '\n\\centering'
@@ -1154,7 +1154,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.no_contractions -= 1
if self.in_title:
self.body.append(r'\texttt{%s}' % content)
- elif node.has_key('role') and node['role'] == 'samp':
+ elif node.get('role') == 'samp':
self.body.append(r'\samp{%s}' % content)
else:
self.body.append(r'\code{%s}' % content)
@@ -1183,10 +1183,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
code = self.verbatim.rstrip('\n')
lang = self.hlsettingstack[-1][0]
linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1
- if node.has_key('language'):
+ if 'language' in node:
# code-block directives
lang = node['language']
- if node.has_key('linenos'):
+ if 'linenos' in node:
linenos = node['linenos']
hlcode = self.highlighter.highlight_block(code, lang, linenos)
# workaround for Unicode issue
From 4adbc983503072918b058d6bfa6bca4fd64b86aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Thu, 29 Apr 2010 19:42:17 +0200
Subject: [PATCH 004/207] Removed map(None, ...) usage
---
sphinx/writers/text.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index 98528d5ba..b28b23792 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -390,7 +390,7 @@ class TextTranslator(nodes.NodeVisitor):
self.add_text(''.join(out) + '\n')
def writerow(row):
- lines = map(None, *row)
+ lines = zip(*row)
for line in lines:
out = ['|']
for i, cell in enumerate(line):
From 21376f21e7cee76761fccef6874c88c1b3f79e3a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Thu, 29 Apr 2010 20:34:08 +0200
Subject: [PATCH 005/207] Don't use execfile() anymore
---
sphinx/config.py | 6 +++++-
tests/test_quickstart.py | 12 ++++++++++--
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/sphinx/config.py b/sphinx/config.py
index 12c2a04ba..f76d330ac 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -165,7 +165,11 @@ class Config(object):
try:
try:
os.chdir(dirname)
- execfile(config['__file__'], config)
+ try:
+ f = open(config_file, 'U')
+ exec f in config
+ finally:
+ f.close()
except SyntaxError, err:
raise ConfigError('There is a syntax error in your '
'configuration file: ' + str(err))
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index cb40d27cf..34c54f95a 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -85,7 +85,11 @@ def test_quickstart_defaults(tempdir):
conffile = tempdir / 'conf.py'
assert conffile.isfile()
ns = {}
- execfile(conffile, ns)
+ try:
+ f = open(conffile, 'U')
+ exec f in ns
+ finally:
+ f.close()
assert ns['extensions'] == []
assert ns['templates_path'] == ['_templates']
assert ns['source_suffix'] == '.rst'
@@ -138,7 +142,11 @@ def test_quickstart_all_answers(tempdir):
conffile = tempdir / 'source' / 'conf.py'
assert conffile.isfile()
ns = {}
- execfile(conffile, ns)
+ try:
+ f = open(conffile, 'U')
+ exec f in ns
+ finally:
+ f.close()
assert ns['extensions'] == ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
assert ns['templates_path'] == ['.templates']
assert ns['source_suffix'] == '.txt'
From 47557af776de7772da88a5d3ceaad62bf9edaef6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Fri, 30 Apr 2010 12:32:42 +0200
Subject: [PATCH 006/207] Make sphinx.domains.cpp.DefExpr unhashable
---
sphinx/domains/cpp.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 4dac89253..90c3533ef 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -132,6 +132,8 @@ class DefExpr(object):
def __ne__(self, other):
return not self.__eq__(other)
+ __hash__ = None
+
def clone(self):
"""Close a definition expression node"""
return deepcopy(self)
From f6bf9b13ff40ae8dfbc9afe19db0da3fcbac8f93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 1 May 2010 19:17:52 +0200
Subject: [PATCH 007/207] Fixed issue #1
---
sphinx/environment.py | 3 ++-
sphinx/pycode/pgen2/tokenize.py | 4 +++-
utils/reindent.py | 3 ++-
3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/sphinx/environment.py b/sphinx/environment.py
index 5edcb4d90..fa8460cb3 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -1488,8 +1488,9 @@ class BuildEnvironment:
i += 1
# group the entries by letter
- def keyfunc2((k, v), letters=string.ascii_uppercase + '_'):
+ def keyfunc2(item, letters=string.ascii_uppercase + '_'):
# hack: mutating the subitems dicts to a list in the keyfunc
+ k, v = item
v[1] = sorted((si, se) for (si, (se, void)) in v[1].iteritems())
# now calculate the key
letter = k[0].upper()
diff --git a/sphinx/pycode/pgen2/tokenize.py b/sphinx/pycode/pgen2/tokenize.py
index 4489db898..7ad9f012c 100644
--- a/sphinx/pycode/pgen2/tokenize.py
+++ b/sphinx/pycode/pgen2/tokenize.py
@@ -143,7 +143,9 @@ class TokenError(Exception): pass
class StopTokenizing(Exception): pass
-def printtoken(type, token, (srow, scol), (erow, ecol), line): # for testing
+def printtoken(type, token, scell, ecell, line): # for testing
+ srow, scol = scell
+ erow, ecol = ecell
print "%d,%d-%d,%d:\t%s\t%s" % \
(srow, scol, erow, ecol, tok_name[type], repr(token))
diff --git a/utils/reindent.py b/utils/reindent.py
index c499f671e..bcb6b4343 100755
--- a/utils/reindent.py
+++ b/utils/reindent.py
@@ -244,12 +244,13 @@ class Reindenter:
return line
# Line-eater for tokenize.
- def tokeneater(self, type, token, (sline, scol), end, line,
+ def tokeneater(self, type, token, scell, end, line,
INDENT=tokenize.INDENT,
DEDENT=tokenize.DEDENT,
NEWLINE=tokenize.NEWLINE,
COMMENT=tokenize.COMMENT,
NL=tokenize.NL):
+ sline, scol = scell
if type == NEWLINE:
# A program statement, or ENDMARKER, will eventually follow,
From eef0b0821d01c3eb4d26eb3a4f8a185b29df8c60 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 1 May 2010 19:18:31 +0200
Subject: [PATCH 008/207] Make sphinx.pycode.nodes.BaseNode unhashable
---
sphinx/pycode/nodes.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/sphinx/pycode/nodes.py b/sphinx/pycode/nodes.py
index e71846779..fc6eb93aa 100644
--- a/sphinx/pycode/nodes.py
+++ b/sphinx/pycode/nodes.py
@@ -29,6 +29,8 @@ class BaseNode(object):
return NotImplemented
return not self._eq(other)
+ __hash__ = None
+
def get_prev_sibling(self):
"""Return previous child in parent's children, or None."""
if self.parent is None:
From 6651f67602a51795447fee8f6f8ac517f26a8f38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 1 May 2010 19:19:24 +0200
Subject: [PATCH 009/207] Removed pre-2.3 workaround for booleans
---
tests/path.py | 6 ------
1 file changed, 6 deletions(-)
diff --git a/tests/path.py b/tests/path.py
index ceb895f50..20deb0489 100644
--- a/tests/path.py
+++ b/tests/path.py
@@ -56,12 +56,6 @@ try:
except AttributeError:
pass
-# Pre-2.3 workaround for booleans
-try:
- True, False
-except NameError:
- True, False = 1, 0
-
# Pre-2.3 workaround for basestring.
try:
basestring
From 4391e63b07c02d317617fc061256fd77eb0cb9b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 1 May 2010 20:26:05 +0200
Subject: [PATCH 010/207] Move open() calls out of the try block
---
sphinx/config.py | 2 +-
tests/test_quickstart.py | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/sphinx/config.py b/sphinx/config.py
index f76d330ac..c22a5ee74 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -165,8 +165,8 @@ class Config(object):
try:
try:
os.chdir(dirname)
+ f = open(config_file, 'U')
try:
- f = open(config_file, 'U')
exec f in config
finally:
f.close()
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index 34c54f95a..71ca95a4d 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -85,8 +85,8 @@ def test_quickstart_defaults(tempdir):
conffile = tempdir / 'conf.py'
assert conffile.isfile()
ns = {}
+ f = open(conffile, 'U')
try:
- f = open(conffile, 'U')
exec f in ns
finally:
f.close()
@@ -142,8 +142,8 @@ def test_quickstart_all_answers(tempdir):
conffile = tempdir / 'source' / 'conf.py'
assert conffile.isfile()
ns = {}
+ f = open(conffile, 'U')
try:
- f = open(conffile, 'U')
exec f in ns
finally:
f.close()
From df236468781b3ef8dc33f31896fe64b7958587a7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Thu, 6 May 2010 16:05:37 +0200
Subject: [PATCH 011/207] Added a file containing the changes made during GSoC.
---
CHANGES.DasIch | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
create mode 100644 CHANGES.DasIch
diff --git a/CHANGES.DasIch b/CHANGES.DasIch
new file mode 100644
index 000000000..856a5d87b
--- /dev/null
+++ b/CHANGES.DasIch
@@ -0,0 +1,19 @@
+Changes
+=======
+
+This file contains changes made by Daniel Neuhäuser, during the Google Summer
+of Code 2010, to port Sphinx to Python 3.x. Changes are ordered descending by
+date.
+
+May 1: - Removed deprecated tuple parameter unpacking.
+ - Removed a pre-2.3 workaround for booleans because this creates a
+ deprecation warning for 3.x, in which you can't assign values to
+ booleans.
+ - Moved :func:`open()` calls out of the try-blocks, which fixes revision
+ c577c25bd44b.
+
+April 30: Made :cls:`sphinx.domains.cpp.DefExpr` unhashable as described by the
+ documentation because classes in 3.x don't inherit ``__hash__`` if
+ they implement ``__eq__``.
+
+April 29: Removed several deprecated function/method calls.
From 89cb0714b1041b77ceae5a777a18dd780c032bf8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 8 May 2010 20:34:19 +0200
Subject: [PATCH 012/207] Removed ez_setup which doesn't work with python3 and
added use_2to3 for distribute
---
setup.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/setup.py b/setup.py
index 183fcceb1..2494851f5 100644
--- a/setup.py
+++ b/setup.py
@@ -47,7 +47,7 @@ A development egg can be found `here
requires = ['Pygments>=0.8', 'Jinja2>=2.2', 'docutils>=0.5']
if sys.version_info < (2, 4):
- print 'ERROR: Sphinx requires at least Python 2.4 to run.'
+ print('ERROR: Sphinx requires at least Python 2.4 to run.')
sys.exit(1)
if sys.version_info < (2, 5):
@@ -198,4 +198,5 @@ setup(
},
install_requires=requires,
cmdclass=cmdclass,
+ use_2to3=True,
)
From c09c74c3dfbbf166d60c80ce6fcb0a85f4df63a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 8 May 2010 20:53:49 +0200
Subject: [PATCH 013/207] Check for unicode before trying to decode input from
raw_input. Also use codecs.open when writing non-binary files.
sphinx-quickstart now works.
---
sphinx/quickstart.py | 42 +++++++++++++++++++++++-------------------
1 file changed, 23 insertions(+), 19 deletions(-)
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 884caca75..5820996f1 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -11,6 +11,7 @@
import sys, os, time
from os import path
+from codecs import open
TERM_ENCODING = getattr(sys.stdin, 'encoding', None)
@@ -659,17 +660,20 @@ def do_prompt(d, key, text, default=None, validator=nonempty):
x = raw_input(prompt)
if default and not x:
x = default
- if x.decode('ascii', 'replace').encode('ascii', 'replace') != x:
- if TERM_ENCODING:
- x = x.decode(TERM_ENCODING)
- else:
- print turquoise('* Note: non-ASCII characters entered '
- 'and terminal encoding unknown -- assuming '
- 'UTF-8 or Latin-1.')
- try:
- x = x.decode('utf-8')
- except UnicodeDecodeError:
- x = x.decode('latin1')
+ # in 3.x raw_input returns a unicode string, those have no decode
+ # method
+ if not isinstance(x, unicode):
+ if x.decode('ascii', 'replace').encode('ascii', 'replace') != x:
+ if TERM_ENCODING:
+ x = x.decode(TERM_ENCODING)
+ else:
+ print turquoise('* Note: non-ASCII characters entered '
+ 'and terminal encoding unknown -- assuming '
+ 'UTF-8 or Latin-1.')
+ try:
+ x = x.decode('utf-8')
+ except UnicodeDecodeError:
+ x = x.decode('latin1')
try:
x = validator(x)
except ValidationError, err:
@@ -834,28 +838,28 @@ directly.'''
if d['ext_intersphinx']:
conf_text += INTERSPHINX_CONFIG
- f = open(path.join(srcdir, 'conf.py'), 'w')
- f.write(conf_text.encode('utf-8'))
+ f = open(path.join(srcdir, 'conf.py'), 'w', encoding='utf-8')
+ f.write(conf_text)
f.close()
masterfile = path.join(srcdir, d['master'] + d['suffix'])
- f = open(masterfile, 'w')
- f.write((MASTER_FILE % d).encode('utf-8'))
+ f = open(masterfile, 'w', encoding='utf-8')
+ f.write(MASTER_FILE % d)
f.close()
if d['makefile']:
d['rsrcdir'] = d['sep'] and 'source' or '.'
d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build'
# use binary mode, to avoid writing \r\n on Windows
- f = open(path.join(d['path'], 'Makefile'), 'wb')
- f.write((MAKEFILE % d).encode('utf-8'))
+ f = open(path.join(d['path'], 'Makefile'), 'wb', encoding='utf-8')
+ f.write(MAKEFILE % d)
f.close()
if d['batchfile']:
d['rsrcdir'] = d['sep'] and 'source' or '.'
d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build'
- f = open(path.join(d['path'], 'make.bat'), 'w')
- f.write((BATCHFILE % d).encode('utf-8'))
+ f = open(path.join(d['path'], 'make.bat'), 'w', encoding='utf-8')
+ f.write(BATCHFILE % d)
f.close()
print
From 66244b8432b9a594848d29e6d43f354e643f7706 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:03:42 +0200
Subject: [PATCH 014/207] Use codecs.open().
---
sphinx/quickstart.py | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 5820996f1..fe2b43a33 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -660,9 +660,8 @@ def do_prompt(d, key, text, default=None, validator=nonempty):
x = raw_input(prompt)
if default and not x:
x = default
- # in 3.x raw_input returns a unicode string, those have no decode
- # method
if not isinstance(x, unicode):
+ # for Python 2.x, try to get a Unicode string out of it
if x.decode('ascii', 'replace').encode('ascii', 'replace') != x:
if TERM_ENCODING:
x = x.decode(TERM_ENCODING)
@@ -838,12 +837,12 @@ directly.'''
if d['ext_intersphinx']:
conf_text += INTERSPHINX_CONFIG
- f = open(path.join(srcdir, 'conf.py'), 'w', encoding='utf-8')
+ f = codecs.open(path.join(srcdir, 'conf.py'), 'w', encoding='utf-8')
f.write(conf_text)
f.close()
masterfile = path.join(srcdir, d['master'] + d['suffix'])
- f = open(masterfile, 'w', encoding='utf-8')
+ f = codecs.open(masterfile, 'w', encoding='utf-8')
f.write(MASTER_FILE % d)
f.close()
@@ -851,14 +850,14 @@ directly.'''
d['rsrcdir'] = d['sep'] and 'source' or '.'
d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build'
# use binary mode, to avoid writing \r\n on Windows
- f = open(path.join(d['path'], 'Makefile'), 'wb', encoding='utf-8')
+ f = codecs.open(path.join(d['path'], 'Makefile'), 'wb', encoding='utf-8')
f.write(MAKEFILE % d)
f.close()
if d['batchfile']:
d['rsrcdir'] = d['sep'] and 'source' or '.'
d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build'
- f = open(path.join(d['path'], 'make.bat'), 'w', encoding='utf-8')
+ f = codecs.open(path.join(d['path'], 'make.bat'), 'w', encoding='utf-8')
f.write(BATCHFILE % d)
f.close()
From 9c29a8cd9c7cd42a84e4243fa8cc20631c466c7a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 8 May 2010 21:47:52 +0200
Subject: [PATCH 015/207] Encode even bytestrings containing ascii tests, they
are unicode in python3
---
sphinx/util/__init__.py | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 8d1298cd3..c1e8d25c9 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -168,11 +168,14 @@ def save_traceback():
"""
exc = traceback.format_exc()
fd, path = tempfile.mkstemp('.log', 'sphinx-err-')
- os.write(fd, '# Sphinx version: %s\n' % sphinx.__version__)
- os.write(fd, '# Docutils version: %s %s\n' % (docutils.__version__,
- docutils.__version_details__))
- os.write(fd, '# Jinja2 version: %s\n' % jinja2.__version__)
- os.write(fd, exc)
+ os.write(fd,
+ (('# Sphinx version: %s\n'
+ '# Docutils version: %s %s\n'
+ '# Jinja2 version: %s\n') % (sphinx.__version__,
+ docutils.__version__,
+ docutils.__version_details__,
+ jinja2.__version__)).encode('utf-8'))
+ os.write(fd, exc.encode('utf-8'))
os.close(fd)
return path
From b81d428b89137a3f763bc2fd95de9a55bcc2e29a Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:08:00 +0200
Subject: [PATCH 016/207] Take string constant out of function.
---
sphinx/util/__init__.py | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index c1e8d25c9..2ef420ed1 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -162,19 +162,22 @@ def copy_static_entry(source, targetdir, builder, context={},
shutil.copytree(source, target)
+_DEBUG_HEADER = '''\
+# Sphinx version: %s
+# Docutils version: %s %s
+# Jinja2 version: %s
+'''
+
def save_traceback():
"""
Save the current exception's traceback in a temporary file.
"""
exc = traceback.format_exc()
fd, path = tempfile.mkstemp('.log', 'sphinx-err-')
- os.write(fd,
- (('# Sphinx version: %s\n'
- '# Docutils version: %s %s\n'
- '# Jinja2 version: %s\n') % (sphinx.__version__,
- docutils.__version__,
- docutils.__version_details__,
- jinja2.__version__)).encode('utf-8'))
+ os.write(fd, (_DEBUG_HEADER %
+ (sphinx.__version__,
+ docutils.__version__, docutils.__version_details__,
+ jinja2.__version__)).encode('utf-8'))
os.write(fd, exc.encode('utf-8'))
os.close(fd)
return path
From 25b16f89e08be50c08fcc0f29ade8172d33cb347 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 8 May 2010 22:00:15 +0200
Subject: [PATCH 017/207] Use code objects for exec statements instead of files
---
sphinx/config.py | 3 ++-
tests/test_quickstart.py | 6 ++++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/sphinx/config.py b/sphinx/config.py
index c22a5ee74..2ec769871 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -167,9 +167,10 @@ class Config(object):
os.chdir(dirname)
f = open(config_file, 'U')
try:
- exec f in config
+ code = compile(f.read(), config_file, 'exec')
finally:
f.close()
+ exec code in config
except SyntaxError, err:
raise ConfigError('There is a syntax error in your '
'configuration file: ' + str(err))
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index 71ca95a4d..8acff5884 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -87,9 +87,10 @@ def test_quickstart_defaults(tempdir):
ns = {}
f = open(conffile, 'U')
try:
- exec f in ns
+ code = compile(f.read(), conffile, 'exec')
finally:
f.close()
+ exec code in ns
assert ns['extensions'] == []
assert ns['templates_path'] == ['_templates']
assert ns['source_suffix'] == '.rst'
@@ -144,9 +145,10 @@ def test_quickstart_all_answers(tempdir):
ns = {}
f = open(conffile, 'U')
try:
- exec f in ns
+ code = compile(f.read(), conffile, 'exec')
finally:
f.close()
+ exec code in ns
assert ns['extensions'] == ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
assert ns['templates_path'] == ['.templates']
assert ns['source_suffix'] == '.txt'
From 8e9709290f5b38e108b975abc3bbc399950bbce7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 8 May 2010 22:28:28 +0200
Subject: [PATCH 018/207] Removing unnecessary ez_setup.py
---
ez_setup.py | 276 ----------------------------------------------------
1 file changed, 276 deletions(-)
delete mode 100644 ez_setup.py
diff --git a/ez_setup.py b/ez_setup.py
deleted file mode 100644
index d24e845e5..000000000
--- a/ez_setup.py
+++ /dev/null
@@ -1,276 +0,0 @@
-#!python
-"""Bootstrap setuptools installation
-
-If you want to use setuptools in your package's setup.py, just include this
-file in the same directory with it, and add this to the top of your setup.py::
-
- from ez_setup import use_setuptools
- use_setuptools()
-
-If you want to require a specific version of setuptools, set a download
-mirror, or use an alternate download directory, you can do so by supplying
-the appropriate options to ``use_setuptools()``.
-
-This file can also be run as a script to install or upgrade setuptools.
-"""
-import sys
-DEFAULT_VERSION = "0.6c9"
-DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
-
-md5_data = {
- 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
- 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
- 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
- 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
- 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
- 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
- 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
- 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
- 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
- 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
- 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
- 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
- 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
- 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
- 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
- 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
- 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
- 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
- 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
- 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
- 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
- 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
- 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
- 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
- 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
- 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
- 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
- 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
- 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
- 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
- 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
- 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
- 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
- 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
-}
-
-import sys, os
-try: from hashlib import md5
-except ImportError: from md5 import md5
-
-def _validate_md5(egg_name, data):
- if egg_name in md5_data:
- digest = md5(data).hexdigest()
- if digest != md5_data[egg_name]:
- print >>sys.stderr, (
- "md5 validation of %s failed! (Possible download problem?)"
- % egg_name
- )
- sys.exit(2)
- return data
-
-def use_setuptools(
- version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
- download_delay=15
-):
- """Automatically find/download setuptools and make it available on sys.path
-
- `version` should be a valid setuptools version number that is available
- as an egg for download under the `download_base` URL (which should end with
- a '/'). `to_dir` is the directory where setuptools will be downloaded, if
- it is not already available. If `download_delay` is specified, it should
- be the number of seconds that will be paused before initiating a download,
- should one be required. If an older version of setuptools is installed,
- this routine will print a message to ``sys.stderr`` and raise SystemExit in
- an attempt to abort the calling script.
- """
- was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
- def do_download():
- egg = download_setuptools(version, download_base, to_dir, download_delay)
- sys.path.insert(0, egg)
- import setuptools; setuptools.bootstrap_install_from = egg
- try:
- import pkg_resources
- except ImportError:
- return do_download()
- try:
- pkg_resources.require("setuptools>="+version); return
- except pkg_resources.VersionConflict, e:
- if was_imported:
- print >>sys.stderr, (
- "The required version of setuptools (>=%s) is not available, and\n"
- "can't be installed while this script is running. Please install\n"
- " a more recent version first, using 'easy_install -U setuptools'."
- "\n\n(Currently using %r)"
- ) % (version, e.args[0])
- sys.exit(2)
- else:
- del pkg_resources, sys.modules['pkg_resources'] # reload ok
- return do_download()
- except pkg_resources.DistributionNotFound:
- return do_download()
-
-def download_setuptools(
- version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
- delay = 15
-):
- """Download setuptools from a specified location and return its filename
-
- `version` should be a valid setuptools version number that is available
- as an egg for download under the `download_base` URL (which should end
- with a '/'). `to_dir` is the directory where the egg will be downloaded.
- `delay` is the number of seconds to pause before an actual download attempt.
- """
- import urllib2, shutil
- egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
- url = download_base + egg_name
- saveto = os.path.join(to_dir, egg_name)
- src = dst = None
- if not os.path.exists(saveto): # Avoid repeated downloads
- try:
- from distutils import log
- if delay:
- log.warn("""
----------------------------------------------------------------------------
-This script requires setuptools version %s to run (even to display
-help). I will attempt to download it for you (from
-%s), but
-you may need to enable firewall access for this script first.
-I will start the download in %d seconds.
-
-(Note: if this machine does not have network access, please obtain the file
-
- %s
-
-and place it in this directory before rerunning this script.)
----------------------------------------------------------------------------""",
- version, download_base, delay, url
- ); from time import sleep; sleep(delay)
- log.warn("Downloading %s", url)
- src = urllib2.urlopen(url)
- # Read/write all in one block, so we don't create a corrupt file
- # if the download is interrupted.
- data = _validate_md5(egg_name, src.read())
- dst = open(saveto,"wb"); dst.write(data)
- finally:
- if src: src.close()
- if dst: dst.close()
- return os.path.realpath(saveto)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-def main(argv, version=DEFAULT_VERSION):
- """Install or upgrade setuptools and EasyInstall"""
- try:
- import setuptools
- except ImportError:
- egg = None
- try:
- egg = download_setuptools(version, delay=0)
- sys.path.insert(0,egg)
- from setuptools.command.easy_install import main
- return main(list(argv)+[egg]) # we're done here
- finally:
- if egg and os.path.exists(egg):
- os.unlink(egg)
- else:
- if setuptools.__version__ == '0.0.1':
- print >>sys.stderr, (
- "You have an obsolete version of setuptools installed. Please\n"
- "remove it from your system entirely before rerunning this script."
- )
- sys.exit(2)
-
- req = "setuptools>="+version
- import pkg_resources
- try:
- pkg_resources.require(req)
- except pkg_resources.VersionConflict:
- try:
- from setuptools.command.easy_install import main
- except ImportError:
- from easy_install import main
- main(list(argv)+[download_setuptools(delay=0)])
- sys.exit(0) # try to force an exit
- else:
- if argv:
- from setuptools.command.easy_install import main
- main(argv)
- else:
- print "Setuptools version",version,"or greater has been installed."
- print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
-
-def update_md5(filenames):
- """Update our built-in md5 registry"""
-
- import re
-
- for name in filenames:
- base = os.path.basename(name)
- f = open(name,'rb')
- md5_data[base] = md5(f.read()).hexdigest()
- f.close()
-
- data = [" %r: %r,\n" % it for it in md5_data.items()]
- data.sort()
- repl = "".join(data)
-
- import inspect
- srcfile = inspect.getsourcefile(sys.modules[__name__])
- f = open(srcfile, 'rb'); src = f.read(); f.close()
-
- match = re.search("\nmd5_data = {\n([^}]+)}", src)
- if not match:
- print >>sys.stderr, "Internal error!"
- sys.exit(2)
-
- src = src[:match.start(1)] + repl + src[match.end(1):]
- f = open(srcfile,'w')
- f.write(src)
- f.close()
-
-
-if __name__=='__main__':
- if len(sys.argv)>2 and sys.argv[1]=='--md5update':
- update_md5(sys.argv[2:])
- else:
- main(sys.argv[1:])
-
-
-
-
-
-
From 4f13ff1ab7424d79dd9ed708315c6cc1d1a2c566 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 8 May 2010 22:33:36 +0200
Subject: [PATCH 019/207] Added a distribute_setup to replace ez_setup
---
distribute_setup.py | 481 ++++++++++++++++++++++++++++++++++++++++++++
setup.py | 4 +-
2 files changed, 483 insertions(+), 2 deletions(-)
create mode 100644 distribute_setup.py
diff --git a/distribute_setup.py b/distribute_setup.py
new file mode 100644
index 000000000..4f7bd08c0
--- /dev/null
+++ b/distribute_setup.py
@@ -0,0 +1,481 @@
+#!python
+"""Bootstrap distribute installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+ from distribute_setup import use_setuptools
+ use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import os
+import sys
+import time
+import fnmatch
+import tempfile
+import tarfile
+from distutils import log
+
+try:
+ from site import USER_SITE
+except ImportError:
+ USER_SITE = None
+
+try:
+ import subprocess
+
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ return subprocess.call(args) == 0
+
+except ImportError:
+ # will be used for python 2.3
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ # quoting arguments if windows
+ if sys.platform == 'win32':
+ def quote(arg):
+ if ' ' in arg:
+ return '"%s"' % arg
+ return arg
+ args = [quote(arg) for arg in args]
+ return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
+
+DEFAULT_VERSION = "0.6.12"
+DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
+SETUPTOOLS_FAKED_VERSION = "0.6c11"
+
+SETUPTOOLS_PKG_INFO = """\
+Metadata-Version: 1.0
+Name: setuptools
+Version: %s
+Summary: xxxx
+Home-page: xxx
+Author: xxx
+Author-email: xxx
+License: xxx
+Description: xxx
+""" % SETUPTOOLS_FAKED_VERSION
+
+
+def _install(tarball):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # installing
+ log.warn('Installing Distribute')
+ if not _python_cmd('setup.py', 'install'):
+ log.warn('Something went wrong during the installation.')
+ log.warn('See the error message above.')
+ finally:
+ os.chdir(old_wd)
+
+
+def _build_egg(egg, tarball, to_dir):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # building an egg
+ log.warn('Building a Distribute egg in %s', to_dir)
+ _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
+
+ finally:
+ os.chdir(old_wd)
+ # returning the result
+ log.warn(egg)
+ if not os.path.exists(egg):
+ raise IOError('Could not build the egg.')
+
+
+def _do_download(version, download_base, to_dir, download_delay):
+ egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
+ % (version, sys.version_info[0], sys.version_info[1]))
+ if not os.path.exists(egg):
+ tarball = download_setuptools(version, download_base,
+ to_dir, download_delay)
+ _build_egg(egg, tarball, to_dir)
+ sys.path.insert(0, egg)
+ import setuptools
+ setuptools.bootstrap_install_from = egg
+
+
+def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, download_delay=15, no_fake=True):
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ was_imported = 'pkg_resources' in sys.modules or \
+ 'setuptools' in sys.modules
+ try:
+ try:
+ import pkg_resources
+ if not hasattr(pkg_resources, '_distribute'):
+ if not no_fake:
+ _fake_setuptools()
+ raise ImportError
+ except ImportError:
+ return _do_download(version, download_base, to_dir, download_delay)
+ try:
+ pkg_resources.require("distribute>="+version)
+ return
+ except pkg_resources.VersionConflict:
+ e = sys.exc_info()[1]
+ if was_imported:
+ sys.stderr.write(
+ "The required version of distribute (>=%s) is not available,\n"
+ "and can't be installed while this script is running. Please\n"
+ "install a more recent version first, using\n"
+ "'easy_install -U distribute'."
+ "\n\n(Currently using %r)\n" % (version, e.args[0]))
+ sys.exit(2)
+ else:
+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ except pkg_resources.DistributionNotFound:
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ finally:
+ if not no_fake:
+ _create_fake_setuptools_pkg_info(to_dir)
+
+def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, delay=15):
+ """Download distribute from a specified location and return its filename
+
+ `version` should be a valid distribute version number that is available
+ as an egg for download under the `download_base` URL (which should end
+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
+ `delay` is the number of seconds to pause before an actual download
+ attempt.
+ """
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ try:
+ from urllib.request import urlopen
+ except ImportError:
+ from urllib2 import urlopen
+ tgz_name = "distribute-%s.tar.gz" % version
+ url = download_base + tgz_name
+ saveto = os.path.join(to_dir, tgz_name)
+ src = dst = None
+ if not os.path.exists(saveto): # Avoid repeated downloads
+ try:
+ log.warn("Downloading %s", url)
+ src = urlopen(url)
+ # Read/write all in one block, so we don't create a corrupt file
+ # if the download is interrupted.
+ data = src.read()
+ dst = open(saveto, "wb")
+ dst.write(data)
+ finally:
+ if src:
+ src.close()
+ if dst:
+ dst.close()
+ return os.path.realpath(saveto)
+
+def _no_sandbox(function):
+ def __no_sandbox(*args, **kw):
+ try:
+ from setuptools.sandbox import DirectorySandbox
+ if not hasattr(DirectorySandbox, '_old'):
+ def violation(*args):
+ pass
+ DirectorySandbox._old = DirectorySandbox._violation
+ DirectorySandbox._violation = violation
+ patched = True
+ else:
+ patched = False
+ except ImportError:
+ patched = False
+
+ try:
+ return function(*args, **kw)
+ finally:
+ if patched:
+ DirectorySandbox._violation = DirectorySandbox._old
+ del DirectorySandbox._old
+
+ return __no_sandbox
+
+@_no_sandbox
+def _patch_file(path, content):
+ """Will backup the file then patch it"""
+ existing_content = open(path).read()
+ if existing_content == content:
+ # already patched
+ log.warn('Already patched.')
+ return False
+ log.warn('Patching...')
+ _rename_path(path)
+ f = open(path, 'w')
+ try:
+ f.write(content)
+ finally:
+ f.close()
+ return True
+
+
+def _same_content(path, content):
+ return open(path).read() == content
+
+def _rename_path(path):
+ new_name = path + '.OLD.%s' % time.time()
+ log.warn('Renaming %s into %s', path, new_name)
+ os.rename(path, new_name)
+ return new_name
+
+@_no_sandbox
+def _remove_flat_installation(placeholder):
+ if not os.path.isdir(placeholder):
+ log.warn('Unkown installation at %s', placeholder)
+ return False
+ found = False
+ for file in os.listdir(placeholder):
+ if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
+ found = True
+ break
+ if not found:
+ log.warn('Could not locate setuptools*.egg-info')
+ return
+
+ log.warn('Removing elements out of the way...')
+ pkg_info = os.path.join(placeholder, file)
+ if os.path.isdir(pkg_info):
+ patched = _patch_egg_dir(pkg_info)
+ else:
+ patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
+
+ if not patched:
+ log.warn('%s already patched.', pkg_info)
+ return False
+ # now let's move the files out of the way
+ for element in ('setuptools', 'pkg_resources.py', 'site.py'):
+ element = os.path.join(placeholder, element)
+ if os.path.exists(element):
+ _rename_path(element)
+ else:
+ log.warn('Could not find the %s element of the '
+ 'Setuptools distribution', element)
+ return True
+
+
+def _after_install(dist):
+ log.warn('After install bootstrap.')
+ placeholder = dist.get_command_obj('install').install_purelib
+ _create_fake_setuptools_pkg_info(placeholder)
+
+@_no_sandbox
+def _create_fake_setuptools_pkg_info(placeholder):
+ if not placeholder or not os.path.exists(placeholder):
+ log.warn('Could not find the install location')
+ return
+ pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
+ setuptools_file = 'setuptools-%s-py%s.egg-info' % \
+ (SETUPTOOLS_FAKED_VERSION, pyver)
+ pkg_info = os.path.join(placeholder, setuptools_file)
+ if os.path.exists(pkg_info):
+ log.warn('%s already exists', pkg_info)
+ return
+
+ log.warn('Creating %s', pkg_info)
+ f = open(pkg_info, 'w')
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+
+ pth_file = os.path.join(placeholder, 'setuptools.pth')
+ log.warn('Creating %s', pth_file)
+ f = open(pth_file, 'w')
+ try:
+ f.write(os.path.join(os.curdir, setuptools_file))
+ finally:
+ f.close()
+
+@_no_sandbox
+def _patch_egg_dir(path):
+ # let's check if it's already patched
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ if os.path.exists(pkg_info):
+ if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
+ log.warn('%s already patched.', pkg_info)
+ return False
+ _rename_path(path)
+ os.mkdir(path)
+ os.mkdir(os.path.join(path, 'EGG-INFO'))
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ f = open(pkg_info, 'w')
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+ return True
+
+
+def _before_install():
+ log.warn('Before install bootstrap.')
+ _fake_setuptools()
+
+
+def _under_prefix(location):
+ if 'install' not in sys.argv:
+ return True
+ args = sys.argv[sys.argv.index('install')+1:]
+ for index, arg in enumerate(args):
+ for option in ('--root', '--prefix'):
+ if arg.startswith('%s=' % option):
+ top_dir = arg.split('root=')[-1]
+ return location.startswith(top_dir)
+ elif arg == option:
+ if len(args) > index:
+ top_dir = args[index+1]
+ return location.startswith(top_dir)
+ elif option == '--user' and USER_SITE is not None:
+ return location.startswith(USER_SITE)
+ return True
+
+
+def _fake_setuptools():
+ log.warn('Scanning installed packages')
+ try:
+ import pkg_resources
+ except ImportError:
+ # we're cool
+ log.warn('Setuptools or Distribute does not seem to be installed.')
+ return
+ ws = pkg_resources.working_set
+ try:
+ setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
+ replacement=False))
+ except TypeError:
+ # old distribute API
+ setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
+
+ if setuptools_dist is None:
+ log.warn('No setuptools distribution found')
+ return
+ # detecting if it was already faked
+ setuptools_location = setuptools_dist.location
+ log.warn('Setuptools installation detected at %s', setuptools_location)
+
+ # if --root or --preix was provided, and if
+ # setuptools is not located in them, we don't patch it
+ if not _under_prefix(setuptools_location):
+ log.warn('Not patching, --root or --prefix is installing Distribute'
+ ' in another location')
+ return
+
+ # let's see if its an egg
+ if not setuptools_location.endswith('.egg'):
+ log.warn('Non-egg installation')
+ res = _remove_flat_installation(setuptools_location)
+ if not res:
+ return
+ else:
+ log.warn('Egg installation')
+ pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
+ if (os.path.exists(pkg_info) and
+ _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
+ log.warn('Already patched.')
+ return
+ log.warn('Patching...')
+ # let's create a fake egg replacing setuptools one
+ res = _patch_egg_dir(setuptools_location)
+ if not res:
+ return
+ log.warn('Patched done.')
+ _relaunch()
+
+
+def _relaunch():
+ log.warn('Relaunching...')
+ # we have to relaunch the process
+ args = [sys.executable] + sys.argv
+ sys.exit(subprocess.call(args))
+
+
+def _extractall(self, path=".", members=None):
+ """Extract all members from the archive to the current working
+ directory and set owner, modification time and permissions on
+ directories afterwards. `path' specifies a different directory
+ to extract to. `members' is optional and must be a subset of the
+ list returned by getmembers().
+ """
+ import copy
+ import operator
+ from tarfile import ExtractError
+ directories = []
+
+ if members is None:
+ members = self
+
+ for tarinfo in members:
+ if tarinfo.isdir():
+ # Extract directories with a safe mode.
+ directories.append(tarinfo)
+ tarinfo = copy.copy(tarinfo)
+ tarinfo.mode = 448 # decimal for oct 0700
+ self.extract(tarinfo, path)
+
+ # Reverse sort directories.
+ if sys.version_info < (2, 4):
+ def sorter(dir1, dir2):
+ return cmp(dir1.name, dir2.name)
+ directories.sort(sorter)
+ directories.reverse()
+ else:
+ directories.sort(key=operator.attrgetter('name'), reverse=True)
+
+ # Set correct owner, mtime and filemode on directories.
+ for tarinfo in directories:
+ dirpath = os.path.join(path, tarinfo.name)
+ try:
+ self.chown(tarinfo, dirpath)
+ self.utime(tarinfo, dirpath)
+ self.chmod(tarinfo, dirpath)
+ except ExtractError:
+ e = sys.exc_info()[1]
+ if self.errorlevel > 1:
+ raise
+ else:
+ self._dbg(1, "tarfile: %s" % e)
+
+
+def main(argv, version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+ tarball = download_setuptools()
+ _install(tarball)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/setup.py b/setup.py
index 2494851f5..8d06f5691 100644
--- a/setup.py
+++ b/setup.py
@@ -2,8 +2,8 @@
try:
from setuptools import setup, find_packages
except ImportError:
- import ez_setup
- ez_setup.use_setuptools()
+ import distribute_setup
+ distribute_setup.use_setuptools()
from setuptools import setup, find_packages
import os
From d6e5dfacd48c2d8bdc92a30b9bbeb3c015a03f12 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:13:57 +0200
Subject: [PATCH 020/207] Add a constant for class types, which lacks
types.ClassType in Py3k.
---
sphinx/application.py | 3 ---
sphinx/environment.py | 4 ++--
sphinx/ext/autodoc.py | 13 ++++---------
sphinx/util/nodes.py | 4 ++--
sphinx/util/pycompat.py | 12 ++++++++++++
5 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/sphinx/application.py b/sphinx/application.py
index 97778d3fb..b3d2aebc4 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -37,9 +37,6 @@ from sphinx.util.osutil import ENOENT
from sphinx.util.console import bold
-# Directive is either new-style or old-style
-clstypes = (type, types.ClassType)
-
# List of all known core events. Maps name to arguments description.
events = {
'builder-inited': '',
diff --git a/sphinx/environment.py b/sphinx/environment.py
index fa8460cb3..21994a746 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -39,7 +39,7 @@ from sphinx.util import url_re, get_matching_docs, docname_join, \
from sphinx.util.nodes import clean_astext, make_refnode
from sphinx.util.osutil import movefile, SEP, ustrftime
from sphinx.util.matching import compile_matchers
-from sphinx.util.pycompat import all
+from sphinx.util.pycompat import all, class_types
from sphinx.errors import SphinxError, ExtensionError
from sphinx.locale import _
@@ -251,7 +251,7 @@ class BuildEnvironment:
if key.startswith('_') or \
isinstance(val, types.ModuleType) or \
isinstance(val, types.FunctionType) or \
- isinstance(val, (type, types.ClassType)):
+ isinstance(val, class_types):
del self.config[key]
try:
pickle.dump(self, picklefile, pickle.HIGHEST_PROTOCOL)
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index adf08bcde..8a827a91f 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -14,7 +14,7 @@
import re
import sys
import inspect
-from types import FunctionType, BuiltinFunctionType, MethodType, ClassType
+from types import FunctionType, BuiltinFunctionType, MethodType
from docutils import nodes
from docutils.utils import assemble_option_dict
@@ -27,15 +27,10 @@ from sphinx.application import ExtensionError
from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.compat import Directive
from sphinx.util.inspect import isdescriptor, safe_getmembers, safe_getattr
+from sphinx.util.pycompat import base_exception, class_types
from sphinx.util.docstrings import prepare_docstring
-try:
- base_exception = BaseException
-except NameError:
- base_exception = Exception
-
-
#: extended signature RE: with explicit module name separated by ::
py_ext_sig_re = re.compile(
r'''^ ([\w.]+::)? # explicit module name
@@ -866,7 +861,7 @@ class ClassDocumenter(ModuleLevelDocumenter):
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
- return isinstance(member, (type, ClassType))
+ return isinstance(member, class_types)
def import_object(self):
ret = ModuleLevelDocumenter.import_object(self)
@@ -972,7 +967,7 @@ class ExceptionDocumenter(ClassDocumenter):
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
- return isinstance(member, (type, ClassType)) and \
+ return isinstance(member, class_types) and \
issubclass(member, base_exception)
diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py
index 97b585696..aab8f0142 100644
--- a/sphinx/util/nodes.py
+++ b/sphinx/util/nodes.py
@@ -10,11 +10,11 @@
"""
import re
-import types
from docutils import nodes
from sphinx import addnodes
+from sphinx.util.pycompat import class_types
# \x00 means the "<" was backslash-escaped
@@ -115,7 +115,7 @@ def _new_traverse(self, condition=None,
if include_self and descend and not siblings and not ascend:
if condition is None:
return self._all_traverse([])
- elif isinstance(condition, (types.ClassType, type)):
+ elif isinstance(condition, class_types):
return self._fast_traverse(condition, [])
return self._old_traverse(condition, include_self,
descend, siblings, ascend)
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index bdd9507df..7bf768fac 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -13,6 +13,18 @@ import sys
import codecs
import encodings
+try:
+ from types import ClassType
+ class_types = (type, ClassType)
+except ImportError:
+ # Python 3
+ class_types = (type,)
+
+try:
+ base_exception = BaseException
+except NameError:
+ base_exception = Exception
+
try:
any = any
From 28349ed8569665196b4b2363ac23e13a22006f72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 8 May 2010 23:23:56 +0200
Subject: [PATCH 021/207] Use 'U' if file is not present (we run under 3.x)
---
tests/path.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tests/path.py b/tests/path.py
index 20deb0489..7b89c0cd3 100644
--- a/tests/path.py
+++ b/tests/path.py
@@ -64,8 +64,13 @@ except NameError:
# Universal newline support
_textmode = 'r'
-if hasattr(file, 'newlines'):
+try:
+ file
+except NameError:
_textmode = 'U'
+else:
+ if hasattr(file, 'newlines'):
+ _textmode = 'U'
class TreeWalkWarning(Warning):
From 84cba9c39c0b36c6837d8cc7ce19bbad05be7ff1 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:18:57 +0200
Subject: [PATCH 022/207] Fix wrong qualified name.
---
sphinx/quickstart.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index fe2b43a33..a63907c7a 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -837,12 +837,12 @@ directly.'''
if d['ext_intersphinx']:
conf_text += INTERSPHINX_CONFIG
- f = codecs.open(path.join(srcdir, 'conf.py'), 'w', encoding='utf-8')
+ f = open(path.join(srcdir, 'conf.py'), 'w', encoding='utf-8')
f.write(conf_text)
f.close()
masterfile = path.join(srcdir, d['master'] + d['suffix'])
- f = codecs.open(masterfile, 'w', encoding='utf-8')
+ f = open(masterfile, 'w', encoding='utf-8')
f.write(MASTER_FILE % d)
f.close()
@@ -850,14 +850,14 @@ directly.'''
d['rsrcdir'] = d['sep'] and 'source' or '.'
d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build'
# use binary mode, to avoid writing \r\n on Windows
- f = codecs.open(path.join(d['path'], 'Makefile'), 'wb', encoding='utf-8')
+ f = open(path.join(d['path'], 'Makefile'), 'wb', encoding='utf-8')
f.write(MAKEFILE % d)
f.close()
if d['batchfile']:
d['rsrcdir'] = d['sep'] and 'source' or '.'
d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build'
- f = codecs.open(path.join(d['path'], 'make.bat'), 'w', encoding='utf-8')
+ f = open(path.join(d['path'], 'make.bat'), 'w', encoding='utf-8')
f.write(BATCHFILE % d)
f.close()
From 420adbce2f75071b556e0d206b9f1887309c44c4 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:19:17 +0200
Subject: [PATCH 023/207] Make it easier for the test suite to override
raw_input for test_quickstart.
---
sphinx/quickstart.py | 5 ++++-
tests/test_quickstart.py | 8 ++++----
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index a63907c7a..892bd641d 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -21,6 +21,9 @@ from sphinx.util.console import purple, bold, red, turquoise, \
nocolor, color_terminal
from sphinx.util import texescape
+# function to get input from terminal -- overridden by the test suite
+term_input = raw_input
+
PROMPT_PREFIX = '> '
@@ -657,7 +660,7 @@ def do_prompt(d, key, text, default=None, validator=nonempty):
prompt = purple(PROMPT_PREFIX + '%s [%s]: ' % (text, default))
else:
prompt = purple(PROMPT_PREFIX + text + ': ')
- x = raw_input(prompt)
+ x = term_input(prompt)
if default and not x:
x = default
if not isinstance(x, unicode):
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index 8acff5884..d0403d3b2 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -37,7 +37,7 @@ def mock_raw_input(answers, needanswer=False):
return raw_input
def teardown_module():
- qs.raw_input = __builtin__.raw_input
+ qs.term_input = raw_input
qs.TERM_ENCODING = getattr(sys.stdin, 'encoding', None)
coloron()
@@ -51,7 +51,7 @@ def test_do_prompt():
'Q5': 'no',
'Q6': 'foo',
}
- qs.raw_input = mock_raw_input(answers)
+ qs.term_input = mock_raw_input(answers)
try:
qs.do_prompt(d, 'k1', 'Q1')
except AssertionError:
@@ -79,7 +79,7 @@ def test_quickstart_defaults(tempdir):
'Author name': 'Georg Brandl',
'Project version': '0.1',
}
- qs.raw_input = mock_raw_input(answers)
+ qs.term_input = mock_raw_input(answers)
qs.inner_main([])
conffile = tempdir / 'conf.py'
@@ -136,7 +136,7 @@ def test_quickstart_all_answers(tempdir):
'Create Windows command file': 'no',
'Do you want to use the epub builder': 'yes',
}
- qs.raw_input = mock_raw_input(answers, needanswer=True)
+ qs.term_input = mock_raw_input(answers, needanswer=True)
qs.TERM_ENCODING = 'utf-8'
qs.inner_main([])
From bcbce5955fe77854bf2a6781fab99fa2e23ab311 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 9 May 2010 00:38:16 +0200
Subject: [PATCH 024/207] Changed tests/run.py so that it's possible to run the
testsuite on python3 more easiely
---
Makefile | 2 +-
tests/run.py | 15 ++++++++++++---
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/Makefile b/Makefile
index 682f03666..593c7ad49 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-PYTHON ?= python -3
+PYTHON ?= python3
export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx
diff --git a/tests/run.py b/tests/run.py
index 0cb41442c..5fd30d62b 100755
--- a/tests/run.py
+++ b/tests/run.py
@@ -11,7 +11,16 @@
"""
import sys
-from os import path
+from os import path, chdir
+
+if sys.version_info >= (3,):
+ print('Copying and converting sources to build/lib/test...')
+ from distutils.util import copydir_run_2to3
+ testroot = path.dirname(__file__) or '.'
+ newroot = path.join(testroot, path.pardir, 'build', 'lib', 'test')
+ copydir_run_2to3(testroot, newroot)
+ # switch to the converted dir so nose tests the right tests
+ chdir(newroot)
# always test the sphinx package from this directory
sys.path.insert(0, path.join(path.dirname(__file__), path.pardir))
@@ -19,8 +28,8 @@ sys.path.insert(0, path.join(path.dirname(__file__), path.pardir))
try:
import nose
except ImportError:
- print "The nose package is needed to run the Sphinx test suite."
+ print("The nose package is needed to run the Sphinx test suite.")
sys.exit(1)
-print "Running Sphinx test suite..."
+print("Running Sphinx test suite...")
nose.main()
From 5243c56d7abe5a4e2a2c5499f29fae8318c665f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 9 May 2010 00:54:14 +0200
Subject: [PATCH 025/207] Fixed DefExpr.__str__
---
sphinx/domains/cpp.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 90c3533ef..8df89459b 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -110,7 +110,7 @@ class DefinitionError(Exception):
return self.description
def __str__(self):
- return unicode(self.encode('utf-8'))
+ return unicode(self).encode('utf-8')
class DefExpr(object):
From e20b61b06e5b029d1b12f8fff392ebf4ca851574 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 9 May 2010 14:57:18 +0200
Subject: [PATCH 026/207] Rename __unicode__ to __str__
---
custom_fixers/__init__.py | 0
custom_fixers/fix_alt_unicode.py | 12 ++++++++++++
setup.py | 1 +
3 files changed, 13 insertions(+)
create mode 100644 custom_fixers/__init__.py
create mode 100644 custom_fixers/fix_alt_unicode.py
diff --git a/custom_fixers/__init__.py b/custom_fixers/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/custom_fixers/fix_alt_unicode.py b/custom_fixers/fix_alt_unicode.py
new file mode 100644
index 000000000..55175e90f
--- /dev/null
+++ b/custom_fixers/fix_alt_unicode.py
@@ -0,0 +1,12 @@
+from lib2to3.fixer_base import BaseFix
+from lib2to3.fixer_util import Name
+
+class FixAltUnicode(BaseFix):
+ PATTERN = """
+ func=funcdef< 'def' name='__unicode__'
+ parameters< '(' NAME ')' > any+ >
+ """
+
+ def transform(self, node, results):
+ name = results['name']
+ name.replace(Name('__str__', prefix=name.prefix))
diff --git a/setup.py b/setup.py
index 8d06f5691..fe4066b80 100644
--- a/setup.py
+++ b/setup.py
@@ -199,4 +199,5 @@ setup(
install_requires=requires,
cmdclass=cmdclass,
use_2to3=True,
+ use_2to3_fixers=['custom_fixers'],
)
From 22a01fab9b7eb02266816c8001ba2db4ecb1573d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 10 May 2010 00:59:50 +0200
Subject: [PATCH 027/207] Added information about the i did during the weekend
to the changes file
---
CHANGES.DasIch | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/CHANGES.DasIch b/CHANGES.DasIch
index 856a5d87b..711f9a534 100644
--- a/CHANGES.DasIch
+++ b/CHANGES.DasIch
@@ -5,6 +5,17 @@ This file contains changes made by Daniel Neuhäuser, during the Google Summer
of Code 2010, to port Sphinx to Python 3.x. Changes are ordered descending by
date.
+May 10: Fixed a couple of tests and made several small changes.
+
+May 9: - Removed ez_setup.py which does not work with Python 3.x. and replaced
+ it with distribute_setup.py
+ - Use distribute (at least on 3.x) in order to run 2to3 automatically.
+ - Reverted some of the changes made in revision bac40c7c924c which
+ caused errors.
+ - Modified tests/run.py to test against the build created by
+ setup.py build in order to run the test suite with 3.x
+ - Several small changes to fix 3.x compatibilty.
+
May 1: - Removed deprecated tuple parameter unpacking.
- Removed a pre-2.3 workaround for booleans because this creates a
deprecation warning for 3.x, in which you can't assign values to
From 36338fe9eaf559cbf7ede349c1bd6b5a650e3e53 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 16 May 2010 14:00:50 +0200
Subject: [PATCH 028/207] Added a clean-backupfiles target to the makefile
---
Makefile | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index 593c7ad49..aa2795147 100644
--- a/Makefile
+++ b/Makefile
@@ -12,17 +12,20 @@ check:
-i doc/_build -i ez_setup.py -i tests/path.py -i tests/coverage.py \
-i env -i .tox .
-clean: clean-pyc clean-patchfiles
+clean: clean-pyc clean-patchfiles clean-backupfiles
clean-pyc:
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
- find . -name '*~' -exec rm -f {} +
clean-patchfiles:
find . -name '*.orig' -exec rm -f {} +
find . -name '*.rej' -exec rm -f {} +
+clean-backupfiles:
+ find . -name '*~' -exec rm -f {} +
+ find . -name '*.bak' -exec rm -f {} +
+
pylint:
@pylint --rcfile utils/pylintrc sphinx
From df444e822cc68e1e4717089c9fbb66bb110113ae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 16 May 2010 16:20:44 +0200
Subject: [PATCH 029/207] Add setup_distribute.py to MANIFEST.in and remove
ez_setup.py
---
MANIFEST.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index 25cbc334f..5e3104a82 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -7,7 +7,7 @@ include TODO
include babel.cfg
include Makefile
-include ez_setup.py
+include setup_distribute.py
include sphinx-autogen.py
include sphinx-build.py
include sphinx-quickstart.py
From 471da5aa21c6be081a68d7fc255456d81c5ee02d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 16 May 2010 16:54:15 +0200
Subject: [PATCH 030/207] Added a build target to the Makefile which we need
for python3 tests
---
Makefile | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index aa2795147..72e047729 100644
--- a/Makefile
+++ b/Makefile
@@ -32,8 +32,11 @@ pylint:
reindent:
@$(PYTHON) utils/reindent.py -r -B .
-test:
+test: build
@cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST)
-covertest:
+covertest: build
@cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage --cover-package=sphinx $(TEST)
+
+build:
+ @$(PYTHON) setup.py build
From d8425102e27723839fc2c5078ab96c7752de1207 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 16 May 2010 17:50:13 +0200
Subject: [PATCH 031/207] Switched check_sources.py from getopt to optparse
---
utils/check_sources.py | 34 ++++++++++++++++------------------
1 file changed, 16 insertions(+), 18 deletions(-)
diff --git a/utils/check_sources.py b/utils/check_sources.py
index 0571ab1e8..1b30f2dc5 100755
--- a/utils/check_sources.py
+++ b/utils/check_sources.py
@@ -12,8 +12,8 @@
"""
import sys, os, re
-import getopt
import cStringIO
+from optparse import OptionParser
from os.path import join, splitext, abspath
@@ -165,34 +165,32 @@ def check_xhtml(fn, lines):
def main(argv):
- try:
- gopts, args = getopt.getopt(argv[1:], "vi:")
- except getopt.GetoptError:
- print "Usage: %s [-v] [-i ignorepath]* [path]" % argv[0]
- return 2
- opts = {}
- for opt, val in gopts:
- if opt == '-i':
- val = abspath(val)
- opts.setdefault(opt, []).append(val)
+ parser = OptionParser(usage='Usage: %prog [-v] [-i ignorepath]* [path]')
+ parser.add_option('-v', '--verbose', dest='verbose', default=False,
+ action='store_true')
+ parser.add_option('-i', '--ignore-path', dest='ignored_paths',
+ default=[], action='append')
+ options, args = parser.parse_args(argv[1:])
if len(args) == 0:
path = '.'
elif len(args) == 1:
path = args[0]
else:
- print "Usage: %s [-v] [-i ignorepath]* [path]" % argv[0]
- return 2
+ print args
+ parser.error('No more then one path supported')
- verbose = '-v' in opts
+ verbose = options.verbose
+ ignored_paths = set(abspath(p) for p in options.ignored_paths)
num = 0
out = cStringIO.StringIO()
for root, dirs, files in os.walk(path):
- if '.svn' in dirs:
- dirs.remove('.svn')
- if '-i' in opts and abspath(root) in opts['-i']:
+ for vcs_dir in ['.svn', '.hg', '.git']:
+ if vcs_dir in dirs:
+ dirs.remove(vcs_dir)
+ if abspath(root) in ignored_paths:
del dirs[:]
continue
in_check_pkg = root.startswith('./sphinx')
@@ -201,7 +199,7 @@ def main(argv):
fn = join(root, fn)
if fn[:2] == './': fn = fn[2:]
- if '-i' in opts and abspath(fn) in opts['-i']:
+ if abspath(fn) in ignored_paths:
continue
ext = splitext(fn)[1]
From ad29ab1b860d13fcd0dacd2395f8f3dea5c2370a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 16 May 2010 19:45:23 +0200
Subject: [PATCH 032/207] Fixed file opening
---
utils/check_sources.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/utils/check_sources.py b/utils/check_sources.py
index 1b30f2dc5..8eeadbf4b 100755
--- a/utils/check_sources.py
+++ b/utils/check_sources.py
@@ -212,7 +212,10 @@ def main(argv):
try:
f = open(fn, 'r')
- lines = list(f)
+ try:
+ lines = list(f)
+ finally:
+ f.close()
except (IOError, OSError), err:
print "%s: cannot open: %s" % (fn, err)
num += 1
From 592a978c25d94579142c50c4be240d5856124891 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 16 May 2010 22:19:38 +0200
Subject: [PATCH 033/207] Scripts in utils are now automatically converted.
They may not work at the moment though
---
Makefile | 14 ++++++++++----
utils/convert.py | 43 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+), 4 deletions(-)
create mode 100644 utils/convert.py
diff --git a/Makefile b/Makefile
index 72e047729..53424b5d6 100644
--- a/Makefile
+++ b/Makefile
@@ -2,17 +2,17 @@ PYTHON ?= python3
export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx
-.PHONY: all check clean clean-pyc clean-patchfiles pylint reindent test
+.PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint reindent test
all: clean-pyc check test
-check:
+check: convert-utils
@$(PYTHON) utils/check_sources.py -i build -i dist -i sphinx/style/jquery.js \
-i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py -i .ropeproject \
-i doc/_build -i ez_setup.py -i tests/path.py -i tests/coverage.py \
-i env -i .tox .
-clean: clean-pyc clean-patchfiles clean-backupfiles
+clean: clean-pyc clean-patchfiles clean-backupfiles clean-generated
clean-pyc:
find . -name '*.pyc' -exec rm -f {} +
@@ -26,10 +26,13 @@ clean-backupfiles:
find . -name '*~' -exec rm -f {} +
find . -name '*.bak' -exec rm -f {} +
+clean-generated:
+ rm utils/*3.py*
+
pylint:
@pylint --rcfile utils/pylintrc sphinx
-reindent:
+reindent: convert-utils
@$(PYTHON) utils/reindent.py -r -B .
test: build
@@ -40,3 +43,6 @@ covertest: build
build:
@$(PYTHON) setup.py build
+
+convert-utils:
+ @python3 utils/convert.py -i utils/convert.py utils/
diff --git a/utils/convert.py b/utils/convert.py
new file mode 100644
index 000000000..65b4d3bdf
--- /dev/null
+++ b/utils/convert.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# coding: utf-8
+"""
+ Converts files with 2to3
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Creates a Python 3 version of each file.
+
+ The Python3 version of a file foo.py will be called foo3.py.
+
+ :copyright: Copyright 2010 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+import os
+import sys
+from glob import iglob
+from optparse import OptionParser
+from shutil import copy
+from distutils.util import run_2to3
+
+def main(argv):
+ parser = OptionParser(usage='%prog [path]')
+ parser.add_option('-i', '--ignorepath', dest='ignored_paths',
+ action='append', default=[])
+ options, args = parser.parse_args(argv)
+
+ ignored_paths = set(options.ignored_paths)
+
+ path = os.path.abspath(args[0]) if args else os.getcwd()
+ convertables = []
+ for filename in iglob(os.path.join(path, '*.py')):
+ if filename in ignored_paths:
+ continue
+ basename, ext = os.path.splitext(filename)
+ if basename.endswith('3'):
+ continue
+ filename3 = basename + '3' + ext
+ copy(filename, filename3)
+ convertables.append(filename3)
+ run_2to3(convertables)
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
From 315ad84fce2beab11457d23b53b48b41fb23332b Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:28:34 +0200
Subject: [PATCH 034/207] Ignore failures in removing converted utils.
---
Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 53424b5d6..4c0cdef87 100644
--- a/Makefile
+++ b/Makefile
@@ -27,7 +27,7 @@ clean-backupfiles:
find . -name '*.bak' -exec rm -f {} +
clean-generated:
- rm utils/*3.py*
+ -rm utils/*3.py*
pylint:
@pylint --rcfile utils/pylintrc sphinx
From e1b7381a94592d04717290f842f49a7aa08e33c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 16 May 2010 22:24:20 +0200
Subject: [PATCH 035/207] convert.py now properly ignores paths
---
utils/convert.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/utils/convert.py b/utils/convert.py
index 65b4d3bdf..f025c49a0 100644
--- a/utils/convert.py
+++ b/utils/convert.py
@@ -24,7 +24,7 @@ def main(argv):
action='append', default=[])
options, args = parser.parse_args(argv)
- ignored_paths = set(options.ignored_paths)
+ ignored_paths = {os.path.abspath(p) for p in options.ignored_paths}
path = os.path.abspath(args[0]) if args else os.getcwd()
convertables = []
From 87d35afcfccd9c42faaf32c424e8de19f0e7b627 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 16 May 2010 22:27:05 +0200
Subject: [PATCH 036/207] Ignore generated files
---
.hgignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.hgignore b/.hgignore
index 70ea36a52..40c00aac7 100644
--- a/.hgignore
+++ b/.hgignore
@@ -15,3 +15,4 @@
^env/
\.DS_Store$
~$
+^utils/.*3\.py$
From e8d98966dec388bce4e9076de0ea03454053e678 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 16 May 2010 23:51:25 +0200
Subject: [PATCH 037/207] Automatically use converted scripts in the makefile
---
Makefile | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/Makefile b/Makefile
index 4c0cdef87..2672aad4b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,16 +1,22 @@
-PYTHON ?= python3
+PYTHON ?= python
export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx
.PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint reindent test
+DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js -i sphinx/pycode/pgen2 \
+ -i sphinx/util/smartypants.py -i .ropeproject -i doc/_build -i tests/path.py \
+ -i tests/coverage.py -i env -i utils/convert.py -i utils/reindent3.py \
+ -i utils/check_sources3.py
+
all: clean-pyc check test
check: convert-utils
- @$(PYTHON) utils/check_sources.py -i build -i dist -i sphinx/style/jquery.js \
- -i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py -i .ropeproject \
- -i doc/_build -i ez_setup.py -i tests/path.py -i tests/coverage.py \
- -i env -i .tox .
+ifeq ($(PYTHON), python3)
+ @$(PYTHON) utils/check_sources3.py $(DONT_CHECK) .
+else
+ @$(PYTHON) utils/check_sources.py $(DONT_CHECK) .
+endif
clean: clean-pyc clean-patchfiles clean-backupfiles clean-generated
@@ -33,7 +39,11 @@ pylint:
@pylint --rcfile utils/pylintrc sphinx
reindent: convert-utils
+ifeq ($(PYTHON), python3)
+ @$(PYTHON) utils/reindent3.py -r -B .
+else
@$(PYTHON) utils/reindent.py -r -B .
+endif
test: build
@cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST)
From 46e5c123d5125524d2563a641189820601004dc6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 16 May 2010 23:56:44 +0200
Subject: [PATCH 038/207] Keep under 80 chars per line in the Makefile
---
Makefile | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/Makefile b/Makefile
index 2672aad4b..cf4f292f7 100644
--- a/Makefile
+++ b/Makefile
@@ -2,12 +2,14 @@ PYTHON ?= python
export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx
-.PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint reindent test
+.PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint \
+ reindent test
-DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js -i sphinx/pycode/pgen2 \
- -i sphinx/util/smartypants.py -i .ropeproject -i doc/_build -i tests/path.py \
- -i tests/coverage.py -i env -i utils/convert.py -i utils/reindent3.py \
- -i utils/check_sources3.py
+DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \
+ -i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \
+ -i .ropeproject -i doc/_build -i tests/path.py \
+ -i tests/coverage.py -i env -i utils/convert.py \
+ -i utils/reindent3.py -i utils/check_sources3.py -i .tox
all: clean-pyc check test
@@ -49,7 +51,8 @@ test: build
@cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST)
covertest: build
- @cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage --cover-package=sphinx $(TEST)
+ @cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage \
+ --cover-package=sphinx $(TEST)
build:
@$(PYTHON) setup.py build
From acd1eaf9d7f6316e3f53e8d96de1d54f624c00cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 17 May 2010 00:44:44 +0200
Subject: [PATCH 039/207] check_sources.py is now ported to 3.x
---
Makefile | 2 +-
utils/check_sources.py | 72 +++++++++++++++++++++++-------------------
2 files changed, 40 insertions(+), 34 deletions(-)
diff --git a/Makefile b/Makefile
index cf4f292f7..209302302 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-PYTHON ?= python
+PYTHON ?= python3
export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx
diff --git a/utils/check_sources.py b/utils/check_sources.py
index 8eeadbf4b..c412742b7 100755
--- a/utils/check_sources.py
+++ b/utils/check_sources.py
@@ -16,6 +16,12 @@ import cStringIO
from optparse import OptionParser
from os.path import join, splitext, abspath
+if sys.version_info >= (3, 0):
+ def b(s):
+ return s.encode('utf-8')
+else:
+ b = str
+
checkers = {}
@@ -30,26 +36,26 @@ def checker(*suffixes, **kwds):
name_mail_re = r'[\w ]+(<.*?>)?'
-copyright_re = re.compile(r'^ :copyright: Copyright 200\d(-20\d\d)? '
- r'by %s(, %s)*[,.]$' %
- (name_mail_re, name_mail_re))
-license_re = re.compile(r" :license: (.*?).\n")
-copyright_2_re = re.compile(r'^ %s(, %s)*[,.]$' %
- (name_mail_re, name_mail_re))
-coding_re = re.compile(r'coding[:=]\s*([-\w.]+)')
-not_ix_re = re.compile(r'\bnot\s+\S+?\s+i[sn]\s\S+')
-is_const_re = re.compile(r'if.*?==\s+(None|False|True)\b')
+copyright_re = re.compile(b(r'^ :copyright: Copyright 200\d(-20\d\d)? '
+ r'by %s(, %s)*[,.]$' %
+ (name_mail_re, name_mail_re)))
+license_re = re.compile(b(r" :license: (.*?).\n"))
+copyright_2_re = re.compile(b(r'^ %s(, %s)*[,.]$' %
+ (name_mail_re, name_mail_re)))
+coding_re = re.compile(b(r'coding[:=]\s*([-\w.]+)'))
+not_ix_re = re.compile(b(r'\bnot\s+\S+?\s+i[sn]\s\S+'))
+is_const_re = re.compile(b(r'if.*?==\s+(None|False|True)\b'))
-misspellings = ["developement", "adress", "verificate", # ALLOW-MISSPELLING
- "informations"] # ALLOW-MISSPELLING
+misspellings = [b("developement"), b("adress"), # ALLOW-MISSPELLING
+ b("verificate"), b("informations")] # ALLOW-MISSPELLING
-
-@checker('.py')
-def check_syntax(fn, lines):
- try:
- compile(''.join(lines), fn, "exec")
- except SyntaxError, err:
- yield 0, "not compilable: %s" % err
+if sys.version_info < (3, 0):
+ @checker('.py')
+ def check_syntax(fn, lines):
+ try:
+ compile(b('').join(lines), fn, "exec")
+ except SyntaxError, err:
+ yield 0, "not compilable: %s" % err
@checker('.py')
@@ -61,8 +67,8 @@ def check_style_and_encoding(fn, lines):
if lno < 2:
co = coding_re.search(line)
if co:
- encoding = co.group(1)
- if line.strip().startswith('#'):
+ encoding = co.group(1).decode('ascii')
+ if line.strip().startswith(b('#')):
continue
#m = not_ix_re.search(line)
#if m:
@@ -82,7 +88,7 @@ def check_style_and_encoding(fn, lines):
def check_fileheader(fn, lines):
# line number correction
c = 1
- if lines[0:1] == ['#!/usr/bin/env python\n']:
+ if lines[0:1] == [b('#!/usr/bin/env python\n')]:
lines = lines[1:]
c = 2
@@ -91,38 +97,38 @@ def check_fileheader(fn, lines):
for lno, l in enumerate(lines):
llist.append(l)
if lno == 0:
- if l == '# -*- coding: rot13 -*-\n':
+ if l == b('# -*- coding: rot13 -*-\n'):
# special-case pony package
return
- elif l != '# -*- coding: utf-8 -*-\n':
+ elif l != b('# -*- coding: utf-8 -*-\n'):
yield 1, "missing coding declaration"
elif lno == 1:
- if l != '"""\n' and l != 'r"""\n':
+ if l != b('"""\n') and l != b('r"""\n'):
yield 2, 'missing docstring begin (""")'
else:
docopen = True
elif docopen:
- if l == '"""\n':
+ if l == b('"""\n'):
# end of docstring
if lno <= 4:
yield lno+c, "missing module name in docstring"
break
- if l != "\n" and l[:4] != ' ' and docopen:
+ if l != b("\n") and l[:4] != b(' ') and docopen:
yield lno+c, "missing correct docstring indentation"
if lno == 2:
# if not in package, don't check the module name
modname = fn[:-3].replace('/', '.').replace('.__init__', '')
while modname:
- if l.lower()[4:-1] == modname:
+ if l.lower()[4:-1] == b(modname):
break
modname = '.'.join(modname.split('.')[1:])
else:
yield 3, "wrong module name in docstring heading"
modnamelen = len(l.strip())
elif lno == 3:
- if l.strip() != modnamelen * "~":
+ if l.strip() != modnamelen * b("~"):
yield 4, "wrong module name underline, should be ~~~...~"
else:
@@ -145,16 +151,16 @@ def check_fileheader(fn, lines):
@checker('.py', '.html', '.rst')
def check_whitespace_and_spelling(fn, lines):
for lno, line in enumerate(lines):
- if "\t" in line:
+ if b("\t") in line:
yield lno+1, "OMG TABS!!!1 "
- if line[:-1].rstrip(' \t') != line[:-1]:
+ if line[:-1].rstrip(b(' \t')) != line[:-1]:
yield lno+1, "trailing whitespace"
for word in misspellings:
- if word in line and 'ALLOW-MISSPELLING' not in line:
+ if word in line and b('ALLOW-MISSPELLING') not in line:
yield lno+1, '"%s" used' % word
-bad_tags = ('', '', '', '
', '', '', '', '
', '
Date: Mon, 17 May 2010 01:11:22 +0200
Subject: [PATCH 040/207] ported utils/reindent.py to python 3.x
---
utils/reindent.py | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/utils/reindent.py b/utils/reindent.py
index bcb6b4343..63ff7ef73 100755
--- a/utils/reindent.py
+++ b/utils/reindent.py
@@ -47,6 +47,17 @@ recurse = 0
dryrun = 0
no_backup = 0
+if sys.version_info >= (3, 0):
+ def tokens(readline, tokeneater):
+ for token in tokenize.tokenize(readline):
+ tokeneater(*token)
+
+ def b(s):
+ return s.encode('utf-8')
+else:
+ tokens = tokenize.tokenize
+ b = str
+
def usage(msg=None):
if msg is not None:
print >> sys.stderr, msg
@@ -106,7 +117,7 @@ def check(file):
if verbose:
print "checking", file, "...",
try:
- f = open(file)
+ f = open(file, 'rb')
except IOError, msg:
errprint("%s: I/O Error: %s" % (file, str(msg)))
return
@@ -129,7 +140,7 @@ def check(file):
os.rename(file, bak)
if verbose:
print "renamed", file, "to", bak
- f = open(file, "w")
+ f = open(file, "wb")
r.write(f)
f.close()
if verbose:
@@ -151,7 +162,7 @@ class Reindenter:
# File lines, rstripped & tab-expanded. Dummy at start is so
# that we can use tokenize's 1-based line numbering easily.
# Note that a line is all-blank iff it's "\n".
- self.lines = [line.rstrip('\n \t').expandtabs() + "\n"
+ self.lines = [line.rstrip(b('\n \t')).expandtabs() + b("\n")
for line in self.raw]
self.lines.insert(0, None)
self.index = 1 # index into self.lines of next line
@@ -163,10 +174,10 @@ class Reindenter:
self.stats = []
def run(self):
- tokenize.tokenize(self.getline, self.tokeneater)
+ tokens(self.getline, self.tokeneater)
# Remove trailing empty lines.
lines = self.lines
- while lines and lines[-1] == "\n":
+ while lines and lines[-1] == b("\n"):
lines.pop()
# Sentinel.
stats = self.stats
@@ -222,10 +233,10 @@ class Reindenter:
else:
for line in lines[thisstmt:nextstmt]:
if diff > 0:
- if line == "\n":
+ if line == b("\n"):
after.append(line)
else:
- after.append(" " * diff + line)
+ after.append(b(" ") * diff + line)
else:
remove = min(getlspace(line), -diff)
after.append(line[remove:])
@@ -237,7 +248,7 @@ class Reindenter:
# Line-getter for tokenize.
def getline(self):
if self.index >= len(self.lines):
- line = ""
+ line = b("")
else:
line = self.lines[self.index]
self.index += 1
@@ -286,7 +297,7 @@ class Reindenter:
# Count number of leading blanks.
def getlspace(line):
i, n = 0, len(line)
- while i < n and line[i] == " ":
+ while i < n and line[i] == b(" "):
i += 1
return i
From 936d68fc8364c2274e1416a951bf2af9b54cbd3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 17 May 2010 01:28:50 +0200
Subject: [PATCH 041/207] Ignore errors when removing generated files
---
Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 209302302..c1b266dcb 100644
--- a/Makefile
+++ b/Makefile
@@ -35,7 +35,7 @@ clean-backupfiles:
find . -name '*.bak' -exec rm -f {} +
clean-generated:
- -rm utils/*3.py*
+ rm -f utils/*3.py*
pylint:
@pylint --rcfile utils/pylintrc sphinx
From 765871c86c1ca26f013b6fc489c24fa6dba96e16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 17 May 2010 02:23:59 +0200
Subject: [PATCH 042/207] Added latest changes to the Changes.DasIch file
---
CHANGES.DasIch | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGES.DasIch b/CHANGES.DasIch
index 711f9a534..3f7167263 100644
--- a/CHANGES.DasIch
+++ b/CHANGES.DasIch
@@ -5,6 +5,12 @@ This file contains changes made by Daniel Neuhäuser, during the Google Summer
of Code 2010, to port Sphinx to Python 3.x. Changes are ordered descending by
date.
+May 16: - Added utils/convert.py which converts entire directories of python
+ files with 2to3 and names the converted files foo3.py.
+ - Modified the Makefile so that in case Python 3 is used the scripts in
+ utils get converted with utils/convert.py and are used instead of the
+ Python 2 scripts.
+
May 10: Fixed a couple of tests and made several small changes.
May 9: - Removed ez_setup.py which does not work with Python 3.x. and replaced
From ad9055868ee81971bf9a7aa0cd2c6d24a6e55522 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 17 May 2010 02:26:31 +0200
Subject: [PATCH 043/207] Added latest reindent.py file
---
Makefile | 4 +-
utils/reindent.py | 96 +++++++++++++++++++++++++----------------------
2 files changed, 53 insertions(+), 47 deletions(-)
diff --git a/Makefile b/Makefile
index c1b266dcb..ecc7722c2 100644
--- a/Makefile
+++ b/Makefile
@@ -42,9 +42,9 @@ pylint:
reindent: convert-utils
ifeq ($(PYTHON), python3)
- @$(PYTHON) utils/reindent3.py -r -B .
+ @$(PYTHON) utils/reindent3.py -r -n .
else
- @$(PYTHON) utils/reindent.py -r -B .
+ @$(PYTHON) utils/reindent.py -r -n .
endif
test: build
diff --git a/utils/reindent.py b/utils/reindent.py
index 63ff7ef73..59828fd86 100755
--- a/utils/reindent.py
+++ b/utils/reindent.py
@@ -1,16 +1,14 @@
#! /usr/bin/env python
# Released to the public domain, by Tim Peters, 03 October 2000.
-# -B option added by Georg Brandl, 2006.
"""reindent [-d][-r][-v] [ path ... ]
--d (--dryrun) Dry run. Analyze, but don't make any changes to files.
--r (--recurse) Recurse. Search for all .py files in subdirectories too.
--B (--no-backup) Don't write .bak backup files.
--v (--verbose) Verbose. Print informative msgs; else only names of \
-changed files.
--h (--help) Help. Print this usage information and exit.
+-d (--dryrun) Dry run. Analyze, but don't make any changes to, files.
+-r (--recurse) Recurse. Search for all .py files in subdirectories too.
+-n (--nobackup) No backup. Does not make a ".bak" file before reindenting.
+-v (--verbose) Verbose. Print informative msgs; else no output.
+-h (--help) Help. Print this usage information and exit.
Change Python (.py) files to use 4-space indents and no hard tab characters.
Also trim excess spaces and tabs from ends of lines, and remove empty lines
@@ -34,29 +32,30 @@ resulting .py file won't change it again).
The hard part of reindenting is figuring out what to do with comment
lines. So long as the input files get a clean bill of health from
tabnanny.py, reindent should do a good job.
+
+The backup file is a copy of the one that is being reindented. The ".bak"
+file is generated with shutil.copy(), but some corner cases regarding
+user/group and permissions could leave the backup file more readable that
+you'd prefer. You can always use the --nobackup option to prevent this.
"""
__version__ = "1"
import tokenize
-import os
+import os, shutil
import sys
-verbose = 0
-recurse = 0
-dryrun = 0
-no_backup = 0
-
if sys.version_info >= (3, 0):
def tokens(readline, tokeneater):
for token in tokenize.tokenize(readline):
- tokeneater(*token)
-
- def b(s):
- return s.encode('utf-8')
+ yield tokeneater(*token)
else:
tokens = tokenize.tokenize
- b = str
+
+verbose = 0
+recurse = 0
+dryrun = 0
+makebackup = True
def usage(msg=None):
if msg is not None:
@@ -72,12 +71,10 @@ def errprint(*args):
def main():
import getopt
- global verbose, recurse, dryrun, no_backup
-
+ global verbose, recurse, dryrun, makebackup
try:
- opts, args = getopt.getopt(sys.argv[1:], "drvhB",
- ["dryrun", "recurse", "verbose", "help",
- "no-backup"])
+ opts, args = getopt.getopt(sys.argv[1:], "drnvh",
+ ["dryrun", "recurse", "nobackup", "verbose", "help"])
except getopt.error, msg:
usage(msg)
return
@@ -86,10 +83,10 @@ def main():
dryrun += 1
elif o in ('-r', '--recurse'):
recurse += 1
+ elif o in ('-n', '--nobackup'):
+ makebackup = False
elif o in ('-v', '--verbose'):
verbose += 1
- elif o in ('-B', '--no-backup'):
- no_backup += 1
elif o in ('-h', '--help'):
usage()
return
@@ -109,7 +106,8 @@ def check(file):
for name in names:
fullname = os.path.join(file, name)
if ((recurse and os.path.isdir(fullname) and
- not os.path.islink(fullname))
+ not os.path.islink(fullname) and
+ not os.path.split(fullname)[1].startswith("."))
or name.lower().endswith(".py")):
check(fullname)
return
@@ -117,7 +115,7 @@ def check(file):
if verbose:
print "checking", file, "...",
try:
- f = open(file, 'rb')
+ f = open(file)
except IOError, msg:
errprint("%s: I/O Error: %s" % (file, str(msg)))
return
@@ -129,26 +127,35 @@ def check(file):
print "changed."
if dryrun:
print "But this is a dry run, so leaving it alone."
- else:
- print "reindented", file, \
- (dryrun and "(dry run => not really)" or "")
if not dryrun:
- if not no_backup:
- bak = file + ".bak"
- if os.path.exists(bak):
- os.remove(bak)
- os.rename(file, bak)
+ bak = file + ".bak"
+ if makebackup:
+ shutil.copyfile(file, bak)
if verbose:
- print "renamed", file, "to", bak
- f = open(file, "wb")
+ print "backed up", file, "to", bak
+ f = open(file, "w")
r.write(f)
f.close()
if verbose:
print "wrote new", file
+ return True
else:
if verbose:
print "unchanged."
+ return False
+def _rstrip(line, JUNK='\n \t'):
+ """Return line stripped of trailing spaces, tabs, newlines.
+
+ Note that line.rstrip() instead also strips sundry control characters,
+ but at least one known Emacs user expects to keep junk like that, not
+ mentioning Barry by name or anything .
+ """
+
+ i = len(line)
+ while i > 0 and line[i-1] in JUNK:
+ i -= 1
+ return line[:i]
class Reindenter:
@@ -162,7 +169,7 @@ class Reindenter:
# File lines, rstripped & tab-expanded. Dummy at start is so
# that we can use tokenize's 1-based line numbering easily.
# Note that a line is all-blank iff it's "\n".
- self.lines = [line.rstrip(b('\n \t')).expandtabs() + b("\n")
+ self.lines = [_rstrip(line).expandtabs() + "\n"
for line in self.raw]
self.lines.insert(0, None)
self.index = 1 # index into self.lines of next line
@@ -177,7 +184,7 @@ class Reindenter:
tokens(self.getline, self.tokeneater)
# Remove trailing empty lines.
lines = self.lines
- while lines and lines[-1] == b("\n"):
+ while lines and lines[-1] == "\n":
lines.pop()
# Sentinel.
stats = self.stats
@@ -233,10 +240,10 @@ class Reindenter:
else:
for line in lines[thisstmt:nextstmt]:
if diff > 0:
- if line == b("\n"):
+ if line == "\n":
after.append(line)
else:
- after.append(b(" ") * diff + line)
+ after.append(" " * diff + line)
else:
remove = min(getlspace(line), -diff)
after.append(line[remove:])
@@ -248,20 +255,19 @@ class Reindenter:
# Line-getter for tokenize.
def getline(self):
if self.index >= len(self.lines):
- line = b("")
+ line = ""
else:
line = self.lines[self.index]
self.index += 1
return line
# Line-eater for tokenize.
- def tokeneater(self, type, token, scell, end, line,
+ def tokeneater(self, type, token, (sline, scol), end, line,
INDENT=tokenize.INDENT,
DEDENT=tokenize.DEDENT,
NEWLINE=tokenize.NEWLINE,
COMMENT=tokenize.COMMENT,
NL=tokenize.NL):
- sline, scol = scell
if type == NEWLINE:
# A program statement, or ENDMARKER, will eventually follow,
@@ -297,7 +303,7 @@ class Reindenter:
# Count number of leading blanks.
def getlspace(line):
i, n = 0, len(line)
- while i < n and line[i] == b(" "):
+ while i < n and line[i] == " ":
i += 1
return i
From 614a7f048fb51de38b1a55460714670c3f6de901 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 23 May 2010 01:34:52 +0200
Subject: [PATCH 044/207] make now works without python3
---
Makefile | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/Makefile b/Makefile
index ecc7722c2..21a87e367 100644
--- a/Makefile
+++ b/Makefile
@@ -11,12 +11,13 @@ DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \
-i tests/coverage.py -i env -i utils/convert.py \
-i utils/reindent3.py -i utils/check_sources3.py -i .tox
-all: clean-pyc check test
+all: clean-pyc clean-backupfiles check test
-check: convert-utils
ifeq ($(PYTHON), python3)
+check: convert-utils
@$(PYTHON) utils/check_sources3.py $(DONT_CHECK) .
else
+check:
@$(PYTHON) utils/check_sources.py $(DONT_CHECK) .
endif
@@ -40,10 +41,11 @@ clean-generated:
pylint:
@pylint --rcfile utils/pylintrc sphinx
-reindent: convert-utils
ifeq ($(PYTHON), python3)
+reindent: convert-utils
@$(PYTHON) utils/reindent3.py -r -n .
else
+reindent:
@$(PYTHON) utils/reindent.py -r -n .
endif
@@ -57,5 +59,7 @@ covertest: build
build:
@$(PYTHON) setup.py build
+ifeq ($(PYTHON), python3)
convert-utils:
@python3 utils/convert.py -i utils/convert.py utils/
+endif
From a83c48ec8a5f275c5ecb67251945eb454969ef90 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 24 May 2010 17:32:00 +0200
Subject: [PATCH 045/207] test_autodoc.test_get_doc now passes
---
sphinx/ext/autodoc.py | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index 8a827a91f..1113f97a0 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -411,9 +411,11 @@ class Documenter(object):
def get_doc(self, encoding=None):
"""Decode and return lines of the docstring(s) for the object."""
docstring = self.get_attr(self.object, '__doc__', None)
- if docstring:
- # make sure we have Unicode docstrings, then sanitize and split
- # into lines
+ # make sure we have Unicode docstrings, then sanitize and split
+ # into lines
+ if isinstance(docstring, unicode):
+ return [prepare_docstring(docstring)]
+ elif docstring:
return [prepare_docstring(force_decode(docstring, encoding))]
return []
@@ -934,9 +936,12 @@ class ClassDocumenter(ModuleLevelDocumenter):
docstrings = [initdocstring]
else:
docstrings.append(initdocstring)
-
- return [prepare_docstring(force_decode(docstring, encoding))
- for docstring in docstrings]
+ doc = []
+ for docstring in docstrings:
+ if not isinstance(docstring, unicode):
+ docstring = force_decode(docstring, encoding)
+ doc.append(prepare_docstring(docstring))
+ return doc
def add_content(self, more_content, no_docstring=False):
if self.doc_as_attr:
From f8b12a45dae561b611769bd4f1cd231497d7c2b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 24 May 2010 17:35:43 +0200
Subject: [PATCH 046/207] use open instead of file
---
tests/path.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/path.py b/tests/path.py
index 7b89c0cd3..f27e58a9d 100644
--- a/tests/path.py
+++ b/tests/path.py
@@ -516,7 +516,7 @@ class path(_base):
def open(self, mode='r'):
""" Open this file. Return a file object. """
- return file(self, mode)
+ return open(self, mode)
def bytes(self):
""" Open this file, read all bytes, return them as a string. """
From d1d5d20f65e0cd298fa1a9885c133ae4bddb4c22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 24 May 2010 18:13:56 +0200
Subject: [PATCH 047/207] Workaround for 2to3
---
sphinx/ext/intersphinx.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 0a210879a..fb1f0e4ff 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -36,8 +36,10 @@ from sphinx.builders.html import INVENTORY_FILENAME
handlers = [urllib2.ProxyHandler(), urllib2.HTTPRedirectHandler(),
urllib2.HTTPHandler()]
-if hasattr(urllib2, 'HTTPSHandler'):
+try:
handlers.append(urllib2.HTTPSHandler)
+except NameError:
+ pass
urllib2.install_opener(urllib2.build_opener(*handlers))
From bedbbe288ed774d9d26dd114fcf6c2769fc1a2f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 24 May 2010 18:25:20 +0200
Subject: [PATCH 048/207] don't assume strings to be byte strings
---
sphinx/pycode/__init__.py | 3 ++-
sphinx/pycode/pgen2/literals.py | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index b8e2fded2..cb9c08878 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -98,7 +98,8 @@ class AttrDocVisitor(nodes.NodeVisitor):
if not pnode or pnode.type not in (token.INDENT, token.DEDENT):
break
prefix = pnode.get_prefix()
- prefix = prefix.decode(self.encoding)
+ if not isinstance(prefix, unicode):
+ prefix = prefix.decode(self.encoding)
docstring = prepare_commentdoc(prefix)
self.add_docstring(node, docstring)
diff --git a/sphinx/pycode/pgen2/literals.py b/sphinx/pycode/pgen2/literals.py
index 319002910..d48937028 100644
--- a/sphinx/pycode/pgen2/literals.py
+++ b/sphinx/pycode/pgen2/literals.py
@@ -66,7 +66,7 @@ uni_escape_re = re.compile(r"\\(\'|\"|\\|[abfnrtv]|x.{0,2}|[0-7]{1,3}|"
def evalString(s, encoding=None):
regex = escape_re
repl = escape
- if encoding:
+ if encoding and not isinstance(s, unicode):
s = s.decode(encoding)
if s.startswith('u') or s.startswith('U'):
regex = uni_escape_re
From 24f1d4c12d76b4eb90818b3a10e0cb26ca120a0d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 24 May 2010 19:41:02 +0200
Subject: [PATCH 049/207] fixed test_markup test
---
sphinx/util/osutil.py | 11 +++++++----
tests/test_markup.py | 9 ++++++++-
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index beab38cbd..1010fb2fe 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -14,6 +14,7 @@ import re
import time
import errno
import shutil
+import sys
from os import path
# Errnos that we need.
@@ -124,7 +125,9 @@ no_fn_re = re.compile(r'[^a-zA-Z0-9_-]')
def make_filename(string):
return no_fn_re.sub('', string)
-
-def ustrftime(format, *args):
- # strftime for unicode strings
- return time.strftime(unicode(format).encode('utf-8'), *args).decode('utf-8')
+if sys.version_info < (3, 0):
+ def ustrftime(format, *args):
+ # strftime for unicode strings
+ return time.strftime(unicode(format).encode('utf-8'), *args).decode('utf-8')
+else:
+ ustrftime = time.strftime
diff --git a/tests/test_markup.py b/tests/test_markup.py
index 31817df62..6d6badbbc 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -10,6 +10,7 @@
"""
import re
+import sys
from util import *
@@ -20,6 +21,12 @@ from sphinx.util import texescape
from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator
from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
+if sys.version_info > (3, 0):
+ def b(s):
+ return s.encode('utf-8')
+else:
+ b = str
+
def setup_module():
global app, settings, parser
texescape.init() # otherwise done by the latex builder
@@ -50,7 +57,7 @@ class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator):
def verify_re(rst, html_expected, latex_expected):
- document = utils.new_document('test data', settings)
+ document = utils.new_document(b('test data'), settings)
document['file'] = 'dummy'
parser.parse(rst, document)
for msg in document.traverse(nodes.system_message):
From ec5a5e739bd93273be9649250443c1267069b203 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:43:40 +0200
Subject: [PATCH 050/207] Move the "b" function to pycompat.
---
sphinx/util/osutil.py | 2 +-
sphinx/util/pycompat.py | 8 ++++++++
tests/test_markup.py | 8 +-------
3 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 1010fb2fe..250e75741 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -11,10 +11,10 @@
import os
import re
+import sys
import time
import errno
import shutil
-import sys
from os import path
# Errnos that we need.
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index 7bf768fac..8787a144e 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -26,6 +26,14 @@ except NameError:
base_exception = Exception
+# the ubiquitous "bytes" helper function
+if sys.version_info > (3, 0):
+ def b(s):
+ return s.encode('utf-8')
+else:
+ b = str
+
+
try:
any = any
all = all
diff --git a/tests/test_markup.py b/tests/test_markup.py
index 6d6badbbc..092113bbe 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -10,7 +10,6 @@
"""
import re
-import sys
from util import *
@@ -18,15 +17,10 @@ from docutils import frontend, utils, nodes
from docutils.parsers import rst
from sphinx.util import texescape
+from sphinx.util.pycompat import b
from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator
from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
-if sys.version_info > (3, 0):
- def b(s):
- return s.encode('utf-8')
-else:
- b = str
-
def setup_module():
global app, settings, parser
texescape.init() # otherwise done by the latex builder
From 6af81b8506d3f5ab7eec86ad031bf89870fd6035 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Tue, 25 May 2010 00:55:30 +0200
Subject: [PATCH 051/207] fix line length
---
sphinx/util/osutil.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 250e75741..9943b207f 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -128,6 +128,7 @@ def make_filename(string):
if sys.version_info < (3, 0):
def ustrftime(format, *args):
# strftime for unicode strings
- return time.strftime(unicode(format).encode('utf-8'), *args).decode('utf-8')
+ return time.strftime(unicode(format).encode('utf-8'), *args) \
+ .decode('utf-8')
else:
ustrftime = time.strftime
From 7f2990455155c67b906b0cc93d9b20bbbf23d85f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 30 May 2010 02:07:53 +0200
Subject: [PATCH 052/207] Use .gettext() instead of .ugettext() on python3
---
sphinx/locale/__init__.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py
index b0b89720c..8df5f0060 100644
--- a/sphinx/locale/__init__.py
+++ b/sphinx/locale/__init__.py
@@ -8,6 +8,7 @@
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+import sys
import gettext
import UserString
@@ -178,8 +179,12 @@ pairindextypes = {
translator = None
-def _(message):
- return translator.ugettext(message)
+if sys.version_info >= (3, 0):
+ def _(message):
+ return translator.gettext(message)
+else:
+ def _(message):
+ return translator.ugettext(message)
def init(locale_dirs, language):
global translator
From ba02a69c1c9c7fa742e37f477cc99f8c8454c4f2 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:46:12 +0200
Subject: [PATCH 053/207] Nit.
---
sphinx/locale/__init__.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py
index 8df5f0060..02958457b 100644
--- a/sphinx/locale/__init__.py
+++ b/sphinx/locale/__init__.py
@@ -8,6 +8,7 @@
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+
import sys
import gettext
import UserString
From 918a509efc91189ab548533e29a839ec62d125c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 30 May 2010 02:28:57 +0200
Subject: [PATCH 054/207] Check if a string is not unicode as a workaround for
3.x
---
sphinx/highlighting.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index f5ea859cb..c94405bc2 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -175,7 +175,7 @@ class PygmentsBridge(object):
return True
def highlight_block(self, source, lang, linenos=False, warn=None):
- if isinstance(source, str):
+ if not isinstance(source, unicode):
source = source.decode()
if not pygments:
return self.unhighlighted(source)
From 23ef216a157c1da346ffc635c128692e367874d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 30 May 2010 15:15:57 +0200
Subject: [PATCH 055/207] encode source code for parsing only on python 2.x
---
sphinx/highlighting.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index c94405bc2..c168aeffc 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -156,7 +156,7 @@ class PygmentsBridge(object):
if sys.version_info >= (2, 5):
src = 'from __future__ import with_statement\n' + src
- if isinstance(src, unicode):
+ if sys.version_info < (3, 0) and isinstance(src, unicode):
# Non-ASCII chars will only occur in string literals
# and comments. If we wanted to give them to the parser
# correctly, we'd have to find out the correct source
From 31275a34c2a80c6243833f658a465c1a3338f4e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 30 May 2010 17:51:14 +0200
Subject: [PATCH 056/207] Fix encoding in config test and open configs in
binary mode to warn for possible encoding errors
---
sphinx/config.py | 2 +-
tests/test_config.py | 5 +++--
tests/util.py | 11 +++++++++--
3 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/sphinx/config.py b/sphinx/config.py
index 2ec769871..07c3d63af 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -165,7 +165,7 @@ class Config(object):
try:
try:
os.chdir(dirname)
- f = open(config_file, 'U')
+ f = open(config_file, 'Ub')
try:
code = compile(f.read(), config_file, 'exec')
finally:
diff --git a/tests/test_config.py b/tests/test_config.py
index cb4e11056..23d92e39c 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -84,11 +84,12 @@ def test_extension_values(app):
@with_tempdir
def test_errors_warnings(dir):
# test the error for syntax errors in the config file
- write_file(dir / 'conf.py', 'project = \n')
+ write_file(dir / 'conf.py', u'project = \n', 'ascii')
raises_msg(ConfigError, 'conf.py', Config, dir, 'conf.py', {}, None)
# test the warning for bytestrings with non-ascii content
- write_file(dir / 'conf.py', '# -*- coding: latin-1\nproject = "foo\xe4"\n')
+ write_file(dir / 'conf.py',
+ u'# -*- coding: latin-1\nproject = "fooä"\n', 'latin-1')
cfg = Config(dir, 'conf.py', {}, None)
warned = [False]
def warn(msg):
diff --git a/tests/util.py b/tests/util.py
index 1b24af0e2..2cf4a775b 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -11,6 +11,7 @@ import sys
import StringIO
import tempfile
import shutil
+from codecs import open
try:
from functools import wraps
@@ -191,8 +192,14 @@ def with_tempdir(func):
return new_func
-def write_file(name, contents):
- f = open(str(name), 'wb')
+def write_file(name, contents, encoding=None):
+ if encoding is None:
+ mode = 'wb'
+ if isinstance(contents, unicode):
+ contents = contents.encode('ascii')
+ else:
+ mode = 'w'
+ f = open(str(name), 'wb', encoding=encoding)
f.write(contents)
f.close()
From a153f84a171c9b6e87a10721491661c27b87cee3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Tue, 1 Jun 2010 18:10:06 +0200
Subject: [PATCH 057/207] Replaced the path module with my own version
---
tests/path.py | 980 +++-----------------------------------------------
1 file changed, 53 insertions(+), 927 deletions(-)
diff --git a/tests/path.py b/tests/path.py
index f27e58a9d..36ab3a9a0 100644
--- a/tests/path.py
+++ b/tests/path.py
@@ -1,952 +1,78 @@
-""" path.py - An object representing a path to a file or directory.
-
-Example:
-
-from path import path
-d = path('/home/guido/bin')
-for f in d.files('*.py'):
- f.chmod(0755)
-
-This module requires Python 2.2 or later.
-
-
-URL: http://www.jorendorff.com/articles/python/path
-Author: Jason Orendorff (and others - see the url!)
-Date: 9 Mar 2007
+#!/usr/bin/env python
+# coding: utf-8
"""
+ path
+ ~~~~
+ :copyright: Copyright 2010 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+import os
+import sys
+import shutil
+from codecs import open
-# TODO
-# - Tree-walking functions don't avoid symlink loops. Matt Harrison
-# sent me a patch for this.
-# - Bug in write_text(). It doesn't support Universal newline mode.
-# - Better error message in listdir() when self isn't a
-# directory. (On Windows, the error message really sucks.)
-# - Make sure everything has a good docstring.
-# - Add methods for regex find and replace.
-# - guess_content_type() method?
-# - Perhaps support arguments to touch().
-from __future__ import generators
+FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding()
-import sys, warnings, os, fnmatch, glob, shutil, codecs
-__version__ = '2.2'
-__all__ = ['path']
-
-# Platform-specific support for path.owner
-if os.name == 'nt':
- try:
- import win32security
- except ImportError:
- win32security = None
-else:
- try:
- import pwd
- except ImportError:
- pwd = None
-
-# Pre-2.3 support. Are unicode filenames supported?
-_base = str
-_getcwd = os.getcwd
-try:
- if os.path.supports_unicode_filenames:
- _base = unicode
- _getcwd = os.getcwdu
-except AttributeError:
- pass
-
-# Pre-2.3 workaround for basestring.
-try:
- basestring
-except NameError:
- basestring = (str, unicode)
-
-# Universal newline support
-_textmode = 'r'
-try:
- file
-except NameError:
- _textmode = 'U'
-else:
- if hasattr(file, 'newlines'):
- _textmode = 'U'
-
-
-class TreeWalkWarning(Warning):
- pass
-
-class path(_base):
- """ Represents a filesystem path.
-
- For documentation on individual methods, consult their
- counterparts in os.path.
- """
-
- # --- Special Python methods.
-
- def __repr__(self):
- return 'path(%s)' % _base.__repr__(self)
-
- # Adding a path and a string yields a path.
- def __add__(self, more):
- try:
- resultStr = _base.__add__(self, more)
- except TypeError: #Python bug
- resultStr = NotImplemented
- if resultStr is NotImplemented:
- return resultStr
- return self.__class__(resultStr)
-
- def __radd__(self, other):
- if isinstance(other, basestring):
- return self.__class__(other.__add__(self))
- else:
- return NotImplemented
-
- # The / operator joins paths.
- def __div__(self, rel):
- """ fp.__div__(rel) == fp / rel == fp.joinpath(rel)
-
- Join two path components, adding a separator character if
- needed.
- """
- return self.__class__(os.path.join(self, rel))
-
- # Make the / operator work even when true division is enabled.
- __truediv__ = __div__
-
- def getcwd(cls):
- """ Return the current working directory as a path object. """
- return cls(_getcwd())
- getcwd = classmethod(getcwd)
-
-
- # --- Operations on path strings.
-
- isabs = os.path.isabs
- def abspath(self): return self.__class__(os.path.abspath(self))
- def normcase(self): return self.__class__(os.path.normcase(self))
- def normpath(self): return self.__class__(os.path.normpath(self))
- def realpath(self): return self.__class__(os.path.realpath(self))
- def expanduser(self): return self.__class__(os.path.expanduser(self))
- def expandvars(self): return self.__class__(os.path.expandvars(self))
- def dirname(self): return self.__class__(os.path.dirname(self))
- basename = os.path.basename
-
- def expand(self):
- """ Clean up a filename by calling expandvars(),
- expanduser(), and normpath() on it.
-
- This is commonly everything needed to clean up a filename
- read from a configuration file, for example.
- """
- return self.expandvars().expanduser().normpath()
-
- def _get_namebase(self):
- base, ext = os.path.splitext(self.name)
- return base
-
- def _get_ext(self):
- f, ext = os.path.splitext(_base(self))
- return ext
-
- def _get_drive(self):
- drive, r = os.path.splitdrive(self)
- return self.__class__(drive)
-
- parent = property(
- dirname, None, None,
- """ This path's parent directory, as a new path object.
-
- For example, path('/usr/local/lib/libpython.so').parent == path('/usr/local/lib')
- """)
-
- name = property(
- basename, None, None,
- """ The name of this file or directory without the full path.
-
- For example, path('/usr/local/lib/libpython.so').name == 'libpython.so'
- """)
-
- namebase = property(
- _get_namebase, None, None,
- """ The same as path.name, but with one file extension stripped off.
-
- For example, path('/home/guido/python.tar.gz').name == 'python.tar.gz',
- but path('/home/guido/python.tar.gz').namebase == 'python.tar'
- """)
-
- ext = property(
- _get_ext, None, None,
- """ The file extension, for example '.py'. """)
-
- drive = property(
- _get_drive, None, None,
- """ The drive specifier, for example 'C:'.
- This is always empty on systems that don't use drive specifiers.
- """)
-
- def splitpath(self):
- """ p.splitpath() -> Return (p.parent, p.name). """
- parent, child = os.path.split(self)
- return self.__class__(parent), child
-
- def splitdrive(self):
- """ p.splitdrive() -> Return (p.drive, ).
-
- Split the drive specifier from this path. If there is
- no drive specifier, p.drive is empty, so the return value
- is simply (path(''), p). This is always the case on Unix.
- """
- drive, rel = os.path.splitdrive(self)
- return self.__class__(drive), rel
-
- def splitext(self):
- """ p.splitext() -> Return (p.stripext(), p.ext).
-
- Split the filename extension from this path and return
- the two parts. Either part may be empty.
-
- The extension is everything from '.' to the end of the
- last path segment. This has the property that if
- (a, b) == p.splitext(), then a + b == p.
- """
- filename, ext = os.path.splitext(self)
- return self.__class__(filename), ext
-
- def stripext(self):
- """ p.stripext() -> Remove one file extension from the path.
-
- For example, path('/home/guido/python.tar.gz').stripext()
- returns path('/home/guido/python.tar').
- """
- return self.splitext()[0]
-
- if hasattr(os.path, 'splitunc'):
- def splitunc(self):
- unc, rest = os.path.splitunc(self)
- return self.__class__(unc), rest
-
- def _get_uncshare(self):
- unc, r = os.path.splitunc(self)
- return self.__class__(unc)
-
- uncshare = property(
- _get_uncshare, None, None,
- """ The UNC mount point for this path.
- This is empty for paths on local drives. """)
-
- def joinpath(self, *args):
- """ Join two or more path components, adding a separator
- character (os.sep) if needed. Returns a new path
- object.
- """
- return self.__class__(os.path.join(self, *args))
-
- def splitall(self):
- r""" Return a list of the path components in this path.
-
- The first item in the list will be a path. Its value will be
- either os.curdir, os.pardir, empty, or the root directory of
- this path (for example, '/' or 'C:\\'). The other items in
- the list will be strings.
-
- path.path.joinpath(*result) will yield the original path.
- """
- parts = []
- loc = self
- while loc != os.curdir and loc != os.pardir:
- prev = loc
- loc, child = prev.splitpath()
- if loc == prev:
- break
- parts.append(child)
- parts.append(loc)
- parts.reverse()
- return parts
-
- def relpath(self):
- """ Return this path as a relative path,
- based from the current working directory.
- """
- cwd = self.__class__(os.getcwd())
- return cwd.relpathto(self)
-
- def relpathto(self, dest):
- """ Return a relative path from self to dest.
-
- If there is no relative path from self to dest, for example if
- they reside on different drives in Windows, then this returns
- dest.abspath().
- """
- origin = self.abspath()
- dest = self.__class__(dest).abspath()
-
- orig_list = origin.normcase().splitall()
- # Don't normcase dest! We want to preserve the case.
- dest_list = dest.splitall()
-
- if orig_list[0] != os.path.normcase(dest_list[0]):
- # Can't get here from there.
- return dest
-
- # Find the location where the two paths start to differ.
- i = 0
- for start_seg, dest_seg in zip(orig_list, dest_list):
- if start_seg != os.path.normcase(dest_seg):
- break
- i += 1
-
- # Now i is the point where the two paths diverge.
- # Need a certain number of "os.pardir"s to work up
- # from the origin to the point of divergence.
- segments = [os.pardir] * (len(orig_list) - i)
- # Need to add the diverging part of dest_list.
- segments += dest_list[i:]
- if len(segments) == 0:
- # If they happen to be identical, use os.curdir.
- relpath = os.curdir
- else:
- relpath = os.path.join(*segments)
- return self.__class__(relpath)
-
- # --- Listing, searching, walking, and matching
-
- def listdir(self, pattern=None):
- """ D.listdir() -> List of items in this directory.
-
- Use D.files() or D.dirs() instead if you want a listing
- of just files or just subdirectories.
-
- The elements of the list are path objects.
-
- With the optional 'pattern' argument, this only lists
- items whose names match the given pattern.
- """
- names = os.listdir(self)
- if pattern is not None:
- names = fnmatch.filter(names, pattern)
- return [self / child for child in names]
-
- def dirs(self, pattern=None):
- """ D.dirs() -> List of this directory's subdirectories.
-
- The elements of the list are path objects.
- This does not walk recursively into subdirectories
- (but see path.walkdirs).
-
- With the optional 'pattern' argument, this only lists
- directories whose names match the given pattern. For
- example, d.dirs('build-*').
- """
- return [p for p in self.listdir(pattern) if p.isdir()]
-
- def files(self, pattern=None):
- """ D.files() -> List of the files in this directory.
-
- The elements of the list are path objects.
- This does not walk into subdirectories (see path.walkfiles).
-
- With the optional 'pattern' argument, this only lists files
- whose names match the given pattern. For example,
- d.files('*.pyc').
- """
-
- return [p for p in self.listdir(pattern) if p.isfile()]
-
- def walk(self, pattern=None, errors='strict'):
- """ D.walk() -> iterator over files and subdirs, recursively.
-
- The iterator yields path objects naming each child item of
- this directory and its descendants. This requires that
- D.isdir().
-
- This performs a depth-first traversal of the directory tree.
- Each directory is returned just before all its children.
-
- The errors= keyword argument controls behavior when an
- error occurs. The default is 'strict', which causes an
- exception. The other allowed values are 'warn', which
- reports the error via warnings.warn(), and 'ignore'.
- """
- if errors not in ('strict', 'warn', 'ignore'):
- raise ValueError("invalid errors parameter")
-
- try:
- childList = self.listdir()
- except Exception:
- if errors == 'ignore':
- return
- elif errors == 'warn':
- warnings.warn(
- "Unable to list directory '%s': %s"
- % (self, sys.exc_info()[1]),
- TreeWalkWarning)
- return
- else:
- raise
-
- for child in childList:
- if pattern is None or child.fnmatch(pattern):
- yield child
- try:
- isdir = child.isdir()
- except Exception:
- if errors == 'ignore':
- isdir = False
- elif errors == 'warn':
- warnings.warn(
- "Unable to access '%s': %s"
- % (child, sys.exc_info()[1]),
- TreeWalkWarning)
- isdir = False
+class path(str):
+ if sys.version_info < (3, 0):
+ def __new__(cls, s, encoding=FILESYSTEMENCODING, errors=None):
+ if isinstance(s, unicode):
+ if errors is None:
+ s = s.encode(encoding)
else:
- raise
+ s = s.encode(encoding, errors=errors)
+ return str.__new__(cls, s)
+ return str.__new__(cls, s)
- if isdir:
- for item in child.walk(pattern, errors):
- yield item
+ @property
+ def parent(self):
+ return self.__class__(os.path.dirname(self))
- def walkdirs(self, pattern=None, errors='strict'):
- """ D.walkdirs() -> iterator over subdirs, recursively.
+ def abspath(self):
+ return self.__class__(os.path.abspath(self))
- With the optional 'pattern' argument, this yields only
- directories whose names match the given pattern. For
- example, mydir.walkdirs('*test') yields only directories
- with names ending in 'test'.
+ def isdir(self):
+ return os.path.isdir(self)
- The errors= keyword argument controls behavior when an
- error occurs. The default is 'strict', which causes an
- exception. The other allowed values are 'warn', which
- reports the error via warnings.warn(), and 'ignore'.
- """
- if errors not in ('strict', 'warn', 'ignore'):
- raise ValueError("invalid errors parameter")
+ def isfile(self):
+ return os.path.isfile(self)
+ def rmtree(self, ignore_errors=False, onerror=None):
+ shutil.rmtree(self, ignore_errors=ignore_errors, onerror=onerror)
+
+ def copytree(self, destination, symlinks=False, ignore=None):
+ shutil.copytree(self, destination, symlinks=symlinks, ignore=ignore)
+
+ def unlink(self):
+ os.unlink(self)
+
+ def write_text(self, text, **kwargs):
+ f = open(self, 'w', **kwargs)
try:
- dirs = self.dirs()
- except Exception:
- if errors == 'ignore':
- return
- elif errors == 'warn':
- warnings.warn(
- "Unable to list directory '%s': %s"
- % (self, sys.exc_info()[1]),
- TreeWalkWarning)
- return
- else:
- raise
+ f.write(text)
+ finally:
+ f.close()
- for child in dirs:
- if pattern is None or child.fnmatch(pattern):
- yield child
- for subsubdir in child.walkdirs(pattern, errors):
- yield subsubdir
-
- def walkfiles(self, pattern=None, errors='strict'):
- """ D.walkfiles() -> iterator over files in D, recursively.
-
- The optional argument, pattern, limits the results to files
- with names that match the pattern. For example,
- mydir.walkfiles('*.tmp') yields only files with the .tmp
- extension.
- """
- if errors not in ('strict', 'warn', 'ignore'):
- raise ValueError("invalid errors parameter")
-
- try:
- childList = self.listdir()
- except Exception:
- if errors == 'ignore':
- return
- elif errors == 'warn':
- warnings.warn(
- "Unable to list directory '%s': %s"
- % (self, sys.exc_info()[1]),
- TreeWalkWarning)
- return
- else:
- raise
-
- for child in childList:
- try:
- isfile = child.isfile()
- isdir = not isfile and child.isdir()
- except:
- if errors == 'ignore':
- continue
- elif errors == 'warn':
- warnings.warn(
- "Unable to access '%s': %s"
- % (self, sys.exc_info()[1]),
- TreeWalkWarning)
- continue
- else:
- raise
-
- if isfile:
- if pattern is None or child.fnmatch(pattern):
- yield child
- elif isdir:
- for f in child.walkfiles(pattern, errors):
- yield f
-
- def fnmatch(self, pattern):
- """ Return True if self.name matches the given pattern.
-
- pattern - A filename pattern with wildcards,
- for example '*.py'.
- """
- return fnmatch.fnmatch(self.name, pattern)
-
- def glob(self, pattern):
- """ Return a list of path objects that match the pattern.
-
- pattern - a path relative to this directory, with wildcards.
-
- For example, path('/users').glob('*/bin/*') returns a list
- of all the files users have in their bin directories.
- """
- cls = self.__class__
- return [cls(s) for s in glob.glob(_base(self / pattern))]
-
-
- # --- Reading or writing an entire file at once.
-
- def open(self, mode='r'):
- """ Open this file. Return a file object. """
- return open(self, mode)
-
- def bytes(self):
- """ Open this file, read all bytes, return them as a string. """
- f = self.open('rb')
+ def text(self, **kwargs):
+ f = open(self, mode='U', **kwargs)
try:
return f.read()
finally:
f.close()
- def write_bytes(self, bytes, append=False):
- """ Open this file and write the given bytes to it.
-
- Default behavior is to overwrite any existing file.
- Call p.write_bytes(bytes, append=True) to append instead.
- """
- if append:
- mode = 'ab'
- else:
- mode = 'wb'
- f = self.open(mode)
- try:
- f.write(bytes)
- finally:
- f.close()
-
- def text(self, encoding=None, errors='strict'):
- r""" Open this file, read it in, return the content as a string.
-
- This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r'
- are automatically translated to '\n'.
-
- Optional arguments:
-
- encoding - The Unicode encoding (or character set) of
- the file. If present, the content of the file is
- decoded and returned as a unicode object; otherwise
- it is returned as an 8-bit str.
- errors - How to handle Unicode errors; see help(str.decode)
- for the options. Default is 'strict'.
- """
- if encoding is None:
- # 8-bit
- f = self.open(_textmode)
- try:
- return f.read()
- finally:
- f.close()
- else:
- # Unicode
- f = codecs.open(self, 'r', encoding, errors)
- # (Note - Can't use 'U' mode here, since codecs.open
- # doesn't support 'U' mode, even in Python 2.3.)
- try:
- t = f.read()
- finally:
- f.close()
- return (t.replace(u'\r\n', u'\n')
- .replace(u'\r\x85', u'\n')
- .replace(u'\r', u'\n')
- .replace(u'\x85', u'\n')
- .replace(u'\u2028', u'\n'))
-
- def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False):
- r""" Write the given text to this file.
-
- The default behavior is to overwrite any existing file;
- to append instead, use the 'append=True' keyword argument.
-
- There are two differences between path.write_text() and
- path.write_bytes(): newline handling and Unicode handling.
- See below.
-
- Parameters:
-
- - text - str/unicode - The text to be written.
-
- - encoding - str - The Unicode encoding that will be used.
- This is ignored if 'text' isn't a Unicode string.
-
- - errors - str - How to handle Unicode encoding errors.
- Default is 'strict'. See help(unicode.encode) for the
- options. This is ignored if 'text' isn't a Unicode
- string.
-
- - linesep - keyword argument - str/unicode - The sequence of
- characters to be used to mark end-of-line. The default is
- os.linesep. You can also specify None; this means to
- leave all newlines as they are in 'text'.
-
- - append - keyword argument - bool - Specifies what to do if
- the file already exists (True: append to the end of it;
- False: overwrite it.) The default is False.
-
-
- --- Newline handling.
-
- write_text() converts all standard end-of-line sequences
- ('\n', '\r', and '\r\n') to your platform's default end-of-line
- sequence (see os.linesep; on Windows, for example, the
- end-of-line marker is '\r\n').
-
- If you don't like your platform's default, you can override it
- using the 'linesep=' keyword argument. If you specifically want
- write_text() to preserve the newlines as-is, use 'linesep=None'.
-
- This applies to Unicode text the same as to 8-bit text, except
- there are three additional standard Unicode end-of-line sequences:
- u'\x85', u'\r\x85', and u'\u2028'.
-
- (This is slightly different from when you open a file for
- writing with fopen(filename, "w") in C or file(filename, 'w')
- in Python.)
-
-
- --- Unicode
-
- If 'text' isn't Unicode, then apart from newline handling, the
- bytes are written verbatim to the file. The 'encoding' and
- 'errors' arguments are not used and must be omitted.
-
- If 'text' is Unicode, it is first converted to bytes using the
- specified 'encoding' (or the default encoding if 'encoding'
- isn't specified). The 'errors' argument applies only to this
- conversion.
-
- """
- if isinstance(text, unicode):
- if linesep is not None:
- # Convert all standard end-of-line sequences to
- # ordinary newline characters.
- text = (text.replace(u'\r\n', u'\n')
- .replace(u'\r\x85', u'\n')
- .replace(u'\r', u'\n')
- .replace(u'\x85', u'\n')
- .replace(u'\u2028', u'\n'))
- text = text.replace(u'\n', linesep)
- if encoding is None:
- encoding = sys.getdefaultencoding()
- bytes = text.encode(encoding, errors)
- else:
- # It is an error to specify an encoding if 'text' is
- # an 8-bit string.
- assert encoding is None
-
- if linesep is not None:
- text = (text.replace('\r\n', '\n')
- .replace('\r', '\n'))
- bytes = text.replace('\n', linesep)
-
- self.write_bytes(bytes, append)
-
- def lines(self, encoding=None, errors='strict', retain=True):
- r""" Open this file, read all lines, return them in a list.
-
- Optional arguments:
- encoding - The Unicode encoding (or character set) of
- the file. The default is None, meaning the content
- of the file is read as 8-bit characters and returned
- as a list of (non-Unicode) str objects.
- errors - How to handle Unicode errors; see help(str.decode)
- for the options. Default is 'strict'
- retain - If true, retain newline characters; but all newline
- character combinations ('\r', '\n', '\r\n') are
- translated to '\n'. If false, newline characters are
- stripped off. Default is True.
-
- This uses 'U' mode in Python 2.3 and later.
- """
- if encoding is None and retain:
- f = self.open(_textmode)
- try:
- return f.readlines()
- finally:
- f.close()
- else:
- return self.text(encoding, errors).splitlines(retain)
-
- def write_lines(self, lines, encoding=None, errors='strict',
- linesep=os.linesep, append=False):
- r""" Write the given lines of text to this file.
-
- By default this overwrites any existing file at this path.
-
- This puts a platform-specific newline sequence on every line.
- See 'linesep' below.
-
- lines - A list of strings.
-
- encoding - A Unicode encoding to use. This applies only if
- 'lines' contains any Unicode strings.
-
- errors - How to handle errors in Unicode encoding. This
- also applies only to Unicode strings.
-
- linesep - The desired line-ending. This line-ending is
- applied to every line. If a line already has any
- standard line ending ('\r', '\n', '\r\n', u'\x85',
- u'\r\x85', u'\u2028'), that will be stripped off and
- this will be used instead. The default is os.linesep,
- which is platform-dependent ('\r\n' on Windows, '\n' on
- Unix, etc.) Specify None to write the lines as-is,
- like file.writelines().
-
- Use the keyword argument append=True to append lines to the
- file. The default is to overwrite the file. Warning:
- When you use this with Unicode data, if the encoding of the
- existing data in the file is different from the encoding
- you specify with the encoding= parameter, the result is
- mixed-encoding data, which can really confuse someone trying
- to read the file later.
- """
- if append:
- mode = 'ab'
- else:
- mode = 'wb'
- f = self.open(mode)
- try:
- for line in lines:
- isUnicode = isinstance(line, unicode)
- if linesep is not None:
- # Strip off any existing line-end and add the
- # specified linesep string.
- if isUnicode:
- if line[-2:] in (u'\r\n', u'\x0d\x85'):
- line = line[:-2]
- elif line[-1:] in (u'\r', u'\n',
- u'\x85', u'\u2028'):
- line = line[:-1]
- else:
- if line[-2:] == '\r\n':
- line = line[:-2]
- elif line[-1:] in ('\r', '\n'):
- line = line[:-1]
- line += linesep
- if isUnicode:
- if encoding is None:
- encoding = sys.getdefaultencoding()
- line = line.encode(encoding, errors)
- f.write(line)
- finally:
- f.close()
-
- # --- Methods for querying the filesystem.
-
- exists = os.path.exists
- isdir = os.path.isdir
- isfile = os.path.isfile
- islink = os.path.islink
- ismount = os.path.ismount
-
- if hasattr(os.path, 'samefile'):
- samefile = os.path.samefile
-
- getatime = os.path.getatime
- atime = property(
- getatime, None, None,
- """ Last access time of the file. """)
-
- getmtime = os.path.getmtime
- mtime = property(
- getmtime, None, None,
- """ Last-modified time of the file. """)
-
- if hasattr(os.path, 'getctime'):
- getctime = os.path.getctime
- ctime = property(
- getctime, None, None,
- """ Creation time of the file. """)
-
- getsize = os.path.getsize
- size = property(
- getsize, None, None,
- """ Size of the file, in bytes. """)
-
- if hasattr(os, 'access'):
- def access(self, mode):
- """ Return true if current user has access to this path.
-
- mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK
- """
- return os.access(self, mode)
-
- def stat(self):
- """ Perform a stat() system call on this path. """
- return os.stat(self)
-
- def lstat(self):
- """ Like path.stat(), but do not follow symbolic links. """
- return os.lstat(self)
-
- def get_owner(self):
- r""" Return the name of the owner of this file or directory.
-
- This follows symbolic links.
-
- On Windows, this returns a name of the form ur'DOMAIN\User Name'.
- On Windows, a group can own a file or directory.
- """
- if os.name == 'nt':
- if win32security is None:
- raise Exception("path.owner requires win32all to be installed")
- desc = win32security.GetFileSecurity(
- self, win32security.OWNER_SECURITY_INFORMATION)
- sid = desc.GetSecurityDescriptorOwner()
- account, domain, typecode = win32security.LookupAccountSid(None, sid)
- return domain + u'\\' + account
- else:
- if pwd is None:
- raise NotImplementedError("path.owner is not implemented on this platform.")
- st = self.stat()
- return pwd.getpwuid(st.st_uid).pw_name
-
- owner = property(
- get_owner, None, None,
- """ Name of the owner of this file or directory. """)
-
- if hasattr(os, 'statvfs'):
- def statvfs(self):
- """ Perform a statvfs() system call on this path. """
- return os.statvfs(self)
-
- if hasattr(os, 'pathconf'):
- def pathconf(self, name):
- return os.pathconf(self, name)
-
-
- # --- Modifying operations on files and directories
-
- def utime(self, times):
- """ Set the access and modified times of this file. """
- os.utime(self, times)
-
- def chmod(self, mode):
- os.chmod(self, mode)
-
- if hasattr(os, 'chown'):
- def chown(self, uid, gid):
- os.chown(self, uid, gid)
-
- def rename(self, new):
- os.rename(self, new)
-
- def renames(self, new):
- os.renames(self, new)
-
-
- # --- Create/delete operations on directories
-
- def mkdir(self, mode=0777):
- os.mkdir(self, mode)
+ def exists(self):
+ return os.path.exists(self)
def makedirs(self, mode=0777):
os.makedirs(self, mode)
- def rmdir(self):
- os.rmdir(self)
+ def joinpath(self, *args):
+ return self.__class__(os.path.join(self, *map(self.__class__, args)))
- def removedirs(self):
- os.removedirs(self)
-
-
- # --- Modifying operations on files
-
- def touch(self):
- """ Set the access/modified times of this file to the current time.
- Create the file if it does not exist.
- """
- fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0666)
- os.close(fd)
- os.utime(self, None)
-
- def remove(self):
- os.remove(self)
-
- def unlink(self):
- os.unlink(self)
-
-
- # --- Links
-
- if hasattr(os, 'link'):
- def link(self, newpath):
- """ Create a hard link at 'newpath', pointing to this file. """
- os.link(self, newpath)
-
- if hasattr(os, 'symlink'):
- def symlink(self, newlink):
- """ Create a symbolic link at 'newlink', pointing here. """
- os.symlink(self, newlink)
-
- if hasattr(os, 'readlink'):
- def readlink(self):
- """ Return the path to which this symbolic link points.
-
- The result may be an absolute or a relative path.
- """
- return self.__class__(os.readlink(self))
-
- def readlinkabs(self):
- """ Return the path to which this symbolic link points.
-
- The result is always an absolute path.
- """
- p = self.readlink()
- if p.isabs():
- return p
- else:
- return (self.parent / p).abspath()
-
-
- # --- High-level functions from shutil
-
- copyfile = shutil.copyfile
- copymode = shutil.copymode
- copystat = shutil.copystat
- copy = shutil.copy
- copy2 = shutil.copy2
- copytree = shutil.copytree
- if hasattr(shutil, 'move'):
- move = shutil.move
- rmtree = shutil.rmtree
-
-
- # --- Special stuff from os
-
- if hasattr(os, 'chroot'):
- def chroot(self):
- os.chroot(self)
-
- if hasattr(os, 'startfile'):
- def startfile(self):
- os.startfile(self)
+ __div__ = __truediv__ = joinpath
+ def __repr__(self):
+ return '%s(%s)' % (self.__class__.__name__, str.__repr__(self))
From 2683b839d5b701e4e7aa46efe1e57fed804afd93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Tue, 1 Jun 2010 19:59:38 +0200
Subject: [PATCH 058/207] Added a movetree method to the path object to make it
more consistent along with documentation for every method of it.
---
tests/path.py | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/tests/path.py b/tests/path.py
index 36ab3a9a0..dccdac3ab 100644
--- a/tests/path.py
+++ b/tests/path.py
@@ -17,6 +17,9 @@ FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding()
class path(str):
+ """
+ Represents a path which behaves like a string.
+ """
if sys.version_info < (3, 0):
def __new__(cls, s, encoding=FILESYSTEMENCODING, errors=None):
if isinstance(s, unicode):
@@ -29,27 +32,103 @@ class path(str):
@property
def parent(self):
+ """
+ The name of the directory the file or directory is in.
+ """
return self.__class__(os.path.dirname(self))
def abspath(self):
+ """
+ Returns the absolute path.
+ """
return self.__class__(os.path.abspath(self))
+ def isabs(self):
+ """
+ Returns ``True`` if the path is absolute.
+ """
+ return os.path.isabs(self)
+
def isdir(self):
+ """
+ Returns ``True`` if the path is a directory.
+ """
return os.path.isdir(self)
def isfile(self):
+ """
+ Returns ``True`` if the path is a file.
+ """
return os.path.isfile(self)
+ def islink(self):
+ """
+ Returns ``True`` if the path is a symbolic link.
+ """
+ return os.path.islink(self)
+
+ def ismount(self):
+ """
+ Returns ``True`` if the path is a mount point.
+ """
+ return os.path.ismount(self)
+
def rmtree(self, ignore_errors=False, onerror=None):
+ """
+ Removes the file or directory and any files or directories it may
+ contain.
+
+ :param ignore_errors:
+ If ``True`` errors are silently ignored, otherwise an exception
+ is raised in case an error occurs.
+
+ :param onerror:
+ A callback which gets called with the arguments `func`, `path` and
+ `exc_info`. `func` is one of :func:`os.listdir`, :func:`os.remove`
+ or :func:`os.rmdir`. `path` is the argument to the function which
+ caused it to fail and `exc_info` is a tuple as returned by
+ :func:`sys.exc_info`.
+ """
shutil.rmtree(self, ignore_errors=ignore_errors, onerror=onerror)
def copytree(self, destination, symlinks=False, ignore=None):
+ """
+ Recursively copy a directory to the given `destination`. If the given
+ `destination` does not exist it will be created.
+
+ :param symlinks:
+ If ``True`` symbolic links in the source tree result in symbolic
+ links in the destination tree otherwise the contents of the files
+ pointed to by the symbolic links are copied.
+
+ :param ignore:
+ A callback which gets called with the path of the directory being
+ copied and a list of paths as returned by :func:`os.listdir`.
+ """
shutil.copytree(self, destination, symlinks=symlinks, ignore=ignore)
+ def movetree(self, destination):
+ """
+ Recursively move the file or directory to the given `destination`
+ similar to the Unix "mv" command.
+
+ If the `destination` is a file it may be overwritten depending on the
+ :func:`os.rename` semantics.
+ """
+ shutil.move(self, destination)
+
+ move = movetree
+
def unlink(self):
+ """
+ Removes a file.
+ """
os.unlink(self)
def write_text(self, text, **kwargs):
+ """
+ Writes the given `text` to the file.
+ """
f = open(self, 'w', **kwargs)
try:
f.write(text)
@@ -57,6 +136,9 @@ class path(str):
f.close()
def text(self, **kwargs):
+ """
+ Returns the text in the file.
+ """
f = open(self, mode='U', **kwargs)
try:
return f.read()
@@ -64,12 +146,28 @@ class path(str):
f.close()
def exists(self):
+ """
+ Returns ``True`` if the path exist.
+ """
return os.path.exists(self)
+ def lexists(self):
+ """
+ Returns ``True`` if the path exists unless it is a broken symbolic
+ link.
+ """
+ return os.path.lexists(self)
+
def makedirs(self, mode=0777):
+ """
+ Recursively create directories.
+ """
os.makedirs(self, mode)
def joinpath(self, *args):
+ """
+ Joins the path with the argument given and returns the result.
+ """
return self.__class__(os.path.join(self, *map(self.__class__, args)))
__div__ = __truediv__ = joinpath
From 87bbe8ddd50deb29eead624ff91356ede8a3fea8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 6 Jun 2010 23:13:06 +0200
Subject: [PATCH 059/207] don't use string.strip anymore
---
tests/test_autosummary.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/tests/test_autosummary.py b/tests/test_autosummary.py
index 7e3093676..20fb06e0e 100644
--- a/tests/test_autosummary.py
+++ b/tests/test_autosummary.py
@@ -9,8 +9,6 @@
:license: BSD, see LICENSE for details.
"""
-import string
-
from util import *
from sphinx.ext.autosummary import mangle_signature
@@ -27,7 +25,7 @@ def test_mangle_signature():
(a, b, c='foobar()', d=123) :: (a, b[, c, d])
"""
- TEST = [map(string.strip, x.split("::")) for x in TEST.split("\n")
+ TEST = [map(lambda x: x.strip(), x.split("::")) for x in TEST.split("\n")
if '::' in x]
for inp, outp in TEST:
res = mangle_signature(inp).strip().replace(u"\u00a0", " ")
From 434da2a4fbd48e437d77b10a70e927ab83309e1c Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:51:57 +0200
Subject: [PATCH 060/207] Use next() function instead of iter.next().
---
sphinx/pycode/__init__.py | 3 ++-
sphinx/util/pycompat.py | 8 ++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index cb9c08878..ef92297c7 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -18,6 +18,7 @@ from sphinx.errors import PycodeError
from sphinx.pycode import nodes
from sphinx.pycode.pgen2 import driver, token, tokenize, parse, literals
from sphinx.util import get_module_source
+from sphinx.util.pycompat import next
from sphinx.util.docstrings import prepare_docstring, prepare_commentdoc
@@ -279,7 +280,7 @@ class ModuleAnalyzer(object):
result[fullname] = (dtype, startline, endline)
expect_indent = False
if tok in ('def', 'class'):
- name = tokeniter.next()[1]
+ name = next(tokeniter)[1]
namespace.append(name)
fullname = '.'.join(namespace)
stack.append((tok, fullname, spos[0], indent))
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index 8787a144e..365cd703c 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -34,6 +34,14 @@ else:
b = str
+try:
+ next
+except NameError:
+ # this is on Python 2, where the method is called "next"
+ def next(iterator):
+ return iterator.next()
+
+
try:
any = any
all = all
From 563f506d1b72188c711df57ad85954869e842550 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 19:08:44 +0200
Subject: [PATCH 061/207] Fix assignment.
---
sphinx/util/pycompat.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index 365cd703c..0725545da 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -35,7 +35,7 @@ else:
try:
- next
+ next = next
except NameError:
# this is on Python 2, where the method is called "next"
def next(iterator):
From 57272142991d7e26fa6bcadb60175a1bd6a23e76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 7 Jun 2010 00:44:09 +0200
Subject: [PATCH 062/207] open file in binary mode to get byte strings on
python3
---
sphinx/directives/code.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index 0647daf09..54d8edf19 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -119,7 +119,7 @@ class LiteralInclude(Directive):
encoding = self.options.get('encoding', env.config.source_encoding)
codec_info = codecs.lookup(encoding)
try:
- f = codecs.StreamReaderWriter(open(fn, 'U'),
+ f = codecs.StreamReaderWriter(open(fn, 'Ub'),
codec_info[2], codec_info[3], 'strict')
lines = f.readlines()
f.close()
From 817e1dd4cda3c23868632c7493c3b764d7bc34ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Mon, 7 Jun 2010 01:34:01 +0200
Subject: [PATCH 063/207] fixed file handling and parsing in intersphinx so it
properly handles encodings
---
sphinx/ext/intersphinx.py | 27 +++++++++++++++++++--------
tests/test_intersphinx.py | 17 ++++++++++-------
2 files changed, 29 insertions(+), 15 deletions(-)
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index fb1f0e4ff..31de2315c 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -28,12 +28,20 @@ import time
import zlib
import urllib2
import posixpath
+import codecs
+import sys
from os import path
from docutils import nodes
from sphinx.builders.html import INVENTORY_FILENAME
+if sys.version_info >= (3, 0):
+ def b(s):
+ return s.encode('utf-8')
+else:
+ b = str
+
handlers = [urllib2.ProxyHandler(), urllib2.HTTPRedirectHandler(),
urllib2.HTTPHandler()]
try:
@@ -43,11 +51,14 @@ except NameError:
urllib2.install_opener(urllib2.build_opener(*handlers))
+UTF8StreamReader = codecs.lookup('utf-8')[2]
+
def read_inventory_v1(f, uri, join):
+ f = UTF8StreamReader(f)
invdata = {}
line = f.next()
- projname = line.rstrip()[11:].decode('utf-8')
+ projname = line.rstrip()[11:]
line = f.next()
version = line.rstrip()[11:]
for line in f:
@@ -70,25 +81,25 @@ def read_inventory_v2(f, uri, join, bufsize=16*1024):
projname = line.rstrip()[11:].decode('utf-8')
line = f.readline()
version = line.rstrip()[11:].decode('utf-8')
- line = f.readline()
+ line = f.readline().decode('utf-8')
if 'zlib' not in line:
raise ValueError
def read_chunks():
decompressor = zlib.decompressobj()
- for chunk in iter(lambda: f.read(bufsize), ''):
+ for chunk in iter(lambda: f.read(bufsize), b('')):
yield decompressor.decompress(chunk)
yield decompressor.flush()
def split_lines(iter):
- buf = ''
+ buf = b('')
for chunk in iter:
buf += chunk
- lineend = buf.find('\n')
+ lineend = buf.find(b('\n'))
while lineend != -1:
yield buf[:lineend].decode('utf-8')
buf = buf[lineend+1:]
- lineend = buf.find('\n')
+ lineend = buf.find(b('\n'))
assert not buf
for line in split_lines(read_chunks()):
@@ -111,13 +122,13 @@ def fetch_inventory(app, uri, inv):
if inv.find('://') != -1:
f = urllib2.urlopen(inv)
else:
- f = open(path.join(app.srcdir, inv))
+ f = open(path.join(app.srcdir, inv), 'rb')
except Exception, err:
app.warn('intersphinx inventory %r not fetchable due to '
'%s: %s' % (inv, err.__class__, err))
return
try:
- line = f.readline().rstrip()
+ line = f.readline().rstrip().decode('utf-8')
try:
if line == '# Sphinx inventory version 1':
invdata = read_inventory_v1(f, uri, join)
diff --git a/tests/test_intersphinx.py b/tests/test_intersphinx.py
index 8b6547e54..4f70bd205 100644
--- a/tests/test_intersphinx.py
+++ b/tests/test_intersphinx.py
@@ -11,7 +11,10 @@
import zlib
import posixpath
-from cStringIO import StringIO
+try:
+ from io import BytesIO
+except ImportError:
+ from cStringIO import StringIO as BytesIO
from docutils import nodes
@@ -28,23 +31,23 @@ inventory_v1 = '''\
# Version: 1.0
module mod foo.html
module.cls class foo.html
-'''
+'''.encode('utf-8')
inventory_v2 = '''\
# Sphinx inventory version 2
# Project: foo
# Version: 2.0
# The remainder of this file is compressed with zlib.
-''' + zlib.compress('''\
+'''.encode('utf-8') + zlib.compress('''\
module1 py:module 0 foo.html#module-module1 Long Module desc
module2 py:module 0 foo.html#module-$ -
module1.func py:function 1 sub/foo.html#$ -
CFunc c:function 2 cfunc.html#CFunc -
-''')
+'''.encode('utf-8'))
def test_read_inventory_v1():
- f = StringIO(inventory_v1)
+ f = BytesIO(inventory_v1)
f.readline()
invdata = read_inventory_v1(f, '/util', posixpath.join)
assert invdata['py:module']['module'] == \
@@ -54,12 +57,12 @@ def test_read_inventory_v1():
def test_read_inventory_v2():
- f = StringIO(inventory_v2)
+ f = BytesIO(inventory_v2)
f.readline()
invdata1 = read_inventory_v2(f, '/util', posixpath.join)
# try again with a small buffer size to test the chunking algorithm
- f = StringIO(inventory_v2)
+ f = BytesIO(inventory_v2)
f.readline()
invdata2 = read_inventory_v2(f, '/util', posixpath.join, bufsize=5)
From 2737ec4dc9ab633f38765e9553a85043fe6fe49b Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:54:24 +0200
Subject: [PATCH 064/207] Use b() from pycompat.
---
sphinx/ext/intersphinx.py | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 31de2315c..07ee24e38 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -26,21 +26,16 @@
import time
import zlib
+import codecs
import urllib2
import posixpath
-import codecs
-import sys
from os import path
from docutils import nodes
from sphinx.builders.html import INVENTORY_FILENAME
+from sphinx.util.pycompat import b
-if sys.version_info >= (3, 0):
- def b(s):
- return s.encode('utf-8')
-else:
- b = str
handlers = [urllib2.ProxyHandler(), urllib2.HTTPRedirectHandler(),
urllib2.HTTPHandler()]
From bade03c8243cbdf7296126c3a1c54bccccc354ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 12 Jun 2010 15:16:57 +0200
Subject: [PATCH 065/207] Copy converted tests to build/lib/tests not
build/lib/test
---
tests/run.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/run.py b/tests/run.py
index 5fd30d62b..f384fe267 100755
--- a/tests/run.py
+++ b/tests/run.py
@@ -14,10 +14,10 @@ import sys
from os import path, chdir
if sys.version_info >= (3,):
- print('Copying and converting sources to build/lib/test...')
+ print('Copying and converting sources to build/lib/tests...')
from distutils.util import copydir_run_2to3
testroot = path.dirname(__file__) or '.'
- newroot = path.join(testroot, path.pardir, 'build', 'lib', 'test')
+ newroot = path.join(testroot, path.pardir, 'build', 'lib', 'tests')
copydir_run_2to3(testroot, newroot)
# switch to the converted dir so nose tests the right tests
chdir(newroot)
From 79a958b867c2941d68fbb9bd594a1c26f511003c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 12 Jun 2010 20:21:14 +0200
Subject: [PATCH 066/207] Remove unnecessary code
---
tests/path.py | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/tests/path.py b/tests/path.py
index dccdac3ab..28a8c22af 100644
--- a/tests/path.py
+++ b/tests/path.py
@@ -21,12 +21,9 @@ class path(str):
Represents a path which behaves like a string.
"""
if sys.version_info < (3, 0):
- def __new__(cls, s, encoding=FILESYSTEMENCODING, errors=None):
+ def __new__(cls, s, encoding=FILESYSTEMENCODING, errors='strict'):
if isinstance(s, unicode):
- if errors is None:
- s = s.encode(encoding)
- else:
- s = s.encode(encoding, errors=errors)
+ s = s.encode(encoding, errors=errors)
return str.__new__(cls, s)
return str.__new__(cls, s)
From f0c316f6c57632dbdd2a13cb7021db90854138f9 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:55:39 +0200
Subject: [PATCH 067/207] Pass document name as bytes.
---
sphinx/builders/html.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 8d96c146c..c5f7312a4 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -35,7 +35,7 @@ from sphinx.util.osutil import SEP, os_path, relative_uri, ensuredir, \
movefile, ustrftime, copyfile
from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.matching import patmatch, compile_matchers
-from sphinx.util.pycompat import any
+from sphinx.util.pycompat import any, b
from sphinx.errors import SphinxError
from sphinx.locale import _
from sphinx.search import js_index
@@ -199,7 +199,7 @@ class StandaloneHTMLBuilder(Builder):
"""Utility: Render a lone doctree node."""
if node is None:
return {'fragment': ''}
- doc = new_document('')
+ doc = new_document(b(''))
doc.append(node)
if self._publisher is None:
From ddefaff0b7144d81e3fcedc03d563becc99f90cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sun, 13 Jun 2010 23:08:32 +0200
Subject: [PATCH 068/207] fix a unboundlocalerror occuring with python3
---
sphinx/ext/doctest.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index 9d681f904..62fbfdff4 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -149,14 +149,14 @@ class TestCode(object):
class SphinxDocTestRunner(doctest.DocTestRunner):
def summarize(self, out, verbose=None):
- io = StringIO.StringIO()
+ string_io = StringIO.StringIO()
old_stdout = sys.stdout
- sys.stdout = io
+ sys.stdout = string_io
try:
res = doctest.DocTestRunner.summarize(self, verbose)
finally:
sys.stdout = old_stdout
- out(io.getvalue())
+ out(string_io.getvalue())
return res
def _DocTestRunner__patched_linecache_getlines(self, filename,
From 4534657f3e37a8aa59e5135f9a9f6d18f1ab6aae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Tue, 15 Jun 2010 23:16:33 +0200
Subject: [PATCH 069/207] update distribute_setup.py from
http://python-distribute.org/distribute_setup.py
---
distribute_setup.py | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/distribute_setup.py b/distribute_setup.py
index 4f7bd08c0..37117b34e 100644
--- a/distribute_setup.py
+++ b/distribute_setup.py
@@ -46,7 +46,7 @@ except ImportError:
args = [quote(arg) for arg in args]
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
-DEFAULT_VERSION = "0.6.12"
+DEFAULT_VERSION = "0.6.13"
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
SETUPTOOLS_FAKED_VERSION = "0.6c11"
@@ -227,7 +227,6 @@ def _no_sandbox(function):
return __no_sandbox
-@_no_sandbox
def _patch_file(path, content):
"""Will backup the file then patch it"""
existing_content = open(path).read()
@@ -244,6 +243,7 @@ def _patch_file(path, content):
f.close()
return True
+_patch_file = _no_sandbox(_patch_file)
def _same_content(path, content):
return open(path).read() == content
@@ -254,7 +254,6 @@ def _rename_path(path):
os.rename(path, new_name)
return new_name
-@_no_sandbox
def _remove_flat_installation(placeholder):
if not os.path.isdir(placeholder):
log.warn('Unkown installation at %s', placeholder)
@@ -288,13 +287,13 @@ def _remove_flat_installation(placeholder):
'Setuptools distribution', element)
return True
+_remove_flat_installation = _no_sandbox(_remove_flat_installation)
def _after_install(dist):
log.warn('After install bootstrap.')
placeholder = dist.get_command_obj('install').install_purelib
_create_fake_setuptools_pkg_info(placeholder)
-@_no_sandbox
def _create_fake_setuptools_pkg_info(placeholder):
if not placeholder or not os.path.exists(placeholder):
log.warn('Could not find the install location')
@@ -322,7 +321,8 @@ def _create_fake_setuptools_pkg_info(placeholder):
finally:
f.close()
-@_no_sandbox
+_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
+
def _patch_egg_dir(path):
# let's check if it's already patched
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
@@ -341,6 +341,7 @@ def _patch_egg_dir(path):
f.close()
return True
+_patch_egg_dir = _no_sandbox(_patch_egg_dir)
def _before_install():
log.warn('Before install bootstrap.')
@@ -360,8 +361,8 @@ def _under_prefix(location):
if len(args) > index:
top_dir = args[index+1]
return location.startswith(top_dir)
- elif option == '--user' and USER_SITE is not None:
- return location.startswith(USER_SITE)
+ if arg == '--user' and USER_SITE is not None:
+ return location.startswith(USER_SITE)
return True
@@ -420,6 +421,9 @@ def _fake_setuptools():
def _relaunch():
log.warn('Relaunching...')
# we have to relaunch the process
+ # pip marker to avoid a relaunch bug
+ if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
+ sys.argv[0] = 'setup.py'
args = [sys.executable] + sys.argv
sys.exit(subprocess.call(args))
From bf14a7c362e97127ca6ab60d962e2731c0f337fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Wed, 16 Jun 2010 23:20:30 +0200
Subject: [PATCH 070/207] Encode strings after they have been formatted
---
sphinx/builders/html.py | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index c5f7312a4..4e07acf78 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -727,10 +727,12 @@ class StandaloneHTMLBuilder(Builder):
self.info(bold('dumping object inventory... '), nonl=True)
f = open(path.join(self.outdir, INVENTORY_FILENAME), 'wb')
try:
- f.write('# Sphinx inventory version 2\n')
- f.write('# Project: %s\n' % self.config.project.encode('utf-8'))
- f.write('# Version: %s\n' % self.config.version.encode('utf-8'))
- f.write('# The remainder of this file is compressed using zlib.\n')
+ f.write((u'# Sphinx inventory version 2\n'
+ u'# Project: %s\n'
+ u'# Version: %s\n'
+ u'# The remainder of this file is compressed using zlib.\n'
+ % (self.config.project, self.config.version))\
+ .encode('utf-8'))
compressor = zlib.compressobj(9)
for domainname, domain in self.env.domains.iteritems():
for name, dispname, type, docname, anchor, prio in \
@@ -742,11 +744,9 @@ class StandaloneHTMLBuilder(Builder):
if dispname == name:
dispname = u'-'
f.write(compressor.compress(
- '%s %s:%s %s %s %s\n' % (name.encode('utf-8'),
- domainname.encode('utf-8'),
- type.encode('utf-8'), prio,
- uri.encode('utf-8'),
- dispname.encode('utf-8'))))
+ (u'%s %s:%s %s %s %s\n' % (name, domainname, type, prio,
+ uri, dispname))\
+ .encode('utf-8')))
f.write(compressor.flush())
finally:
f.close()
From 7add1755985875eb79b2bc724cbbfde23d685c61 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 18:58:18 +0200
Subject: [PATCH 071/207] Fix code formatting.
---
sphinx/builders/html.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 4e07acf78..0b39d38e7 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -731,8 +731,8 @@ class StandaloneHTMLBuilder(Builder):
u'# Project: %s\n'
u'# Version: %s\n'
u'# The remainder of this file is compressed using zlib.\n'
- % (self.config.project, self.config.version))\
- .encode('utf-8'))
+ % (self.config.project, self.config.version)
+ ).encode('utf-8'))
compressor = zlib.compressobj(9)
for domainname, domain in self.env.domains.iteritems():
for name, dispname, type, docname, anchor, prio in \
@@ -744,9 +744,9 @@ class StandaloneHTMLBuilder(Builder):
if dispname == name:
dispname = u'-'
f.write(compressor.compress(
- (u'%s %s:%s %s %s %s\n' % (name, domainname, type, prio,
- uri, dispname))\
- .encode('utf-8')))
+ (u'%s %s:%s %s %s %s\n' % (name, domainname, type,
+ prio, uri, dispname)
+ ).encode('utf-8')))
f.write(compressor.flush())
finally:
f.close()
From 9e56fac893192187c033fbdf5e008459bc4b3ee7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Thu, 17 Jun 2010 05:26:19 +0200
Subject: [PATCH 072/207] Decode templates using utf-8 as jinja2 requires that
for python3. This change will very likely cause problems for some people
---
sphinx/util/__init__.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 2ef420ed1..ec48009f4 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -18,6 +18,7 @@ import tempfile
import posixpath
import traceback
from os import path
+from codecs import open
import docutils
from docutils.utils import relative_path
@@ -140,8 +141,8 @@ def copy_static_entry(source, targetdir, builder, context={},
target = path.join(targetdir, path.basename(source))
if source.lower().endswith('_t') and builder.templates:
# templated!
- fsrc = open(source, 'rb')
- fdst = open(target[:-2], 'wb')
+ fsrc = open(source, 'r', encoding='utf-8')
+ fdst = open(target[:-2], 'w', encoding='utf-8')
fdst.write(builder.templates.render_string(fsrc.read(), context))
fsrc.close()
fdst.close()
From 24a1512293a09f865c16a28e4d42580a4589e1d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Thu, 17 Jun 2010 05:38:22 +0200
Subject: [PATCH 073/207] Removed duplicated code
---
sphinx/builders/qthelp.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py
index ffc52334c..53c7a9b15 100644
--- a/sphinx/builders/qthelp.py
+++ b/sphinx/builders/qthelp.py
@@ -230,7 +230,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
link = node['refuri']
title = escape(node.astext()).replace('"','"')
item = section_template % {'title': title, 'ref': link}
- item = ' '*4*indentlevel + item.encode('ascii', 'xmlcharrefreplace')
+ item = u' ' * 4 * indentlevel + item
parts.append(item.encode('ascii', 'xmlcharrefreplace'))
elif isinstance(node, nodes.bullet_list):
for subnode in node:
From 579528e0b3182e30ee00577b1a753d20533e6976 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Thu, 17 Jun 2010 05:47:44 +0200
Subject: [PATCH 074/207] Fixed error caused by mixing of string types
---
sphinx/builders/qthelp.py | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py
index 53c7a9b15..e86f19217 100644
--- a/sphinx/builders/qthelp.py
+++ b/sphinx/builders/qthelp.py
@@ -130,8 +130,16 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
for indexname, indexcls, content, collapse in self.domain_indices:
item = section_template % {'title': indexcls.localname,
'ref': '%s.html' % indexname}
- sections.append(' '*4*4 + item)
- sections = '\n'.join(sections)
+ sections.append((' ' * 4 * 4 + item).encode('utf-8'))
+ # sections may be unicode strings or byte strings, we have to make sure
+ # they are all byte strings before joining them
+ new_sections = []
+ for section in sections:
+ if isinstance(section, unicode):
+ new_sections.append(section.encode('utf-8'))
+ else:
+ new_sections.append(section)
+ sections = u'\n'.encode('utf-8').join(new_sections)
# keywords
keywords = []
From b1f29495ab10503e8c942d4f49d4b195d325b23f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Thu, 17 Jun 2010 05:51:24 +0200
Subject: [PATCH 075/207] Don't mix string types when writing to a stream
---
sphinx/builders/htmlhelp.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
index 538f4c848..e3a58e724 100644
--- a/sphinx/builders/htmlhelp.py
+++ b/sphinx/builders/htmlhelp.py
@@ -258,7 +258,8 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
def write_index(title, refs, subitems):
def write_param(name, value):
item = ' \n' % (name, value)
- f.write(item.encode('ascii', 'xmlcharrefreplace'))
+ f.write(item.encode('ascii', 'xmlcharrefreplace')
+ .decode('ascii'))
title = cgi.escape(title)
f.write('
From 2d0c9a0e7155bb71a8bc76fd6d5f2d2145c9cdbd Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 19:43:30 +0200
Subject: [PATCH 172/207] Move item to the correct section.
---
CHANGES | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/CHANGES b/CHANGES
index 573b4edd6..f1fb982b8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
Release 1.1 (in development)
============================
+* Added Python 3.x support.
+
+
Release 1.0.2 (in development)
==============================
@@ -23,8 +26,6 @@ Release 1.0.1 (Jul 27, 2010)
* Fix hyperrefs in object descriptions for LaTeX.
- * Added Python 3.x support.
-
Release 1.0 (Jul 23, 2010)
==========================
From c683c6ed833766c63c20fc33c830a8db5604979a Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 19:49:06 +0200
Subject: [PATCH 173/207] Add some changes not picked up in the transplantation
process.
---
Makefile | 2 +-
README | 12 ++++++++++++
doc/intro.rst | 8 ++++----
tests/test_build_html.py | 9 +++++----
4 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/Makefile b/Makefile
index 13228c788..fc1140f33 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-PYTHON ?= python3
+PYTHON ?= python
.PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint \
reindent test
diff --git a/README b/README
index bb2dea9d6..e31d6b936 100644
--- a/README
+++ b/README
@@ -26,6 +26,18 @@ Then, direct your browser to ``_build/html/index.html``.
Or read them online at .
+Testing
+=======
+
+To run the tests with the interpreter available as ``python``, use::
+
+ make test
+
+If you want to use a different interpreter, e.g. ``python3``, use::
+
+ PYTHON=python3 make test
+
+
Contributing
============
diff --git a/doc/intro.rst b/doc/intro.rst
index 1a39e266c..c85fbbad3 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -45,10 +45,10 @@ See the :ref:`pertinent section in the FAQ list `.
Prerequisites
-------------
-Sphinx needs at least **Python 2.4** to run, as well as the docutils_ and
-Jinja2_ libraries. Sphinx should work with docutils version 0.5 or some
-(not broken) SVN trunk snapshot. If you like to have source code highlighting
-support, you must also install the Pygments_ library.
+Sphinx needs at least **Python 2.4** or **Python 3.1** to run, as well as the
+docutils_ and Jinja2_ libraries. Sphinx should work with docutils version 0.5
+or some (not broken) SVN trunk snapshot. If you like to have source code
+highlighting support, you must also install the Pygments_ library.
.. _reStructuredText: http://docutils.sf.net/rst.html
.. _docutils: http://docutils.sf.net/
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 3ca2c757f..0c59d9cca 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -51,6 +51,11 @@ HTML_WARNINGS = ENV_WARNINGS + """\
%(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; '
"""
+if sys.version_info >= (3, 0):
+ ENV_WARNINGS = remove_unicode_literals(ENV_WARNINGS)
+ HTML_WARNINGS = remove_unicode_literals(HTML_WARNINGS)
+
+
def tail_check(check):
rex = re.compile(check)
def checker(nodes):
@@ -61,10 +66,6 @@ def tail_check(check):
return checker
-if sys.version_info >= (3, 0):
- ENV_WARNINGS = remove_unicode_literals(ENV_WARNINGS)
- HTML_WARNINGS = remove_unicode_literals(HTML_WARNINGS)
-
HTML_XPATH = {
'images.html': [
(".//img[@src='_images/img.png']", ''),
From 7866ce29c69e6a843c4bf0d5472e29f15e70ae87 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 19:58:17 +0200
Subject: [PATCH 174/207] Keep sphinx/__init__.py executable with Python 3.
---
sphinx/__init__.py | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 31c61a86a..1ea2e7bf7 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -9,6 +9,9 @@
:license: BSD, see LICENSE for details.
"""
+# Keep this file executable as-is in Python 3!
+# (Otherwise getting the version out of it from setup.py is impossible.)
+
import sys
from os import path
@@ -35,13 +38,14 @@ if '+' in __version__ or 'pre' in __version__:
def main(argv=sys.argv):
if sys.version_info[:3] < (2, 4, 0):
- print >>sys.stderr, \
- 'Error: Sphinx requires at least Python 2.4 to run.'
+ sys.stderr.write('Error: Sphinx requires at least '
+ 'Python 2.4 to run.\n')
return 1
try:
from sphinx import cmdline
- except ImportError, err:
+ except ImportError:
+ err = sys.exc_info()[1]
errstr = str(err)
if errstr.lower().startswith('no module named'):
whichmod = errstr[16:]
@@ -54,14 +58,14 @@ def main(argv=sys.argv):
whichmod = 'roman module (which is distributed with Docutils)'
hint = ('This can happen if you upgraded docutils using\n'
'easy_install without uninstalling the old version'
- 'first.')
+ 'first.\n')
else:
whichmod += ' module'
- print >>sys.stderr, ('Error: The %s cannot be found. '
- 'Did you install Sphinx and its dependencies '
- 'correctly?' % whichmod)
+ sys.stderr.write('Error: The %s cannot be found. '
+ 'Did you install Sphinx and its dependencies '
+ 'correctly?\n' % whichmod)
if hint:
- print >> sys.stderr, hint
+ sys.stderr.write(hint)
return 1
raise
return cmdline.main(argv)
From 829a05e1ddf9bcf06911804b292a287a7ad7ff27 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 19:58:47 +0200
Subject: [PATCH 175/207] Ignore distribute files.
---
.hgignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.hgignore b/.hgignore
index 40c00aac7..b68cccf91 100644
--- a/.hgignore
+++ b/.hgignore
@@ -16,3 +16,4 @@
\.DS_Store$
~$
^utils/.*3\.py$
+^distribute-
From 67ffb9bf5477b6d9489157ba761065f9218b937b Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 20:24:35 +0200
Subject: [PATCH 176/207] Fix raw_input which is not converted by 2to3 if not
called.
---
sphinx/quickstart.py | 8 ++++++--
tests/test_quickstart.py | 7 ++++++-
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 557f8c094..92a5bea2a 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -22,7 +22,11 @@ from sphinx.util.console import purple, bold, red, turquoise, \
from sphinx.util import texescape
# function to get input from terminal -- overridden by the test suite
-term_input = raw_input
+try:
+ # this raw_input is not converted by 2to3
+ term_input = raw_input
+except NameError:
+ term_input = input
PROMPT_PREFIX = '> '
@@ -692,7 +696,7 @@ if sys.version_info >= (3, 0):
return _unicode_string_re.sub('\\1', source)
for f in ['QUICKSTART_CONF', 'EPUB_CONFIG', 'INTERSPHINX_CONFIG']:
- globals()[f] = convert_python_source(globals()[f])
+ globals()[f] = _convert_python_source(globals()[f])
del _unicode_string_re, _convert_python_source
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index 72ae764d6..541959bd3 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -36,8 +36,13 @@ def mock_raw_input(answers, needanswer=False):
return ''
return raw_input
+try:
+ real_raw_input = raw_input
+except NameError:
+ real_raw_input = input
+
def teardown_module():
- qs.term_input = raw_input
+ qs.term_input = real_raw_input
qs.TERM_ENCODING = getattr(sys.stdin, 'encoding', None)
coloron()
From 27b4265a2f002e317872149eb5e8a28df407f005 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 28 Jul 2010 20:30:05 +0200
Subject: [PATCH 177/207] Update phony targets list.
---
Makefile | 4 ++--
sphinx/ext/oldcmarkup.py | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index fc1140f33..09aa3c962 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
PYTHON ?= python
-.PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint \
- reindent test
+.PHONY: all check clean clean-pyc clean-patchfiles clean-backupfiles \
+ clean-generated pylint reindent test covertest build convert-utils
DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \
-i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \
diff --git a/sphinx/ext/oldcmarkup.py b/sphinx/ext/oldcmarkup.py
index 84ae61dd2..3aa53fd86 100644
--- a/sphinx/ext/oldcmarkup.py
+++ b/sphinx/ext/oldcmarkup.py
@@ -31,6 +31,7 @@ class OldCDirective(Directive):
def run(self):
env = self.state.document.settings.env
if not env.app._oldcmarkup_warned:
+ print 'XXXYYY'
env.warn(env.docname, WARNING_MSG, self.lineno)
env.app._oldcmarkup_warned = True
newname = 'c:' + self.name[1:]
From cb26c7807943853a3393a7e8deba57131762342f Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 29 Jul 2010 09:27:46 +0200
Subject: [PATCH 178/207] Describe the "deprecated" directive.
---
doc/markup/para.rst | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/doc/markup/para.rst b/doc/markup/para.rst
index be06f6365..ecc6b4a6b 100644
--- a/doc/markup/para.rst
+++ b/doc/markup/para.rst
@@ -42,15 +42,25 @@ units as well as normal text:
Example::
.. versionadded:: 2.5
- The `spam` parameter.
+ The *spam* parameter.
Note that there must be no blank line between the directive head and the
explanation; this is to make these blocks visually continuous in the markup.
.. rst:directive:: .. versionchanged:: version
- Similar to :rst:dir:`versionadded`, but describes when and what changed in the named
- feature in some way (new parameters, changed side effects, etc.).
+ Similar to :rst:dir:`versionadded`, but describes when and what changed in
+ the named feature in some way (new parameters, changed side effects, etc.).
+
+.. rst:directive:: .. deprecated:: vesion
+
+ Similar to :rst:dir:`versionchanged`, but describes when the feature was
+ deprecated. An explanation can also be given, for example to inform the
+ reader what should be used instead. Example::
+
+ .. deprecated:: 3.1
+ Use :func:`spam` instead.
+
--------------
From 86cd745dc1f5338804fa5adf24447a9427c09fb8 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Fri, 30 Jul 2010 16:59:47 +0200
Subject: [PATCH 179/207] Run 2to3 on config files which contain Python 2.x
unicode literals.
---
sphinx/config.py | 27 +++++++++++------
sphinx/quickstart.py | 8 ++++-
sphinx/util/pycompat.py | 67 ++++++++++++++++++++++++++++++++++++++---
3 files changed, 87 insertions(+), 15 deletions(-)
diff --git a/sphinx/config.py b/sphinx/config.py
index 708da162e..efa9f7407 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -13,13 +13,20 @@ import os
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."""
@@ -167,15 +174,17 @@ class Config(object):
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)
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index 92a5bea2a..7e38a742a 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -31,7 +31,13 @@ except NameError:
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
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index 229b54b45..2ec71e72b 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -12,6 +12,7 @@
import sys
import codecs
import encodings
+import re
try:
from types import ClassType
@@ -20,11 +21,6 @@ except ImportError:
# 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):
@@ -34,6 +30,66 @@ else:
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:
@@ -41,6 +97,7 @@ except NameError:
def next(iterator):
return iterator.next()
+
try:
bytes = bytes
except NameError:
From 0e84c758229f0a71a52b33c5e19d2e0b7d8b0ef1 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sat, 31 Jul 2010 19:47:15 +0200
Subject: [PATCH 180/207] Improve support for automatic 2to3 conversion of
config files. It now kicks in whenever the original file raises SyntaxErrors
on compiling.
---
sphinx/config.py | 37 +++++++++++++----------
sphinx/util/pycompat.py | 65 +++++++++++------------------------------
tests/test_config.py | 6 ++++
3 files changed, 45 insertions(+), 63 deletions(-)
diff --git a/sphinx/config.py b/sphinx/config.py
index efa9f7407..6c27f85f0 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -13,14 +13,10 @@ import os
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, should_run_2to3, run_2to3
+from sphinx.util.pycompat import bytes, b, convert_with_2to3
nonascii_re = re.compile(b(r'[\x80-\xff]'))
@@ -172,17 +168,28 @@ class Config(object):
config['tags'] = tags
olddir = os.getcwd()
try:
+ # we promise to have the config dir as current dir while the
+ # config file is executed
+ os.chdir(dirname)
+ # get config source
+ f = open(config_file, 'rb')
try:
- os.chdir(dirname)
- 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
+ source = f.read()
+ finally:
+ f.close()
+ try:
+ # compile to a code object, handle syntax errors
+ try:
+ code = compile(source, config_file, '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(config_file)
+ code = compile(source, config_file, 'exec')
+ else:
+ raise
+ exec code in config
except SyntaxError, err:
raise ConfigError(CONFIG_SYNTAX_ERROR % err)
finally:
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index 2ec71e72b..5f23bbe18 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -30,58 +30,26 @@ else:
b = str
-encoding_re = re.compile(b(r'coding[=:]\s*([-\w.]+)'))
-unicode_literal_re = re.compile(ur"""
-(?:
- "(?:[^"\]]*(?:\\.[^"\\]*)*)"|
- '(?:[^'\]]*(?:\\.[^'\\]*)*)'
-)
-""", re.VERBOSE)
+# Support for running 2to3 over config files
-
-try:
- from lib2to3.refactor import RefactoringTool, get_fixers_from_package
-except ImportError:
- _run_2to3 = None
- def should_run_2to3(filepath):
- return False
+if sys.version_info < (3, 0):
+ # no need to refactor on 2.x versions
+ convert_with_2to3 = None
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('..')
+ def convert_with_2to3(filepath):
+ from lib2to3.refactor import RefactoringTool, get_fixers_from_package
+ from lib2to3.pgen2.parse import ParseError
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:
+ tree = refactoring_tool.refactor_string(source, 'conf.py')
+ except ParseError, 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 unicode(tree)
try:
@@ -93,7 +61,8 @@ except NameError:
try:
next = next
except NameError:
- # this is on Python 2, where the method is called "next"
+ # this is on Python 2, where the method is called "next" (it is refactored
+ # to __next__ by 2to3, but in that case never executed)
def next(iterator):
return iterator.next()
diff --git a/tests/test_config.py b/tests/test_config.py
index ecf90f609..7fce4495b 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -88,6 +88,12 @@ def test_errors_warnings(dir):
write_file(dir / 'conf.py', u'project = \n', 'ascii')
raises_msg(ConfigError, 'conf.py', Config, dir, 'conf.py', {}, None)
+ # test the automatic conversion of 2.x only code in configs
+ write_file(dir / 'conf.py', u'\n\nproject = u"Jägermeister"\n', 'utf-8')
+ cfg = Config(dir, 'conf.py', {}, None)
+ cfg.init_values()
+ assert cfg.project == u'Jägermeister'
+
# test the warning for bytestrings with non-ascii content
# bytestrings with non-ascii content are a syntax error in python3 so we
# skip the test there
From 1e010aa858ba7e53f26cdbc0ef7b6f6fb5de4302 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=89ric=20Araujo?=
Date: Mon, 2 Aug 2010 13:30:59 +0200
Subject: [PATCH 181/207] Fix ASCII diagram
---
sphinx/ext/inheritance_diagram.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index b930d8cab..a12bad256 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-"""
+r"""
sphinx.ext.inheritance_diagram
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
From bf159679697f9940298ca99582b71314b4d144a1 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 10:14:10 +0200
Subject: [PATCH 182/207] #486: Fix removal of ``!`` for all cross-reference
roles.
---
CHANGES | 3 +++
sphinx/roles.py | 4 ++--
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGES b/CHANGES
index 48d03e785..dce6e713a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
Release 1.0.2 (in development)
==============================
+* #486: Fix removal of ``!`` for all cross-reference roles.
+
+
Release 1.0.1 (Jul 27, 2010)
============================
diff --git a/sphinx/roles.py b/sphinx/roles.py
index d3f3c67e3..bacdad5b6 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -105,9 +105,9 @@ class XRefRole(object):
classes = ['xref', domain, '%s-%s' % (domain, role)]
# if the first character is a bang, don't cross-reference at all
if text[0:1] == '!':
- text = utils.unescape(text)
+ text = utils.unescape(text)[1:]
if self.fix_parens:
- text, tgt = self._fix_parens(env, False, text[1:], "")
+ text, tgt = self._fix_parens(env, False, text, "")
innernode = self.innernodeclass(rawtext, text, classes=classes)
return self.result_nodes(inliner.document, env, innernode,
is_ref=False)
From 01c501054e4e42e2c614c7a244f8b769938909f8 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 11:58:43 +0200
Subject: [PATCH 183/207] #480: Fix handling of target naming in intersphinx.
---
CHANGES | 2 ++
doc/conf.py | 2 ++
doc/ext/intersphinx.rst | 6 ++--
sphinx/ext/intersphinx.py | 17 +++++++++--
tests/test_intersphinx.py | 60 +++++++++++++++++++++++----------------
5 files changed, 57 insertions(+), 30 deletions(-)
diff --git a/CHANGES b/CHANGES
index dce6e713a..cb80d72c6 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
Release 1.0.2 (in development)
==============================
+* #480: Fix handling of target naming in intersphinx.
+
* #486: Fix removal of ``!`` for all cross-reference roles.
diff --git a/doc/conf.py b/doc/conf.py
index 299f321ac..21e8d2f54 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -8,6 +8,8 @@ import sphinx
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
'sphinx.ext.autosummary', 'sphinx.ext.extlinks']
+#intersphinx_mapping = {'python': ('http://docs.python.org/dev', None)}
+
master_doc = 'contents'
templates_path = ['_templates']
exclude_patterns = ['_build']
diff --git a/doc/ext/intersphinx.rst b/doc/ext/intersphinx.rst
index bb2a5a8c4..7997472a7 100644
--- a/doc/ext/intersphinx.rst
+++ b/doc/ext/intersphinx.rst
@@ -84,7 +84,7 @@ linking:
To add links to modules and objects in the Python standard library
documentation, use::
- intersphinx_mapping = {'python': ('http://docs.python.org/', None)}
+ intersphinx_mapping = {'python': ('http://docs.python.org/3.2', None)}
This will download the corresponding :file:`objects.inv` file from the
Internet and generate links to the pages under the given URI. The downloaded
@@ -94,12 +94,12 @@ linking:
A second example, showing the meaning of a non-``None`` value of the second
tuple item::
- intersphinx_mapping = {'python': ('http://docs.python.org/',
+ intersphinx_mapping = {'python': ('http://docs.python.org/3.2',
'python-inv.txt')}
This will read the inventory from :file:`python-inv.txt` in the source
directory, but still generate links to the pages under
- ``http://docs.python.org/``. It is up to you to update the inventory file as
+ ``http://docs.python.org/3.2``. It is up to you to update the inventory file as
new objects are added to the Python documentation.
.. confval:: intersphinx_cache_limit
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 0a210879a..055fc9cd9 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -205,9 +205,20 @@ def missing_reference(app, env, node, contnode):
proj, version, uri, dispname = inventory[objtype][target]
newnode = nodes.reference('', '', internal=False, refuri=uri,
reftitle='(in %s v%s)' % (proj, version))
- if dispname == '-':
- dispname = target
- newnode.append(contnode.__class__(dispname, dispname))
+ if node.get('refexplicit'):
+ # use whatever title was given
+ newnode.append(contnode)
+ elif dispname == '-':
+ # use whatever title was given, but strip prefix
+ title = contnode.astext()
+ if in_set and title.startswith(in_set+':'):
+ newnode.append(contnode.__class__(title[len(in_set)+1:],
+ title[len(in_set)+1:]))
+ else:
+ newnode.append(contnode)
+ else:
+ # else use the given display name (used for :ref:)
+ newnode.append(contnode.__class__(dispname, dispname))
return newnode
# at least get rid of the ':' in the target if no explicit title given
if in_set is not None and not node.get('refexplicit', True):
diff --git a/tests/test_intersphinx.py b/tests/test_intersphinx.py
index 8b6547e54..3b50cc78c 100644
--- a/tests/test_intersphinx.py
+++ b/tests/test_intersphinx.py
@@ -94,46 +94,58 @@ def test_missing_reference(tempdir, app):
('foo', '2.0', 'http://docs.python.org/foo.html#module-module2', '-')
# create fake nodes and check referencing
- contnode = nodes.emphasis('foo', 'foo')
- refnode = addnodes.pending_xref('')
- refnode['reftarget'] = 'module1.func'
- refnode['reftype'] = 'func'
- refnode['refdomain'] = 'py'
- rn = missing_reference(app, app.env, refnode, contnode)
+ def fake_node(domain, type, target, content, **attrs):
+ contnode = nodes.emphasis(content, content)
+ node = addnodes.pending_xref('')
+ node['reftarget'] = target
+ node['reftype'] = type
+ node['refdomain'] = domain
+ node.attributes.update(attrs)
+ node += contnode
+ return node, contnode
+
+ def reference_check(*args, **kwds):
+ node, contnode = fake_node(*args, **kwds)
+ return missing_reference(app, app.env, node, contnode)
+
+ # check resolution when a target is found
+ rn = reference_check('py', 'func', 'module1.func', 'foo')
assert isinstance(rn, nodes.reference)
assert rn['refuri'] == 'http://docs.python.org/sub/foo.html#module1.func'
assert rn['reftitle'] == '(in foo v2.0)'
- assert rn[0].astext() == 'module1.func'
+ assert rn[0].astext() == 'foo'
# create unresolvable nodes and check None return value
- refnode['reftype'] = 'foo'
- assert missing_reference(app, app.env, refnode, contnode) is None
-
- refnode['reftype'] = 'function'
- refnode['reftarget'] = 'foo.func'
- assert missing_reference(app, app.env, refnode, contnode) is None
+ assert reference_check('py', 'foo', 'module1.func', 'foo') is None
+ assert reference_check('py', 'func', 'foo', 'foo') is None
+ assert reference_check('py', 'func', 'foo', 'foo') is None
# check handling of prefixes
# prefix given, target found: prefix is stripped
- refnode['reftype'] = 'mod'
- refnode['reftarget'] = 'py3k:module2'
- rn = missing_reference(app, app.env, refnode, contnode)
+ rn = reference_check('py', 'mod', 'py3k:module2', 'py3k:module2')
assert rn[0].astext() == 'module2'
+ # prefix given, but not in title: nothing stripped
+ rn = reference_check('py', 'mod', 'py3k:module2', 'module2')
+ assert rn[0].astext() == 'module2'
+
+ # prefix given, but explicit: nothing stripped
+ rn = reference_check('py', 'mod', 'py3k:module2', 'py3k:module2',
+ refexplicit=True)
+ assert rn[0].astext() == 'py3k:module2'
+
# prefix given, target not found and nonexplicit title: prefix is stripped
- refnode['reftarget'] = 'py3k:unknown'
- refnode['refexplicit'] = False
- contnode[0] = nodes.Text('py3k:unknown')
- rn = missing_reference(app, app.env, refnode, contnode)
+ node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown',
+ refexplicit=False)
+ rn = missing_reference(app, app.env, node, contnode)
assert rn is None
assert contnode[0].astext() == 'unknown'
# prefix given, target not found and explicit title: nothing is changed
- refnode['reftarget'] = 'py3k:unknown'
- refnode['refexplicit'] = True
- contnode[0] = nodes.Text('py3k:unknown')
- rn = missing_reference(app, app.env, refnode, contnode)
+ node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown',
+ refexplicit=True)
+ rn = missing_reference(app, app.env, node, contnode)
assert rn is None
assert contnode[0].astext() == 'py3k:unknown'
From 29547f798172a34e00fded1da2f9dc0b0f31bc09 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 12:02:27 +0200
Subject: [PATCH 184/207] #488: Fix crash when json-py is installed, which
provides a ``json`` module but is incompatible to simplejson.
---
CHANGES | 3 +++
sphinx/util/jsonimpl.py | 4 +++-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/CHANGES b/CHANGES
index cb80d72c6..60c55e7a9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
Release 1.0.2 (in development)
==============================
+* #488: Fix crash when json-py is installed, which provides a
+ ``json`` module but is incompatible to simplejson.
+
* #480: Fix handling of target naming in intersphinx.
* #486: Fix removal of ``!`` for all cross-reference roles.
diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py
index b83661a7e..fda85b5e3 100644
--- a/sphinx/util/jsonimpl.py
+++ b/sphinx/util/jsonimpl.py
@@ -13,8 +13,10 @@ import UserString
try:
import json
+ # json-py's json module has not JSONEncoder; this will raise AttributeError
+ # if json-py is imported instead of the built-in json module
JSONEncoder = json.JSONEncoder
-except ImportError:
+except (ImportError, AttributeError):
try:
import simplejson as json
JSONEncoder = json.JSONEncoder
From eedef65aa6d3673b81ac6fcd6c9b05ec74a2567e Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 12:03:45 +0200
Subject: [PATCH 185/207] #487: Fix setting the default role to one provided by
the ``oldcmarkup`` extension.
---
CHANGES | 3 +++
sphinx/ext/oldcmarkup.py | 2 ++
2 files changed, 5 insertions(+)
diff --git a/CHANGES b/CHANGES
index 60c55e7a9..bc122e9c9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
Release 1.0.2 (in development)
==============================
+* #487: Fix setting the default role to one provided by the
+ ``oldcmarkup`` extension.
+
* #488: Fix crash when json-py is installed, which provides a
``json`` module but is incompatible to simplejson.
diff --git a/sphinx/ext/oldcmarkup.py b/sphinx/ext/oldcmarkup.py
index 84ae61dd2..00ac37495 100644
--- a/sphinx/ext/oldcmarkup.py
+++ b/sphinx/ext/oldcmarkup.py
@@ -42,6 +42,8 @@ class OldCDirective(Directive):
def old_crole(typ, rawtext, text, lineno, inliner, options={}, content=[]):
env = inliner.document.settings.env
+ if not typ:
+ typ = env.config.default_role
if not env.app._oldcmarkup_warned:
env.warn(env.docname, WARNING_MSG)
env.app._oldcmarkup_warned = True
From 8c91fb78ce0ac364870c3cd1df2149397f46bd8a Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 12:53:05 +0200
Subject: [PATCH 186/207] #484: Fix crash when duplicating a parameter in an
info field list.
Problem was that the :type: info nodes were inserted twice into the
doctree, which led to inconsistencies when reference nodes were resolved.
---
CHANGES | 2 ++
sphinx/util/docfields.py | 7 +++++--
tests/root/objects.txt | 2 ++
tests/test_build_html.py | 2 +-
4 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/CHANGES b/CHANGES
index bc122e9c9..f9b3b9a49 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
Release 1.0.2 (in development)
==============================
+* #484: Fix crash when duplicating a parameter in an info field list.
+
* #487: Fix setting the default role to one provided by the
``oldcmarkup`` extension.
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index c8e58f48a..6ce6d82bf 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -142,9 +142,12 @@ class TypedField(GroupedField):
par += self.make_xref(self.rolename, domain, fieldarg, nodes.strong)
if fieldarg in types:
par += nodes.Text(' (')
- fieldtype = types[fieldarg]
+ # NOTE: using .pop() here to prevent a single type node to be
+ # inserted twice into the doctree, which leads to
+ # inconsistencies later when references are resolved
+ fieldtype = types.pop(fieldarg)
if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text):
- typename = u''.join(n.astext() for n in types[fieldarg])
+ typename = u''.join(n.astext() for n in fieldtype)
par += self.make_xref(self.typerolename, domain, typename)
else:
par += fieldtype
diff --git a/tests/root/objects.txt b/tests/root/objects.txt
index 334827de2..d6b8bdf64 100644
--- a/tests/root/objects.txt
+++ b/tests/root/objects.txt
@@ -62,6 +62,8 @@ Testing object descriptions
:ivar int hour: like *hour*
:ivar minute: like *minute*
:vartype minute: int
+ :param hour: Duplicate param. Should not lead to crashes.
+ :type hour: Duplicate type.
C items
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 4dee513ae..813c962fe 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -38,7 +38,7 @@ http://www.python.org/logo.png
reading included file u'wrongenc.inc' seems to be wrong, try giving an \
:encoding: option\\n?
%(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png
-%(root)s/objects.txt:84: WARNING: using old C markup; please migrate to \
+%(root)s/objects.txt:86: WARNING: using old C markup; please migrate to \
new-style markup \(e.g. c:function instead of cfunction\), see \
http://sphinx.pocoo.org/domains.html
"""
From 580e1c90d36c9d126f0a4301e0b492606b755e46 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 13:39:23 +0200
Subject: [PATCH 187/207] #481, #482: fix ``.name`` reference matching.
#482: When doing a non-exact search, match only the given type of object.
#481: Apply non-exact search for Python reference targets with ``.name`` for modules too.
---
CHANGES | 6 +++
doc/builders.rst | 8 ++--
doc/conf.py | 6 ++-
doc/config.rst | 21 +++------
doc/domains.rst | 19 +++++---
doc/ext/appapi.rst | 4 +-
doc/ext/autodoc.rst | 40 +++++++++--------
doc/ext/inheritance.rst | 2 +-
doc/ext/math.rst | 8 ++--
doc/markup/inline.rst | 2 +-
doc/markup/toctree.rst | 2 +-
doc/templating.rst | 12 ++---
sphinx/domains/python.py | 94 +++++++++++++++++++++++-----------------
sphinx/domains/rst.py | 2 +-
sphinx/environment.py | 2 +-
15 files changed, 124 insertions(+), 104 deletions(-)
diff --git a/CHANGES b/CHANGES
index f9b3b9a49..33a3cca2c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,12 @@
Release 1.0.2 (in development)
==============================
+* #482: When doing a non-exact search, match only the given type
+ of object.
+
+* #481: Apply non-exact search for Python reference targets with
+ ``.name`` for modules too.
+
* #484: Fix crash when duplicating a parameter in an info field list.
* #487: Fix setting the default role to one provided by the
diff --git a/doc/builders.rst b/doc/builders.rst
index 6e90ccc62..80203e759 100644
--- a/doc/builders.rst
+++ b/doc/builders.rst
@@ -255,11 +255,11 @@ All serialization builders outputs one file per source file and a few special
files. They also copy the reST source files in the directory ``_sources``
under the output directory.
-The :class:`PickleHTMLBuilder` is a builtin subclass that implements the pickle
+The :class:`.PickleHTMLBuilder` is a builtin subclass that implements the pickle
serialization interface.
The files per source file have the extensions of
-:attr:`~SerializingHTMLBuilder.out_suffix`, and are arranged in directories
+:attr:`~.SerializingHTMLBuilder.out_suffix`, and are arranged in directories
just as the source files are. They unserialize to a dictionary (or dictionary
like structure) with these keys:
@@ -290,7 +290,7 @@ like structure) with these keys:
The special files are located in the root output directory. They are:
-:attr:`SerializingHTMLBuilder.globalcontext_filename`
+:attr:`.SerializingHTMLBuilder.globalcontext_filename`
A pickled dict with these keys:
``project``, ``copyright``, ``release``, ``version``
@@ -309,7 +309,7 @@ The special files are located in the root output directory. They are:
``titles``
A dictionary of all documents' titles, as HTML strings.
-:attr:`SerializingHTMLBuilder.searchindex_filename`
+:attr:`.SerializingHTMLBuilder.searchindex_filename`
An index that can be used for searching the documentation. It is a pickled
list with these entries:
diff --git a/doc/conf.py b/doc/conf.py
index 21e8d2f54..b3a1cda79 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -8,8 +8,6 @@ import sphinx
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
'sphinx.ext.autosummary', 'sphinx.ext.extlinks']
-#intersphinx_mapping = {'python': ('http://docs.python.org/dev', None)}
-
master_doc = 'contents'
templates_path = ['_templates']
exclude_patterns = ['_build']
@@ -66,6 +64,10 @@ man_pages = [
'template generator', '', 1),
]
+# We're not using intersphinx right now, but if we did, this would be part of
+# the mapping:
+intersphinx_mapping = {'python': ('http://docs.python.org/dev', None)}
+
# -- Extension interface -------------------------------------------------------
diff --git a/doc/config.rst b/doc/config.rst
index bf8ad3c2d..e0fbeb46e 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -346,12 +346,12 @@ Project information
A boolean that decides whether module names are prepended to all
:term:`object` names (for object types where a "module" of some kind is
- defined), e.g. for :rst:dir:`function` directives. Default is ``True``.
+ defined), e.g. for :rst:dir:`py:function` directives. Default is ``True``.
.. confval:: show_authors
- A boolean that decides whether :rst:dir:`moduleauthor` and :rst:dir:`sectionauthor`
- directives produce any output in the built files.
+ A boolean that decides whether :rst:dir:`codeauthor` and
+ :rst:dir:`sectionauthor` directives produce any output in the built files.
.. confval:: modindex_common_prefix
@@ -388,6 +388,8 @@ Options for HTML output
These options influence HTML as well as HTML Help output, and other builders
that use Sphinx' HTMLWriter class.
+.. XXX document html_context
+
.. confval:: html_theme
The "theme" that the HTML output should use. See the :doc:`section about
@@ -553,19 +555,6 @@ that use Sphinx' HTMLWriter class.
This will render the template ``customdownload.html`` as the page
``download.html``.
- .. note::
-
- Earlier versions of Sphinx had a value called :confval:`html_index` which
- was a clumsy way of controlling the content of the "index" document. If
- you used this feature, migrate it by adding an ``'index'`` key to this
- setting, with your custom template as the value, and in your custom
- template, use ::
-
- {% extend "defindex.html" %}
- {% block tables %}
- ... old template content ...
- {% endblock %}
-
.. confval:: html_domain_indices
If true, generate domain-specific indices in addition to the general index.
diff --git a/doc/domains.rst b/doc/domains.rst
index c64930a24..6bbe12150 100644
--- a/doc/domains.rst
+++ b/doc/domains.rst
@@ -138,11 +138,12 @@ declarations:
.. rst:directive:: .. py:currentmodule:: name
This directive tells Sphinx that the classes, functions etc. documented from
- here are in the given module (like :rst:dir:`py:module`), but it will not create
- index entries, an entry in the Global Module Index, or a link target for
- :rst:role:`mod`. This is helpful in situations where documentation for things in
- a module is spread over multiple files or sections -- one location has the
- :rst:dir:`py:module` directive, the others only :rst:dir:`py:currentmodule`.
+ here are in the given module (like :rst:dir:`py:module`), but it will not
+ create index entries, an entry in the Global Module Index, or a link target
+ for :rst:role:`py:mod`. This is helpful in situations where documentation
+ for things in a module is spread over multiple files or sections -- one
+ location has the :rst:dir:`py:module` directive, the others only
+ :rst:dir:`py:currentmodule`.
The following directives are provided for module and class contents:
@@ -363,6 +364,9 @@ dot, this order is reversed. For example, in the documentation of Python's
:mod:`codecs` module, ``:py:func:`open``` always refers to the built-in
function, while ``:py:func:`.open``` refers to :func:`codecs.open`.
+A similar heuristic is used to determine whether the name is an attribute of the
+currently documented class.
+
Also, if the name is prefixed with a dot, and no exact match is found, the
target is taken as a suffix and all object names with that suffix are
searched. For example, ``:py:meth:`.TarFile.close``` references the
@@ -370,8 +374,9 @@ searched. For example, ``:py:meth:`.TarFile.close``` references the
``tarfile``. Since this can get ambiguous, if there is more than one possible
match, you will get a warning from Sphinx.
-A similar heuristic is used to determine whether the name is an attribute of the
-currently documented class.
+Note that you can combine the ``~`` and ``.`` prefixes:
+``:py:meth:`~.TarFile.close``` will reference the ``tarfile.TarFile.close()``
+method, but the visible link caption will only be ``close()``.
.. _c-domain:
diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst
index 402dd72f0..2717c6a8e 100644
--- a/doc/ext/appapi.rst
+++ b/doc/ext/appapi.rst
@@ -210,7 +210,7 @@ the following public API:
standard Sphinx roles (see :ref:`xref-syntax`).
This method is also available under the deprecated alias
- :meth:`add_description_unit`.
+ ``add_description_unit``.
.. method:: Sphinx.add_crossref_type(directivename, rolename, indextemplate='', ref_nodeclass=None, objname='')
@@ -272,6 +272,8 @@ the following public API:
This allows to auto-document new types of objects. See the source of the
autodoc module for examples on how to subclass :class:`Documenter`.
+ .. XXX add real docs for Documenter and subclassing
+
.. versionadded:: 0.6
.. method:: Sphinx.add_autodoc_attrgetter(type, getter)
diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst
index bd725cfaa..813331015 100644
--- a/doc/ext/autodoc.rst
+++ b/doc/ext/autodoc.rst
@@ -27,20 +27,21 @@ two locations for documentation, while at the same time avoiding
auto-generated-looking pure API documentation.
:mod:`autodoc` provides several directives that are versions of the usual
-:rst:dir:`module`, :rst:dir:`class` and so forth. On parsing time, they import the
-corresponding module and extract the docstring of the given objects, inserting
-them into the page source under a suitable :rst:dir:`module`, :rst:dir:`class` etc.
-directive.
+:rst:dir:`py:module`, :rst:dir:`py:class` and so forth. On parsing time, they
+import the corresponding module and extract the docstring of the given objects,
+inserting them into the page source under a suitable :rst:dir:`py:module`,
+:rst:dir:`py:class` etc. directive.
.. note::
- Just as :rst:dir:`class` respects the current :rst:dir:`module`, :rst:dir:`autoclass`
- will also do so, and likewise with :rst:dir:`method` and :rst:dir:`class`.
+ Just as :rst:dir:`py:class` respects the current :rst:dir:`py:module`,
+ :rst:dir:`autoclass` will also do so. Likewise, :rst:dir:`automethod` will
+ respect the current :rst:dir:`py:class`.
.. rst:directive:: automodule
- autoclass
- autoexception
+ autoclass
+ autoexception
Document a module, class or exception. All three directives will by default
only insert the docstring of the object itself::
@@ -127,23 +128,24 @@ directive.
.. versionadded:: 0.4
- * The :rst:dir:`automodule`, :rst:dir:`autoclass` and :rst:dir:`autoexception` directives
- also support a flag option called ``show-inheritance``. When given, a list
- of base classes will be inserted just below the class signature (when used
- with :rst:dir:`automodule`, this will be inserted for every class that is
- documented in the module).
+ * The :rst:dir:`automodule`, :rst:dir:`autoclass` and
+ :rst:dir:`autoexception` directives also support a flag option called
+ ``show-inheritance``. When given, a list of base classes will be inserted
+ just below the class signature (when used with :rst:dir:`automodule`, this
+ will be inserted for every class that is documented in the module).
.. versionadded:: 0.4
* All autodoc directives support the ``noindex`` flag option that has the
- same effect as for standard :rst:dir:`function` etc. directives: no index
- entries are generated for the documented object (and all autodocumented
- members).
+ same effect as for standard :rst:dir:`py:function` etc. directives: no
+ index entries are generated for the documented object (and all
+ autodocumented members).
.. versionadded:: 0.4
* :rst:dir:`automodule` also recognizes the ``synopsis``, ``platform`` and
- ``deprecated`` options that the standard :rst:dir:`module` directive supports.
+ ``deprecated`` options that the standard :rst:dir:`py:module` directive
+ supports.
.. versionadded:: 0.5
@@ -213,8 +215,8 @@ There are also new config values that you can set:
``"class"``
Only the class' docstring is inserted. This is the default. You can
- still document ``__init__`` as a separate method using :rst:dir:`automethod`
- or the ``members`` option to :rst:dir:`autoclass`.
+ still document ``__init__`` as a separate method using
+ :rst:dir:`automethod` or the ``members`` option to :rst:dir:`autoclass`.
``"both"``
Both the class' and the ``__init__`` method's docstring are concatenated
and inserted.
diff --git a/doc/ext/inheritance.rst b/doc/ext/inheritance.rst
index 76388a94c..cdd017917 100644
--- a/doc/ext/inheritance.rst
+++ b/doc/ext/inheritance.rst
@@ -17,7 +17,7 @@ It adds this directive:
This directive has one or more arguments, each giving a module or class
name. Class names can be unqualified; in that case they are taken to exist
- in the currently described module (see :rst:dir:`module`).
+ in the currently described module (see :rst:dir:`py:module`).
For each given class, and each class in each given module, the base classes
are determined. Then, from all classes and their base classes, a graph is
diff --git a/doc/ext/math.rst b/doc/ext/math.rst
index b9f6ab12b..f2896c39c 100644
--- a/doc/ext/math.rst
+++ b/doc/ext/math.rst
@@ -17,15 +17,15 @@ if possible, reuse that support too.
.. note::
- :mod:`sphinx.ext.mathbase` is not meant to be added to the
- :confval:`extensions` config value, instead, use either
- :mod:`sphinx.ext.pngmath` or :mod:`sphinx.ext.jsmath` as described below.
+ :mod:`.mathbase` is not meant to be added to the :confval:`extensions` config
+ value, instead, use either :mod:`sphinx.ext.pngmath` or
+ :mod:`sphinx.ext.jsmath` as described below.
The input language for mathematics is LaTeX markup. This is the de-facto
standard for plain-text math notation and has the added advantage that no
further translation is necessary when building LaTeX output.
-:mod:`mathbase` defines these new markup elements:
+:mod:`.mathbase` defines these new markup elements:
.. rst:role:: math
diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst
index bb1ed68ec..4453ab00a 100644
--- a/doc/markup/inline.rst
+++ b/doc/markup/inline.rst
@@ -260,7 +260,7 @@ in a different style:
.. rst:role:: samp
A piece of literal text, such as code. Within the contents, you can use
- curly braces to indicate a "variable" part, as in :rst:dir:`file`. For
+ curly braces to indicate a "variable" part, as in :rst:role:`file`. For
example, in ``:samp:`print 1+{variable}```, the part ``variable`` would be
emphasized.
diff --git a/doc/markup/toctree.rst b/doc/markup/toctree.rst
index 474427d72..2c0a418a2 100644
--- a/doc/markup/toctree.rst
+++ b/doc/markup/toctree.rst
@@ -151,7 +151,7 @@ The special document names (and pages generated for them) are:
:ref:`object descriptions `, and from :rst:dir:`index`
directives.
- The module index contains one entry per :rst:dir:`module` directive.
+ The Python module index contains one entry per :rst:dir:`py:module` directive.
The search page contains a form that uses the generated JSON search index and
JavaScript to full-text search the generated documents for search words; it
diff --git a/doc/templating.rst b/doc/templating.rst
index 6880663d3..193a90bd9 100644
--- a/doc/templating.rst
+++ b/doc/templating.rst
@@ -21,10 +21,10 @@ No. You have several other options:
configuration value accordingly.
* You can :ref:`write a custom builder ` that derives from
- :class:`~sphinx.builders.StandaloneHTMLBuilder` and calls your template engine
- of choice.
+ :class:`~sphinx.builders.html.StandaloneHTMLBuilder` and calls your template
+ engine of choice.
-* You can use the :class:`~sphinx.builders.PickleHTMLBuilder` that produces
+* You can use the :class:`~sphinx.builders.html.PickleHTMLBuilder` that produces
pickle files with the page contents, and postprocess them using a custom tool,
or use them in your Web application.
@@ -261,9 +261,9 @@ in the future.
.. data:: file_suffix
- The value of the builder's :attr:`out_suffix` attribute, i.e. the file name
- extension that the output files will get. For a standard HTML builder, this
- is usually ``.html``.
+ The value of the builder's :attr:`~.SerializingHTMLBuilder.out_suffix`
+ attribute, i.e. the file name extension that the output files will get. For
+ a standard HTML builder, this is usually ``.html``.
.. data:: has_source
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index fc8086995..cd87bfbda 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -356,6 +356,9 @@ class PyModule(Directive):
env.domaindata['py']['modules'][modname] = \
(env.docname, self.options.get('synopsis', ''),
self.options.get('platform', ''), 'deprecated' in self.options)
+ # make a duplicate entry in 'objects' to facilitate searching for the
+ # module in PythonDomain.find_obj()
+ env.domaindata['py']['objects'][modname] = (env.docname, 'module')
targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True)
self.state.document.note_explicit_target(targetnode)
ret = [targetnode]
@@ -544,7 +547,7 @@ class PythonDomain(Domain):
if fn == docname:
del self.data['modules'][modname]
- def find_obj(self, env, modname, classname, name, type, searchorder=0):
+ def find_obj(self, env, modname, classname, name, type, searchmode=0):
"""
Find a Python object for "name", perhaps using the given module and/or
classname. Returns a list of (name, object entry) tuples.
@@ -560,22 +563,31 @@ class PythonDomain(Domain):
matches = []
newname = None
- if searchorder == 1:
- if modname and classname and \
- modname + '.' + classname + '.' + name in objects:
- newname = modname + '.' + classname + '.' + name
- elif modname and modname + '.' + name in objects:
- newname = modname + '.' + name
- elif name in objects:
- newname = name
- else:
- # "fuzzy" searching mode
- searchname = '.' + name
- matches = [(name, objects[name]) for name in objects
- if name.endswith(searchname)]
+ if searchmode == 1:
+ objtypes = self.objtypes_for_role(type)
+ if modname and classname:
+ fullname = modname + '.' + classname + '.' + name
+ if fullname in objects and objects[fullname][1] in objtypes:
+ newname = fullname
+ if not newname:
+ if modname and modname + '.' + name in objects and \
+ objects[modname + '.' + name][1] in objtypes:
+ newname = modname + '.' + name
+ elif name in objects and objects[name][1] in objtypes:
+ newname = name
+ else:
+ # "fuzzy" searching mode
+ searchname = '.' + name
+ matches = [(name, objects[name]) for name in objects
+ if name.endswith(searchname)
+ and objects[name][1] in objtypes]
else:
+ # NOTE: searching for exact match, object type is not considered
if name in objects:
newname = name
+ elif type == 'mod':
+ # only exact matches allowed for modules
+ return []
elif classname and classname + '.' + name in objects:
newname = classname + '.' + name
elif modname and modname + '.' + name in objects:
@@ -597,33 +609,35 @@ class PythonDomain(Domain):
def resolve_xref(self, env, fromdocname, builder,
type, target, node, contnode):
- if (type == 'mod' or
- type == 'obj' and target in self.data['modules']):
- docname, synopsis, platform, deprecated = \
- self.data['modules'].get(target, ('','','', ''))
- if not docname:
- return None
- else:
- title = '%s%s%s' % ((platform and '(%s) ' % platform),
- synopsis,
- (deprecated and ' (deprecated)' or ''))
- return make_refnode(builder, fromdocname, docname,
- 'module-' + target, contnode, title)
+ modname = node.get('py:module')
+ clsname = node.get('py:class')
+ searchmode = node.hasattr('refspecific') and 1 or 0
+ matches = self.find_obj(env, modname, clsname, target,
+ type, searchmode)
+ if not matches:
+ return None
+ elif len(matches) > 1:
+ env.warn(fromdocname,
+ 'more than one target found for cross-reference '
+ '%r: %s' % (target,
+ ', '.join(match[0] for match in matches)),
+ node.line)
+ name, obj = matches[0]
+
+ if obj[1] == 'module':
+ # get additional info for modules
+ docname, synopsis, platform, deprecated = self.data['modules'][name]
+ assert docname == obj[0]
+ title = name
+ if synopsis:
+ title += ': ' + synopsis
+ if deprecated:
+ title += _(' (deprecated)')
+ if platform:
+ title += ' (' + platform + ')'
+ return make_refnode(builder, fromdocname, docname,
+ 'module-' + name, contnode, title)
else:
- modname = node.get('py:module')
- clsname = node.get('py:class')
- searchorder = node.hasattr('refspecific') and 1 or 0
- matches = self.find_obj(env, modname, clsname, target,
- type, searchorder)
- if not matches:
- return None
- elif len(matches) > 1:
- env.warn(fromdocname,
- 'more than one target found for cross-reference '
- '%r: %s' % (target,
- ', '.join(match[0] for match in matches)),
- node.line)
- name, obj = matches[0]
return make_refnode(builder, fromdocname, obj[0], name,
contnode, name)
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index 61809f330..98f40d845 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -28,7 +28,7 @@ class ReSTMarkup(ObjectDescription):
"""
def add_target_and_index(self, name, sig, signode):
- targetname = name + '-' + self.objtype
+ targetname = self.objtype + '-' + name
if targetname not in self.state.document.ids:
signode['names'].append(targetname)
signode['ids'].append(targetname)
diff --git a/sphinx/environment.py b/sphinx/environment.py
index 5edcb4d90..19ff62ca1 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -63,7 +63,7 @@ default_settings = {
# This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files.
-ENV_VERSION = 37
+ENV_VERSION = 38
default_substitutions = set([
From 0d8dd33b4d752277c8f9632b726c517ff167ef07 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 15:07:44 +0200
Subject: [PATCH 188/207] #471: Fix LaTeX references to figures.
---
CHANGES | 2 ++
sphinx/writers/latex.py | 14 ++++++++++++--
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/CHANGES b/CHANGES
index 33a3cca2c..41f0b1c4d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
Release 1.0.2 (in development)
==============================
+* #471: Fix LaTeX references to figures.
+
* #482: When doing a non-exact search, match only the given type
of object.
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 5674b388c..03c90fe27 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -224,6 +224,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
else:
self.top_sectionlevel = 1
self.next_section_ids = set()
+ self.next_figure_ids = set()
# flags
self.verbatim = None
self.in_title = 0
@@ -887,11 +888,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
pass
def visit_figure(self, node):
+ ids = ''
+ for id in self.next_figure_ids:
+ ids += self.hypertarget(id, anchor=False)
+ self.next_figure_ids.clear()
if node.has_key('width') and node.get('align', '') in ('left', 'right'):
self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' %
(node['align'] == 'right' and 'r' or 'l',
node['width']))
- self.context.append('\\end{wrapfigure}\n')
+ self.context.append(ids + '\\end{wrapfigure}\n')
else:
if (not node.attributes.has_key('align') or
node.attributes['align'] == 'center'):
@@ -903,7 +908,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
align = '\\begin{flush%s}' % node.attributes['align']
align_end = '\\end{flush%s}' % node.attributes['align']
self.body.append('\\begin{figure}[htbp]%s\n' % align)
- self.context.append('%s\\end{figure}\n' % align_end)
+ self.context.append(ids + align_end + '\\end{figure}\n')
def depart_figure(self, node):
self.body.append(self.context.pop())
@@ -983,6 +988,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.next_section_ids.add(node['refid'])
self.next_section_ids.update(node['ids'])
return
+ elif isinstance(next, nodes.figure):
+ if node.get('refid'):
+ self.next_figure_ids.add(node['refid'])
+ self.next_figure_ids.update(node['ids'])
+ return
except IndexError:
pass
if 'refuri' in node:
From f3c2220ad1007a3a57db868206968b98dcb5abce Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 15:16:48 +0200
Subject: [PATCH 189/207] Include hypcap package, to get better references to
floats.
---
sphinx/texinputs/sphinx.sty | 1 +
1 file changed, 1 insertion(+)
diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty
index be8a6c15d..b5381392a 100644
--- a/sphinx/texinputs/sphinx.sty
+++ b/sphinx/texinputs/sphinx.sty
@@ -446,6 +446,7 @@
linkcolor=InnerLinkColor,filecolor=OuterLinkColor,
menucolor=OuterLinkColor,urlcolor=OuterLinkColor,
citecolor=InnerLinkColor]{hyperref}
+\RequirePackage[figure,table]{hypcap}
% From docutils.writers.latex2e
\providecommand{\DUspan}[2]{%
From 5b2b0ecfd3dda77ad3a110286eea24fd484b42dc Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 15:17:12 +0200
Subject: [PATCH 190/207] Better references to tables, as well.
---
sphinx/writers/latex.py | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 03c90fe27..1bb534a0b 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -225,6 +225,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.top_sectionlevel = 1
self.next_section_ids = set()
self.next_figure_ids = set()
+ self.next_table_ids = set()
# flags
self.verbatim = None
self.in_title = 0
@@ -634,7 +635,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('{|' + ('L|' * self.table.colcount) + '}\n')
if self.table.longtable and self.table.caption is not None:
self.body.append(u'\\caption{%s} \\\\\n' % self.table.caption)
-
+ if self.table.caption is not None:
+ for id in self.next_table_ids:
+ self.body.append(self.hypertarget(id, anchor=False))
+ self.next_table_ids.clear()
if self.table.longtable:
self.body.append('\\hline\n')
self.body.append('\\endfirsthead\n\n')
@@ -989,10 +993,19 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.next_section_ids.update(node['ids'])
return
elif isinstance(next, nodes.figure):
+ # labels for figures go in the figure body, not before
if node.get('refid'):
self.next_figure_ids.add(node['refid'])
self.next_figure_ids.update(node['ids'])
return
+ elif isinstance(next, nodes.table):
+ # same for tables, but only if they have a caption
+ for n in node:
+ if isinstance(n, nodes.title):
+ if node.get('refid'):
+ self.next_table_ids.add(node['refid'])
+ self.next_table_ids.update(node['ids'])
+ return
except IndexError:
pass
if 'refuri' in node:
From b9529a21a5c9252c7298836467034b0f2c3e9d07 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 15:17:23 +0200
Subject: [PATCH 191/207] Fix references to reST domain items.
---
sphinx/domains/rst.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index 98f40d845..d3ffc6bdc 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -130,8 +130,9 @@ class ReSTDomain(Domain):
if (objtype, target) in objects:
return make_refnode(builder, fromdocname,
objects[objtype, target],
- target, contnode, target)
+ objtype + '-' + target,
+ contnode, target + ' ' + objtype)
def get_objects(self):
for (typ, name), docname in self.data['objects'].iteritems():
- yield name, name, typ, docname, name, 1
+ yield name, name, typ, docname, typ + '-' + name, 1
From 279d025ffe93a05ac3be7b1432409d77e9bbb7a0 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 15:29:45 +0200
Subject: [PATCH 192/207] Add Tau.
---
EXAMPLES | 1 +
1 file changed, 1 insertion(+)
diff --git a/EXAMPLES b/EXAMPLES
index 44c511e9e..778b235e1 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -97,6 +97,7 @@ Documentation using the sphinxdoc theme
* Satchmo: http://www.satchmoproject.com/docs/svn/
* Sphinx: http://sphinx.pocoo.org/
* Sqlkit: http://sqlkit.argolinux.org/
+* Tau: http://www.tango-controls.org/static/tau/latest/doc/html/index.html
* Total Open Station: http://tops.berlios.de/
* WebFaction: http://docs.webfaction.com/
From 92142bbdb68e98515112c34ec70ca18fd6f3ea6e Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 15:42:15 +0200
Subject: [PATCH 193/207] Allow references to PEPs and RFCs with explicit
anchors.
---
CHANGES | 2 ++
doc/markup/inline.rst | 6 ++++--
sphinx/roles.py | 14 ++++++++++++--
3 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/CHANGES b/CHANGES
index 41f0b1c4d..ad51ffeff 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
Release 1.0.2 (in development)
==============================
+* Allow references to PEPs and RFCs with explicit anchors.
+
* #471: Fix LaTeX references to figures.
* #482: When doing a non-exact search, match only the given type
diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst
index 4453ab00a..4b704228f 100644
--- a/doc/markup/inline.rst
+++ b/doc/markup/inline.rst
@@ -274,13 +274,15 @@ The following roles generate external links:
A reference to a Python Enhancement Proposal. This generates appropriate
index entries. The text "PEP *number*\ " is generated; in the HTML output,
- this text is a hyperlink to an online copy of the specified PEP.
+ this text is a hyperlink to an online copy of the specified PEP. You can
+ link to a specific section by saying ``:pep:`number#anchor```.
.. rst:role:: rfc
A reference to an Internet Request for Comments. This generates appropriate
index entries. The text "RFC *number*\ " is generated; in the HTML output,
- this text is a hyperlink to an online copy of the specified RFC.
+ this text is a hyperlink to an online copy of the specified RFC. You can
+ link to a specific section by saying ``:rfc:`number#anchor```.
Note that there are no special roles for including hyperlinks as you can use
diff --git a/sphinx/roles.py b/sphinx/roles.py
index bacdad5b6..0ea0ec485 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -173,6 +173,10 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
indexnode['entries'] = [
('single', _('Python Enhancement Proposals!PEP %s') % text,
targetid, 'PEP %s' % text)]
+ anchor = ''
+ anchorindex = text.find('#')
+ if anchorindex > 0:
+ text, anchor = text[:anchorindex], text[anchorindex:]
try:
pepnum = int(text)
except ValueError:
@@ -182,12 +186,17 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
return [prb], [msg]
ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum
sn = nodes.strong('PEP '+text, 'PEP '+text)
- rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ])
+ rn = nodes.reference('', '', internal=False, refuri=ref+anchor,
+ classes=[typ])
rn += sn
return [indexnode, targetnode, rn], []
elif typ == 'rfc':
indexnode['entries'] = [('single', 'RFC; RFC %s' % text,
targetid, 'RFC %s' % text)]
+ anchor = ''
+ anchorindex = text.find('#')
+ if anchorindex > 0:
+ text, anchor = text[:anchorindex], text[anchorindex:]
try:
rfcnum = int(text)
except ValueError:
@@ -197,7 +206,8 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
return [prb], [msg]
ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
sn = nodes.strong('RFC '+text, 'RFC '+text)
- rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ])
+ rn = nodes.reference('', '', internal=False, refuri=ref+anchor,
+ classes=[typ])
rn += sn
return [indexnode, targetnode, rn], []
From 5b09e2b41a2c40522f2c6dc4063526ea3a4fc594 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 15:47:10 +0200
Subject: [PATCH 194/207] Fix unwanted styling of C domain references (because
of a namespace clash with Pygments styles).
---
CHANGES | 3 +++
sphinx/highlighting.py | 2 +-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/CHANGES b/CHANGES
index ad51ffeff..0e06938df 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
Release 1.0.2 (in development)
==============================
+* Fix unwanted styling of C domain references (because of a
+ namespace clash with Pygments styles).
+
* Allow references to PEPs and RFCs with explicit anchors.
* #471: Fix LaTeX references to figures.
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index f5ea859cb..0dcbc0219 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -240,7 +240,7 @@ class PygmentsBridge(object):
# no HTML styles needed
return ''
if self.dest == 'html':
- return self.fmter[0].get_style_defs()
+ return self.fmter[0].get_style_defs('.highlight')
else:
styledefs = self.fmter[0].get_style_defs()
# workaround for Pygments < 0.12
From df107e70f8f684c7fd4475f596ffbc3151478f41 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 16:03:36 +0200
Subject: [PATCH 195/207] Allow breaking long signatures, continuing with
backlash-escaped newlines.
---
CHANGES | 7 +++++--
doc/domains.rst | 11 ++++++++++-
sphinx/directives/__init__.py | 7 +++++--
tests/root/objects.txt | 4 ++++
tests/test_build_html.py | 4 +++-
5 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/CHANGES b/CHANGES
index 0e06938df..a158267b0 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,8 +1,11 @@
Release 1.0.2 (in development)
==============================
-* Fix unwanted styling of C domain references (because of a
- namespace clash with Pygments styles).
+* Allow breaking long signatures, continuing with backlash-escaped
+ newlines.
+
+* Fix unwanted styling of C domain references (because of a namespace
+ clash with Pygments styles).
* Allow references to PEPs and RFCs with explicit anchors.
diff --git a/doc/domains.rst b/doc/domains.rst
index 6bbe12150..8cd7a0c7d 100644
--- a/doc/domains.rst
+++ b/doc/domains.rst
@@ -52,10 +52,19 @@ flag ``:noindex:``. An example using a Python domain directive::
.. py:function:: spam(eggs)
ham(eggs)
- :noindex:
Spam or ham the foo.
+This describes the two Python functions ``spam`` and ``ham``. (Note that when
+signatures become too long, you can break them if you add a backslash to lines
+that are continued in the next line. Example::
+
+ .. py:function:: filterwarnings(action, message='', category=Warning, \
+ module='', lineno=0, append=False)
+ :noindex:
+
+(This example also shows how to use the ``:noindex:`` flag.)
+
The domains also provide roles that link back to these object descriptions. For
example, to link to one of the functions described in the example above, you
could say ::
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index 6c03b8e5f..48c44178d 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -32,6 +32,7 @@ except AttributeError:
# RE to strip backslash escapes
+nl_escape_re = re.compile(r'\\\n')
strip_backslash_re = re.compile(r'\\(?=[^\\])')
@@ -57,10 +58,12 @@ class ObjectDescription(Directive):
"""
Retrieve the signatures to document from the directive arguments. By
default, signatures are given as arguments, one per line.
+
+ Backslash-escaping of newlines is supported.
"""
+ lines = nl_escape_re.sub('', self.arguments[0]).split('\n')
# remove backslashes to support (dummy) escapes; helps Vim highlighting
- return [strip_backslash_re.sub('', sig.strip())
- for sig in self.arguments[0].split('\n')]
+ return [strip_backslash_re.sub('', line.strip()) for line in lines]
def handle_signature(self, sig, signode):
"""
diff --git a/tests/root/objects.txt b/tests/root/objects.txt
index d6b8bdf64..e62f6d962 100644
--- a/tests/root/objects.txt
+++ b/tests/root/objects.txt
@@ -41,6 +41,10 @@ Testing object descriptions
.. function:: func_without_module2() -> annotation
+.. object:: long(parameter, \
+ list)
+ another one
+
.. class:: TimeInt
:param moo: |test|
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 813c962fe..6857d3309 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -38,7 +38,7 @@ http://www.python.org/logo.png
reading included file u'wrongenc.inc' seems to be wrong, try giving an \
:encoding: option\\n?
%(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png
-%(root)s/objects.txt:86: WARNING: using old C markup; please migrate to \
+%(root)s/objects.txt:\\d*: WARNING: using old C markup; please migrate to \
new-style markup \(e.g. c:function instead of cfunction\), see \
http://sphinx.pocoo.org/domains.html
"""
@@ -161,6 +161,8 @@ HTML_XPATH = {
'objects.html': [
(".//dt[@id='mod.Cls.meth1']", ''),
(".//dt[@id='errmod.Error']", ''),
+ (".//dt/tt", r'long\(parameter,\s* list\)'),
+ (".//dt/tt", 'another one'),
(".//a[@href='#mod.Cls'][@class='reference internal']", ''),
(".//dl[@class='userdesc']", ''),
(".//dt[@id='userdesc-myobj']", ''),
From 7518d194376994f4efa3234a12a0435b62dd4df0 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 18:59:04 +0200
Subject: [PATCH 196/207] #469: document __all__ ordering in members.
---
doc/ext/autodoc.rst | 3 +++
1 file changed, 3 insertions(+)
diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst
index 813331015..a1d9d98fb 100644
--- a/doc/ext/autodoc.rst
+++ b/doc/ext/autodoc.rst
@@ -84,6 +84,9 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
will document all non-private member functions and properties (that is,
those whose name doesn't start with ``_``).
+ For modules, ``__all__`` will be respected when looking for members; the
+ order of the members will also be the order in ``__all__``.
+
You can also give an explicit list of members; only these will then be
documented::
From 0ed09e9c5314290db52de09fe59a688ed3c44009 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Thu, 5 Aug 2010 22:35:12 +0200
Subject: [PATCH 197/207] #258: get a bit smarter about closing double quotes.
---
sphinx/util/smartypants.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py
index 75888ea4d..f83f5689b 100644
--- a/sphinx/util/smartypants.py
+++ b/sphinx/util/smartypants.py
@@ -83,6 +83,7 @@ def sphinx_smarty_pants(t):
# Constants for quote education.
punct_class = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]"""
+end_of_word_class = r"""[\s.,;:!?)]"""
close_class = r"""[^\ \t\r\n\[\{\(\-]"""
dec_dashes = r"""–|—"""
@@ -117,8 +118,8 @@ opening_double_quotes_regex = re.compile(r"""
closing_double_quotes_regex = re.compile(r"""
#(%s)? # character that indicates the quote should be closing
"
- (?=\s)
- """ % (close_class,), re.VERBOSE)
+ (?=%s)
+ """ % (close_class, end_of_word_class), re.VERBOSE)
closing_double_quotes_regex_2 = re.compile(r"""
(%s) # character that indicates the quote should be closing
From e227abe1df51d701e00d3f55c08c954a3b342e5b Mon Sep 17 00:00:00 2001
From: Ali Afshar
Date: Fri, 6 Aug 2010 11:35:48 +0100
Subject: [PATCH 198/207] Make the dot command part of the caching system for
dot output generation
---
sphinx/ext/graphviz.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py
index 106de7a65..257ff1b63 100644
--- a/sphinx/ext/graphviz.py
+++ b/sphinx/ext/graphviz.py
@@ -93,6 +93,7 @@ def render_dot(self, code, options, format, prefix='graphviz'):
Render graphviz code into a PNG or PDF output file.
"""
hashkey = code.encode('utf-8') + str(options) + \
+ str(self.builder.config.graphviz_dot) + \
str(self.builder.config.graphviz_dot_args)
fname = '%s-%s.%s' % (prefix, sha(hashkey).hexdigest(), format)
if hasattr(self.builder, 'imgpath'):
From bd950c3bb4c687142559def3345fe36b84dd86cd Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Tue, 10 Aug 2010 17:16:49 +0200
Subject: [PATCH 199/207] Fix test_config under 2.x.
---
tests/test_config.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tests/test_config.py b/tests/test_config.py
index 7fce4495b..b5f88a6f5 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -89,7 +89,8 @@ def test_errors_warnings(dir):
raises_msg(ConfigError, 'conf.py', Config, dir, 'conf.py', {}, None)
# test the automatic conversion of 2.x only code in configs
- write_file(dir / 'conf.py', u'\n\nproject = u"Jägermeister"\n', 'utf-8')
+ write_file(dir / 'conf.py', u'# -*- coding: utf-8\n\n'
+ u'project = u"Jägermeister"\n', 'utf-8')
cfg = Config(dir, 'conf.py', {}, None)
cfg.init_values()
assert cfg.project == u'Jägermeister'
From 8a17f7a84fdb2bc62a64068875dac65faf223572 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Tue, 10 Aug 2010 17:18:46 +0200
Subject: [PATCH 200/207] Prepare for 1.0.2.
---
CHANGES | 4 ++--
sphinx/__init__.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/CHANGES b/CHANGES
index a158267b0..da9a80d75 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,5 @@
-Release 1.0.2 (in development)
-==============================
+Release 1.0.2 (Aug 05, 2010)
+============================
* Allow breaking long signatures, continuing with backlash-escaped
newlines.
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 50aa28cd6..c193d095c 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -12,8 +12,8 @@
import sys
from os import path
-__version__ = '1.0.1+'
-__released__ = '1.0.1' # used when Sphinx builds its own docs
+__version__ = '1.0.2'
+__released__ = '1.0.2' # used when Sphinx builds its own docs
package_dir = path.abspath(path.dirname(__file__))
From e1ce9a63a664712ceeeda651e4dc039cd9f9eaa8 Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Wed, 11 Aug 2010 17:16:20 +0200
Subject: [PATCH 201/207] Fix file name in manifest.
---
MANIFEST.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index 5e3104a82..cfc44c17e 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -7,7 +7,7 @@ include TODO
include babel.cfg
include Makefile
-include setup_distribute.py
+include distribute_setup.py
include sphinx-autogen.py
include sphinx-build.py
include sphinx-quickstart.py
From a484c6f793e4dc352b98d0202b6d3154664eac2c Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sat, 14 Aug 2010 16:51:04 +0200
Subject: [PATCH 202/207] #490: Fix cross-references to objects of types added
by the :func:`~.Sphinx.add_object_type` API function.
---
CHANGES | 3 +++
sphinx/domains/std.py | 8 +++++++-
tests/test_build_html.py | 2 +-
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/CHANGES b/CHANGES
index da9a80d75..bd368e17c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
Release 1.0.2 (Aug 05, 2010)
============================
+* #490: Fix cross-references to objects of types added by the
+ :func:`~.Sphinx.add_object_type` API function.
+
* Allow breaking long signatures, continuing with backlash-escaped
newlines.
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index 63a3bf6dc..6194ace9a 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -484,7 +484,13 @@ class StandardDomain(Domain):
return make_refnode(builder, fromdocname, docname,
labelid, contnode)
else:
- docname, labelid = self.data['objects'].get((typ, target), ('', ''))
+ objtypes = self.objtypes_for_role(typ) or []
+ for objtype in objtypes:
+ if (objtype, target) in self.data['objects']:
+ docname, labelid = self.data['objects'][objtype, target]
+ break
+ else:
+ docname, labelid = '', ''
if not docname:
if typ == 'term':
env.warn(node.get('refdoc', fromdocname),
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 6857d3309..912a0b426 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -166,7 +166,7 @@ HTML_XPATH = {
(".//a[@href='#mod.Cls'][@class='reference internal']", ''),
(".//dl[@class='userdesc']", ''),
(".//dt[@id='userdesc-myobj']", ''),
- (".//a[@href='#userdesc-myobj']", ''),
+ (".//a[@href='#userdesc-myobj'][@class='reference internal']", ''),
# C references
(".//span[@class='pre']", 'CFunction()'),
(".//a[@href='#Sphinx_DoSomething']", ''),
From 572e6019817685b811c637deb0be9293732536ab Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sat, 14 Aug 2010 17:04:25 +0200
Subject: [PATCH 203/207] Fix handling of doc field types for different
directive types.
---
CHANGES | 2 ++
sphinx/directives/__init__.py | 1 -
sphinx/util/docfields.py | 2 +-
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/CHANGES b/CHANGES
index bd368e17c..89f33dd27 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,8 @@ Release 1.0.2 (Aug 05, 2010)
* #490: Fix cross-references to objects of types added by the
:func:`~.Sphinx.add_object_type` API function.
+* Fix handling of doc field types for different directive types.
+
* Allow breaking long signatures, continuing with backlash-escaped
newlines.
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index 48c44178d..b7adbcafd 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -162,7 +162,6 @@ class ObjectDescription(Directive):
self.env.temp_data['object'] = self.names[0]
self.before_content()
self.state.nested_parse(self.content, self.content_offset, contentnode)
- #self.handle_doc_fields(contentnode)
DocFieldTransformer(self).transform_all(contentnode)
self.env.temp_data['object'] = None
self.after_content()
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index 6ce6d82bf..89f81e8cb 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -167,7 +167,7 @@ class DocFieldTransformer(object):
def __init__(self, directive):
self.domain = directive.domain
- if not hasattr(directive, '_doc_field_type_map'):
+ if '_doc_field_type_map' not in directive.__class__.__dict__:
directive.__class__._doc_field_type_map = \
self.preprocess_fieldtypes(directive.__class__.doc_field_types)
self.typemap = directive._doc_field_type_map
From d1cc9be1d00bfe88887aeec74ec34f81edefb9fa Mon Sep 17 00:00:00 2001
From: Georg Brandl
Date: Sat, 14 Aug 2010 17:09:23 +0200
Subject: [PATCH 204/207] Update release date.
---
CHANGES | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGES b/CHANGES
index 89f33dd27..a640ea60b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,4 @@
-Release 1.0.2 (Aug 05, 2010)
+Release 1.0.2 (Aug 14, 2010)
============================
* #490: Fix cross-references to objects of types added by the
From fae5b3c1339ef08fac98ca3ed0a52350350acd70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 14 Aug 2010 19:52:04 +0200
Subject: [PATCH 206/207] Fix doctest to work with Python 2.5 and lower
---
tests/root/doctest.txt | 37 ++++++++++++++++++++-----------------
1 file changed, 20 insertions(+), 17 deletions(-)
diff --git a/tests/root/doctest.txt b/tests/root/doctest.txt
index 6ac0b2863..ba9a72c52 100644
--- a/tests/root/doctest.txt
+++ b/tests/root/doctest.txt
@@ -50,23 +50,24 @@ Special directives
.. testsetup:: *
- from math import factorial
+ def squared(x):
+ return x * x
.. doctest::
- >>> factorial(1)
- 1
+ >>> squared(2)
+ 4
.. testcode::
- print(factorial(1))
+ print(squared(2))
.. testoutput::
- 1
+ 4
- >>> factorial(1)
- 1
+ >>> squared(2)
+ 4
* options for testcode/testoutput blocks
@@ -85,36 +86,38 @@ Special directives
.. testsetup:: group1
- from math import trunc
+ def add(x, y):
+ return x + y
- ``trunc`` is now known in "group1", but not in others.
+
+ ``add`` is now known in "group1", but not in others.
.. doctest:: group1
- >>> trunc(1.1)
- 1
+ >>> add(1, 1)
+ 2
.. doctest:: group2
- >>> trunc(1.1)
+ >>> add(1, 1)
Traceback (most recent call last):
...
- NameError: name 'trunc' is not defined
+ NameError: name 'add' is not defined
Interleaving testcode/testoutput:
.. testcode:: group1
- print(factorial(3))
+ print(squared(3))
.. testcode:: group2
- print(factorial(4))
+ print(squared(4))
.. testoutput:: group1
- 6
+ 9
.. testoutput:: group2
- 24
+ 16
From 860fc27177370cc55d990584f45d39b3583ca378 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?=
Date: Sat, 14 Aug 2010 19:48:42 +0200
Subject: [PATCH 207/207] shutil.copytree doesn't have an ignore argument in
Python 2.4
---
tests/path.py | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/tests/path.py b/tests/path.py
index df96bce45..8e9afeaa8 100644
--- a/tests/path.py
+++ b/tests/path.py
@@ -88,7 +88,7 @@ class path(str):
"""
shutil.rmtree(self, ignore_errors=ignore_errors, onerror=onerror)
- def copytree(self, destination, symlinks=False, ignore=None):
+ def copytree(self, destination, symlinks=False):
"""
Recursively copy a directory to the given `destination`. If the given
`destination` does not exist it will be created.
@@ -97,12 +97,8 @@ class path(str):
If ``True`` symbolic links in the source tree result in symbolic
links in the destination tree otherwise the contents of the files
pointed to by the symbolic links are copied.
-
- :param ignore:
- A callback which gets called with the path of the directory being
- copied and a list of paths as returned by :func:`os.listdir`.
"""
- shutil.copytree(self, destination, symlinks=symlinks, ignore=ignore)
+ shutil.copytree(self, destination, symlinks=symlinks)
def movetree(self, destination):
"""