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.000000000 +0200
+++ sphinx-1.2.3+dfsg/debian/changelog	2015-01-28 15:39:42.000000000 +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 +0000
+
 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.000000000 +0100
+++ sphinx-1.2.3+dfsg/debian/patches/remove_non_determinism.diff	2015-01-28 16:03:13.000000000 +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
+--- sphinx-1.2.3+dfsg.orig/sphinx/search/__init__.py
++++ sphinx-1.2.3+dfsg/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)
+ 
+--- sphinx-1.2.3+dfsg.orig/sphinx/util/inspect.py
++++ sphinx-1.2.3+dfsg/sphinx/util/inspect.py
+@@ -9,6 +9,7 @@
+     :license: BSD, see LICENSE for details.
+ """
+ 
++import re
+ import sys
+ 
+ # this imports the standard library inspect module without resorting to
+@@ -135,7 +136,10 @@ def safe_repr(object):
+     except Exception:
+         raise ValueError
+     if isinstance(s, bytes):
+-        return force_decode(s, None).replace('\n', ' ')
++        s = force_decode(s, None)
++    # Strip non-deterministic memory addresses such as
++    # ``<__main__.A at 0x7f68cb685710>``
++    s = re.sub(r' at 0x[0-9a-f]{8,12}(?=>$)', '', s)
+     return s.replace('\n', ' ')
+ 
+ 
+--- sphinx-1.2.3+dfsg.orig/sphinx/util/jsdump.py
++++ sphinx-1.2.3+dfsg/sphinx/util/jsdump.py
+@@ -87,11 +87,13 @@ def dumps(obj, key=False):
+     elif isinstance(obj, (int, long, float)):
+         return str(obj)
+     elif isinstance(obj, dict):
+-        return '{%s}' % ','.join('%s:%s' % (
++        return '{%s}' % ','.join(sorted('%s:%s' % (
+             dumps(key, True),
+             dumps(value)
+-        ) for key, value in obj.iteritems())
+-    elif isinstance(obj, (tuple, list, set)):
++        ) for key, value in obj.iteritems()))
++    elif isinstance(obj, set):
++        return '[%s]' % ','.join(sorted(dumps(x) for x in obj))
++    elif isinstance(obj, (tuple, list)):
+         return '[%s]' % ','.join(dumps(x) for x in obj)
+     elif isinstance(obj, basestring):
+         return encode_string(obj)
diff -Nru sphinx-1.2.3+dfsg/debian/patches/series sphinx-1.2.3+dfsg/debian/patches/series
--- sphinx-1.2.3+dfsg/debian/patches/series	2014-09-12 10:33:39.000000000 +0200
+++ sphinx-1.2.3+dfsg/debian/patches/series	2015-01-28 15:37:54.000000000 +0100
@@ -4,3 +4,4 @@
 parallel_2to3.diff
 no_external_css.diff
 fix_latex_hlines.diff
+remove_non_determinism.diff

Attachment: signature.asc
Description: 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

Reply via email to