Merge pull request #889 from Intevation/dev-issue881

Implement more flexibility for storage/filesystem
This commit is contained in:
Hugo Osvaldo Barrera 2021-06-11 11:37:12 +02:00 committed by GitHub
commit 5844480588
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 51 additions and 3 deletions

View file

@ -408,6 +408,7 @@ Local
fileext = "..." fileext = "..."
#encoding = "utf-8" #encoding = "utf-8"
#post_hook = null #post_hook = null
#fileignoreext = ".tmp"
Can be used with `khal <http://lostpackets.de/khal/>`_. See :doc:`vdir` for Can be used with `khal <http://lostpackets.de/khal/>`_. See :doc:`vdir` for
a more formal description of the format. a more formal description of the format.
@ -421,11 +422,15 @@ Local
:param fileext: The file extension to use (e.g. ``.txt``). Contained in the :param fileext: The file extension to use (e.g. ``.txt``). Contained in the
href, so if you change the file extension after a sync, this will href, so if you change the file extension after a sync, this will
trigger a re-download of everything (but *should* not cause data-loss trigger a re-download of everything (but *should* not cause data-loss
of any kind). of any kind). To be compatible with the ``vset`` format you have
to either use ``.vcf`` or ``.ics``. Note that metasync won't work
if you use an empty string here.
:param encoding: File encoding for items, both content and filename. :param encoding: File encoding for items, both content and filename.
:param post_hook: A command to call for each item creation and :param post_hook: A command to call for each item creation and
modification. The command will be called with the path of the modification. The command will be called with the path of the
new/updated file. new/updated file.
:param fileeignoreext: The file extention to ignore. It is only useful
if fileext is set to the empty string. The default is ``.tmp``.
.. storage:: singlefile .. storage:: singlefile

View file

@ -44,6 +44,36 @@ class TestFilesystemStorage(StorageTests):
(item_file,) = tmpdir.listdir() (item_file,) = tmpdir.listdir()
assert "/" not in item_file.basename and item_file.isfile() assert "/" not in item_file.basename and item_file.isfile()
def test_ignore_tmp_files(self, tmpdir):
"""Test that files with .tmp suffix beside .ics files are ignored."""
s = self.storage_class(str(tmpdir), '.ics')
s.upload(Item('UID:xyzxyz'))
item_file, = tmpdir.listdir()
item_file.copy(item_file.new(ext='tmp'))
assert len(tmpdir.listdir()) == 2
assert len(list(s.list())) == 1
def test_ignore_tmp_files_empty_fileext(self, tmpdir):
"""Test that files with .tmp suffix are ignored with empty fileext."""
s = self.storage_class(str(tmpdir), '')
s.upload(Item('UID:xyzxyz'))
item_file, = tmpdir.listdir()
item_file.copy(item_file.new(ext='tmp'))
assert len(tmpdir.listdir()) == 2
# assert False, tmpdir.listdir() # enable to see the created filename
assert len(list(s.list())) == 1
def test_ignore_files_typical_backup(self, tmpdir):
"""Test file-name ignorance with typical backup ending ~."""
ignorext = "~" # without dot
s = self.storage_class(str(tmpdir), '', fileignoreext="~")
s.upload(Item('UID:xyzxyz'))
item_file, = tmpdir.listdir()
item_file.copy(item_file.new(basename=item_file.basename+'~'))
assert len(tmpdir.listdir()) == 2
#assert False, tmpdir.listdir() # enable to see the created filename
assert len(list(s.list())) == 1
def test_too_long_uid(self, tmpdir): def test_too_long_uid(self, tmpdir):
s = self.storage_class(str(tmpdir), ".txt") s = self.storage_class(str(tmpdir), ".txt")
item = Item("UID:" + "hue" * 600) item = Item("UID:" + "hue" * 600)

View file

@ -22,13 +22,22 @@ class FilesystemStorage(Storage):
storage_name = "filesystem" storage_name = "filesystem"
_repr_attributes = ("path",) _repr_attributes = ("path",)
def __init__(self, path, fileext, encoding="utf-8", post_hook=None, **kwargs): def __init__(
self,
path,
fileext,
encoding="utf-8",
post_hook=None,
fileignoreext=".tmp",
**kwargs
):
super().__init__(**kwargs) super().__init__(**kwargs)
path = expand_path(path) path = expand_path(path)
checkdir(path, create=False) checkdir(path, create=False)
self.path = path self.path = path
self.encoding = encoding self.encoding = encoding
self.fileext = fileext self.fileext = fileext
self.fileignoreext = fileignoreext
self.post_hook = post_hook self.post_hook = post_hook
@classmethod @classmethod
@ -80,7 +89,11 @@ class FilesystemStorage(Storage):
def list(self): def list(self):
for fname in os.listdir(self.path): for fname in os.listdir(self.path):
fpath = os.path.join(self.path, fname) fpath = os.path.join(self.path, fname)
if os.path.isfile(fpath) and fname.endswith(self.fileext): if (
os.path.isfile(fpath)
and fname.endswith(self.fileext)
and (not fname.endswith(self.fileignoreext))
):
yield fname, get_etag_from_file(fpath) yield fname, get_etag_from_file(fpath)
def get(self, href): def get(self, href):