Skip to content

Commit 3a22d72

Browse files
authored
Merge pull request #118 from jaraco/feature/is-symlink
Implement is_symlink
2 parents 051250e + dc5fe8f commit 3a22d72

File tree

3 files changed

+23
-12
lines changed

3 files changed

+23
-12
lines changed

newsfragments/117.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Implement is_symlink.

tests/test_path.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import contextlib
44
import pathlib
55
import pickle
6+
import stat
67
import sys
78
import unittest
89
from .compat.overlay import zipfile
@@ -15,12 +16,17 @@
1516
from ._test_params import parameterize, Invoked
1617

1718

19+
def _make_link(info: zipfile.ZipInfo): # type: ignore[name-defined]
20+
info.external_attr |= stat.S_IFLNK << 16
21+
22+
1823
def build_alpharep_fixture():
1924
"""
2025
Create a zip file with this structure:
2126
2227
.
2328
├── a.txt
29+
├── n.txt (-> a.txt)
2430
├── b
2531
│ ├── c.txt
2632
│ ├── d
@@ -41,6 +47,7 @@ def build_alpharep_fixture():
4147
- multiple files in a directory (b/c, b/f)
4248
- a directory containing only a directory (g/h)
4349
- a directory with files of different extensions (j/klm)
50+
- a symlink (n) pointing to (a)
4451
4552
"alpha" because it uses alphabet
4653
"rep" because it's a representative example
@@ -55,6 +62,9 @@ def build_alpharep_fixture():
5562
zf.writestr("j/k.bin", b"content of k")
5663
zf.writestr("j/l.baz", b"content of l")
5764
zf.writestr("j/m.bar", b"content of m")
65+
zf.writestr("n.txt", b"a.txt")
66+
_make_link(zf.infolist()[-1])
67+
5868
zf.filename = "alpharep.zip"
5969
return zf
6070

@@ -85,7 +95,7 @@ def zipfile_ondisk(self, alpharep):
8595
def test_iterdir_and_types(self, alpharep):
8696
root = zipfile.Path(alpharep)
8797
assert root.is_dir()
88-
a, b, g, j = root.iterdir()
98+
a, k, b, g, j = root.iterdir()
8999
assert a.is_file()
90100
assert b.is_dir()
91101
assert g.is_dir()
@@ -105,7 +115,7 @@ def test_is_file_missing(self, alpharep):
105115
@pass_alpharep
106116
def test_iterdir_on_file(self, alpharep):
107117
root = zipfile.Path(alpharep)
108-
a, b, g, j = root.iterdir()
118+
a, k, b, g, j = root.iterdir()
109119
with self.assertRaises(ValueError):
110120
a.iterdir()
111121

@@ -120,7 +130,7 @@ def test_subdir_is_dir(self, alpharep):
120130
@pass_alpharep
121131
def test_open(self, alpharep):
122132
root = zipfile.Path(alpharep)
123-
a, b, g, j = root.iterdir()
133+
a, k, b, g, j = root.iterdir()
124134
with a.open(encoding="utf-8") as strm:
125135
data = strm.read()
126136
self.assertEqual(data, "content of a")
@@ -224,7 +234,7 @@ def test_open_missing_directory(self, alpharep):
224234
@pass_alpharep
225235
def test_read(self, alpharep):
226236
root = zipfile.Path(alpharep)
227-
a, b, g, j = root.iterdir()
237+
a, k, b, g, j = root.iterdir()
228238
assert a.read_text(encoding="utf-8") == "content of a"
229239
# Also check positional encoding arg (gh-101144).
230240
assert a.read_text("utf-8") == "content of a"
@@ -290,7 +300,7 @@ def test_mutability(self, alpharep):
290300
reflect that change.
291301
"""
292302
root = zipfile.Path(alpharep)
293-
a, b, g, j = root.iterdir()
303+
a, k, b, g, j = root.iterdir()
294304
alpharep.writestr('foo.txt', 'foo')
295305
alpharep.writestr('bar/baz.txt', 'baz')
296306
assert any(child.name == 'foo.txt' for child in root.iterdir())
@@ -507,12 +517,9 @@ def test_eq_hash(self, alpharep):
507517

508518
@pass_alpharep
509519
def test_is_symlink(self, alpharep):
510-
"""
511-
See python/cpython#82102 for symlink support beyond this object.
512-
"""
513-
514520
root = zipfile.Path(alpharep)
515-
assert not root.is_symlink()
521+
assert not root.joinpath('a.txt').is_symlink()
522+
assert root.joinpath('n.txt').is_symlink()
516523

517524
@pass_alpharep
518525
def test_relative_to(self, alpharep):

zipp/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import contextlib
66
import pathlib
77
import re
8+
import stat
89
import sys
910

1011
from .compat.py310 import text_encoding
@@ -391,9 +392,11 @@ def match(self, path_pattern):
391392

392393
def is_symlink(self):
393394
"""
394-
Return whether this path is a symlink. Always false (python/cpython#82102).
395+
Return whether this path is a symlink.
395396
"""
396-
return False
397+
info = self.root.getinfo(self.at)
398+
mode = info.external_attr >> 16
399+
return stat.S_ISLNK(mode)
397400

398401
def glob(self, pattern):
399402
if not pattern:

0 commit comments

Comments
 (0)