Skip to content

Commit

Permalink
Merge pull request #426 from lsst/tickets/DM-27435
Browse files Browse the repository at this point in the history
DM-27435: Unification of file-like datastores
  • Loading branch information
timj committed Nov 11, 2020
2 parents e1fa02c + 19300f8 commit 520f072
Show file tree
Hide file tree
Showing 34 changed files with 460 additions and 519 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ jobs:
- name: Install cryptography package for moto
run: pip install cryptography

- name: Install WebDAV packages for testing
run: pip install cheroot wsgidav

- name: Install dependencies
run: pip install -r requirements.txt

Expand Down
2 changes: 1 addition & 1 deletion python/lsst/daf/butler/_butlerConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def __init__(self, other=None, searchPaths=None):
if isinstance(other, str):
# This will only allow supported schemes
uri = ButlerURI(other)
if uri.scheme == "file" or not uri.scheme:
if uri.isLocal:
# Check explicitly that we have a directory
if os.path.isdir(uri.ospath):
other = uri.join("butler.yaml")
Expand Down
4 changes: 2 additions & 2 deletions python/lsst/daf/butler/configs/datastore.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
datastore:
# Use the PosixDatastore as a default for a default butler
cls: lsst.daf.butler.datastores.posixDatastore.PosixDatastore
# Use file datastore as a default for a default butler
cls: lsst.daf.butler.datastores.fileDatastore.FileDatastore
10 changes: 10 additions & 0 deletions python/lsst/daf/butler/configs/datastores/fileDatastore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
datastore:
cls: lsst.daf.butler.datastores.fileDatastore.FileDatastore
root: <butlerRoot>/datastore
records:
table: file_datastore_records
create: true
templates:
default: "{run:/}/{datasetType}.{component:?}/{tract:?}/{patch:?}/{label:?}/{band:?}/{subfilter:?}/{physical_filter:?}/{visit:?}/{datasetType}_{component:?}_{tract:?}_{patch:?}_{label:?}_{band:?}_{physical_filter:?}_{visit:?}_{exposure:?}_{detector:?}_{instrument:?}_{skymap:?}_{skypix:?}_{run}"
formatters: !include formatters.yaml
composites: !include composites.yaml
13 changes: 0 additions & 13 deletions python/lsst/daf/butler/configs/datastores/posixDatastore.yaml

This file was deleted.

13 changes: 0 additions & 13 deletions python/lsst/daf/butler/configs/datastores/s3Datastore.yaml

This file was deleted.

1 change: 1 addition & 0 deletions python/lsst/daf/butler/configs/datastores/s3Datastore.yaml
13 changes: 0 additions & 13 deletions python/lsst/daf/butler/configs/datastores/webdavDatastore.yaml

This file was deleted.

54 changes: 51 additions & 3 deletions python/lsst/daf/butler/core/_butlerUri/_butlerUri.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from __future__ import annotations

import contextlib
import urllib
import posixpath
import copy
Expand All @@ -34,6 +35,7 @@
from typing import (
TYPE_CHECKING,
Any,
Iterator,
Optional,
Tuple,
Type,
Expand Down Expand Up @@ -77,6 +79,8 @@ class ButlerURI:
forceDirectory: `bool`, optional
If `True` forces the URI to end with a separator, otherwise given URI
is interpreted as is.
isTemporary : `bool`, optional
If `True` indicates that this URI points to a temporary resource.
"""

_pathLib: Type[PurePath] = PurePosixPath
Expand Down Expand Up @@ -104,18 +108,22 @@ class ButlerURI:
be made whether to quote it to be consistent.
"""

isLocal = False
"""If `True` this URI refers to a local file."""

# This is not an ABC with abstract methods because the __new__ being
# a factory confuses mypy such that it assumes that every constructor
# returns a ButlerURI and then determines that all the abstract methods
# are still abstract. If they are not marked abstract but just raise
# mypy is fine with it.

# mypy is confused without this
# mypy is confused without these
_uri: urllib.parse.ParseResult
isTemporary: bool

def __new__(cls, uri: Union[str, urllib.parse.ParseResult, ButlerURI],
root: Optional[Union[str, ButlerURI]] = None, forceAbsolute: bool = True,
forceDirectory: bool = False) -> ButlerURI:
forceDirectory: bool = False, isTemporary: bool = False) -> ButlerURI:
parsed: urllib.parse.ParseResult
dirLike: bool
subclass: Optional[Type] = None
Expand Down Expand Up @@ -185,6 +193,7 @@ def __new__(cls, uri: Union[str, urllib.parse.ParseResult, ButlerURI],
self = object.__new__(subclass)
self._uri = parsed
self.dirLike = dirLike
self.isTemporary = isTemporary
return self

@property
Expand Down Expand Up @@ -494,10 +503,12 @@ def isabs(self) -> bool:
"""
return True

def as_local(self) -> Tuple[str, bool]:
def _as_local(self) -> Tuple[str, bool]:
"""Return the location of the (possibly remote) resource in the
local file system.
This is a helper function for ``as_local`` context manager.
Returns
-------
path : `str`
Expand All @@ -510,6 +521,43 @@ def as_local(self) -> Tuple[str, bool]:
"""
raise NotImplementedError()

@contextlib.contextmanager
def as_local(self) -> Iterator[ButlerURI]:
"""Return the location of the (possibly remote) resource in the
local file system.
Yields
------
local : `ButlerURI`
If this is a remote resource, it will be a copy of the resource
on the local file system, probably in a temporary directory.
For a local resource this should be the actual path to the
resource.
Notes
-----
The context manager will automatically delete any local temporary
file.
Examples
--------
Should be used as a context manager:
.. code-block:: py
with uri.as_local() as local:
ospath = local.ospath
"""
local_src, is_temporary = self._as_local()
local_uri = ButlerURI(local_src, isTemporary=is_temporary)

try:
yield local_uri
finally:
# The caller might have relocated the temporary file
if is_temporary and local_uri.exists():
local_uri.remove()

def read(self, size: int = -1) -> bytes:
"""Open the resource and return the contents in bytes.
Expand Down

0 comments on commit 520f072

Please sign in to comment.