Atomic writes (fix #440)
This commit is contained in:
		| @@ -38,6 +38,7 @@ from importlib import import_module | |||||||
| from itertools import groupby | from itertools import groupby | ||||||
| from random import getrandbits | from random import getrandbits | ||||||
|  |  | ||||||
|  | from atomicwrites import AtomicWriter | ||||||
| import vobject | import vobject | ||||||
|  |  | ||||||
| if os.name == "nt": | if os.name == "nt": | ||||||
| @@ -151,6 +152,15 @@ def path_to_filesystem(root, *paths): | |||||||
|     return safe_path |     return safe_path | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class _EncodedAtomicWriter(AtomicWriter): | ||||||
|  |     def __init__(self, path, encoding, mode="w", overwrite=True): | ||||||
|  |         self._encoding = encoding | ||||||
|  |         return super().__init__(path, mode, overwrite=True) | ||||||
|  |  | ||||||
|  |     def get_fileobject(self, **kwargs): | ||||||
|  |         return super().get_fileobject(encoding=self._encoding, **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Item: | class Item: | ||||||
|     def __init__(self, collection, item, href, last_modified=None): |     def __init__(self, collection, item, href, last_modified=None): | ||||||
|         self.collection = collection |         self.collection = collection | ||||||
| @@ -319,6 +329,12 @@ class Collection(BaseCollection): | |||||||
|             self.owner = None |             self.owner = None | ||||||
|         self.is_principal = principal |         self.is_principal = principal | ||||||
|  |  | ||||||
|  |     @contextmanager | ||||||
|  |     def _atomic_write(self, path, mode="w"): | ||||||
|  |         with _EncodedAtomicWriter( | ||||||
|  |                 path, self.storage_encoding, mode).open() as fd: | ||||||
|  |             yield fd | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def discover(cls, path, depth="1"): |     def discover(cls, path, depth="1"): | ||||||
|         # path == None means wrong URL |         # path == None means wrong URL | ||||||
| @@ -447,7 +463,7 @@ class Collection(BaseCollection): | |||||||
|             path = path_to_filesystem(self._filesystem_path, href) |             path = path_to_filesystem(self._filesystem_path, href) | ||||||
|             if not os.path.exists(path): |             if not os.path.exists(path): | ||||||
|                 item = Item(self, vobject_item, href) |                 item = Item(self, vobject_item, href) | ||||||
|                 with open(path, "w", encoding=self.storage_encoding) as fd: |                 with self._atomic_write(path) as fd: | ||||||
|                     fd.write(item.serialize()) |                     fd.write(item.serialize()) | ||||||
|                 return item |                 return item | ||||||
|         else: |         else: | ||||||
| @@ -465,7 +481,7 @@ class Collection(BaseCollection): | |||||||
|                     text = fd.read() |                     text = fd.read() | ||||||
|                 if not etag or etag == get_etag(text): |                 if not etag or etag == get_etag(text): | ||||||
|                     item = Item(self, vobject_item, href) |                     item = Item(self, vobject_item, href) | ||||||
|                     with open(path, "w", encoding=self.storage_encoding) as fd: |                     with self._atomic_write(path) as fd: | ||||||
|                         fd.write(item.serialize()) |                         fd.write(item.serialize()) | ||||||
|                     return item |                     return item | ||||||
|         else: |         else: | ||||||
| @@ -516,7 +532,7 @@ class Collection(BaseCollection): | |||||||
|         else: |         else: | ||||||
|             properties.pop(key, None) |             properties.pop(key, None) | ||||||
|  |  | ||||||
|         with open(props_path, "w+", encoding=self.storage_encoding) as prop: |         with self._atomic_write(props_path, "w+") as prop: | ||||||
|             json.dump(properties, prop) |             json.dump(properties, prop) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							| @@ -62,7 +62,7 @@ setup( | |||||||
|     packages=["radicale"], |     packages=["radicale"], | ||||||
|     provides=["radicale"], |     provides=["radicale"], | ||||||
|     scripts=["bin/radicale"], |     scripts=["bin/radicale"], | ||||||
|     install_requires=["vobject"], |     install_requires=["vobject", "atomicwrites"], | ||||||
|     setup_requires=["pytest-runner"], |     setup_requires=["pytest-runner"], | ||||||
|     tests_require=["pytest-cov", "pytest-flake8", "pytest-isort", "pytest"], |     tests_require=["pytest-cov", "pytest-flake8", "pytest-isort", "pytest"], | ||||||
|     extras_require={ |     extras_require={ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Guillaume Ayoub
					Guillaume Ayoub