diff --git a/radicale/storage.py b/radicale/storage.py index c89a898..51a14ff 100644 --- a/radicale/storage.py +++ b/radicale/storage.py @@ -321,6 +321,19 @@ class BaseCollection: """Upload a new item.""" raise NotImplementedError + def upload_all(self, collections): + """Upload a new set of items. + + This takes a mapping of href and vobject items and + returns a list of uploaded items. + Might bring optimizations on some storages. + + """ + return [ + self.upload(href, vobject_item) + for href, vobject_item in collections.items() + ] + def update(self, href, vobject_item): """Update an item. @@ -552,12 +565,23 @@ class Collection(BaseCollection): items.extend( getattr(collection, "%s_list" % content, [])) items_by_uid = groupby(sorted(items, key=get_uid), get_uid) + collections = {} for uid, items in items_by_uid: new_collection = vobject.iCalendar() for item in items: new_collection.add(item) - self.upload( - self._find_available_file_name(), new_collection) + + # Prevent infinite loop + for _ in range(10000): + href = self._find_available_file_name() + if href not in collections: + break + else: + raise FileExistsError( + errno.EEXIST, "No usable file name found") + collections[href] = new_collection + + self.upload_all(collections) elif props.get("tag") == "VCARD": for card in collection: self.upload(self._find_available_file_name(), card) @@ -796,4 +820,4 @@ class Collection(BaseCollection): if (cls.configuration.getboolean("storage", "close_lock_file") and cls._readers == 0 and not cls._waiters): cls._lock_file.close() - cls._lock_file = None \ No newline at end of file + cls._lock_file = None