Skip to content

Commit c3dd5ec

Browse files
committed
Address comments @wonder-sk
- unify get_pull_changes with get_pull_delta - add pull action with rebase and no_rebase - simplify PullAction class
1 parent d1585fb commit c3dd5ec

File tree

7 files changed

+103
-179
lines changed

7 files changed

+103
-179
lines changed

mergin/client.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -789,10 +789,9 @@ def get_project_delta(self, project_id: str, since: str, to: typing.Optional[str
789789
:param project_id: Project's id
790790
:type project_id: String
791791
:param since: Version to track history of files from
792-
:type since: String
792+
:type since: String (e.g. v1)
793793
:param to: Optional version to track history of files to, if not given latest version is used
794-
:type since: String
795-
:rtype: Dict
794+
:type to: String (e.g. v2)
796795
"""
797796
# If it is not enabled on the server, raise error
798797
if not self.server_features().get("v2_pull_enabled", False):
@@ -805,7 +804,7 @@ def get_project_delta(self, project_id: str, since: str, to: typing.Optional[str
805804
resp_parsed = json.load(resp)
806805
return ProjectDelta(
807806
to_version=resp_parsed.get("to_version"),
808-
items=[
807+
changes=[
809808
ProjectDeltaChange(
810809
path=item["path"],
811810
size=item.get("size"),

mergin/client_pull.py

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ def __init__(
6868
self.is_cancelled = False
6969
self.project_info = project_info # parsed JSON with project info returned from the server
7070
self.failure_log_file = None # log file, copied from the project directory if download fails
71+
self.futures = [] # list of concurrent.futures.Future instances
7172

7273
def dump(self):
7374
print("--- JOB ---", self.total_size, "bytes")
@@ -137,9 +138,7 @@ def download_blocking(self, mc, mp):
137138
"""Starts download and only returns once the file has been fully downloaded and saved"""
138139

139140
mp.log.debug(f"Downloading diff {self.diff_id}")
140-
resp = mc.get(
141-
f"/v2/projects/{mp.project_id()}/raw/diff/{self.diff_id}",
142-
)
141+
resp = mc.get(f"/v2/projects/{mp.project_id()}/raw/diff", {"file": self.diff_id})
143142
if resp.status in [200, 206]:
144143
mp.log.debug(f"Download finished: {self.diff_id}")
145144
save_to_file(resp, self.download_file_path)
@@ -431,8 +430,8 @@ def __init__(
431430
total_size,
432431
version,
433432
download_files: List[DownloadFile],
434-
download_queue_items,
435-
tmp_dir,
433+
download_queue_items: List[typing.Union[DownloadQueueItem, DownloadDiffQueueItem]],
434+
tmp_dir: tempfile.TemporaryDirectory,
436435
mp,
437436
project_info,
438437
basefiles_to_patch,
@@ -446,7 +445,7 @@ def __init__(
446445
self.version = version
447446
self.download_files = download_files # list of DownloadFile instances
448447
self.download_queue_items = download_queue_items
449-
self.tmp_dir = tmp_dir # TemporaryDirectory instance where we store downloaded files
448+
self.tmp_dir: tempfile.TemporaryDirectory = tmp_dir
450449
self.mp: MerginProject = mp
451450
self.is_cancelled = False
452451
self.project_info = project_info # parsed JSON with project info returned from the server
@@ -466,7 +465,10 @@ def dump(self):
466465
print("patch basefile {} with {} diffs".format(basefile, len(diffs)))
467466
print("--")
468467
for item in self.download_queue_items:
469-
print("- {} {} {} {}".format(item.file_path, item.version, item.part_index, item.size))
468+
if isinstance(item, DownloadDiffQueueItem):
469+
print("- diff {} size {} dest {}".format(item.diff_id, item.size, item.download_file_path))
470+
else:
471+
print("- {} {} {} {}".format(item.file_path, item.version, item.part_index, item.size))
470472
print("--- END ---")
471473

472474

@@ -517,7 +519,7 @@ def pull_project_async(mc, directory) -> Optional[PullJob]:
517519
else:
518520
server_info = mc.project_info(project_path, since=local_version)
519521
server_version = server_info.get("version")
520-
delta = mp.get_pull_delta(server_info)
522+
delta = mp.get_pull_delta(server_info.get("files", []), server_version)
521523
except ClientError as err:
522524
mp.log.error("Error getting project info: " + str(err))
523525
mp.log.info("--- pull aborted")
@@ -537,28 +539,28 @@ def pull_project_async(mc, directory) -> Optional[PullJob]:
537539
pull_actions = []
538540
local_delta = mp.get_local_delta(tmp_dir.name)
539541
# Converting local to PullActions
540-
for item in delta.items:
542+
for change in delta.changes:
541543
# find corresponding local delta item
542-
local_item = next((i for i in local_delta if i.path == item.path), None)
544+
local_item = next((i for i in local_delta if i.path == change.path), None)
543545
local_item_change = local_item.type if local_item else None
544546

545547
# compare server and local changes to decide what to do in pull
546-
pull_action_type = mp.get_pull_action(item.type, local_item_change)
548+
pull_action_type = mp.get_pull_action(change.type, local_item_change)
547549
if not pull_action_type:
548550
continue # no action needed
549551

550-
pull_action = PullAction(pull_action_type, item, local_item)
551-
if pull_action_type == PullActionType.APPLY_DIFF or (
552-
pull_action_type == PullActionType.COPY_CONFLICT and item.type == DeltaChangeType.UPDATE_DIFF
552+
pull_action = PullAction(pull_action_type, change.path)
553+
if pull_action_type in (PullActionType.APPLY_DIFF_REBASE, PullActionType.APPLY_DIFF_NO_REBASE) or (
554+
pull_action_type == PullActionType.COPY_CONFLICT and change.type == DeltaChangeType.UPDATE_DIFF
553555
):
554-
basefile = mp.fpath_meta(item.path)
556+
basefile = mp.fpath_meta(change.path)
555557
if not os.path.exists(basefile):
556558
# The basefile does not exist for some reason. This should not happen normally (maybe user removed the file
557559
# or we removed it within previous pull because we failed to apply patch the older version for some reason).
558560
# But it's not a problem - we will download the newest version and we're sorted.
559-
mp.log.info(f"missing base file for {item.path} -> going to download it (version {server_version})")
560-
items = get_download_items(item.path, item.size, server_version, tmp_dir.name)
561-
dest_file_path = mp.fpath(item.path, tmp_dir.name)
561+
mp.log.info(f"missing base file for {change.path} -> going to download it (version {server_version})")
562+
items = get_download_items(change.path, change.size, server_version, tmp_dir.name)
563+
dest_file_path = mp.fpath(change.path, tmp_dir.name)
562564
download_files.append(DownloadFile(dest_file_path, items))
563565

564566
# Force use COPY_CONFLICT action to apply the new version instead of trying to apply diffs
@@ -574,22 +576,22 @@ def pull_project_async(mc, directory) -> Optional[PullJob]:
574576
diff_files.extend(
575577
[
576578
DownloadDiffQueueItem(diff_item.id, os.path.join(tmp_dir.name, diff_item.id))
577-
for diff_item in item.diffs
579+
for diff_item in change.diffs
578580
]
579581
)
580-
basefiles_to_patch.append((item.path, [diff.id for diff in item.diffs]))
582+
basefiles_to_patch.append((change.path, [diff.id for diff in change.diffs]))
581583

582584
else:
583585
# fallback for diff files using v1 endpoint /raw
584586
# download chunks and create DownloadFile instances for each diff file
585-
diff_download_files = get_download_diff_files(item, tmp_dir.name)
587+
diff_download_files = get_download_diff_files(change, tmp_dir.name)
586588
download_files.extend(diff_download_files)
587-
basefiles_to_patch.append((item.path, [diff.id for diff in item.diffs]))
589+
basefiles_to_patch.append((change.path, [diff.id for diff in change.diffs]))
588590

589591
elif pull_action_type == PullActionType.COPY or pull_action_type == PullActionType.COPY_CONFLICT:
590592
# simply download the server version of the files
591-
dest_file_path = os.path.normpath(os.path.join(tmp_dir.name, item.path))
592-
download_items = get_download_items(item.path, item.size, server_version, tmp_dir.name)
593+
dest_file_path = os.path.normpath(os.path.join(tmp_dir.name, change.path))
594+
download_items = get_download_items(change.path, change.size, server_version, tmp_dir.name)
593595
download_files.append(DownloadFile(dest_file_path, download_items))
594596

595597
pull_actions.append(pull_action)
@@ -604,8 +606,8 @@ def pull_project_async(mc, directory) -> Optional[PullJob]:
604606
total_size += diff_file.size
605607
for download_file in download_files:
606608
download_queue_items.extend(download_file.downloaded_items)
607-
for item in download_file.downloaded_items:
608-
total_size += item.size
609+
for change in download_file.downloaded_items:
610+
total_size += change.size
609611

610612
mp.log.info(f"will download {len(download_queue_items)} chunks, total size {total_size}")
611613

@@ -626,8 +628,8 @@ def pull_project_async(mc, directory) -> Optional[PullJob]:
626628

627629
# start download
628630
job.executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
629-
for item in download_queue_items:
630-
future = job.executor.submit(_do_download, item, mc, mp, project_path, job)
631+
for change in download_queue_items:
632+
future = job.executor.submit(_do_download, change, mc, mp, project_path, job)
631633
job.futures.append(future)
632634

633635
return job
@@ -912,7 +914,7 @@ def download_files_async(
912914
mp.log.info(f"Got project info. version {project_info['version']}")
913915

914916
# set temporary directory for download
915-
tmp_dir = tempfile.mkdtemp(prefix="python-api-client-")
917+
tmp_dir = tempfile.TemporaryDirectory(prefix="python-api-client-")
916918

917919
if output_paths is None:
918920
output_paths = []
@@ -922,7 +924,7 @@ def download_files_async(
922924
if len(output_paths) != len(file_paths):
923925
warn = "Output file paths are not of the same length as file paths. Cannot store required files."
924926
mp.log.warning(warn)
925-
shutil.rmtree(tmp_dir)
927+
cleanup_tmp_dir(mp, tmp_dir)
926928
raise ClientError(warn)
927929

928930
download_list = []
@@ -956,7 +958,7 @@ def download_files_async(
956958
if not download_list or missing_files:
957959
warn = f"No [{', '.join(missing_files)}] exists at version {version}"
958960
mp.log.warning(warn)
959-
shutil.rmtree(tmp_dir)
961+
cleanup_tmp_dir(mp, tmp_dir)
960962
raise ClientError(warn)
961963

962964
mp.log.info(
@@ -972,7 +974,7 @@ def download_files_async(
972974
return job
973975

974976

975-
def download_files_finalize(job):
977+
def download_files_finalize(job: DownloadJob):
976978
"""
977979
To be called when download_file_async is finished
978980
"""
@@ -989,5 +991,5 @@ def download_files_finalize(job):
989991
task.apply(job.tmp_dir, job.mp)
990992

991993
# Remove temporary download directory
992-
if job.tmp_dir is not None and os.path.exists(job.tmp_dir):
993-
shutil.rmtree(job.tmp_dir)
994+
if job.tmp_dir is not None and os.path.exists(job.tmp_dir.name):
995+
cleanup_tmp_dir(job.mp, job.tmp_dir)

mergin/common.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,5 +164,6 @@ class DeltaChangeType(Enum):
164164
class PullActionType(Enum):
165165
COPY = "copy"
166166
COPY_CONFLICT = "copy_conflict"
167-
APPLY_DIFF = "apply_diff"
167+
APPLY_DIFF_REBASE = "apply_diff_rebase"
168+
APPLY_DIFF_NO_REBASE = "apply_diff_no_rebase"
168169
DELETE = "delete"

0 commit comments

Comments
 (0)