Skip to content

Commit 6afdcf4

Browse files
committed
Fix separator handling
1 parent bc3d0e7 commit 6afdcf4

File tree

10 files changed

+59
-35
lines changed

10 files changed

+59
-35
lines changed

dirdiff/differ.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
)
1717
from dirdiff.filelib import DirectoryManager, FileManager, PathManager
1818
from dirdiff.filelib_tar import TarDirectoryManager, TarFileLoader
19+
from dirdiff.osshim import posix_join
1920
from dirdiff.output import DiffOutput, StatInfo
2021

2122
LOGGER = logging.getLogger(__name__)
@@ -100,7 +101,7 @@ def stats_differ(self, x: StatInfo, y: StatInfo) -> bool:
100101

101102
def _open_dir(path_dir: DifferPathLike) -> DirectoryManager:
102103
if isinstance(path_dir, tarfile.TarFile):
103-
return TarDirectoryManager(TarFileLoader(path_dir), os.path.sep) # type: ignore
104+
return TarDirectoryManager(TarFileLoader(path_dir), "/") # type: ignore
104105
return DirectoryManager(path_dir)
105106

106107

@@ -216,7 +217,7 @@ def _diff_dirs(
216217

217218
for dir_entry in merged_entries:
218219
dir_entry_type = _dir_entry_type(dir_entry)
219-
cpath = os.path.join(archive_path, dir_entry.name)
220+
cpath = posix_join(archive_path, dir_entry.name)
220221

221222
lower_type = lower_map.pop(dir_entry.name, None)
222223
if dir_entry_type == DirEntryType.DIRECTORY:
@@ -250,7 +251,7 @@ def _diff_dirs(
250251
for name in lower_map:
251252
self._flush_pending()
252253
try:
253-
self.output.delete_marker(os.path.join(archive_path, name))
254+
self.output.delete_marker(posix_join(archive_path, name))
254255
except IOErrors:
255256
self._output_error(archive_path, "creating delete marker")
256257

@@ -395,7 +396,7 @@ def _insert_dir(
395396
except IOErrors:
396397
self._output_error(archive_path, "creating dir")
397398
for dir_entry in dir_entries:
398-
cpath = os.path.join(archive_path, dir_entry.name)
399+
cpath = posix_join(archive_path, dir_entry.name)
399400
if dir_entry.is_dir(follow_symlinks=False):
400401
self._insert_dir(cpath, obj.child_dir(dir_entry.name))
401402
elif dir_entry.is_file(follow_symlinks=False):

dirdiff/filelib_tar.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import collections
22
import contextlib
33
import functools
4-
import os
54
import stat
65
import tarfile
76
from tarfile import TarFile, TarInfo
87
from typing import Iterator
98

109
from dirdiff.filelib import StatInfo
11-
from dirdiff.osshim import makedev
10+
from dirdiff.osshim import makedev, posix_join, posix_norm, posix_split
1211

1312
_MODE_MAPPING = {
1413
tarfile.REGTYPE: stat.S_IFREG,
@@ -21,7 +20,7 @@
2120

2221

2322
def _norm_name(name: str) -> str:
24-
return os.path.normpath(os.path.join(os.path.sep, name))
23+
return posix_norm(posix_join("/", name))
2524

2625

2726
class TarFileLoader:
@@ -33,15 +32,15 @@ def __init__(self, tf: TarFile) -> None:
3332
self.info = {}
3433
for ti in tf.getmembers():
3534
path = _norm_name(ti.name)
36-
parent_path, filename = os.path.split(path)
35+
parent_path, filename = posix_split(path)
3736
self.info[path] = ti
3837

3938
while filename:
4039
self.children[parent_path].append((filename, ti))
4140
if parent_path in self.info:
4241
break
4342
self.info[parent_path] = self.MISSING_DIRECTORY_TARINFO
44-
parent_path, filename = os.path.split(parent_path)
43+
parent_path, filename = posix_split(parent_path)
4544

4645

4746
class TarManagerBase:
@@ -120,10 +119,10 @@ def exists_in_archive(self) -> bool:
120119
return self.ti is not self.loader.MISSING_DIRECTORY_TARINFO
121120

122121
def child_dir(self, name: str) -> "TarDirectoryManager":
123-
return TarDirectoryManager(self.loader, os.path.join(self.name, name))
122+
return TarDirectoryManager(self.loader, posix_join(self.name, name))
124123

125124
def child_file(self, name: str) -> TarFileManager:
126-
return TarFileManager(self.loader, os.path.join(self.name, name))
125+
return TarFileManager(self.loader, posix_join(self.name, name))
127126

128127
def child_path(self, name: str) -> TarPathManager:
129-
return TarPathManager(self.loader, os.path.join(self.name, name))
128+
return TarPathManager(self.loader, posix_join(self.name, name))

dirdiff/osshim.py

+13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
# pylint: disable=redefined-outer-name,unused-import
2+
import os
3+
from typing import List, Tuple
4+
5+
try:
6+
from posixpath import basename as posix_basename
7+
from posixpath import join as posix_join
8+
from posixpath import normpath as posix_norm
9+
from posixpath import split as posix_split
10+
except ImportError:
11+
from os.path import basename as posix_basename
12+
from os.path import join as posix_join
13+
from os.path import normpath as posix_norm
14+
from os.path import split as posix_split
215

316
try:
417
from os import makedev

dirdiff/output_aufs.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import io
22
import logging
3-
import os
43
import stat
54

5+
from dirdiff.osshim import posix_basename, posix_join, posix_split
66
from dirdiff.output import DiffOutputForwarding, OutputBackend, StatInfo
77

88
LOGGER = logging.getLogger(__name__)
@@ -18,12 +18,12 @@ def __init__(self, backend: OutputBackend) -> None:
1818

1919
@classmethod
2020
def is_whiteout_path(cls, path: str) -> bool:
21-
return os.path.basename(path).startswith(cls.WHITEOUT_PREFIX)
21+
return posix_basename(path).startswith(cls.WHITEOUT_PREFIX)
2222

2323
def delete_marker(self, path: str) -> None:
24-
head, tail = os.path.split(path)
24+
head, tail = posix_split(path)
2525
self.backend.write_file(
26-
os.path.join(head, self.WHITEOUT_PREFIX + tail),
26+
posix_join(head, self.WHITEOUT_PREFIX + tail),
2727
StatInfo(
2828
mode=stat.S_IFREG | 0o444,
2929
uid=self.whiteout_uid,

dirdiff/output_tar.py

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
11
import logging
2-
import os
32
import stat
43
import tarfile
54
from tarfile import TarFile, TarInfo
65

76
from dirdiff.exceptions import DirDiffOutputException
8-
from dirdiff.osshim import major, minor
7+
from dirdiff.osshim import major, minor, posix_join
98
from dirdiff.output import OutputBackend, StatInfo
109

1110
LOGGER = logging.getLogger(__name__)
1211

1312

1413
class OutputBackendTarfile(OutputBackend):
14+
"""
15+
Tarfile output backend.
16+
17+
Note that all paths are expected to be '/' separated when using this module.
18+
Other separators will be considered parts of file names.
19+
"""
20+
1521
def __init__(self, tf: TarFile, *, archive_root=".") -> None:
1622
self.tf = tf
1723
self.archive_root = archive_root
1824

1925
def _get_tar_info(self, name: str, st: StatInfo) -> TarInfo:
20-
if name in (".", os.path.sep):
26+
if name in (".", "/"):
2127
arch_name = self.archive_root
22-
elif name.startswith(f".{os.path.sep}"):
23-
arch_name = os.path.join(self.archive_root, name[2:])
24-
elif name.startswith(os.path.sep):
25-
arch_name = os.path.join(self.archive_root, name[1:])
28+
elif name.startswith("./"):
29+
arch_name = posix_join(self.archive_root, name[2:])
30+
elif name.startswith("/"):
31+
arch_name = posix_join(self.archive_root, name[1:])
2632
else:
27-
arch_name = os.path.join(self.archive_root, name)
33+
arch_name = posix_join(self.archive_root, name)
2834
ti = TarInfo(arch_name)
2935
ti.mode = stat.S_IMODE(st.mode)
3036
ti.mtime = st.mtime

tests/cases/backslash/diff.tgz

146 Bytes
Binary file not shown.

tests/cases/backslash/lower.tgz

144 Bytes
Binary file not shown.

tests/cases/backslash/merged.tgz

154 Bytes
Binary file not shown.

tests/test_e2e.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
def tar_summarize(tf: tarfile.TarFile):
1010
result = {}
1111
while ti := tf.next():
12-
assert ti.name == "." or ti.name.startswith(f".{os.path.sep}")
12+
assert ti.name == "." or ti.name.startswith("./")
1313
assert ti.name not in result
1414
parent = os.path.dirname(ti.name)
1515

@@ -65,4 +65,10 @@ def run_test(name: str) -> None:
6565

6666

6767
def test_generic():
68+
"""Test several different misc things"""
69+
run_test("generic")
70+
71+
72+
def test_backslash():
73+
"""Test that files with backslashes work okay for tar archives."""
6874
run_test("generic")

tests/test_io_tar.py

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import io
2-
import os
32
import stat
43
import tarfile
54

65
import pytest
76

87
from dirdiff.filelib import StatInfo
98
from dirdiff.filelib_tar import TarDirectoryManager, TarFileLoader
10-
from dirdiff.osshim import major, makedev, minor
9+
from dirdiff.osshim import major, makedev, minor, posix_join
1110
from dirdiff.output_tar import OutputBackendTarfile
1211

1312
DEFAULT_UID = 1234
@@ -38,14 +37,14 @@ def test_file_write_dir():
3837
data.seek(0)
3938
with tarfile.open(fileobj=data, mode="r") as tf:
4039
assert len(tf.getmembers()) == 1
41-
ti = tf.getmember(os.path.join(backend.archive_root, file_name))
40+
ti = tf.getmember(posix_join(backend.archive_root, file_name))
4241
assert st.mode == mode
4342
assert ti.isdir()
4443
assert ti.uid == DEFAULT_UID
4544
assert ti.gid == DEFAULT_GID
4645

4746
loader = TarFileLoader(tf)
48-
with TarDirectoryManager(loader, os.path.sep) as dm:
47+
with TarDirectoryManager(loader, "/") as dm:
4948
assert not dm.exists_in_archive()
5049
assert [(entry.name, entry.is_dir()) for entry in dm] == [(file_name, True)]
5150
with dm.child_dir(file_name) as sdm:
@@ -67,7 +66,7 @@ def test_file_write_reg():
6766
data.seek(0)
6867
with tarfile.open(fileobj=data, mode="r") as tf:
6968
assert len(tf.getmembers()) == 1
70-
ti = tf.getmember(os.path.join(backend.archive_root, file_name))
69+
ti = tf.getmember(posix_join(backend.archive_root, file_name))
7170
assert st.mode == mode
7271
assert ti.isreg()
7372
assert ti.uid == DEFAULT_UID
@@ -76,7 +75,7 @@ def test_file_write_reg():
7675
assert fdata.read() == file_data
7776

7877
loader = TarFileLoader(tf)
79-
with TarDirectoryManager(loader, os.path.sep) as dm:
78+
with TarDirectoryManager(loader, "/") as dm:
8079
assert not dm.exists_in_archive()
8180
assert [(entry.name, entry.is_file()) for entry in dm] == [
8281
(file_name, True)
@@ -107,14 +106,14 @@ def test_file_write_link():
107106
data.seek(0)
108107
with tarfile.open(fileobj=data, mode="r") as tf:
109108
assert len(tf.getmembers()) == 2
110-
ti = tf.getmember(os.path.join(backend.archive_root, sym_file_name))
109+
ti = tf.getmember(posix_join(backend.archive_root, sym_file_name))
111110
assert ti.issym()
112111
assert ti.linkname == file_name
113112
assert ti.uid == DEFAULT_UID
114113
assert ti.gid == DEFAULT_GID
115114

116115
loader = TarFileLoader(tf)
117-
with TarDirectoryManager(loader, os.path.sep) as dm:
116+
with TarDirectoryManager(loader, "/") as dm:
118117
assert not dm.exists_in_archive()
119118
assert sorted(
120119
(entry.name, entry.is_file(follow_symlinks=False)) for entry in dm
@@ -146,15 +145,15 @@ def test_file_write_device(ftype, attest):
146145
data.seek(0)
147146
with tarfile.open(fileobj=data, mode="r") as tf:
148147
assert len(tf.getmembers()) == 1
149-
ti = tf.getmember(os.path.join(backend.archive_root, file_name))
148+
ti = tf.getmember(posix_join(backend.archive_root, file_name))
150149
assert attest(ti)
151150
assert ti.devmajor == dev_major
152151
assert ti.devminor == dev_minor
153152
assert ti.uid == DEFAULT_UID
154153
assert ti.gid == DEFAULT_GID
155154

156155
loader = TarFileLoader(tf)
157-
with TarDirectoryManager(loader, os.path.sep) as dm:
156+
with TarDirectoryManager(loader, "/") as dm:
158157
assert not dm.exists_in_archive()
159158
assert [(entry.name, entry.is_file()) for entry in dm] == [
160159
(file_name, False)

0 commit comments

Comments
 (0)