From f2e7d3a73164f19582b26ebc126a9844fb40c3d1 Mon Sep 17 00:00:00 2001 From: Gabe Fierro Date: Tue, 14 Jan 2025 23:48:18 -0700 Subject: [PATCH 1/7] add errors! --- buildingmotif/database/errors.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/buildingmotif/database/errors.py b/buildingmotif/database/errors.py index 301cc091..524ee056 100644 --- a/buildingmotif/database/errors.py +++ b/buildingmotif/database/errors.py @@ -47,3 +47,16 @@ def __str__(self): if self.template_name: return f"Name: {self.template_name}" return f"ID: {self.template_id}" + + +class ShapeCollectionNotFound(Exception): + def __init__(self, name: Optional[str] = None, idnum: Optional[int] = None): + self.shape_collection_name = name + self.shape_collection_id = idnum + + def __str__(self): + if self.shape_collection_name: + return ( + f"Name: {self.shape_collection_name}" + ) + return f"ID: {self.shape_collection_id}" From a6c50cf740b65a5f2eb1cff5e4627ee4b238c9e4 Mon Sep 17 00:00:00 2001 From: Gabe Fierro Date: Wed, 15 Jan 2025 09:23:52 -0700 Subject: [PATCH 2/7] made these changes when building the demo; this removes dangling templates/etc when deleting libraries --- buildingmotif/database/tables.py | 43 +++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/buildingmotif/database/tables.py b/buildingmotif/database/tables.py index b2fdd6b3..8eb246fe 100644 --- a/buildingmotif/database/tables.py +++ b/buildingmotif/database/tables.py @@ -1,6 +1,15 @@ from typing import Dict, List -from sqlalchemy import Column, ForeignKey, Integer, String, Text, UniqueConstraint +from sqlalchemy import ( + Column, + ForeignKey, + Integer, + String, + Text, + UniqueConstraint, + event, +) +from sqlalchemy.engine import Engine from sqlalchemy.orm import Mapped, declarative_base, relationship # from sqlalchemy.dialects.postgresql import JSON @@ -9,6 +18,14 @@ Base = declarative_base() +# https://docs.sqlalchemy.org/en/14/dialects/sqlite.html#foreign-key-support +@event.listens_for(Engine, "connect") +def set_sqlite_pragma(dbapi_connection, connection_record): + cursor = dbapi_connection.cursor() + cursor.execute("PRAGMA foreign_keys=ON") + cursor.close() + + class DBModel(Base): """A Model is a metadata model of all or part of a building.""" @@ -18,12 +35,13 @@ class DBModel(Base): description: Mapped[str] = Column(Text(), default="", nullable=False) graph_id: Mapped[str] = Column(String()) manifest_id: Mapped[int] = Column( - Integer, ForeignKey("shape_collection.id"), nullable=False + Integer, ForeignKey("shape_collection.id", ondelete="CASCADE"), nullable=False ) manifest: "DBShapeCollection" = relationship( "DBShapeCollection", uselist=False, - cascade="all,delete", + cascade="all", + passive_deletes=True, ) @@ -45,16 +63,17 @@ class DBLibrary(Base): name: Mapped[str] = Column(String(), nullable=False, unique=True) templates: Mapped[List["DBTemplate"]] = relationship( - "DBTemplate", back_populates="library", cascade="all,delete" + "DBTemplate", back_populates="library", cascade="all", passive_deletes=True ) shape_collection_id = Column( - Integer, ForeignKey("shape_collection.id"), nullable=False + Integer, ForeignKey("shape_collection.id", ondelete="CASCADE"), nullable=False ) shape_collection: DBShapeCollection = relationship( "DBShapeCollection", uselist=False, - cascade="all,delete", + cascade="all", + passive_deletes=True, ) @@ -64,8 +83,8 @@ class DepsAssociation(Base): __tablename__ = "deps_association_table" id: Mapped[int] = Column(Integer, primary_key=True) - dependant_id: Mapped[int] = Column(ForeignKey("template.id")) - dependee_id: Mapped[int] = Column(ForeignKey("template.id")) + dependant_id: Mapped[int] = Column(ForeignKey("template.id", ondelete="CASCADE")) + dependee_id: Mapped[int] = Column(ForeignKey("template.id", ondelete="CASCADE")) # args are a mapping of dependee args to dependant args args: Mapped[Dict[str, str]] = Column(JSONType) # type: ignore @@ -89,7 +108,9 @@ class DBTemplate(Base): body_id: Mapped[str] = Column(String()) optional_args: Mapped[List[str]] = Column(JSONType) # type: ignore - library_id: Mapped[int] = Column(Integer, ForeignKey("library.id"), nullable=False) + library_id: Mapped[int] = Column( + Integer, ForeignKey("library.id", ondelete="CASCADE"), nullable=False + ) library: Mapped[DBLibrary] = relationship("DBLibrary", back_populates="templates") dependencies: Mapped[List["DBTemplate"]] = relationship( "DBTemplate", @@ -97,6 +118,8 @@ class DBTemplate(Base): primaryjoin=id == DepsAssociation.dependant_id, secondaryjoin=id == DepsAssociation.dependee_id, back_populates="dependants", + cascade="all", + passive_deletes=True, ) dependants: Mapped[List["DBTemplate"]] = relationship( "DBTemplate", @@ -104,6 +127,8 @@ class DBTemplate(Base): primaryjoin=id == DepsAssociation.dependee_id, secondaryjoin=id == DepsAssociation.dependant_id, back_populates="dependencies", + cascade="all", + passive_deletes=True, ) __table_args__ = ( From 7d02d7688c3dd8a4d6c7953902a5740f0c6e4b37 Mon Sep 17 00:00:00 2001 From: TShapinsky Date: Mon, 17 Feb 2025 14:44:48 -0700 Subject: [PATCH 3/7] Fix styling issues --- buildingmotif/database/errors.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/buildingmotif/database/errors.py b/buildingmotif/database/errors.py index 524ee056..301cc091 100644 --- a/buildingmotif/database/errors.py +++ b/buildingmotif/database/errors.py @@ -47,16 +47,3 @@ def __str__(self): if self.template_name: return f"Name: {self.template_name}" return f"ID: {self.template_id}" - - -class ShapeCollectionNotFound(Exception): - def __init__(self, name: Optional[str] = None, idnum: Optional[int] = None): - self.shape_collection_name = name - self.shape_collection_id = idnum - - def __str__(self): - if self.shape_collection_name: - return ( - f"Name: {self.shape_collection_name}" - ) - return f"ID: {self.shape_collection_id}" From 84aa27593432eeaed53386e7a5952bdc0452162f Mon Sep 17 00:00:00 2001 From: "Gabe Fierro (aider)" Date: Mon, 17 Feb 2025 17:49:44 -0700 Subject: [PATCH 4/7] Add tests for cascading delete behavior on models and libraries --- buildingmotif/dataclasses/library.py | 58 ++++++--------- .../table_connection/test_db_library.py | 20 ++--- .../table_connection/test_db_model.py | 14 ++-- .../table_connection/test_db_shape.py | 12 +-- .../test_cascading_deletes_cascades.py | 74 +++++++++++++++++++ 5 files changed, 119 insertions(+), 59 deletions(-) create mode 100644 tests/unit/database/test_cascading_deletes_cascades.py diff --git a/buildingmotif/dataclasses/library.py b/buildingmotif/dataclasses/library.py index 6a0ff4ac..7fe42011 100644 --- a/buildingmotif/dataclasses/library.py +++ b/buildingmotif/dataclasses/library.py @@ -422,45 +422,31 @@ def _resolve_dependency( :return: the template instance this dependency points to :rtype: Template """ - # if dep is a _template_dependency, turn it into a template + dependee = None + binding_args = {} if isinstance(dep, _template_dependency): dependee = dep.to_template(template_id_lookup) - template.add_dependency(dependee, dep.bindings) - return - - # now, we know that dep is a dict - - # if dependency names a library explicitly, load that library and get the template by name - if "library" in dep: - dependee = Library.load(name=dep["library"]).get_template_by_name( - dep["template"] - ) - template.add_dependency(dependee, dep["args"]) - return - # if no library is provided, try to resolve the dependency from this library - if dep["template"] in template_id_lookup: - dependee = Template.load(template_id_lookup[dep["template"]]) - template.add_dependency(dependee, dep["args"]) - return - # check documentation for skip_uri for what URIs get skipped - if skip_uri(dep["template"]): - return - - # if the dependency is not in the local cache, then search through this library's imports - # for the template - for imp in self.graph_imports: - try: - library = Library.load(name=str(imp)) - dependee = library.get_template_by_name(dep["template"]) - template.add_dependency(dependee, dep["args"]) + binding_args = dep.bindings + elif isinstance(dep, dict): + binding_args = dep.get("args", {}) + if "library" in dep: + dependee = Library.load(name=dep["library"]).get_template_by_name(dep["template"]) + elif dep["template"] in template_id_lookup: + dependee = Template.load(template_id_lookup[dep["template"]]) + elif skip_uri(dep["template"]): return - except Exception as e: - logging.debug( - f"Could not find dependee {dep['template']} in library {imp}: {e}" - ) - logging.warning( - f"Warning: could not find dependee {dep['template']} in libraries {self.graph_imports}" - ) + else: + for imp in self.graph_imports: + try: + library = Library.load(name=str(imp)) + dependee = library.get_template_by_name(dep["template"]) + break + except Exception as e: + logging.debug(f"Could not find dependee {dep['template']} in library {imp}: {e}") + if dependee is not None: + template.add_dependency(dependee, binding_args) + else: + logging.warning(f"Warning: could not find dependee {dep['template']} in libraries {self.graph_imports}") def _resolve_template_dependencies( self, diff --git a/tests/unit/database/table_connection/test_db_library.py b/tests/unit/database/table_connection/test_db_library.py index 9eded8e2..9b992298 100644 --- a/tests/unit/database/table_connection/test_db_library.py +++ b/tests/unit/database/table_connection/test_db_library.py @@ -10,7 +10,7 @@ from buildingmotif.database.tables import DBLibrary, DBShapeCollection, DBTemplate -def test_create_db_library(table_connection, monkeypatch): +def test_create_db_library(bm, monkeypatch): mocked_uuid = uuid.uuid4() def mockreturn(): @@ -18,7 +18,7 @@ def mockreturn(): monkeypatch.setattr(uuid, "uuid4", mockreturn) - db_library = table_connection.create_db_library(name="my_db_library") + db_library = bm.table_connection.create_db_library(name="my_db_library") assert db_library.name == "my_db_library" assert db_library.templates == [] @@ -26,11 +26,11 @@ def mockreturn(): assert db_library.shape_collection.graph_id == str(mocked_uuid) -def test_get_db_libraries(table_connection): - table_connection.create_db_library(name="my_db_library") - table_connection.create_db_library(name="your_db_library") +def test_get_db_libraries(bm): + bm.table_connection.create_db_library(name="my_db_library") + bm.table_connection.create_db_library(name="your_db_library") - db_libraries = table_connection.get_all_db_libraries() + db_libraries = bm.table_connection.get_all_db_libraries() assert len(db_libraries) == 2 assert all(type(tl) == DBLibrary for tl in db_libraries) @@ -40,11 +40,11 @@ def test_get_db_libraries(table_connection): } -def test_get_db_library(table_connection): - db_library = table_connection.create_db_library(name="my_library") - table_connection.create_db_template("my_db_template", library_id=db_library.id) +def test_get_db_library(bm): + db_library = bm.table_connection.create_db_library(name="my_library") + bm.table_connection.create_db_template("my_db_template", library_id=db_library.id) - db_library = table_connection.get_db_library(id=db_library.id) + db_library = bm.table_connection.get_db_library(id=db_library.id) assert db_library.name == "my_library" assert len(db_library.templates) == 1 assert type(db_library.templates[0]) == DBTemplate diff --git a/tests/unit/database/table_connection/test_db_model.py b/tests/unit/database/table_connection/test_db_model.py index 7d305c80..974355d5 100644 --- a/tests/unit/database/table_connection/test_db_model.py +++ b/tests/unit/database/table_connection/test_db_model.py @@ -24,15 +24,15 @@ def test_create_db_model(mock_uuid4, table_connection): assert db_model.manifest.graph_id == str(mocked_manifest_uuid) -def test_get_db_models(table_connection): - table_connection.create_db_model( +def test_get_db_models(bm): + bm.table_connection.create_db_model( name="my_db_model", description="a very good model" ) - table_connection.create_db_model( + bm.table_connection.create_db_model( name="your_db_model", description="an ok good model" ) - db_models = table_connection.get_all_db_models() + db_models = bm.table_connection.get_all_db_models() assert len(db_models) == 2 assert all(type(m) == DBModel for m in db_models) @@ -43,13 +43,13 @@ def test_get_db_models(table_connection): @mock.patch("uuid.uuid4") -def test_get_db_model(mock_uuid4, table_connection): +def test_get_db_model(mock_uuid4, bm): mocked_graph_uuid = uuid.uuid4() mocked_manifest_uuid = uuid.uuid4() mock_uuid4.side_effect = [mocked_graph_uuid, mocked_manifest_uuid] - db_model = table_connection.create_db_model(name="my_db_model") - db_model = table_connection.get_db_model(id=db_model.id) + db_model = bm.table_connection.create_db_model(name="my_db_model") + db_model = bm.table_connection.get_db_model(id=db_model.id) assert db_model.name == "my_db_model" assert db_model.graph_id == str(mocked_graph_uuid) diff --git a/tests/unit/database/table_connection/test_db_shape.py b/tests/unit/database/table_connection/test_db_shape.py index e813df50..ad2435fe 100644 --- a/tests/unit/database/table_connection/test_db_shape.py +++ b/tests/unit/database/table_connection/test_db_shape.py @@ -6,7 +6,7 @@ from buildingmotif.database.tables import DBShapeCollection -def test_create_db_shape_collection(monkeypatch, table_connection): +def test_create_db_shape_collection(monkeypatch, bm): mocked_uuid = uuid.uuid4() def mockreturn(): @@ -14,16 +14,16 @@ def mockreturn(): monkeypatch.setattr(uuid, "uuid4", mockreturn) - db_shape_collection = table_connection.create_db_shape_collection() + db_shape_collection = bm.table_connection.create_db_shape_collection() assert db_shape_collection.graph_id == str(mocked_uuid) -def test_get_db_shape_collections(table_connection): - shape_collection1 = table_connection.create_db_shape_collection() - shape_collection2 = table_connection.create_db_shape_collection() +def test_get_db_shape_collections(bm): + shape_collection1 = bm.table_connection.create_db_shape_collection() + shape_collection2 = bm.table_connection.create_db_shape_collection() - db_shape_collections = table_connection.get_all_db_shape_collections() + db_shape_collections = bm.table_connection.get_all_db_shape_collections() assert len(db_shape_collections) == 2 assert all(type(m) == DBShapeCollection for m in db_shape_collections) diff --git a/tests/unit/database/test_cascading_deletes_cascades.py b/tests/unit/database/test_cascading_deletes_cascades.py new file mode 100644 index 00000000..f8b8be05 --- /dev/null +++ b/tests/unit/database/test_cascading_deletes_cascades.py @@ -0,0 +1,74 @@ +import pytest +from buildingmotif.dataclasses.template import Template +from buildingmotif.database.errors import ( + LibraryNotFound, + TemplateNotFound, + ModelNotFound, + ShapeCollectionNotFound, +) + +def test_cascade_delete_model_shape_collection(bm): + # Create a model; its manifest (shape collection) should be cascading deleted. + db_model = bm.table_connection.create_db_model( + name="cascade_model", description="test cascading delete on model" + ) + shape_collection_id = db_model.manifest.id + # assert we can get the shape collection + assert bm.table_connection.get_db_shape_collection(shape_collection_id) + # now delete the model, and assert the shape collection is gone + bm.table_connection.delete_db_model(db_model.id) + with pytest.raises(ShapeCollectionNotFound): + bm.table_connection.get_db_shape_collection(shape_collection_id) + + +def test_cascade_delete_library_cascades(bm): + # Create a library, two templates within it, and a dependency relationship. + db_library = bm.table_connection.create_db_library(name="cascade_library") + shape_collection_id = db_library.shape_collection.id + template1 = bm.table_connection.create_db_template(name="template1", library_id=db_library.id) + template2 = bm.table_connection.create_db_template(name="template2", library_id=db_library.id) + + # Add a dependency relationship between template1 and template2. + template1 = Template.load(template1.id) + template2 = Template.load(template2.id) + template1.add_dependency(template2, {"name": "dependency"}) + # Verify the dependency exists. + deps = bm.table_connection.get_db_template_dependencies(template1.id) + assert len(deps) == 1 + + # Deleting the library should cascade-delete the library, its templates, and its associated shape collection. + bm.table_connection.delete_db_library(db_library.id) + + with pytest.raises(LibraryNotFound): + bm.table_connection.get_db_library(db_library.id) + with pytest.raises(TemplateNotFound): + bm.table_connection.get_db_template(template1.id) + with pytest.raises(TemplateNotFound): + bm.table_connection.get_db_template(template2.id) + with pytest.raises(ShapeCollectionNotFound): + bm.table_connection.get_db_shape_collection(shape_collection_id) + +def test_cascade_delete_multi_library(bm): + # Create two libraries + library1 = bm.table_connection.create_db_library(name="cascade_library1") + library2 = bm.table_connection.create_db_library(name="cascade_library2") + # Create template1 in library1 and template2 in library2 + template1 = bm.table_connection.create_db_template(name="template1", library_id=library1.id) + template2 = bm.table_connection.create_db_template(name="template2", library_id=library2.id) + # Load templates to add dependency and verify dependency relationship. + template1 = Template.load(template1.id) + template2 = Template.load(template2.id) + # Add dependency: template1 depends on template2 + template1.add_dependency(template2, {"name": "dependency"}) + # Verify dependency exists. + deps = bm.table_connection.get_db_template_dependencies(template1.id) + assert len(deps) == 1 + # Delete library1 and ensure cascading deletion + bm.table_connection.delete_db_library(library1.id) + with pytest.raises(LibraryNotFound): + bm.table_connection.get_db_library(library1.id) + with pytest.raises(TemplateNotFound): + bm.table_connection.get_db_template(template1.id) + # Library2 and its template should still exist + assert bm.table_connection.get_db_library(library2.id) + assert bm.table_connection.get_db_template(template2.id) From e1415a7138137c81759b4b5bc2302e4afa15e35b Mon Sep 17 00:00:00 2001 From: Gabe Fierro Date: Mon, 17 Feb 2025 23:44:19 -0700 Subject: [PATCH 5/7] formatting --- tests/integration/conftest.py | 1 + .../test_cascading_deletes_cascades.py | 24 +++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 51a3fbe6..b658e98d 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,6 +1,7 @@ """ Generates tests automatically """ + import glob from pathlib import Path diff --git a/tests/unit/database/test_cascading_deletes_cascades.py b/tests/unit/database/test_cascading_deletes_cascades.py index f8b8be05..c6b9ce36 100644 --- a/tests/unit/database/test_cascading_deletes_cascades.py +++ b/tests/unit/database/test_cascading_deletes_cascades.py @@ -1,11 +1,12 @@ import pytest -from buildingmotif.dataclasses.template import Template + from buildingmotif.database.errors import ( LibraryNotFound, - TemplateNotFound, - ModelNotFound, ShapeCollectionNotFound, + TemplateNotFound, ) +from buildingmotif.dataclasses.template import Template + def test_cascade_delete_model_shape_collection(bm): # Create a model; its manifest (shape collection) should be cascading deleted. @@ -25,8 +26,12 @@ def test_cascade_delete_library_cascades(bm): # Create a library, two templates within it, and a dependency relationship. db_library = bm.table_connection.create_db_library(name="cascade_library") shape_collection_id = db_library.shape_collection.id - template1 = bm.table_connection.create_db_template(name="template1", library_id=db_library.id) - template2 = bm.table_connection.create_db_template(name="template2", library_id=db_library.id) + template1 = bm.table_connection.create_db_template( + name="template1", library_id=db_library.id + ) + template2 = bm.table_connection.create_db_template( + name="template2", library_id=db_library.id + ) # Add a dependency relationship between template1 and template2. template1 = Template.load(template1.id) @@ -48,13 +53,18 @@ def test_cascade_delete_library_cascades(bm): with pytest.raises(ShapeCollectionNotFound): bm.table_connection.get_db_shape_collection(shape_collection_id) + def test_cascade_delete_multi_library(bm): # Create two libraries library1 = bm.table_connection.create_db_library(name="cascade_library1") library2 = bm.table_connection.create_db_library(name="cascade_library2") # Create template1 in library1 and template2 in library2 - template1 = bm.table_connection.create_db_template(name="template1", library_id=library1.id) - template2 = bm.table_connection.create_db_template(name="template2", library_id=library2.id) + template1 = bm.table_connection.create_db_template( + name="template1", library_id=library1.id + ) + template2 = bm.table_connection.create_db_template( + name="template2", library_id=library2.id + ) # Load templates to add dependency and verify dependency relationship. template1 = Template.load(template1.id) template2 = Template.load(template2.id) From f8882a4b7baeefc32c27ec4f7a5bc49b7661981f Mon Sep 17 00:00:00 2001 From: Gabe Fierro Date: Mon, 17 Feb 2025 23:48:06 -0700 Subject: [PATCH 6/7] adding comments to tests, add another test case --- .../test_cascading_deletes_cascades.py | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/unit/database/test_cascading_deletes_cascades.py b/tests/unit/database/test_cascading_deletes_cascades.py index c6b9ce36..afe103cb 100644 --- a/tests/unit/database/test_cascading_deletes_cascades.py +++ b/tests/unit/database/test_cascading_deletes_cascades.py @@ -54,7 +54,9 @@ def test_cascade_delete_library_cascades(bm): bm.table_connection.get_db_shape_collection(shape_collection_id) -def test_cascade_delete_multi_library(bm): +# library1 has template1, library2 has template2, template1 depends on template2 +# then deleting library1 should delete template1 but leave library2 and template2 +def test_cascade_delete_dependent_multi_library(bm): # Create two libraries library1 = bm.table_connection.create_db_library(name="cascade_library1") library2 = bm.table_connection.create_db_library(name="cascade_library2") @@ -82,3 +84,37 @@ def test_cascade_delete_multi_library(bm): # Library2 and its template should still exist assert bm.table_connection.get_db_library(library2.id) assert bm.table_connection.get_db_template(template2.id) + + +# library1 has template1, library2 has template2, template1 depends on template2 +# deleting library2 should delete template2 but leave library1 and template1 +def test_cascade_delete_dependency_multi_library(bm): + # Create two libraries + library1 = bm.table_connection.create_db_library(name="cascade_library1") + library2 = bm.table_connection.create_db_library(name="cascade_library2") + # Create template1 in library1 and template2 in library2 + template1 = bm.table_connection.create_db_template( + name="template1", library_id=library1.id + ) + template2 = bm.table_connection.create_db_template( + name="template2", library_id=library2.id + ) + # Load templates to add dependency and verify dependency relationship. + template1 = Template.load(template1.id) + template2 = Template.load(template2.id) + # Add dependency: template1 depends on template2 + template1.add_dependency(template2, {"name": "dependency"}) + # Verify dependency exists. + deps = bm.table_connection.get_db_template_dependencies(template1.id) + assert len(deps) == 1 + # Delete library2 and ensure cascading deletion + bm.table_connection.delete_db_library(library2.id) + with pytest.raises(LibraryNotFound): + bm.table_connection.get_db_library(library2.id) + with pytest.raises(TemplateNotFound): + bm.table_connection.get_db_template(template2.id) + # Library1 and its template should be gone + with pytest.raises(LibraryNotFound): + bm.table_connection.get_db_library(library1.id) + with pytest.raises(TemplateNotFound): + bm.table_connection.get_db_template(template1.id) From 2137943277e6c1d4fe074635d125e7d434937a8d Mon Sep 17 00:00:00 2001 From: Gabe Fierro Date: Mon, 17 Feb 2025 23:54:47 -0700 Subject: [PATCH 7/7] formatting --- buildingmotif/dataclasses/library.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/buildingmotif/dataclasses/library.py b/buildingmotif/dataclasses/library.py index 7fe42011..c0bddc6a 100644 --- a/buildingmotif/dataclasses/library.py +++ b/buildingmotif/dataclasses/library.py @@ -430,7 +430,9 @@ def _resolve_dependency( elif isinstance(dep, dict): binding_args = dep.get("args", {}) if "library" in dep: - dependee = Library.load(name=dep["library"]).get_template_by_name(dep["template"]) + dependee = Library.load(name=dep["library"]).get_template_by_name( + dep["template"] + ) elif dep["template"] in template_id_lookup: dependee = Template.load(template_id_lookup[dep["template"]]) elif skip_uri(dep["template"]): @@ -442,11 +444,15 @@ def _resolve_dependency( dependee = library.get_template_by_name(dep["template"]) break except Exception as e: - logging.debug(f"Could not find dependee {dep['template']} in library {imp}: {e}") + logging.debug( + f"Could not find dependee {dep['template']} in library {imp}: {e}" + ) if dependee is not None: template.add_dependency(dependee, binding_args) else: - logging.warning(f"Warning: could not find dependee {dep['template']} in libraries {self.graph_imports}") + logging.warning( + f"Warning: could not find dependee {dep} in libraries {self.graph_imports}" + ) def _resolve_template_dependencies( self,