Coverage for enderchest/sync/__init__.py: 100%
27 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-03 20:14 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-03 20:14 +0000
1"""Low-level functionality for synchronizing across different machines"""
2import importlib
3from contextlib import contextmanager
4from pathlib import Path
5from tempfile import TemporaryDirectory
6from typing import Collection, Generator
7from urllib.parse import ParseResult
9from ..loggers import SYNC_LOGGER
10from .utils import Operation as Op
11from .utils import (
12 abspath_from_uri,
13 diff,
14 filter_contents,
15 generate_sync_report,
16 get_default_netloc,
17 is_identical,
18 render_remote,
19 uri_to_ssh,
20)
22SUPPORTED_PROTOCOLS = ("rsync", "sftp", "file")
24DEFAULT_PROTOCOL = SUPPORTED_PROTOCOLS[0]
27def pull(
28 remote_uri: ParseResult,
29 local_path: Path,
30 exclude: Collection[str] | None = None,
31 dry_run: bool = False,
32 **kwargs,
33) -> None:
34 """Pull all upstream changes from a remote into the specified location
36 Parameters
37 ----------
38 remote_uri : ParseResult
39 The URI for the remote resource to pull
40 local_path : Path
41 The local destination
42 exclude : list of str, optional
43 Any patterns that should be excluded from the sync
44 dry_run : bool, optional
45 Whether to only simulate this sync (report the operations to be performed
46 but not actually perform them). Default is False.
47 **kwargs
48 Any additional options to pass into the sync command
49 """
50 try:
51 protocol = importlib.import_module(f"{__package__}.{remote_uri.scheme.lower()}")
52 protocol.pull(remote_uri, local_path, exclude or (), dry_run, **kwargs)
53 except ModuleNotFoundError as not_installed: # pragma: no cover
54 raise NotImplementedError(
55 f"Protocol {remote_uri.scheme} is not currently implemented"
56 ) from not_installed
59def push(
60 local_path: Path,
61 remote_uri: ParseResult,
62 exclude: Collection[str] | None = None,
63 dry_run: bool = False,
64 **kwargs,
65) -> None:
66 """Push all local changes in the specified directory into the specified remote
68 Parameters
69 ----------
70 local_path
71 The local path to push
72 remote_uri : ParseResult
73 The URI for the remote destination
74 exclude : list of str, optional
75 Any patterns that should be excluded from the sync
76 dry_run : bool, optional
77 Whether to only simulate this sync (report the operations to be performed
78 but not actually perform them). Default is False.
79 **kwargs
80 Any additional options to pass into the sync command
81 """
82 try:
83 protocol = importlib.import_module(f"{__package__}.{remote_uri.scheme.lower()}")
84 protocol.push(local_path, remote_uri, exclude or (), dry_run, **kwargs)
85 except ModuleNotFoundError as not_installed:
86 raise NotImplementedError(
87 f"Protocol {remote_uri.scheme} is not currently implemented"
88 ) from not_installed
91@contextmanager
92def remote_file(uri: ParseResult) -> Generator[Path, None, None]:
93 """Grab a file from a remote filesystem by its URI and read its contents
95 Parameters
96 ----------
97 uri : parsed URI
98 The URI of the file to read
100 Yields
101 ------
102 Path
103 A path to a local (temp) copy of the file
104 """
105 with TemporaryDirectory(ignore_cleanup_errors=True) as tmpdir:
106 pull(uri, Path(tmpdir), verbosity=-1)
107 yield Path(tmpdir) / Path(uri.path).name
110__all__ = [
111 "SYNC_LOGGER",
112 "SUPPORTED_PROTOCOLS",
113 "DEFAULT_PROTOCOL",
114 "get_default_netloc",
115 "render_remote",
116 "remote_file",
117 "abspath_from_uri",
118 "pull",
119 "push",
120 "uri_to_ssh",
121]