[reprotest] 01/01: utils: Add AttributeReplacer and move AttributeFunctor here

Ximin Luo infinity0 at debian.org
Wed Sep 27 19:27:09 UTC 2017


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository reprotest.

commit c2bd30c304c0181168ab2ec03a88e17967848aa7
Author: Ximin Luo <infinity0 at debian.org>
Date:   Wed Sep 27 21:25:43 2017 +0200

    utils: Add AttributeReplacer and move AttributeFunctor here
---
 reprotest/__init__.py |  5 ++---
 reprotest/build.py    |  8 +++++---
 reprotest/presets.py  |  7 +------
 reprotest/utils.py    | 41 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/reprotest/__init__.py b/reprotest/__init__.py
index a5972e2..551b2d5 100644
--- a/reprotest/__init__.py
+++ b/reprotest/__init__.py
@@ -8,7 +8,6 @@ import contextlib
 import logging
 import os
 import random
-import re
 import shlex
 import shutil
 import subprocess
@@ -280,7 +279,7 @@ class TestArgs(collections.namedtuple('_Test',
                         raise ValueError("already built '%s'" % name)
                     names_seen.add(name)
 
-                    var = var._replace(spec=var.spec.apply_dynamic_defaults(source_root))
+                    var = var.replace.spec.apply_dynamic_defaults(source_root)
                     bctx = BuildContext(testbed.scratch, result_dir, source_root, name, var)
 
                     build = bctx.make_build_commands(
@@ -377,7 +376,7 @@ def check_auto(test_args, testbed_args, build_variations=Variations.of(Variation
         varnames = VariationSpec.all_names()
         random.shuffle(varnames)
         for v in varnames:
-            var_test = var_cur.replace_spec(**{v: var_x1.spec[v]})
+            var_test = var_cur.replace.spec._replace(**{v: var_x1.spec[v]})
             if is_reproducible(v, var_test):
                 # vary it for the next test as well, it's OK to vary it
                 var_cur = var_test
diff --git a/reprotest/build.py b/reprotest/build.py
index 976495c..be22ff6 100644
--- a/reprotest/build.py
+++ b/reprotest/build.py
@@ -15,6 +15,7 @@ import types
 
 from reprotest import _shell_ast
 from reprotest import mdiffconf
+from reprotest.utils import AttributeReplacer
 
 
 def tool_required(*tools):
@@ -455,15 +456,16 @@ class Variations(collections.namedtuple('_Variations', 'spec verbosity')):
     def of(cls, *specs, zero=VariationSpec.empty(), verbosity=0):
         return [cls(spec, verbosity) for spec in [zero] + list(specs)]
 
-    def replace_spec(self, *args, **kwargs):
-        return self._replace(spec=self.spec._replace(*args, **kwargs))
+    @property
+    def replace(self):
+        return AttributeReplacer(self, [])
 
 
 if __name__ == "__main__":
     import sys
     d = VariationSpec()
     for s in sys.argv[1:]:
-        d = d.append(s)
+        d = d.extend([s])
         print(s)
         print(">>>", d)
     print("result", d.apply_dynamic_defaults("."))
diff --git a/reprotest/presets.py b/reprotest/presets.py
index 9f4f340..ca2a0e5 100644
--- a/reprotest/presets.py
+++ b/reprotest/presets.py
@@ -6,12 +6,7 @@ import os
 import shlex
 import subprocess
 
-
-class AttributeFunctor(collections.namedtuple('_AttributeFunctor', 'x f')):
-    def __getattr__(self, name):
-        return lambda *args: self.x._replace(**{
-            name: self.f(getattr(self.x, name), *args)
-        })
+from reprotest.utils import AttributeFunctor
 
 
 class ReprotestPreset(collections.namedtuple('_ReprotestPreset',
diff --git a/reprotest/utils.py b/reprotest/utils.py
new file mode 100644
index 0000000..83a8417
--- /dev/null
+++ b/reprotest/utils.py
@@ -0,0 +1,41 @@
+# Licensed under the GPL: https://www.gnu.org/licenses/gpl-3.0.en.html
+# For details: reprotest/debian/copyright
+
+import collections
+import functools
+
+
+class AttributeFunctor(collections.namedtuple('_AttributeFunctor', 'x f')):
+    """Functor for applying functions to attributes of namedtuples."""
+    def __getattr__(self, name):
+        return lambda *args: self.x._replace(**{
+            name: self.f(getattr(self.x, name), *args)
+        })
+
+
+class AttributeReplacer(object):
+    """Proxy for setting attributes deeply on trees of namedtuples."""
+    __slots__ = ['top', 'attrs']
+
+    def __init__(self, top, attrs):
+        self.top = top
+        self.attrs = attrs
+
+    def rgetattr(self, attrs):
+        return functools.reduce(getattr, attrs, self.top)
+
+    def __getattr__(self, name):
+        attr = self.rgetattr(self.attrs + [name])
+        return self.__class__(self.top, self.attrs + [name])
+
+    def __call__(self, *args, **kwargs):
+        f = self.rgetattr(self.attrs)
+        if not callable(f):
+            raise TypeError("not callable: %s of %s" % (self.attrs, self.top))
+        result = f(*args, **kwargs)
+        parents = [(self.rgetattr(self.attrs[:i]), self.attrs[i])
+            for i in range(len(self.attrs))]
+        return functools.reduce(
+            lambda v, p: p[0]._replace(**{p[1]: v}),
+            reversed(parents[:-1]), result)
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/reprotest.git



More information about the Reproducible-commits mailing list