Package: bind9-doc Version: 1:9.19.21-1 Severity: wishlist Tags: patch upstream User: reproducible-bui...@lists.alioth.debian.org Usertags: randomness
Dear Maintainer, I'm an occasional volunteer contributor to the Reproducible Builds[1] project, and noticed recently that the bind9-doc package failed[2] an automated Debian package build test. >From inspecting the differences in the generated documentation, it appears that some tag annotations (example[3]) in the source documentation are de-duplicated in a non-deterministic manner during Sphinx documentation builds. The cause is that the de-duplication is performed using a non-order-preserving Python set[4] object, meaning that the resulting output documentation package can vary at build-time. Please find attached a patch that implements order-preserving de-duplication of these tags. I've confirmed that the package compiles with it applied, and believe that it should produce deterministic tag output in the built package. Regards, James [1] - https://reproducible-builds.org/ [2] - https://tests.reproducible-builds.org/debian/rb-pkg/bookworm/amd64/diffoscope-results/bind9.html [3] - https://sources.debian.org/src/bind9/1%3A9.18.19-1~deb12u1/doc/arm/reference.rst/#L5648 [4] - https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset
diff --git a/doc/arm/_ext/iscconf.py b/doc/arm/_ext/iscconf.py index b5bd966e2..0b96d7bd3 100644 --- a/doc/arm/_ext/iscconf.py +++ b/doc/arm/_ext/iscconf.py @@ -41,12 +41,18 @@ logger = logging.getLogger(__name__) def split_csv(argument, required): argument = argument or "" - outlist = list(filter(len, (s.strip() for s in argument.split(",")))) - if required and not outlist: + values = list(filter(len, (s.strip() for s in argument.split(",")))) + if required and not values: raise ValueError( "a non-empty list required; provide at least one value or remove" " this option" ) + # Order-preserving de-duplication + outlist, seen = list(), set() + for value in values: + if value not in seen: + seen.add(value) + outlist.append(value) return outlist @@ -73,10 +79,8 @@ def domain_factory(domainname, domainlabel, todolist, grammar): def run(self): placeholder = todolist("") - placeholder["isc_filter_tags"] = set(self.options.get("filter_tags", [])) - placeholder["isc_filter_blocks"] = set( - self.options.get("filter_blocks", []) - ) + placeholder["isc_filter_tags"] = self.options.get("filter_tags", []) + placeholder["isc_filter_blocks"] = self.options.get("filter_blocks", []) return [placeholder] class ISCConfDomain(Domain): @@ -127,7 +131,7 @@ def domain_factory(domainname, domainlabel, todolist, grammar): @property def isc_tags(self): - return set(self.options.get("tags", [])) + return self.options.get("tags", []) @property def isc_short(self):