11# This code is part of OpenFE and is licensed under the MIT license.
22# For details, see https://github.com/OpenFreeEnergy/gufe
33import json
4+ import pathlib
45import re
5- from typing import Literal , TypedDict
6+ from typing import Generator , Literal , TypedDict
67
78from gufe .protocols .protocoldag import ProtocolDAG
89from gufe .protocols .protocolunit import ProtocolUnit
@@ -28,6 +29,12 @@ class WarehouseStores(TypedDict):
2829 Storage location for setup-related objects and configurations.
2930 result : ExternalStorage
3031 Storage location for result-related object.
32+ shared : ExternalStorage
33+ Storage location for non-permanent shared data.
34+ tasks: ExternalStorage
35+ Storage location for execution tasks.
36+ protocol_dags: ExternalStorage
37+ Storage location for ProtocolDAGs that correspond to the ProtocolUnits stored in 'tasks'.
3138
3239 Notes
3340 -----
@@ -38,6 +45,7 @@ class WarehouseStores(TypedDict):
3845 result : ExternalStorage
3946 shared : ExternalStorage
4047 tasks : ExternalStorage
48+ protocol_dags : ExternalStorage
4149
4250
4351class WarehouseBaseClass :
@@ -58,8 +66,11 @@ class WarehouseBaseClass:
5866 The storage locations managed by this warehouse instance.
5967 """
6068
61- def __init__ (self , stores : WarehouseStores ):
69+ def __init__ (self , stores : WarehouseStores , name : str ):
6270 self .stores = stores
71+ if not isinstance (name , str ) or len (name ) == 0 :
72+ raise ValueError ("Warehouse name must be a string." )
73+ self .name = name
6374
6475 def __eq__ (self , other ):
6576 return isinstance (other , self .__class__ ) and self .stores == other .stores
@@ -106,6 +117,7 @@ def store_setup_tokenizable(self, obj: GufeTokenizable):
106117 self ._store_gufe_tokenizable ("setup" , obj )
107118
108119 def load_setup_tokenizable (self , obj : GufeKey ) -> GufeTokenizable :
120+ # TODO: this doesn't actually look specifically in the setup store, which is misleading
109121 """Load a GufeTokenizable object from the setup store.
110122
111123 Parameters
@@ -131,6 +143,7 @@ def store_result_tokenizable(self, obj: GufeTokenizable):
131143 return self ._store_gufe_tokenizable ("result" , obj )
132144
133145 def load_result_tokenizable (self , obj : GufeKey ) -> GufeTokenizable :
146+ # TODO: this doesn't actually look specifically in the result store, which is misleading
134147 """Load a GufeTokenizable object from the result store.
135148
136149 Parameters
@@ -145,6 +158,38 @@ def load_result_tokenizable(self, obj: GufeKey) -> GufeTokenizable:
145158 """
146159 return self ._load_gufe_tokenizable (gufe_key = obj )
147160
161+ def store_protocol_dag (self , dag : ProtocolDAG ):
162+ """Store a ProtocolDAG in the "protocol_dags" store of this warehouse.
163+ Parameters
164+ ----------
165+ dag : ProtocolDAG
166+ The ProtocolDAG object to store.
167+
168+ Raises
169+ ------
170+ ValueError
171+ If `dag` is not a ProtocolDAG instance.
172+ """
173+ if not isinstance (dag , ProtocolDAG ):
174+ raise ValueError ("Only ProtocolDAGs may be written to the 'protocol_dags' store." )
175+ self ._store_gufe_tokenizable ("protocol_dags" , dag )
176+
177+ def load_protocol_dag (self , gufe_key = GufeKey ) -> GufeTokenizable :
178+ """Load a GufeTokenizable object from the protocol_dag store.
179+
180+ Parameters
181+ ----------
182+ obj : GufeKey
183+ The key of the protocoldag to load.
184+
185+ Returns
186+ -------
187+ GufeTokenizable
188+ The loaded object.
189+ """
190+ # TODO: type check that it is a protocol dag before returning?
191+ return self ._load_gufe_tokenizable (gufe_key = gufe_key )
192+
148193 def exists (self , key : GufeKey ) -> bool :
149194 """Check if an object with the given key exists in any store that holds tokenizables.
150195
@@ -188,7 +233,7 @@ def _get_store_for_key(self, key: GufeKey) -> ExternalStorage:
188233
189234 def _store_gufe_tokenizable (
190235 self ,
191- store_name : Literal ["setup" , "result" , "tasks" ],
236+ store_name : Literal ["setup" , "result" , "tasks" , "protocol_dags" ],
192237 obj : GufeTokenizable ,
193238 name : str | None = None ,
194239 ):
@@ -294,6 +339,23 @@ def recursive_build_object_cache(key: GufeKey) -> GufeTokenizable:
294339
295340 return recursive_build_object_cache (gufe_key )
296341
342+ def get_protocol_dags (self ) -> Generator [ProtocolDAG , None , None ]:
343+ """Yield the protocol dags present in the Warehouse's 'protocol_dags' store.
344+
345+ Note that this requires the name of the item to start with 'ProtocolDAG'.
346+
347+ Yields
348+ ------
349+ Generator[ProtocolDAG]
350+ The ProtocolDAGs found in this Warehouse's 'protocol_dags' store.
351+ """
352+ # NOTE: this can be made more robust (but slower) by using isinstance(obj, openfe.ProtocolDAG)
353+ # _after_ loading each item, rather than filtering by name
354+ for item in self .stores ["protocol_dags" ]:
355+ if item .startswith ("ProtocolDAG" ):
356+ dag = self .load_protocol_dag (item )
357+ yield dag
358+
297359 @property
298360 def setup_store (self ):
299361 """Get the setup store
@@ -346,13 +408,20 @@ class FileSystemWarehouse(WarehouseBaseClass):
346408 for results and other data types.
347409 """
348410
349- def __init__ (self , root_dir : str = "warehouse" ):
350- self .root_dir = root_dir
351- setup_store = FileStorage (f"{ root_dir } /setup" )
352- result_store = FileStorage (f"{ root_dir } /result" )
353- shared_store = FileStorage (f"{ root_dir } /shared" )
354- tasks_store = FileStorage (f"{ root_dir } /tasks" )
411+ def __init__ (self , name ):
412+ # TODO: should name and location be different?
413+ self .root_dir = pathlib .Path (f"{ name } " )
414+ setup_store = FileStorage (f"{ self .root_dir } /setup" )
415+ result_store = FileStorage (f"{ self .root_dir } /result" )
416+ shared_store = FileStorage (f"{ self .root_dir } /shared" )
417+ tasks_store = FileStorage (f"{ self .root_dir } /tasks" )
418+ # TODO: we can store dags in setup if we have a performant way of accessing them
419+ protocol_dag_store = FileStorage (f"{ self .root_dir } /protocol_dags" )
355420 stores = WarehouseStores (
356- setup = setup_store , result = result_store , shared = shared_store , tasks = tasks_store
421+ setup = setup_store ,
422+ result = result_store ,
423+ shared = shared_store ,
424+ tasks = tasks_store ,
425+ protocol_dags = protocol_dag_store ,
357426 )
358- super ().__init__ (stores )
427+ super ().__init__ (stores , name )
0 commit comments