Skip to content

Commit 5c14a10

Browse files
committed
cover all Python filesystem operations
1 parent fdfdb75 commit 5c14a10

6 files changed

Lines changed: 50 additions & 89 deletions

File tree

mergin/client.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
int_version,
6767
is_version_acceptable,
6868
normalize_role,
69+
long_path,
6970
)
7071
from . import fs
7172
from .version import __version__
@@ -205,7 +206,7 @@ def setup_logging(self):
205206
self.log.setLevel(logging.DEBUG) # log everything (it would otherwise log just warnings+errors)
206207
if not self.log.handlers:
207208
if client_log_file:
208-
log_handler = logging.FileHandler(client_log_file)
209+
log_handler = logging.FileHandler(long_path(client_log_file))
209210
log_handler.setFormatter(logging.Formatter("%(asctime)s %(message)s"))
210211
self.log.addHandler(log_handler)
211212
else:
@@ -532,7 +533,7 @@ def create_project_and_push(self, project_name, directory, is_public=False, name
532533
:param namespace: Deprecated. project_name should be full project name. Optional namespace for a new project. If empty username is used.
533534
:type namespace: String
534535
"""
535-
if os.path.exists(os.path.join(directory, ".mergin")):
536+
if fs.exists(os.path.join(directory, ".mergin")):
536537
raise ClientError("Directory is already assigned to a Mergin Maps project (contains .mergin sub-dir)")
537538

538539
if namespace and "/" not in project_name:
@@ -1242,7 +1243,7 @@ def get_file_diff(self, project_dir, file_path, output_diff, version_from, versi
12421243
# concatenate diffs, if needed
12431244
output_dir = os.path.dirname(output_diff)
12441245
if len(diffs) >= 1:
1245-
os.makedirs(output_dir, exist_ok=True)
1246+
fs.makedirs(output_dir, exist_ok=True)
12461247
if len(diffs) > 1:
12471248
mp.geodiff.concat_changes(diffs, output_diff)
12481249
elif len(diffs) == 1:
@@ -1612,14 +1613,14 @@ def send_logs(
16121613
local_logs_file_size_to_send = int(MAX_LOG_FILE_SIZE_TO_SEND * 0.8)
16131614

16141615
global_logs = b""
1615-
if global_log_file and os.path.exists(global_log_file):
1616-
with open(global_log_file, "rb") as f:
1617-
if os.path.getsize(global_log_file) > global_logs_file_size_to_send:
1616+
if global_log_file and fs.exists(global_log_file):
1617+
with fs.open_file(global_log_file, "rb") as f:
1618+
if fs.getsize(global_log_file) > global_logs_file_size_to_send:
16181619
f.seek(-global_logs_file_size_to_send, os.SEEK_END)
16191620
global_logs = f.read() + b"\n--------------------------------\n\n"
16201621

1621-
with open(logfile, "rb") as f:
1622-
if os.path.getsize(logfile) > local_logs_file_size_to_send:
1622+
with fs.open_file(logfile, "rb") as f:
1623+
if fs.getsize(logfile) > local_logs_file_size_to_send:
16231624
f.seek(-local_logs_file_size_to_send, os.SEEK_END)
16241625
logs = f.read()
16251626

mergin/client_pull.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ def _cleanup_failed_download(mergin_project: MerginProject = None):
234234
log_file = os.path.join(mergin_project.dir, ".mergin", "client-log.txt")
235235
dest_path = None
236236

237-
if os.path.exists(log_file):
237+
if fs.exists(log_file):
238238
tmp_file = tempfile.NamedTemporaryFile(prefix="mergin-", suffix=".txt", delete=False)
239239
tmp_file.close()
240240
dest_path = tmp_file.name
@@ -251,9 +251,9 @@ def download_project_async(mc, project_path, directory, project_version=None):
251251

252252
if "/" not in project_path:
253253
raise ClientError("Project name needs to be fully qualified, e.g. <username>/<projectname>")
254-
if os.path.exists(directory):
254+
if fs.exists(directory):
255255
raise ClientError("Project directory already exists")
256-
os.makedirs(directory)
256+
fs.makedirs(directory)
257257
mp = MerginProject(directory)
258258

259259
mp.log.info("--- version: " + mc.user_agent_info())
@@ -409,7 +409,7 @@ def apply(self, directory, mp):
409409
else:
410410
file_dir = os.path.dirname(os.path.normpath(self.destination_file))
411411
dest_file_path = self.destination_file
412-
os.makedirs(file_dir, exist_ok=True)
412+
fs.makedirs(file_dir, exist_ok=True)
413413

414414
# ignore check if we download not-latest version of gpkg file (possibly reconstructed on server on demand)
415415
check_size = self.latest_version or not mp.is_versioned_file(self.file_path)
@@ -993,5 +993,5 @@ def download_files_finalize(job: DownloadJob):
993993
task.apply(job.tmp_dir, job.mp)
994994

995995
# Remove temporary download directory
996-
if job.tmp_dir is not None and os.path.exists(job.tmp_dir.name):
996+
if job.tmp_dir is not None and fs.exists(job.tmp_dir.name):
997997
cleanup_tmp_dir(job.mp, job.tmp_dir)

mergin/fs.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ def makedirs(path, exist_ok=False):
4343
os.makedirs(long_path(path), exist_ok=exist_ok)
4444

4545

46+
def mkdir(path):
47+
os.mkdir(long_path(path))
48+
49+
50+
def rmtree(path):
51+
shutil.rmtree(long_path(path))
52+
53+
4654
def connect(path):
4755
return sqlite3.connect(long_path(path))
4856

mergin/merginproject.py

Lines changed: 22 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import math
44
import os
55
import re
6-
import shutil
76
from typing import List, Optional, Dict
87
import typing
98
import uuid
@@ -41,54 +40,6 @@
4140
import pygeodiff
4241

4342

44-
class GeoDiffLongPath:
45-
"""
46-
Wraps a pygeodiff.GeoDiff instance so that every filesystem path passed to it is prefixed
47-
with the Windows extended-length ("\\?\") marker.
48-
49-
Only the geodiff methods that take path arguments are listed explicitly.
50-
"""
51-
52-
def __init__(self, geodiff):
53-
self._geodiff = geodiff
54-
55-
def create_changeset(self, base, modified, changeset):
56-
return self._geodiff.create_changeset(long_path(base), long_path(modified), long_path(changeset))
57-
58-
def apply_changeset(self, base, changeset):
59-
return self._geodiff.apply_changeset(long_path(base), long_path(changeset))
60-
61-
def rebase(self, base, modified_their, modified, conflict):
62-
return self._geodiff.rebase(
63-
long_path(base), long_path(modified_their), long_path(modified), long_path(conflict)
64-
)
65-
66-
def make_copy_sqlite(self, src, dst):
67-
return self._geodiff.make_copy_sqlite(long_path(src), long_path(dst))
68-
69-
def has_changes(self, changeset):
70-
return self._geodiff.has_changes(long_path(changeset))
71-
72-
def changes_count(self, changeset):
73-
return self._geodiff.changes_count(long_path(changeset))
74-
75-
def read_changeset(self, changeset):
76-
return self._geodiff.read_changeset(long_path(changeset))
77-
78-
def list_changes_summary(self, changeset, json):
79-
return self._geodiff.list_changes_summary(long_path(changeset), long_path(json))
80-
81-
def concat_changes(self, list_changesets, output_changeset):
82-
return self._geodiff.concat_changes([long_path(p) for p in list_changesets], long_path(output_changeset))
83-
84-
def schema(self, driver, driver_info, src, json):
85-
return self._geodiff.schema(driver, driver_info, long_path(src), long_path(json))
86-
87-
def __getattr__(self, name):
88-
# everything without path arguments (set_logger_callback, level/table setters, version(), ...)
89-
return getattr(self._geodiff, name)
90-
91-
9243
class MerginProject:
9344
"""Base class for Mergin Maps local projects.
9445
@@ -97,19 +48,19 @@ class MerginProject:
9748

9849
def __init__(self, directory):
9950
self.dir = os.path.abspath(directory)
100-
if not os.path.exists(self.dir):
51+
if not fs.exists(self.dir):
10152
raise InvalidProject("Project directory does not exist")
10253

10354
self.meta_dir = os.path.join(self.dir, ".mergin")
104-
if not os.path.exists(self.meta_dir):
105-
os.mkdir(self.meta_dir)
55+
if not fs.exists(self.meta_dir):
56+
fs.mkdir(self.meta_dir)
10657

10758
# location for files from unfinished pull
10859
self.unfinished_pull_dir = os.path.join(self.meta_dir, "unfinished_pull")
10960

11061
self.cache_dir = os.path.join(self.meta_dir, ".cache")
111-
if not os.path.exists(self.cache_dir):
112-
os.mkdir(self.cache_dir)
62+
if not fs.exists(self.cache_dir):
63+
fs.mkdir(self.cache_dir)
11364

11465
# metadata from JSON are lazy loaded
11566
self._metadata = None
@@ -119,7 +70,7 @@ def __init__(self, directory):
11970

12071
# make sure we can load correct pygeodiff
12172
try:
122-
self.geodiff = GeoDiffLongPath(pygeodiff.GeoDiff())
73+
self.geodiff = pygeodiff.GeoDiff()
12374
except pygeodiff.geodifflib.GeoDiffLibVersionError:
12475
# this is a fatal error, we can't live without geodiff
12576
self.log.error("Unable to load geodiff! (lib version error)")
@@ -145,7 +96,9 @@ def setup_logging(self, logger_name):
14596
if not self.log.handlers:
14697
# we only need to set the handler once
14798
# (otherwise we would get things logged multiple times as loggers are cached)
148-
log_handler = logging.FileHandler(os.path.join(self.meta_dir, "client-log.txt"), encoding="utf-8")
99+
log_handler = logging.FileHandler(
100+
long_path(os.path.join(self.meta_dir, "client-log.txt")), encoding="utf-8"
101+
)
149102
log_handler.setFormatter(logging.Formatter("%(asctime)s %(message)s"))
150103
self.log.addHandler(log_handler)
151104

@@ -172,7 +125,7 @@ def fpath(self, file, other_dir=None):
172125
root = other_dir or self.dir
173126
abs_path = os.path.abspath(os.path.join(root, file))
174127
f_dir = os.path.dirname(abs_path)
175-
os.makedirs(f_dir, exist_ok=True)
128+
fs.makedirs(f_dir, exist_ok=True)
176129
return abs_path
177130

178131
def fpath_meta(self, file):
@@ -280,9 +233,9 @@ def _read_metadata(self) -> None:
280233
"""Loads the project's metadata from JSON"""
281234
if self._metadata is not None:
282235
return
283-
if not os.path.exists(self.fpath_meta("mergin.json")):
236+
if not fs.exists(self.fpath_meta("mergin.json")):
284237
raise InvalidProject("Project metadata has not been created yet")
285-
with open(self.fpath_meta("mergin.json"), "r") as file:
238+
with fs.open_file(self.fpath_meta("mergin.json"), "r") as file:
286239
self._metadata = json.load(file)
287240

288241
self.is_old_metadata = "/" in self._metadata["name"]
@@ -302,9 +255,9 @@ def write_metadata(project_directory: str, data: dict):
302255
(and therefore creating MerginProject would fail).
303256
"""
304257
meta_dir = os.path.join(project_directory, ".mergin")
305-
os.makedirs(meta_dir, exist_ok=True)
258+
fs.makedirs(meta_dir, exist_ok=True)
306259
metadata_json_file = os.path.abspath(os.path.join(meta_dir, "mergin.json"))
307-
with open(metadata_json_file, "w") as file:
260+
with fs.open_file(metadata_json_file, "w") as file:
308261
json.dump(data, file, indent=2)
309262

310263
def is_versioned_file(self, file):
@@ -760,7 +713,7 @@ def copy_versioned_file_for_upload(self, f: FileChange, tmp_dir: str) -> str:
760713
path = f.path
761714
self.log.info("Making a temporary copy (full upload): " + path)
762715
tmp_file = os.path.join(tmp_dir, path)
763-
os.makedirs(os.path.dirname(tmp_file), exist_ok=True)
716+
fs.makedirs(os.path.dirname(tmp_file), exist_ok=True)
764717
self.geodiff.make_copy_sqlite(self.fpath(path), tmp_file)
765718
f.size = fs.getsize(tmp_file)
766719
f.checksum = generate_checksum(tmp_file)
@@ -777,10 +730,10 @@ def get_list_of_push_changes(self, push_changes):
777730
result_file = self.fpath("change_list" + str(idx), self.meta_dir)
778731
try:
779732
self.geodiff.list_changes_summary(changeset, result_file)
780-
with open(result_file, "r") as f:
733+
with fs.open_file(result_file, "r") as f:
781734
change = f.read()
782735
changes[file["path"]] = json.loads(change)
783-
os.remove(result_file)
736+
fs.remove(result_file)
784737
except (pygeodiff.GeoDiffLibError, pygeodiff.GeoDiffLibConflictError):
785738
pass
786739
return changes
@@ -1061,7 +1014,7 @@ def has_unfinished_pull(self):
10611014
:returns: whether there is an unfinished pull
10621015
:rtype: bool
10631016
"""
1064-
return os.path.exists(self.unfinished_pull_dir)
1017+
return fs.exists(self.unfinished_pull_dir)
10651018

10661019
def resolve_unfinished_pull(self, user_name):
10671020
"""
@@ -1093,9 +1046,8 @@ def resolve_unfinished_pull(self, user_name):
10931046

10941047
for root, dirs, files in fs.walk(self.unfinished_pull_dir):
10951048
for file_name in files:
1096-
src = os.path.join(root, file_name)
1097-
# the relpath base must be prefixed as well to strip it correctly.
1098-
file_path = os.path.relpath(src, long_path(self.unfinished_pull_dir))
1049+
file_path = os.path.relpath(os.path.join(root, file_name), long_path(self.unfinished_pull_dir))
1050+
src = self.fpath_unfinished_pull(file_path)
10991051
dest = self.fpath(file_path)
11001052
basefile = self.fpath_meta(file_path)
11011053

@@ -1116,7 +1068,7 @@ def resolve_unfinished_pull(self, user_name):
11161068
self.log.error("unable to apply changes from previous unfinished pull!")
11171069
raise ClientError("Unable to resolve unfinished pull!")
11181070

1119-
shutil.rmtree(self.unfinished_pull_dir)
1071+
fs.rmtree(self.unfinished_pull_dir)
11201072
self.log.info("unfinished pull resolved successfuly!")
11211073
return conflicts
11221074

@@ -1162,7 +1114,7 @@ def get_geodiff_changes_count(self, diff_rel_path: str):
11621114

11631115
diff_abs = self.fpath_meta(diff_rel_path)
11641116
try:
1165-
return GeoDiffLongPath(pygeodiff.GeoDiff()).changes_count(diff_abs)
1117+
return pygeodiff.GeoDiff().changes_count(diff_abs)
11661118
except (
11671119
pygeodiff.GeoDiffLibError,
11681120
pygeodiff.GeoDiffLibConflictError,

mergin/report.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,8 @@ def create_report(mc, directory, since, to, out_file):
286286

287287
# export report to csv file
288288
out_dir = os.path.dirname(out_file)
289-
os.makedirs(out_dir, exist_ok=True)
290-
with open(out_file, "w", newline="") as f_csv:
289+
fs.makedirs(out_dir, exist_ok=True)
290+
with fs.open_file(out_file, "w", newline="") as f_csv:
291291
writer = csv.DictWriter(f_csv, fieldnames=headers)
292292
writer.writeheader()
293293
writer.writerows(records)

mergin/utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ def save_to_file(stream, path):
5252

5353
def move_file(src, dest):
5454
dest_dir = os.path.dirname(dest)
55-
os.makedirs(dest_dir, exist_ok=True)
56-
os.rename(src, dest)
55+
os.makedirs(long_path(dest_dir), exist_ok=True)
56+
os.rename(long_path(src), long_path(dest))
5757

5858

5959
class DateTimeEncoder(json.JSONEncoder):
@@ -167,13 +167,13 @@ def unique_path_name(path):
167167
"""
168168
unique_path = str(path)
169169

170-
is_dir = os.path.isdir(path)
170+
is_dir = os.path.isdir(long_path(path))
171171
head, tail = os.path.split(os.path.normpath(path))
172172
ext = "".join(Path(tail).suffixes)
173173
file_name = tail.replace(ext, "")
174174

175175
i = 0
176-
while os.path.exists(unique_path):
176+
while os.path.exists(long_path(unique_path)):
177177
i += 1
178178

179179
if is_dir:

0 commit comments

Comments
 (0)