mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-04-27 14:57:41 +00:00
Create cli.tasks
This commit is contained in:
parent
e297e6848a
commit
f2d34f4784
4 changed files with 95 additions and 79 deletions
|
|
@ -90,7 +90,7 @@ def test_storage_instance_from_config(monkeypatch):
|
||||||
import vdirsyncer.storage
|
import vdirsyncer.storage
|
||||||
monkeypatch.setitem(vdirsyncer.storage.storage_names, 'lol', lol)
|
monkeypatch.setitem(vdirsyncer.storage.storage_names, 'lol', lol)
|
||||||
config = {'type': 'lol', 'foo': 'bar', 'baz': 1}
|
config = {'type': 'lol', 'foo': 'bar', 'baz': 1}
|
||||||
assert cli.utils._storage_instance_from_config(config) == 'OK'
|
assert cli.utils.storage_instance_from_config(config) == 'OK'
|
||||||
|
|
||||||
|
|
||||||
def test_parse_pairs_args():
|
def test_parse_pairs_args():
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,9 @@ import functools
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from .tasks import sync_pair
|
||||||
from .utils import CliError, WorkerQueue, cli_logger, collections_for_pair, \
|
from .utils import CliError, WorkerQueue, cli_logger, collections_for_pair, \
|
||||||
handle_cli_error, load_config, parse_pairs_args, sync_pair
|
handle_cli_error, load_config, parse_pairs_args
|
||||||
from .. import __version__, log
|
from .. import __version__, log
|
||||||
from ..doubleclick import click
|
from ..doubleclick import click
|
||||||
from ..utils import expand_path
|
from ..utils import expand_path
|
||||||
|
|
|
||||||
83
vdirsyncer/cli/tasks.py
Normal file
83
vdirsyncer/cli/tasks.py
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
'''
|
||||||
|
vdirsyncer.cli.tasks
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
:copyright: (c) 2014 Markus Unterwaditzer & contributors
|
||||||
|
:license: MIT, see LICENSE for more details.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import functools
|
||||||
|
|
||||||
|
from .utils import CliError, JobFailed, cli_logger, collections_for_pair, \
|
||||||
|
get_status_name, handle_cli_error, load_status, save_status, \
|
||||||
|
storage_instance_from_config
|
||||||
|
|
||||||
|
from ..sync import sync
|
||||||
|
|
||||||
|
|
||||||
|
def sync_pair(wq, pair_name, collections_to_sync, general, all_pairs,
|
||||||
|
all_storages, force_delete):
|
||||||
|
key = ('prepare', pair_name)
|
||||||
|
if key in wq.handled_jobs:
|
||||||
|
cli_logger.warning('Already prepared {}, skipping'.format(pair_name))
|
||||||
|
return
|
||||||
|
wq.handled_jobs.add(key)
|
||||||
|
|
||||||
|
a_name, b_name, pair_options = all_pairs[pair_name]
|
||||||
|
|
||||||
|
all_collections = dict(collections_for_pair(
|
||||||
|
general['status_path'], a_name, b_name, pair_name,
|
||||||
|
all_storages[a_name], all_storages[b_name], pair_options
|
||||||
|
))
|
||||||
|
|
||||||
|
# spawn one worker less because we can reuse the current one
|
||||||
|
new_workers = -1
|
||||||
|
for collection in (collections_to_sync or all_collections):
|
||||||
|
try:
|
||||||
|
config_a, config_b = all_collections[collection]
|
||||||
|
except KeyError:
|
||||||
|
raise CliError('Pair {}: Collection {} not found. These are the '
|
||||||
|
'configured collections:\n{}'.format(
|
||||||
|
pair_name, collection, list(all_collections)))
|
||||||
|
new_workers += 1
|
||||||
|
wq.put(functools.partial(
|
||||||
|
sync_collection, pair_name=pair_name, collection=collection,
|
||||||
|
config_a=config_a, config_b=config_b, pair_options=pair_options,
|
||||||
|
general=general, force_delete=force_delete
|
||||||
|
))
|
||||||
|
|
||||||
|
for i in range(new_workers):
|
||||||
|
wq.spawn_worker()
|
||||||
|
|
||||||
|
|
||||||
|
def sync_collection(wq, pair_name, collection, config_a, config_b,
|
||||||
|
pair_options, general, force_delete):
|
||||||
|
status_name = get_status_name(pair_name, collection)
|
||||||
|
|
||||||
|
key = ('sync', pair_name, collection)
|
||||||
|
if key in wq.handled_jobs:
|
||||||
|
cli_logger.warning('Already syncing {}, skipping'.format(status_name))
|
||||||
|
return
|
||||||
|
wq.handled_jobs.add(key)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cli_logger.info('Syncing {}'.format(status_name))
|
||||||
|
|
||||||
|
status = load_status(general['status_path'], pair_name,
|
||||||
|
collection, data_type='items') or {}
|
||||||
|
cli_logger.debug('Loaded status for {}'.format(status_name))
|
||||||
|
|
||||||
|
a = storage_instance_from_config(config_a)
|
||||||
|
b = storage_instance_from_config(config_b)
|
||||||
|
sync(
|
||||||
|
a, b, status,
|
||||||
|
conflict_resolution=pair_options.get('conflict_resolution', None),
|
||||||
|
force_delete=force_delete
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
if not handle_cli_error(status_name):
|
||||||
|
raise JobFailed()
|
||||||
|
|
||||||
|
save_status(general['status_path'], pair_name, collection,
|
||||||
|
data_type='items', data=status)
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
import functools
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
@ -20,7 +19,7 @@ from itertools import chain
|
||||||
from .. import DOCS_HOME, PROJECT_HOME, log
|
from .. import DOCS_HOME, PROJECT_HOME, log
|
||||||
from ..doubleclick import click
|
from ..doubleclick import click
|
||||||
from ..storage import storage_names
|
from ..storage import storage_names
|
||||||
from ..sync import StorageEmpty, SyncConflict, sync
|
from ..sync import StorageEmpty, SyncConflict
|
||||||
from ..utils import expand_path, get_class_init_args, parse_options, \
|
from ..utils import expand_path, get_class_init_args, parse_options, \
|
||||||
safe_write
|
safe_write
|
||||||
|
|
||||||
|
|
@ -109,7 +108,7 @@ def _parse_old_config_list_value(d, key):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def _get_status_name(pair, collection):
|
def get_status_name(pair, collection):
|
||||||
if collection is None:
|
if collection is None:
|
||||||
return pair
|
return pair
|
||||||
return pair + '/' + collection
|
return pair + '/' + collection
|
||||||
|
|
@ -163,7 +162,7 @@ def collections_for_pair(status_path, name_a, name_b, pair_name, config_a,
|
||||||
|
|
||||||
def _discover_from_config(config):
|
def _discover_from_config(config):
|
||||||
storage_type = config['type']
|
storage_type = config['type']
|
||||||
cls, config = _storage_class_from_config(config)
|
cls, config = storage_class_from_config(config)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
discovered = list(cls.discover(**config))
|
discovered = list(cls.discover(**config))
|
||||||
|
|
@ -182,7 +181,7 @@ def _get_coll(pair_name, storage_name, collection, discovered, config):
|
||||||
return discovered[collection]
|
return discovered[collection]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
storage_type = config['type']
|
storage_type = config['type']
|
||||||
cls, config = _storage_class_from_config(config)
|
cls, config = storage_class_from_config(config)
|
||||||
try:
|
try:
|
||||||
args = cls.join_collection(collection=collection, **config)
|
args = cls.join_collection(collection=collection, **config)
|
||||||
args['type'] = storage_type
|
args['type'] = storage_type
|
||||||
|
|
@ -293,7 +292,7 @@ def load_config(f):
|
||||||
|
|
||||||
def load_status(base_path, pair, collection=None, data_type=None):
|
def load_status(base_path, pair, collection=None, data_type=None):
|
||||||
assert data_type is not None
|
assert data_type is not None
|
||||||
status_name = _get_status_name(pair, collection)
|
status_name = get_status_name(pair, collection)
|
||||||
path = expand_path(os.path.join(base_path, status_name))
|
path = expand_path(os.path.join(base_path, status_name))
|
||||||
if os.path.isfile(path) and data_type == 'items':
|
if os.path.isfile(path) and data_type == 'items':
|
||||||
new_path = path + '.items'
|
new_path = path + '.items'
|
||||||
|
|
@ -323,7 +322,7 @@ def load_status(base_path, pair, collection=None, data_type=None):
|
||||||
def save_status(base_path, pair, collection=None, data_type=None, data=None):
|
def save_status(base_path, pair, collection=None, data_type=None, data=None):
|
||||||
assert data_type is not None
|
assert data_type is not None
|
||||||
assert data is not None
|
assert data is not None
|
||||||
status_name = _get_status_name(pair, collection)
|
status_name = get_status_name(pair, collection)
|
||||||
path = expand_path(os.path.join(base_path, status_name)) + '.' + data_type
|
path = expand_path(os.path.join(base_path, status_name)) + '.' + data_type
|
||||||
base_path = os.path.dirname(path)
|
base_path = os.path.dirname(path)
|
||||||
|
|
||||||
|
|
@ -343,7 +342,7 @@ def save_status(base_path, pair, collection=None, data_type=None, data=None):
|
||||||
json.dump(data, f)
|
json.dump(data, f)
|
||||||
|
|
||||||
|
|
||||||
def _storage_class_from_config(config):
|
def storage_class_from_config(config):
|
||||||
config = dict(config)
|
config = dict(config)
|
||||||
storage_name = config.pop('type')
|
storage_name = config.pop('type')
|
||||||
cls = storage_names.get(storage_name, None)
|
cls = storage_names.get(storage_name, None)
|
||||||
|
|
@ -352,14 +351,14 @@ def _storage_class_from_config(config):
|
||||||
return cls, config
|
return cls, config
|
||||||
|
|
||||||
|
|
||||||
def _storage_instance_from_config(config):
|
def storage_instance_from_config(config):
|
||||||
'''
|
'''
|
||||||
:param config: A configuration dictionary to pass as kwargs to the class
|
:param config: A configuration dictionary to pass as kwargs to the class
|
||||||
corresponding to config['type']
|
corresponding to config['type']
|
||||||
:param description: A name for the storage for debugging purposes
|
:param description: A name for the storage for debugging purposes
|
||||||
'''
|
'''
|
||||||
|
|
||||||
cls, config = _storage_class_from_config(config)
|
cls, config = storage_class_from_config(config)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return cls(**config)
|
return cls(**config)
|
||||||
|
|
@ -416,73 +415,6 @@ def parse_pairs_args(pairs_args, all_pairs):
|
||||||
return rv.items()
|
return rv.items()
|
||||||
|
|
||||||
|
|
||||||
def sync_pair(wq, pair_name, collections_to_sync, general, all_pairs,
|
|
||||||
all_storages, force_delete):
|
|
||||||
key = ('prepare', pair_name)
|
|
||||||
if key in wq.handled_jobs:
|
|
||||||
cli_logger.warning('Already prepared {}, skipping'.format(pair_name))
|
|
||||||
return
|
|
||||||
wq.handled_jobs.add(key)
|
|
||||||
|
|
||||||
a_name, b_name, pair_options = all_pairs[pair_name]
|
|
||||||
|
|
||||||
all_collections = dict(collections_for_pair(
|
|
||||||
general['status_path'], a_name, b_name, pair_name,
|
|
||||||
all_storages[a_name], all_storages[b_name], pair_options
|
|
||||||
))
|
|
||||||
|
|
||||||
# spawn one worker less because we can reuse the current one
|
|
||||||
new_workers = -1
|
|
||||||
for collection in (collections_to_sync or all_collections):
|
|
||||||
try:
|
|
||||||
config_a, config_b = all_collections[collection]
|
|
||||||
except KeyError:
|
|
||||||
raise CliError('Pair {}: Collection {} not found. These are the '
|
|
||||||
'configured collections:\n{}'.format(
|
|
||||||
pair_name, collection, list(all_collections)))
|
|
||||||
new_workers += 1
|
|
||||||
wq.put(functools.partial(
|
|
||||||
sync_collection, pair_name=pair_name, collection=collection,
|
|
||||||
config_a=config_a, config_b=config_b, pair_options=pair_options,
|
|
||||||
general=general, force_delete=force_delete
|
|
||||||
))
|
|
||||||
|
|
||||||
for i in range(new_workers):
|
|
||||||
wq.spawn_worker()
|
|
||||||
|
|
||||||
|
|
||||||
def sync_collection(wq, pair_name, collection, config_a, config_b,
|
|
||||||
pair_options, general, force_delete):
|
|
||||||
status_name = _get_status_name(pair_name, collection)
|
|
||||||
|
|
||||||
key = ('sync', pair_name, collection)
|
|
||||||
if key in wq.handled_jobs:
|
|
||||||
cli_logger.warning('Already syncing {}, skipping'.format(status_name))
|
|
||||||
return
|
|
||||||
wq.handled_jobs.add(key)
|
|
||||||
|
|
||||||
try:
|
|
||||||
cli_logger.info('Syncing {}'.format(status_name))
|
|
||||||
|
|
||||||
status = load_status(general['status_path'], pair_name,
|
|
||||||
collection, data_type='items') or {}
|
|
||||||
cli_logger.debug('Loaded status for {}'.format(status_name))
|
|
||||||
|
|
||||||
a = _storage_instance_from_config(config_a)
|
|
||||||
b = _storage_instance_from_config(config_b)
|
|
||||||
sync(
|
|
||||||
a, b, status,
|
|
||||||
conflict_resolution=pair_options.get('conflict_resolution', None),
|
|
||||||
force_delete=force_delete
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
if not handle_cli_error(status_name):
|
|
||||||
raise JobFailed()
|
|
||||||
|
|
||||||
save_status(general['status_path'], pair_name, collection,
|
|
||||||
data_type='items', data=status)
|
|
||||||
|
|
||||||
|
|
||||||
class WorkerQueue(object):
|
class WorkerQueue(object):
|
||||||
def __init__(self, max_workers):
|
def __init__(self, max_workers):
|
||||||
self._queue = Queue()
|
self._queue = Queue()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue