From e3b593d62870bd3d4369dbed1ced1f4f7a39011e Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 10 Sep 2024 20:10:15 -0500 Subject: [PATCH] fix: add `sync.make_selector()` convenience function wraps `fabsync.ItemSelector.new()` --- src/wuttamess/sync.py | 32 ++++++++++++++++++++++++++++---- tests/test_sync.py | 18 +++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/wuttamess/sync.py b/src/wuttamess/sync.py index fa48447..6147ca7 100644 --- a/src/wuttamess/sync.py +++ b/src/wuttamess/sync.py @@ -44,20 +44,41 @@ def make_root(path, dest='/'): return fabsync.load(path, dest) -def isync(c, root, selector=None, echo=True, **kwargs): +def make_selector(subpath=None, **kwargs): + """ + Make and return an "item selector" for use with a sync call. + + This is a convenience wrapper around + :meth:`fabsync:fabsync.ItemSelector.new()`. + + :param subpath: (Optional) Relative subpath of the file tree to + sync, e.g. ``'etc/postfix'``. + + :param tags: Optional iterable of tags to include; excluding any + files which are not so tagged. E.g. ``{'foo'}`` + """ + return fabsync.ItemSelector.new(subpath, **kwargs) + + +def isync(c, root, selector=None, tags=None, echo=True, **kwargs): """ Sync files, yielding the result for each as it goes. This is a convenience wrapper around :func:`fabsync:fabsync.isync()`. - :param c: Connection object. + :param c: Fabric connection. :param root: File tree "root" object as obtained from :func:`make_root()`. :param selector: This can be a simple "subpath" string, indicating - a section of the file tree. For instance: ``'etc/postfix'`` + a section of the file tree (e.g. ``'etc/postfix'``). Or can be + a :class:`fabsync.ItemSelector` instance. + + :param tags: Optional iterable of tags to select. If ``selector`` + is a subpath string, and you specify ``tags`` then they will be + included when creating the actual selector. :param echo: Flag indicating whether the path for each file synced should be echoed to stdout. Generally thought to be useful but @@ -68,7 +89,10 @@ def isync(c, root, selector=None, echo=True, **kwargs): """ if selector: if not isinstance(selector, fabsync.ItemSelector): - selector = fabsync.ItemSelector.new(selector) + kw = {} + if tags: + kw['tags'] = tags + selector = make_selector(selector, **kw) kwargs['selector'] = selector for result in fabsync.isync(c, root, **kwargs): diff --git a/tests/test_sync.py b/tests/test_sync.py index 23f4714..0a11084 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -18,6 +18,14 @@ class TestMakeRoot(TestCase): self.assertEqual(root.dest, Path('/')) +class TestMakeSelector(TestCase): + + def test_basic(self): + selector = mod.make_selector('etc/postfix') + self.assertIsInstance(selector, ItemSelector) + self.assertEqual(selector.subpath, Path('etc/postfix')) + + class TestIsync(TestCase): def test_basic(self): @@ -40,7 +48,7 @@ class TestIsync(TestCase): self.assertEqual(results, [result]) fabsync.isync.assert_called_once_with(c, root) - # sync with selector + # sync with selector (subpath) fabsync.isync.reset_mock() result = MagicMock(path='/foo', modified=True) fabsync.isync.return_value = [result] @@ -48,6 +56,14 @@ class TestIsync(TestCase): self.assertEqual(results, [result]) fabsync.isync.assert_called_once_with(c, root, selector=fabsync.ItemSelector.new('foo')) + # sync with selector (subpath + tags) + fabsync.isync.reset_mock() + result = MagicMock(path='/foo', modified=True) + fabsync.isync.return_value = [result] + results = list(mod.isync(c, root, 'foo', tags={'bar'})) + self.assertEqual(results, [result]) + fabsync.isync.assert_called_once_with(c, root, selector=fabsync.ItemSelector.new('foo', tags={'bar'})) + class TestCheckIsync(TestCase):