# Source code for openff.evaluator.storage.localfile

    """
    A local file based storage backend.
    """
    import json
    import shutil
    from os import makedirs, path

    from openff.evaluator.storage import StorageBackend
    from openff.evaluator.storage.data import BaseStoredData
    from openff.evaluator.utils.serialization import TypedJSONEncoder

    class LocalFileStorage(StorageBackend):
        """A storage backend which stores files in directories on the local
        disk.
        """

        @property
        def root_directory(self):
            """str: Returns the directory in which all stored objects are located."""
            return self.root_directory

        def __init__(self, root_directory="stored_data"):

            self._root_directory = root_directory

            if not path.isdir(root_directory) and len(root_directory) > 0:
                makedirs(root_directory)

            super().__init__()

        def _store_object(
            self, object_to_store, storage_key=None, ancillary_data_path=None
        ):

            file_path = path.join(self._root_directory, f"{storage_key}.json")
            directory_path = path.join(self._root_directory, f"{storage_key}")

            with open(file_path, "w") as file:
                json.dump(object_to_store, file, cls=TypedJSONEncoder)

            if object_to_store.has_ancillary_data():

                if path.isdir(directory_path):
                    shutil.rmtree(directory_path, ignore_errors=True)

                shutil.move(ancillary_data_path, directory_path)

        def _retrieve_object(self, storage_key, expected_type=None):

            if not self._object_exists(storage_key):
                return None, None

            file_path = path.join(self._root_directory, f"{storage_key}.json")
            directory_path = None

            loaded_object = BaseStoredData.from_json(file_path)

            # Make sure the data has the correct type.
            if expected_type is not None and not isinstance(loaded_object, expected_type):

                raise ValueError(
                    f"The retrieve object is of type {loaded_object.__class__.__name__} and not "
                    f"{expected_type.__name__} as expected."
                )

            assert isinstance(loaded_object, BaseStoredData)

            # Check whether there is a data directory
            if loaded_object.has_ancillary_data():
                directory_path = path.join(self._root_directory, f"{storage_key}")

            return loaded_object, directory_path

        def _object_exists(self, storage_key):

            file_path = path.join(self._root_directory, f"{storage_key}.json")

            if not path.isfile(file_path):
                return False

            return True