From 6f23e3450a8180521040515cc04d11bb6be0e05c Mon Sep 17 00:00:00 2001
From: Jameson Graef Rollins <jrollins@finestructure.net>
Date: Wed, 6 Nov 2019 16:12:11 -0800
Subject: [PATCH] struct: Struct improve initialization signature to be more
 dict-like

Improves update() method to be able to update from another Struct, and
not overwrite existing Struct attributes, and removes need to
from_dict classmethod.
---
 gwinc/gwinc_matlab.py |  2 +-
 gwinc/struct.py       | 57 +++++++++++++++++++++++++++----------------
 2 files changed, 37 insertions(+), 22 deletions(-)

diff --git a/gwinc/gwinc_matlab.py b/gwinc/gwinc_matlab.py
index f5f9fa12..22d19eec 100644
--- a/gwinc/gwinc_matlab.py
+++ b/gwinc/gwinc_matlab.py
@@ -120,7 +120,7 @@ def ifo_matlab_transform(ifo):
     """
     # add constants
     CONSTS = {k:v for k, v in const.__dict__ if not k.startswith('__')}
-    ifo.Constants = Struct.from_dict(CONSTS)
+    ifo.Constants = Struct(CONSTS)
 
     # copy tempurature into Constants
     ifo.Constants.Temp = ifo.Infrastructure.Temp
diff --git a/gwinc/struct.py b/gwinc/struct.py
index 07a722e7..819b70e4 100644
--- a/gwinc/struct.py
+++ b/gwinc/struct.py
@@ -75,11 +75,15 @@ class Struct(object):
 
     ##########
 
-    def __init__(self, **kwargs):
-        """Arguments can pre-fill the structure
+    def __init__(self, *args, **kwargs):
+        """Initialize Struct object
+
+        Initializes similar to dict(), taking a single dict or mapping
+        argument, or keyword arguments to initially populate the
+        Struct.
 
         """
-        self.__dict__.update(kwargs)
+        self.update(dict(*args, **kwargs))
 
     def __getitem__(self, key):
         """Get a (possibly nested) value from the struct.
@@ -115,6 +119,34 @@ class Struct(object):
     def setdefault(self, key, default):
         return self.__dict__.setdefault(key, default)
 
+    def update(self, other):
+        """Update Struct from other Struct or dict.
+
+        """
+        if isinstance(other, Struct):
+            d = other.__dict__
+        else:
+            d = dict(other)
+        for k, v in d.items():
+            if k in self:
+                if isinstance(self[k], Struct) \
+                   and isinstance(v, (dict, Struct)):
+                    self[k].update(v)
+                    continue
+                try:
+                    delattr(self, k)
+                except AttributeError:
+                    delattr(self.__class__, k)
+            if isinstance(v, dict):
+                self[k] = Struct(v)
+            elif isinstance(v, (list, tuple)):
+                try:
+                    self[k] = list(map(Struct, v))
+                except TypeError:
+                    self[k] = v
+            else:
+                self[k] = v
+
     def items(self):
         return self.__dict__.items()
 
@@ -254,30 +286,13 @@ class Struct(object):
             return txt.getvalue()
 
 
-    @classmethod
-    def from_dict(cls, d):
-        """Create Struct from nested dict.
-
-        """
-        c = cls()
-        for k,v in d.items():
-            if type(v) == dict:
-                c.__dict__[k] = Struct.from_dict(v)
-            else:
-                try:
-                    c.__dict__[k] = list(map(Struct.from_dict, v))
-                except (AttributeError, TypeError):
-                    c.__dict__[k] = v
-        return c
-
-
     @classmethod
     def from_yaml(cls, y):
         """Create Struct from YAML string.
 
         """
         d = yaml.load(y, Loader=yaml_loader)
-        return cls.from_dict(d)
+        return cls(d)
 
 
     @classmethod
-- 
GitLab