8000 [3.13] gh-119588: Implement zipfile.Path.is_symlink (zipp 3.19.0). (GH-119591) by miss-islington · Pull Request #119985 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

[3.13] gh-119588: Implement zipfile.Path.is_symlink (zipp 3.19.0). (GH-119591) #119985

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Doc/library/zipfile.rst
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,15 @@ Path objects are traversable using the ``/`` operator or ``joinpath``.

Return ``True`` if the current context references a file.

.. method:: Path.is_symlink()

Return ``True`` if the current context references a symbolic link.

.. versionadded:: 3.12

.. versionchanged:: 3.12.4
Prior to 3.12.4, ``is_symlink`` would unconditionally return ``False``.

.. method:: Path.exists()

Return ``True`` if the current context references a file or
Expand Down
27 changes: 17 additions & 10 deletions Lib/test/test_zipfile/_path/test_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import contextlib
import pathlib
import pickle
import stat
import sys
import unittest
import zipfile
Expand All @@ -21,12 +22,17 @@ class itertools:
Counter = Counter


def _make_link(info: zipfile.ZipInfo): # type: ignore[name-defined]
info.external_attr |= stat.S_IFLNK << 16


def build_alpharep_fixture():
"""
Create a zip file with this structure:

.
├── a.txt
├── n.txt (-> a.txt)
├── b
│ ├── c.txt
│ ├── d
Expand All @@ -47,6 +53,7 @@ def build_alpharep_fixture():
- multiple files in a directory (b/c, b/f)
- a directory containing only a directory (g/h)
- a directory with files of different extensions (j/klm)
- a symlink (n) pointing to (a)

"alpha" because it uses alphabet
"rep" because it's a representative example
Expand All @@ -61,6 +68,9 @@ def build_alpharep_fixture():
zf.writestr("j/k.bin", b"content of k")
zf.writestr("j/l.baz", b"content of l")
zf.writestr("j/m.bar", b"content of m")
zf.writestr("n.txt", b"a.txt")
_make_link(zf.infolist()[-1])

zf.filename = "alpharep.zip"
return zf

Expand Down Expand Up @@ -91,7 +101,7 @@ def zipfile_ondisk(self, alpharep):
def test_iterdir_and_types(self, alpharep):
root = zipfile.Path(alpharep)
assert root.is_dir()
a, b, g, j = root.iterdir()
a, k, b, g, j = root.iterdir()
assert a.is_file()
assert b.is_dir()
assert g.is_dir()
Expand All @@ -111,7 +121,7 @@ def test_is_file_missing(self, alpharep):
@pass_alpharep
def test_iterdir_on_file(self, alpharep):
root = zipfile.Path(alpharep)
a, b, g, j = root.iterdir()
a, k, b, g, j = root.iterdir()
with self.assertRaises(ValueError):
a.iterdir()

Expand All @@ -126,7 +136,7 @@ def test_subdir_is_dir(self, alpharep):
@pass_alpharep
def test_open(self, alpharep):
root = zipfile.Path(alpharep)
a, b, g, j = root.iterdir()
a, k, b, g, j = root.iterdir()
with a.open(encoding="utf-8") as strm:
data = strm.read()
self.assertEqual(data, "content of a")
Expand Down Expand Up @@ -230,7 +240,7 @@ def test_open_missing_directory(self, alpharep):
@pass_alpharep
def test_read(self, alpharep):
root = zipfile.Path(alpharep)
a, b, g, j = root.iterdir()
a, k, b, g, j = root.iterdir()
assert a.read_text(encoding="utf-8") == "content of a"
# Also check positional encoding arg (gh-101144).
assert a.read_text("utf-8") == "content of a"
Expand Down Expand Up @@ -296,7 +306,7 @@ def test_mutability(self, alpharep):
reflect that change.
"""
root = zipfile.Path(alpharep)
a, b, g, j = root.iterdir()
a, k, b, g, j = root.iterdir()
alpharep.writestr('foo.txt', 'foo')
alpharep.writestr('bar/baz.txt', 'baz')
assert any(child.name == 'foo.txt' for child in root.iterdir())
Expand Down Expand Up @@ -513,12 +523,9 @@ def test_eq_hash(self, alpharep):

@pass_alpharep
def test_is_symlink(self, alpharep):
"""
See python/cpython#82102 for symlink support beyond this object.
"""
8000
root = zipfile.Path(alpharep)
assert not root.is_symlink()
assert not root.joinpath('a.txt').is_symlink()
assert root.joinpath('n.txt').is_symlink()

@pass_alpharep
def test_relative_to(self, alpharep):
Expand Down
7 changes: 5 additions & 2 deletions Lib/zipfile/_path/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import contextlib
import pathlib
import re
import stat
import sys

from .glob import Translator
Expand Down Expand Up @@ -390,9 +391,11 @@ def match(self, path_pattern):

def is_symlink(self):
"""
Return whether this path is a symlink. Always false (python/cpython#82102).
Return whether this path is a symlink.
"""
return False
info = self.root.getinfo(self.at)
mode = info.external_attr >> 16
return stat.S_ISLNK(mode)

def glob(self, pattern):
if not pattern:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
``zipfile.Path.is_symlink`` now assesses if the given path is a symlink.
Loading
0