mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-04-27 14:57:41 +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
|
type = filesystem
|
||||||
path = ~/.calendars/
|
path = ~/.calendars/
|
||||||
fileext = .ics
|
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]
|
[storage bob_calendar_remote]
|
||||||
type = caldav
|
type = caldav
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
@ -66,3 +67,29 @@ class TestFilesystemStorage(StorageTests):
|
||||||
items = list(href for href, etag in s.list())
|
items = list(href for href, etag in s.list())
|
||||||
assert len(items) == 1
|
assert len(items) == 1
|
||||||
assert len(set(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 errno
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from atomicwrites import atomic_write
|
from atomicwrites import atomic_write
|
||||||
|
|
@ -35,13 +36,15 @@ class FilesystemStorage(Storage):
|
||||||
storage_name = 'filesystem'
|
storage_name = 'filesystem'
|
||||||
_repr_attributes = ('path',)
|
_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)
|
super(FilesystemStorage, self).__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.post_hook = post_hook
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def discover(cls, path, **kwargs):
|
def discover(cls, path, **kwargs):
|
||||||
|
|
@ -103,7 +106,7 @@ class FilesystemStorage(Storage):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
href = self._deterministic_href(item)
|
href = self._deterministic_href(item)
|
||||||
return self._upload_impl(item, href)
|
fpath, etag = self._upload_impl(item, href)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno in (
|
if e.errno in (
|
||||||
errno.ENAMETOOLONG, # Unix
|
errno.ENAMETOOLONG, # Unix
|
||||||
|
|
@ -112,16 +115,20 @@ class FilesystemStorage(Storage):
|
||||||
logger.debug('UID as filename rejected, trying with random '
|
logger.debug('UID as filename rejected, trying with random '
|
||||||
'one.')
|
'one.')
|
||||||
href = self._random_href()
|
href = self._random_href()
|
||||||
return self._upload_impl(item, href)
|
fpath, etag = self._upload_impl(item, href)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
if self.post_hook:
|
||||||
|
self._run_post_hook(fpath)
|
||||||
|
return href, etag
|
||||||
|
|
||||||
def _upload_impl(self, item, href):
|
def _upload_impl(self, item, href):
|
||||||
fpath = self._get_filepath(href)
|
fpath = self._get_filepath(href)
|
||||||
try:
|
try:
|
||||||
with atomic_write(fpath, mode='wb', overwrite=False) as f:
|
with atomic_write(fpath, mode='wb', overwrite=False) as f:
|
||||||
f.write(item.raw.encode(self.encoding))
|
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:
|
except OSError as e:
|
||||||
if e.errno == errno.EEXIST:
|
if e.errno == errno.EEXIST:
|
||||||
raise exceptions.AlreadyExistingError(item)
|
raise exceptions.AlreadyExistingError(item)
|
||||||
|
|
@ -141,7 +148,11 @@ class FilesystemStorage(Storage):
|
||||||
|
|
||||||
with atomic_write(fpath, mode='wb', overwrite=True) as f:
|
with atomic_write(fpath, mode='wb', overwrite=True) as f:
|
||||||
f.write(item.raw.encode(self.encoding))
|
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):
|
def delete(self, href, etag):
|
||||||
fpath = self._get_filepath(href)
|
fpath = self._get_filepath(href)
|
||||||
|
|
@ -151,3 +162,11 @@ class FilesystemStorage(Storage):
|
||||||
if etag != actual_etag:
|
if etag != actual_etag:
|
||||||
raise exceptions.WrongEtagError(etag, actual_etag)
|
raise exceptions.WrongEtagError(etag, actual_etag)
|
||||||
os.remove(fpath)
|
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