mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-03-25 08:55:50 +00:00
Introduce post_hook for filesystem storage
This commit is contained in:
parent
ba4407af13
commit
208453408f
3 changed files with 53 additions and 5 deletions
|
|
@ -58,6 +58,8 @@ collections = ["private", "work"]
|
|||
type = filesystem
|
||||
path = ~/.calendars/
|
||||
fileext = .ics
|
||||
# For each new / updated file f, invoke the following script with argument f:
|
||||
#post_hook = /usr/local/bin/post_process.sh
|
||||
|
||||
[storage bob_calendar_remote]
|
||||
type = caldav
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
|
|
@ -66,3 +67,29 @@ class TestFilesystemStorage(StorageTests):
|
|||
items = list(href for href, etag in s.list())
|
||||
assert len(items) == 1
|
||||
assert len(set(items)) == 1
|
||||
|
||||
def test_post_hook_inactive(self, tmpdir, monkeypatch):
|
||||
|
||||
def check_call_mock(*args, **kwargs):
|
||||
assert False
|
||||
|
||||
monkeypatch.setattr(subprocess, 'call', check_call_mock)
|
||||
|
||||
s = self.storage_class(str(tmpdir), '.txt', post_hook=None)
|
||||
s.upload(Item(u'UID:a/b/c'))
|
||||
|
||||
def test_post_hook_active(self, tmpdir, monkeypatch):
|
||||
|
||||
calls = []
|
||||
exe = 'foo'
|
||||
|
||||
def check_call_mock(l, *args, **kwargs):
|
||||
calls.append(True)
|
||||
assert len(l) == 2
|
||||
assert l[0] == exe
|
||||
|
||||
monkeypatch.setattr(subprocess, 'call', check_call_mock)
|
||||
|
||||
s = self.storage_class(str(tmpdir), '.txt', post_hook=exe)
|
||||
s.upload(Item(u'UID:a/b/c'))
|
||||
assert calls
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import errno
|
||||
import os
|
||||
import subprocess
|
||||
import uuid
|
||||
|
||||
from atomicwrites import atomic_write
|
||||
|
|
@ -35,13 +36,15 @@ class FilesystemStorage(Storage):
|
|||
storage_name = 'filesystem'
|
||||
_repr_attributes = ('path',)
|
||||
|
||||
def __init__(self, path, fileext, encoding='utf-8', **kwargs):
|
||||
def __init__(self, path, fileext, encoding='utf-8', post_hook=None,
|
||||
**kwargs):
|
||||
super(FilesystemStorage, self).__init__(**kwargs)
|
||||
path = expand_path(path)
|
||||
checkdir(path, create=False)
|
||||
self.path = path
|
||||
self.encoding = encoding
|
||||
self.fileext = fileext
|
||||
self.post_hook = post_hook
|
||||
|
||||
@classmethod
|
||||
def discover(cls, path, **kwargs):
|
||||
|
|
@ -103,7 +106,7 @@ class FilesystemStorage(Storage):
|
|||
|
||||
try:
|
||||
href = self._deterministic_href(item)
|
||||
return self._upload_impl(item, href)
|
||||
fpath, etag = self._upload_impl(item, href)
|
||||
except OSError as e:
|
||||
if e.errno in (
|
||||
errno.ENAMETOOLONG, # Unix
|
||||
|
|
@ -112,16 +115,20 @@ class FilesystemStorage(Storage):
|
|||
logger.debug('UID as filename rejected, trying with random '
|
||||
'one.')
|
||||
href = self._random_href()
|
||||
return self._upload_impl(item, href)
|
||||
fpath, etag = self._upload_impl(item, href)
|
||||
else:
|
||||
raise
|
||||
|
||||
if self.post_hook:
|
||||
self._run_post_hook(fpath)
|
||||
return href, etag
|
||||
|
||||
def _upload_impl(self, item, href):
|
||||
fpath = self._get_filepath(href)
|
||||
try:
|
||||
with atomic_write(fpath, mode='wb', overwrite=False) as f:
|
||||
f.write(item.raw.encode(self.encoding))
|
||||
return href, get_etag_from_fileobject(f)
|
||||
return fpath, get_etag_from_fileobject(f)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST:
|
||||
raise exceptions.AlreadyExistingError(item)
|
||||
|
|
@ -141,7 +148,11 @@ class FilesystemStorage(Storage):
|
|||
|
||||
with atomic_write(fpath, mode='wb', overwrite=True) as f:
|
||||
f.write(item.raw.encode(self.encoding))
|
||||
return get_etag_from_fileobject(f)
|
||||
etag = get_etag_from_fileobject(f)
|
||||
|
||||
if self.post_hook:
|
||||
self._run_post_hook(fpath)
|
||||
return etag
|
||||
|
||||
def delete(self, href, etag):
|
||||
fpath = self._get_filepath(href)
|
||||
|
|
@ -151,3 +162,11 @@ class FilesystemStorage(Storage):
|
|||
if etag != actual_etag:
|
||||
raise exceptions.WrongEtagError(etag, actual_etag)
|
||||
os.remove(fpath)
|
||||
|
||||
def _run_post_hook(self, fpath):
|
||||
logger.info('Calling post_hook={} with argument={}'.format(
|
||||
self.post_hook, fpath))
|
||||
try:
|
||||
subprocess.call([self.post_hook, fpath])
|
||||
except OSError:
|
||||
logger.exception('Error executing external hook.')
|
||||
|
|
|
|||
Loading…
Reference in a new issue