Skip to content

Commit

Permalink
Introduce post_hook for filesystem storage
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Adler authored and untitaker committed Feb 8, 2015
1 parent ba4407a commit 2084534
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 5 deletions.
2 changes: 2 additions & 0 deletions example.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions tests/storage/test_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import os
import sys
import subprocess

import pytest

Expand Down Expand Up @@ -66,3 +67,29 @@ def test_case_sensitive_uids(self, s, get_item):
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
29 changes: 24 additions & 5 deletions vdirsyncer/storage/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import errno
import os
import subprocess
import uuid

from atomicwrites import atomic_write
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -103,7 +106,7 @@ def upload(self, item):

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
Expand All @@ -112,16 +115,20 @@ def upload(self, item):
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)
Expand All @@ -141,7 +148,11 @@ def update(self, href, item, etag):

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)
Expand All @@ -151,3 +162,11 @@ def delete(self, href, etag):
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.')

0 comments on commit 2084534

Please sign in to comment.