Skip to content

Commit

Permalink
Add stub RemoteButler
Browse files Browse the repository at this point in the history
Add a no-op RemoteButler that can be instantiated via the Butler() constructor.
  • Loading branch information
dhirving committed Oct 23, 2023
1 parent d9d7cda commit b123831
Show file tree
Hide file tree
Showing 4 changed files with 378 additions and 0 deletions.
28 changes: 28 additions & 0 deletions python/lsst/daf/butler/remote_butler/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This file is part of daf_butler.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (http://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This software is dual licensed under the GNU General Public License and also
# under a 3-clause BSD license. Recipients may choose which of these licenses
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
# respectively. If you choose the GPL option then the following text applies
# (but note that there is still no warranty even if you opt for BSD instead):
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from ._remote_butler import RemoteButler
38 changes: 38 additions & 0 deletions python/lsst/daf/butler/remote_butler/_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# This file is part of daf_butler.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (http://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This software is dual licensed under the GNU General Public License and also
# under a 3-clause BSD license. Recipients may choose which of these licenses
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
# respectively. If you choose the GPL option then the following text applies
# (but note that there is still no warranty even if you opt for BSD instead):
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from pydantic import AnyHttpUrl

from .._compat import _BaseModelCompat


class RemoteButlerOptionsModel(_BaseModelCompat):
url: AnyHttpUrl


class RemoteButlerConfigModel(_BaseModelCompat):
remote_butler: RemoteButlerOptionsModel
263 changes: 263 additions & 0 deletions python/lsst/daf/butler/remote_butler/_remote_butler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
# This file is part of daf_butler.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (http://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This software is dual licensed under the GNU General Public License and also
# under a 3-clause BSD license. Recipients may choose which of these licenses
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
# respectively. If you choose the GPL option then the following text applies
# (but note that there is still no warranty even if you opt for BSD instead):
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from collections.abc import Collection, Iterable, Sequence
from contextlib import AbstractContextManager
from typing import Any, TextIO

from lsst.resources import ResourcePath, ResourcePathExpression

from .._butler import Butler
from .._butler_config import ButlerConfig
from .._config import Config
from .._dataset_existence import DatasetExistence
from .._dataset_ref import DatasetIdGenEnum, DatasetRef
from .._dataset_type import DatasetType
from .._deferredDatasetHandle import DeferredDatasetHandle
from .._file_dataset import FileDataset
from .._limited_butler import LimitedButler
from .._storage_class import StorageClass
from ..datastore import DatasetRefURIs
from ..dimensions import DataId, DimensionUniverse
from ..registry import Registry
from ..transfers import RepoExportContext
from ._config import RemoteButlerConfigModel


class RemoteButler(Butler):
def __init__(
self,
config: Config | ResourcePathExpression | None = None,
searchPaths: Sequence[ResourcePathExpression] | None = None,
**kwargs: Any,
):
butler_config = ButlerConfig(config, searchPaths, without_datastore=True)
self._config = RemoteButlerConfigModel.model_validate(butler_config)

def isWriteable(self) -> bool:
# Docstring inherited.
return False

@property
def dimensions(self) -> DimensionUniverse:
# Docstring inherited.
raise NotImplementedError()

def transaction(self) -> AbstractContextManager[None]:
"""Will always raise NotImplementedError.
Transactions are not supported by RemoteButler.
"""
raise NotImplementedError()

def put(
self,
obj: Any,
datasetRefOrType: DatasetRef | DatasetType | str,
/,
dataId: DataId | None = None,
*,
run: str | None = None,
**kwargs: Any,
) -> DatasetRef:
# Docstring inherited.
raise NotImplementedError()

def getDeferred(
self,
datasetRefOrType: DatasetRef | DatasetType | str,
/,
dataId: DataId | None = None,
*,
parameters: dict | None = None,
collections: Any = None,
storageClass: str | StorageClass | None = None,
**kwargs: Any,
) -> DeferredDatasetHandle:
# Docstring inherited.
raise NotImplementedError()

def get(
self,
datasetRefOrType: DatasetRef | DatasetType | str,
/,
dataId: DataId | None = None,
*,
parameters: dict[str, Any] | None = None,
collections: Any = None,
storageClass: StorageClass | str | None = None,
**kwargs: Any,
) -> Any:
# Docstring inherited.
raise NotImplementedError()

def getURIs(
self,
datasetRefOrType: DatasetRef | DatasetType | str,
/,
dataId: DataId | None = None,
*,
predict: bool = False,
collections: Any = None,
run: str | None = None,
**kwargs: Any,
) -> DatasetRefURIs:
# Docstring inherited.
raise NotImplementedError()

def getURI(
self,
datasetRefOrType: DatasetRef | DatasetType | str,
/,
dataId: DataId | None = None,
*,
predict: bool = False,
collections: Any = None,
run: str | None = None,
**kwargs: Any,
) -> ResourcePath:
# Docstring inherited.
raise NotImplementedError()

def retrieveArtifacts(
self,
refs: Iterable[DatasetRef],
destination: ResourcePathExpression,
transfer: str = "auto",
preserve_path: bool = True,
overwrite: bool = False,
) -> list[ResourcePath]:
# Docstring inherited.
raise NotImplementedError()

def exists(
self,
dataset_ref_or_type: DatasetRef | DatasetType | str,
/,
data_id: DataId | None = None,
*,
full_check: bool = True,
collections: Any = None,
**kwargs: Any,
) -> DatasetExistence:
# Docstring inherited.
raise NotImplementedError()

def _exists_many(
self,
refs: Iterable[DatasetRef],
/,
*,
full_check: bool = True,
) -> dict[DatasetRef, DatasetExistence]:
# Docstring inherited.
raise NotImplementedError()

def removeRuns(self, names: Iterable[str], unstore: bool = True) -> None:
# Docstring inherited.
raise NotImplementedError()

def ingest(
self,
*datasets: FileDataset,
transfer: str | None = "auto",
run: str | None = None,
idGenerationMode: DatasetIdGenEnum | None = None,
record_validation_info: bool = True,
) -> None:
# Docstring inherited.
raise NotImplementedError()

def export(
self,
*,
directory: str | None = None,
filename: str | None = None,
format: str | None = None,
transfer: str | None = None,
) -> AbstractContextManager[RepoExportContext]:
# Docstring inherited.
raise NotImplementedError()

def import_(
self,
*,
directory: ResourcePathExpression | None = None,
filename: ResourcePathExpression | TextIO | None = None,
format: str | None = None,
transfer: str | None = None,
skip_dimensions: set | None = None,
) -> None:
# Docstring inherited.
raise NotImplementedError()

def transfer_from(
self,
source_butler: LimitedButler,
source_refs: Iterable[DatasetRef],
transfer: str = "auto",
skip_missing: bool = True,
register_dataset_types: bool = False,
transfer_dimensions: bool = False,
) -> Collection[DatasetRef]:
# Docstring inherited.
raise NotImplementedError()

def validateConfiguration(
self,
logFailures: bool = False,
datasetTypeNames: Iterable[str] | None = None,
ignore: Iterable[str] | None = None,
) -> None:
# Docstring inherited.
raise NotImplementedError()

@property
def collections(self) -> Sequence[str]:
# Docstring inherited.
raise NotImplementedError()

@property
def run(self) -> str | None:
# Docstring inherited.
raise NotImplementedError()

@property
def registry(self) -> Registry:
# Docstring inherited.
raise NotImplementedError()

def pruneDatasets(
self,
refs: Iterable[DatasetRef],
*,
disassociate: bool = True,
unstore: bool = False,
tags: Iterable[str] = (),
purge: bool = False,
) -> None:
# Docstring inherited.
raise NotImplementedError()
49 changes: 49 additions & 0 deletions tests/test_remote_butler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# This file is part of daf_butler.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (http://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This software is dual licensed under the GNU General Public License and also
# under a 3-clause BSD license. Recipients may choose which of these licenses
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
# respectively. If you choose the GPL option then the following text applies
# (but note that there is still no warranty even if you opt for BSD instead):
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import unittest

from lsst.daf.butler import Butler
from lsst.daf.butler.remote_butler import RemoteButler
from pydantic import ValidationError


class RemoteButlerConfigTests(unittest.TestCase):
"""Test construction of RemoteButler via Butler()"""

def test_instantiate_via_butler(self):
butler = Butler(
{
"cls": "lsst.daf.butler.remote_butler.RemoteButler",
"remote_butler": {"url": "https://validurl.example"},
}
)
assert isinstance(butler, RemoteButler)

def test_bad_config(self):
with self.assertRaises(ValidationError):
Butler({"cls": "lsst.daf.butler.remote_butler.RemoteButler", "remote_butler": {"url": "!"}})

0 comments on commit b123831

Please sign in to comment.