Merge pull request #463 from Unrud/atomicmove

Atomic MOVE
This commit is contained in:
Guillaume Ayoub 2016-08-06 13:27:38 +02:00 committed by GitHub
commit 0cda9f611d
2 changed files with 34 additions and 5 deletions

View File

@ -473,8 +473,6 @@ class Application:
to_path = storage.sanitize_path(to_url.path) to_path = storage.sanitize_path(to_url.path)
if not self._access(user, to_path, "w"): if not self._access(user, to_path, "w"):
return NOT_ALLOWED return NOT_ALLOWED
if to_path.strip("/").startswith(path.strip("/")):
return client.CONFLICT, {}, None
with self._lock_collection("w", user): with self._lock_collection("w", user):
item = next(self.Collection.discover(path), None) item = next(self.Collection.discover(path), None)
@ -488,15 +486,17 @@ class Application:
return client.CONFLICT, {}, None return client.CONFLICT, {}, None
to_item = next(self.Collection.discover(to_path), None) to_item = next(self.Collection.discover(to_path), None)
if (isinstance(to_item, self.Collection) or
to_item and environ.get("HTTP_OVERWRITE", "F") != "T"):
return client.CONFLICT, {}, None
to_parent_path = storage.sanitize_path( to_parent_path = storage.sanitize_path(
"/%s/" % posixpath.dirname(to_path.strip("/"))) "/%s/" % posixpath.dirname(to_path.strip("/")))
to_collection = next( to_collection = next(
self.Collection.discover(to_parent_path), None) self.Collection.discover(to_parent_path), None)
if not to_collection or to_item: if not to_collection:
return client.CONFLICT, {}, None return client.CONFLICT, {}, None
to_href = posixpath.basename(to_path.strip("/")) to_href = posixpath.basename(to_path.strip("/"))
to_collection.upload(to_href, item.item) self.Collection.move(item, to_collection, to_href)
item.collection.delete(item.href)
return client.CREATED, {}, None return client.CREATED, {}, None
def do_OPTIONS(self, environ, path, content, user): def do_OPTIONS(self, environ, path, content, user):

View File

@ -265,6 +265,26 @@ class BaseCollection:
""" """
raise NotImplementedError raise NotImplementedError
@classmethod
def move(cls, item, to_collection, to_href):
"""Move an object.
``item`` is the item to move.
``to_collection`` is the target collection.
``to_href`` is the target name in ``to_collection``. An item with the
same name might already exist.
"""
if item.collection.path == to_collection.path and item.href == to_href:
return
if to_collection.has(to_href):
to_collection.update(to_href, item.item)
else:
to_collection.upload(to_href, item.item)
item.collection.delete(item.href)
@property @property
def etag(self): def etag(self):
return get_etag(self.serialize()) return get_etag(self.serialize())
@ -514,6 +534,15 @@ class Collection(BaseCollection):
return cls(sane_path, principal=principal) return cls(sane_path, principal=principal)
@classmethod
def move(cls, item, to_collection, to_href):
os.rename(
path_to_filesystem(item.collection._filesystem_path, item.href),
path_to_filesystem(to_collection._filesystem_path, to_href))
sync_directory(to_collection._filesystem_path)
if item.collection._filesystem_path != to_collection._filesystem_path:
sync_directory(item.collection._filesystem_path)
def list(self): def list(self):
try: try:
hrefs = os.listdir(self._filesystem_path) hrefs = os.listdir(self._filesystem_path)