Repository: rhayes777/PyAutoFit
Pull request #1044 (2024-09-06 12:26:53)

feature/build fixes
Fixes for #1041, #1042 and sqlalchemy



Patch:
diff --git a/autofit/database/model/array.py b/autofit/database/model/array.py
index f2c032a08..fc665c383 100644
--- a/autofit/database/model/array.py
+++ b/autofit/database/model/array.py
@@ -29,7 +29,12 @@ class Array(Object):
     _shape = sa.Column(sa.String)
 
     fit_id = sa.Column(sa.String, sa.ForeignKey("fit.id"))
-    fit = sa.orm.relationship("Fit", uselist=False, foreign_keys=[fit_id])
+    fit = sa.orm.relationship(
+        "Fit",
+        uselist=False,
+        foreign_keys=[fit_id],
+        back_populates="arrays",
+    )
 
     @property
     def shape(self):
@@ -84,6 +89,13 @@ class HDU(Array):
         "polymorphic_identity": "hdu",
     }
 
+    fit = sa.orm.relationship(
+        "Fit",
+        uselist=False,
+        foreign_keys=[Array.fit_id],
+        back_populates="hdus",
+    )
+
     @property
     def header(self):
         """
diff --git a/autofit/database/model/common.py b/autofit/database/model/common.py
index 75b71b741..da7f2dc15 100644
--- a/autofit/database/model/common.py
+++ b/autofit/database/model/common.py
@@ -1,11 +1,7 @@
-from typing import Union
+from typing import Union, ItemsView, Any, Iterable, Tuple
 
 from ..sqlalchemy_ import sa
 
-from autofit.mapper.prior import abstract
-from autofit.mapper.prior_model import prior_model
-from autofit.mapper.prior_model import collection
-
 from .model import Object
 
 
@@ -26,7 +22,15 @@ class Dict(Object):
     __mapper_args__ = {"polymorphic_identity": "dict"}
 
     def __call__(self):
-        return {child.name: child() for child in self.children}
+        d = {}
+        for child in self.children:
+            instance = child()
+            if child.name != "":
+                d[child.name] = instance
+            else:
+                d[instance[0]] = instance[1]
+
+        return d
 
     @classmethod
     def _from_object(cls, source: dict):
@@ -34,3 +38,22 @@ class Dict(Object):
         instance._add_children(source.items())
         instance.cls = dict
         return instance
+
+    def _add_children(
+        self, items: Union[ItemsView[str, Any], Iterable[Tuple[str, Any]]]
+    ):
+        """
+        Add database representations of child attributes
+
+        Parameters
+        ----------
+        items
+            Attributes such as floats or priors that are associated
+            with the real object
+        """
+        self.children = [
+            Object.from_object(value, name=key)
+            if isinstance(key, str)
+            else Object.from_object((key, value))
+            for key, value in items
+        ]
diff --git a/autofit/database/model/fit.py b/autofit/database/model/fit.py
index 2ec84df43..e97c47abd 100644
--- a/autofit/database/model/fit.py
+++ b/autofit/database/model/fit.py
@@ -30,7 +30,11 @@ class Pickle(Base):
     name = sa.Column(sa.String)
     string = sa.Column(sa.String)
     fit_id = sa.Column(sa.String, sa.ForeignKey("fit.id"))
-    fit = sa.orm.relationship("Fit", uselist=False)
+    fit = sa.orm.relationship(
+        "Fit",
+        uselist=False,
+        back_populates="pickles",
+    )
 
     @property
     def value(self):
@@ -64,7 +68,11 @@ class JSON(Base):
     name = sa.Column(sa.String)
     string = sa.Column(sa.String)
     fit_id = sa.Column(sa.String, sa.ForeignKey("fit.id"))
-    fit = sa.orm.relationship("Fit", uselist=False)
+    fit = sa.orm.relationship(
+        "Fit",
+        uselist=False,
+        back_populates="jsons",
+    )
 
     @property
     def dict(self):
@@ -111,7 +119,10 @@ class NamedInstance(Base):
     instance_id = sa.Column(sa.Integer, sa.ForeignKey("object.id"))
 
     __instance = sa.orm.relationship(
-        "Object", uselist=False, backref="named_instance", foreign_keys=[instance_id]
+        "Object",
+        uselist=False,
+        backref="named_instance",
+        foreign_keys=[instance_id],
     )
 
     @property
@@ -182,7 +193,10 @@ class Fit(Base):
     )
     is_complete = sa.Column(sa.Boolean)
 
-    _named_instances: Mapped[List[NamedInstance]] = sa.orm.relationship("NamedInstance")
+    _named_instances: Mapped[List[NamedInstance]] = sa.orm.relationship(
+        "NamedInstance",
+        back_populates="fit",
+    )
 
     @property
     @try_none
@@ -204,7 +218,7 @@ class Fit(Base):
     def total_parameters(self):
         return self.model.prior_count if self.model else 0
 
-    _info: Mapped[List[Info]] = sa.orm.relationship("Info")
+    _info: Mapped[List[Info]] = sa.orm.relationship("Info", back_populates="fit")
 
     def __init__(self, **kwargs):
         try:
@@ -302,17 +316,22 @@ class Fit(Base):
     def model(self, model: AbstractPriorModel):
         self.__model = Object.from_object(model)
 
-    pickles: Mapped[List[Pickle]] = sa.orm.relationship("Pickle", lazy="joined")
+    pickles: Mapped[List[Pickle]] = sa.orm.relationship(
+        "Pickle",
+        lazy="joined",
+    )
     jsons: Mapped[List[JSON]] = sa.orm.relationship("JSON", lazy="joined")
     arrays: Mapped[List[Array]] = sa.orm.relationship(
         "Array",
         lazy="joined",
         foreign_keys=[Array.fit_id],
+        viewonly=True,
     )
     hdus: Mapped[List[HDU]] = sa.orm.relationship(
         "HDU",
         lazy="joined",
         foreign_keys=[HDU.fit_id],
+        viewonly=True,
     )
 
     def __getitem__(self, item: str):
diff --git a/autofit/database/model/model.py b/autofit/database/model/model.py
index 7fd984111..46c1a3cea 100644
--- a/autofit/database/model/model.py
+++ b/autofit/database/model/model.py
@@ -34,7 +34,10 @@ class Object(Base):
 
     samples_for_id = sa.Column(sa.String, sa.ForeignKey("fit.id"))
     samples_for = sa.orm.relationship(
-        "Fit", uselist=False, foreign_keys=[samples_for_id]
+        "Fit",
+        uselist=False,
+        foreign_keys=[samples_for_id],
+        back_populates="_samples",
     )
 
     latent_samples_for_id = sa.Column(sa.String, sa.ForeignKey("fit.id"))
@@ -42,11 +45,13 @@ class Object(Base):
         "Fit",
         uselist=False,
         foreign_keys=[latent_samples_for_id],
+        back_populates="_latent_samples",
     )
 
     children: Mapped[List["Object"]] = sa.orm.relationship(
         "Object",
         uselist=True,
+        back_populates="parent",
     )
 
     def __len__(self):
diff --git a/autofit/non_linear/analysis/analysis.py b/autofit/non_linear/analysis/analysis.py
index 9981437e2..e3fa92eca 100644
--- a/autofit/non_linear/analysis/analysis.py
+++ b/autofit/non_linear/analysis/analysis.py
@@ -1,3 +1,4 @@
+import inspect
 import logging
 from abc import ABC
 from typing import Optional, Dict
@@ -40,6 +41,10 @@ class Analysis(ABC):
             raise AttributeError(f"Analysis has no attribute {item}")
 
         def method(*args, **kwargs):
+            parameters = inspect.signature(_method).parameters
+            if "analyses" in parameters:
+                logger.debug(f"Skipping {item} as this is not a combined analysis")
+                return
             return _method(self, *args, **kwargs)
 
         return method


Test patch:
diff --git a/test_autofit/database/test_regression.py b/test_autofit/database/test_regression.py
index 19d90ac31..c05ef92ba 100644
--- a/test_autofit/database/test_regression.py
+++ b/test_autofit/database/test_regression.py
@@ -48,3 +48,8 @@ def test_samples_summary_model():
     fit.model = model
 
     assert fit["samples_summary"].model.cls == af.Gaussian
+
+
+def test_dict_with_tuple_keys():
+    d = {("a", "b"): 1}
+    assert db.Object.from_object(d)() == d