[master 1/1] Add a class constructor for namedtuples with default values

vpodzime installerbot-noreply at redhat.com
Tue Nov 3 17:58:01 UTC 2015


From: Vratislav Podzimek <vpodzime at redhat.com>

namedtuple classes are great as pure data objects one can store things in and
then access them really easily. The problem is that if a new field is added to a
namedtuple, all places creating instances of it has to be modified to provide a
value for that new field. The default_namedtuple class constructor implemented
in this commit constructs namedtuples with default values for (some) fields and
fields without any value passed defaulting to None.

Such default namedtuple classes are still great for storing data and
e.g. passing data to functions/methods grouped into a few objects (like
DeviceInfo, FormatInfo,...) passed as arguments, but allow us to add extra
fields in the future (e.g. a new device attribute to DeviceInfo) without
breaking the API.
---
 blivet/util.py | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/blivet/util.py b/blivet/util.py
index f23ea83..1583fc1 100644
--- a/blivet/util.py
+++ b/blivet/util.py
@@ -15,6 +15,7 @@
 from decimal import Decimal
 from contextlib import contextmanager
 from functools import wraps
+from collections import namedtuple
 
 import gi
 gi.require_version("BlockDev", "1.0")
@@ -805,3 +806,43 @@ def the_func(*args, **kwargs):
         return the_func
 
     return deprecate_func
+
+def default_namedtuple(name, fields, doc=""):
+    """Create a namedtuple class
+
+    The difference between a namedtuple class and this class is that default
+    values may be specified for fields and fields with missing values on
+    initialization being initialized to None.
+
+    :param str name: name of the new class
+    :param fields: field descriptions - an iterable of either "name" or ("name", default_value)
+    :type fields: list of str or (str, object) objects
+    :param str doc: the docstring for the new class (should at least describe the meanings and
+                    types of fields)
+
+    """
+    field_names = list()
+    for field in fields:
+        if isinstance(field, tuple):
+            field_names.append(field[0])
+        else:
+            field_names.append(field)
+    nt = namedtuple(name, field_names)
+
+    class TheDefaultNamedTuple(nt):
+        if doc:
+            __doc__ = doc
+        def __new__(cls, *args, **kwargs):
+            args_list = list(args)
+            sorted_kwargs = sorted(kwargs.keys(), key=lambda x: field_names.index(x))
+            for i in range(len(args), len(field_names)):
+                if field_names[i] in sorted_kwargs:
+                    args_list.append(kwargs[field_names[i]])
+                elif isinstance(fields[i], tuple):
+                    args_list.append(fields[i][1])
+                else:
+                    args_list.append(None)
+
+            return nt.__new__(cls, *args_list)
+
+    return TheDefaultNamedTuple


-- 
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/e454a4258706a29f2aaf0f2d7d6ebf1bad24a95a


More information about the anaconda-patches mailing list