[Python-modules-team] Bug#776443: sphinx: please make output reproducible

2015-01-28 Thread Jérémy Bobbio
Chris Lamb:
 The attached patch attempts to remedy these issues. Once applied, many
 packages that use sphinx--but alas not sphinx itself yet!--can be built
 reproducibly in our current experimental
 framework.

Attached is a full patch of the package uploaded in the reproducible
experimental toolchain [1]. It also fixes a minor issue with Chris'
original patch regarding domains ordering.

  [1]: https://wiki.debian.org/ReproducibleBuilds/ExperimentalToolchain

-- 
Lunar.''`. 
lu...@debian.org: :Ⓐ  :  # apt-get install anarchism
`. `'` 
  `-   
diff -Nru sphinx-1.2.3+dfsg/debian/changelog sphinx-1.2.3+dfsg/debian/changelog
--- sphinx-1.2.3+dfsg/debian/changelog	2014-09-12 10:33:57.0 +0200
+++ sphinx-1.2.3+dfsg/debian/changelog	2015-01-28 15:39:42.0 +0100
@@ -1,3 +1,11 @@
+sphinx (1.2.3+dfsg-1.0~reproducible1) UNRELEASED; urgency=low
+
+  [ Chris Lamb ]
+  * Add remove_non_determinism.diff to make Sphinx output reproducible
+from one build to the other (closes: #776443).
+
+ -- Jérémy Bobbio lu...@debian.org  Wed, 28 Jan 2015 14:38:24 +
+
 sphinx (1.2.3+dfsg-1) unstable; urgency=medium
 
   * New upstream bugfix release.
diff -Nru sphinx-1.2.3+dfsg/debian/patches/remove_non_determinism.diff sphinx-1.2.3+dfsg/debian/patches/remove_non_determinism.diff
--- sphinx-1.2.3+dfsg/debian/patches/remove_non_determinism.diff	1970-01-01 01:00:00.0 +0100
+++ sphinx-1.2.3+dfsg/debian/patches/remove_non_determinism.diff	2015-01-28 16:03:13.0 +0100
@@ -0,0 +1,133 @@
+Description: remove non-determinism
+ To enable packages using Sphinx to build reproducibly, its output
+ needs to be the same from one build to another.
+ .
+ Its output now strips memory references such as:
+ .
+   __main__.A at 0x7f68cb685710
+ .
+ In addition, various generated files (objects.inv, searchindex.js,
+ translations) are now written with their keys in a determinstic order.
+Author: Chris Lamb la...@debian.org
+
+--- sphinx-1.2.3+dfsg.orig/sphinx/builders/html.py
 sphinx-1.2.3+dfsg/sphinx/builders/html.py
+@@ -269,7 +269,8 @@ class StandaloneHTMLBuilder(Builder):
+ # html_domain_indices can be False/True or a list of index names
+ indices_config = self.config.html_domain_indices
+ if indices_config:
+-for domain in self.env.domains.itervalues():
++for domain_name in sorted(self.env.domains.keys()):
++domain = self.env.domains[domain_name]
+ for indexcls in domain.indices:
+ indexname = '%s-%s' % (domain.name, indexcls.name)
+ if isinstance(indices_config, list):
+@@ -808,7 +809,7 @@ class StandaloneHTMLBuilder(Builder):
+ compressor = zlib.compressobj(9)
+ for domainname, domain in self.env.domains.iteritems():
+ for name, dispname, type, docname, anchor, prio in \
+-domain.get_objects():
++sorted(domain.get_objects()):
+ if anchor.endswith(name):
+ # this can shorten the inventory by as much as 25%
+ anchor = anchor[:-len(name)] + '$'
+--- sphinx-1.2.3+dfsg.orig/sphinx/ext/autodoc.py
 sphinx-1.2.3+dfsg/sphinx/ext/autodoc.py
+@@ -60,7 +60,6 @@ class DefDict(dict):
+ 
+ identity = lambda x: x
+ 
+-
+ class Options(dict):
+ A dict/attribute hybrid that returns None on nonexisting keys.
+ def __getattr__(self, name):
+@@ -975,7 +974,8 @@ class FunctionDocumenter(DocstringSignat
+ argspec = getargspec(self.object.__init__)
+ if argspec[0]:
+ del argspec[0][0]
+-args = inspect.formatargspec(*argspec)
++args = inspect.formatargspec(*argspec,
++ formatvalue=lambda x: '=' + safe_repr(x))
+ # escape backslashes for reST
+ args = args.replace('\\', '')
+ return args
+@@ -1030,7 +1030,8 @@ class ClassDocumenter(ModuleLevelDocumen
+ return None
+ if argspec[0] and argspec[0][0] in ('cls', 'self'):
+ del argspec[0][0]
+-return inspect.formatargspec(*argspec)
++return inspect.formatargspec(*argspec,
++ formatvalue=lambda x: '=' + safe_repr(x))
+ 
+ def format_signature(self):
+ if self.doc_as_attr:
+@@ -1229,7 +1230,8 @@ class MethodDocumenter(DocstringSignatur
+ argspec = getargspec(self.object)
+ if argspec[0] and argspec[0][0] in ('cls', 'self'):
+ del argspec[0][0]
+-args = inspect.formatargspec(*argspec)
++args = inspect.formatargspec(*argspec,
++ formatvalue=lambda x: '=' + safe_repr(x))
+ # escape backslashes for reST
+ args = args.replace('\\', '')
+ return args
+--- 

[Python-modules-team] Bug#776443: sphinx: please make output reproducible

2015-01-28 Thread Dmitry Shachnev
Control: forwarded -1 https://github.com/sphinx-doc/sphinx/pull/1694

I have now forwarded your patch upstream (needed a bit of rebasing),
let's see what upstream thinks about it.

As we are in freeze now, I guess this can wait until the new
upstream release, right?

--
Dmitry Shachnev

signature.asc
Description: OpenPGP digital signature
___
Python-modules-team mailing list
Python-modules-team@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/python-modules-team

[Python-modules-team] Bug#776443: sphinx: please make output reproducible

2015-01-27 Thread Chris Lamb
Source: sphinx
Version: 1.2.3+dfsg-1
Severity: wishlist
Tags: patch
User: reproducible-bui...@lists.alioth.debian.org
Usertags: toolchain randomness
X-Debbugs-Cc: reproducible-bui...@lists.alioth.debian.org

Hi,

While working on the reproducible builds effort [1], we have noticed
that sphinx is generating documentation that is not repoducible.

For example, its output includes non-deterministic memory references
such as:

   __main__.A at 0x7f68cb685710

In addition, various generated (objects.inv, searchindex.js,
translations) do not output their keys in a determinstic order,
resulting in further randomness.

The attached patch attempts to remedy these issues. Once applied, many
packages that use sphinx--but alas not sphinx itself yet!--can be built
reproducibly in our current experimental
framework.

 [1]: https://wiki.debian.org/ReproducibleBuilds


Regards,

-- 
  ,''`.
 : :'  : Chris Lamb
 `. `'`  la...@debian.org / chris-lamb.co.uk
   `-
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 9c039e3..f489a35 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -269,7 +269,7 @@ class StandaloneHTMLBuilder(Builder):
 # html_domain_indices can be False/True or a list of index names
 indices_config = self.config.html_domain_indices
 if indices_config:
-for domain in self.env.domains.itervalues():
+for domain in sorted(self.env.domains.itervalues()):
 for indexcls in domain.indices:
 indexname = '%s-%s' % (domain.name, indexcls.name)
 if isinstance(indices_config, list):
@@ -808,7 +808,7 @@ class StandaloneHTMLBuilder(Builder):
 compressor = zlib.compressobj(9)
 for domainname, domain in self.env.domains.iteritems():
 for name, dispname, type, docname, anchor, prio in \
-domain.get_objects():
+sorted(domain.get_objects()):
 if anchor.endswith(name):
 # this can shorten the inventory by as much as 25%
 anchor = anchor[:-len(name)] + '$'
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index 423f921..721fbb4 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -60,7 +60,6 @@ class DefDict(dict):
 
 identity = lambda x: x
 
-
 class Options(dict):
 A dict/attribute hybrid that returns None on nonexisting keys.
 def __getattr__(self, name):
@@ -975,7 +974,8 @@ class FunctionDocumenter(DocstringSignatureMixin, 
ModuleLevelDocumenter):
 argspec = getargspec(self.object.__init__)
 if argspec[0]:
 del argspec[0][0]
-args = inspect.formatargspec(*argspec)
+args = inspect.formatargspec(*argspec,
+ formatvalue=lambda x: '=' + safe_repr(x))
 # escape backslashes for reST
 args = args.replace('\\', '')
 return args
@@ -1030,7 +1030,8 @@ class ClassDocumenter(ModuleLevelDocumenter):
 return None
 if argspec[0] and argspec[0][0] in ('cls', 'self'):
 del argspec[0][0]
-return inspect.formatargspec(*argspec)
+return inspect.formatargspec(*argspec,
+ formatvalue=lambda x: '=' + safe_repr(x))
 
 def format_signature(self):
 if self.doc_as_attr:
@@ -1229,7 +1230,8 @@ class MethodDocumenter(DocstringSignatureMixin, 
ClassLevelDocumenter):
 argspec = getargspec(self.object)
 if argspec[0] and argspec[0][0] in ('cls', 'self'):
 del argspec[0][0]
-args = inspect.formatargspec(*argspec)
+args = inspect.formatargspec(*argspec,
+ formatvalue=lambda x: '=' + safe_repr(x))
 # escape backslashes for reST
 args = args.replace('\\', '')
 return args
diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py
index bd95ecc..760b137 100644
--- a/sphinx/search/__init__.py
+++ b/sphinx/search/__init__.py
@@ -268,13 +268,13 @@ class IndexBuilder(object):
 if fn in fn2index:
 rv[k] = fn2index[fn]
 else:
-rv[k] = [fn2index[fn] for fn in v if fn in fn2index]
+rv[k] = sorted([fn2index[fn] for fn in v if fn in 
fn2index])
 return rvs
 
 def freeze(self):
 Create a usable data structure for serializing.
-filenames = self._titles.keys()
-titles = self._titles.values()
+filenames = sorted(self._titles.keys())
+titles = sorted(self._titles.values())
 fn2index = dict((f, i) for (i, f) in enumerate(filenames))
 terms, title_terms = self.get_terms(fn2index)
 
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index cdbfea7..e04f1fa 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@