objectstore: add Object.read_at method
Implement a new `read_at` method that will bind mount the tree of the
object to a specified location, instead of a temporary directory as
it done in the `read` method. Implement the latter via `read_at`.
Implement the corresponding methods for `Store{Client,Server}`. Since
the `ObjectStore.read_at` method will fail if the target directory
does not exist (or is of the wrong type), catch any exceptions in
the `StoreServer` and send those to the `StoreClient` via an `error`
entry.
This one is for David: also fix a missing blank line.
This commit is contained in:
parent
c9327a7a79
commit
f8428e56e2
1 changed files with 52 additions and 8 deletions
|
|
@ -111,16 +111,22 @@ class Object:
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def read(self) -> str:
|
def read(self) -> str:
|
||||||
|
with self.tempdir("reader") as target:
|
||||||
|
with self.read_at(target) as path:
|
||||||
|
yield path
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def read_at(self, target: PathLike) -> str:
|
||||||
self._check_writable()
|
self._check_writable()
|
||||||
self._check_writer()
|
self._check_writer()
|
||||||
with self.tempdir("reader") as target:
|
|
||||||
mount(self._path, target)
|
mount(self._path, target)
|
||||||
try:
|
try:
|
||||||
self._readers += 1
|
self._readers += 1
|
||||||
yield target
|
yield target
|
||||||
finally:
|
finally:
|
||||||
umount(target)
|
umount(target)
|
||||||
self._readers -= 1
|
self._readers -= 1
|
||||||
|
|
||||||
def store_tree(self, destination: str):
|
def store_tree(self, destination: str):
|
||||||
"""Store the tree at destination and reset itself
|
"""Store the tree at destination and reset itself
|
||||||
|
|
@ -219,6 +225,7 @@ class HostTree:
|
||||||
`objectstore.Object` that can be used to read
|
`objectstore.Object` that can be used to read
|
||||||
the host file-system.
|
the host file-system.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, store):
|
def __init__(self, store):
|
||||||
self.store = store
|
self.store = store
|
||||||
|
|
||||||
|
|
@ -390,6 +397,25 @@ class StoreServer(api.BaseAPI):
|
||||||
path = self._stack.enter_context(reader)
|
path = self._stack.enter_context(reader)
|
||||||
sock.send({"path": path})
|
sock.send({"path": path})
|
||||||
|
|
||||||
|
def _read_tree_at(self, msg, sock):
|
||||||
|
object_id = msg["object-id"]
|
||||||
|
target = msg["target"]
|
||||||
|
|
||||||
|
obj = self.store.get(object_id)
|
||||||
|
if not obj:
|
||||||
|
sock.send({"path": None})
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
reader = obj.read_at(target)
|
||||||
|
path = self._stack.enter_context(reader)
|
||||||
|
# pylint: disable=broad-except
|
||||||
|
except Exception as e:
|
||||||
|
sock.send({"error": str(e)})
|
||||||
|
return
|
||||||
|
|
||||||
|
sock.send({"path": path})
|
||||||
|
|
||||||
def _mkdtemp(self, msg, sock):
|
def _mkdtemp(self, msg, sock):
|
||||||
args = {
|
args = {
|
||||||
"suffix": msg.get("suffix"),
|
"suffix": msg.get("suffix"),
|
||||||
|
|
@ -409,6 +435,8 @@ class StoreServer(api.BaseAPI):
|
||||||
def _message(self, msg, _fds, sock):
|
def _message(self, msg, _fds, sock):
|
||||||
if msg["method"] == "read-tree":
|
if msg["method"] == "read-tree":
|
||||||
self._read_tree(msg, sock)
|
self._read_tree(msg, sock)
|
||||||
|
elif msg["method"] == "read-tree-at":
|
||||||
|
self._read_tree_at(msg, sock)
|
||||||
elif msg["method"] == "mkdtemp":
|
elif msg["method"] == "mkdtemp":
|
||||||
self._mkdtemp(msg, sock)
|
self._mkdtemp(msg, sock)
|
||||||
elif msg["method"] == "source":
|
elif msg["method"] == "source":
|
||||||
|
|
@ -448,6 +476,22 @@ class StoreClient:
|
||||||
|
|
||||||
return msg["path"]
|
return msg["path"]
|
||||||
|
|
||||||
|
def read_tree_at(self, object_id: str, target: str):
|
||||||
|
msg = {
|
||||||
|
"method": "read-tree-at",
|
||||||
|
"object-id": object_id,
|
||||||
|
"target": os.fspath(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.client.send(msg)
|
||||||
|
msg, _, _ = self.client.recv()
|
||||||
|
|
||||||
|
err = msg.get("error")
|
||||||
|
if err:
|
||||||
|
raise RuntimeError(err)
|
||||||
|
|
||||||
|
return msg["path"]
|
||||||
|
|
||||||
def source(self, name: str) -> str:
|
def source(self, name: str) -> str:
|
||||||
msg = {
|
msg = {
|
||||||
"method": "source",
|
"method": "source",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue