mirror of
https://github.com/python/cpython.git
synced 2025-09-27 10:50:04 +00:00
[3.13] gh-119588: Implement zipfile.Path.is_symlink (zipp 3.19.0). (GH-119591) (#119985)
gh-119588: Implement zipfile.Path.is_symlink (zipp 3.19.0). (GH-119591)
(cherry picked from commit 42a34ddb0b
)
Co-authored-by: Jason R. Coombs <jaraco@jaraco.com>
This commit is contained in:
parent
23ebf87eaa
commit
34a6d89788
4 changed files with 32 additions and 12 deletions
|
@ -585,6 +585,15 @@ Path objects are traversable using the ``/`` operator or ``joinpath``.
|
||||||
|
|
||||||
Return ``True`` if the current context references a file.
|
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()
|
.. method:: Path.exists()
|
||||||
|
|
||||||
Return ``True`` if the current context references a file or
|
Return ``True`` if the current context references a file or
|
||||||
|
|
|
@ -3,6 +3,7 @@ import itertools
|
||||||
import contextlib
|
import contextlib
|
||||||
import pathlib
|
import pathlib
|
||||||
import pickle
|
import pickle
|
||||||
|
import stat
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
import zipfile
|
import zipfile
|
||||||
|
@ -21,12 +22,17 @@ class jaraco:
|
||||||
Counter = Counter
|
Counter = Counter
|
||||||
|
|
||||||
|
|
||||||
|
def _make_link(info: zipfile.ZipInfo): # type: ignore[name-defined]
|
||||||
|
info.external_attr |= stat.S_IFLNK << 16
|
||||||
|
|
||||||
|
|
||||||
def build_alpharep_fixture():
|
def build_alpharep_fixture():
|
||||||
"""
|
"""
|
||||||
Create a zip file with this structure:
|
Create a zip file with this structure:
|
||||||
|
|
||||||
.
|
.
|
||||||
├── a.txt
|
├── a.txt
|
||||||
|
├── n.txt (-> a.txt)
|
||||||
├── b
|
├── b
|
||||||
│ ├── c.txt
|
│ ├── c.txt
|
||||||
│ ├── d
|
│ ├── d
|
||||||
|
@ -47,6 +53,7 @@ def build_alpharep_fixture():
|
||||||
- multiple files in a directory (b/c, b/f)
|
- multiple files in a directory (b/c, b/f)
|
||||||
- a directory containing only a directory (g/h)
|
- a directory containing only a directory (g/h)
|
||||||
- a directory with files of different extensions (j/klm)
|
- a directory with files of different extensions (j/klm)
|
||||||
|
- a symlink (n) pointing to (a)
|
||||||
|
|
||||||
"alpha" because it uses alphabet
|
"alpha" because it uses alphabet
|
||||||
"rep" because it's a representative example
|
"rep" because it's a representative example
|
||||||
|
@ -61,6 +68,9 @@ def build_alpharep_fixture():
|
||||||
zf.writestr("j/k.bin", b"content of k")
|
zf.writestr("j/k.bin", b"content of k")
|
||||||
zf.writestr("j/l.baz", b"content of l")
|
zf.writestr("j/l.baz", b"content of l")
|
||||||
zf.writestr("j/m.bar", b"content of m")
|
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"
|
zf.filename = "alpharep.zip"
|
||||||
return zf
|
return zf
|
||||||
|
|
||||||
|
@ -91,7 +101,7 @@ class TestPath(unittest.TestCase):
|
||||||
def test_iterdir_and_types(self, alpharep):
|
def test_iterdir_and_types(self, alpharep):
|
||||||
root = zipfile.Path(alpharep)
|
root = zipfile.Path(alpharep)
|
||||||
assert root.is_dir()
|
assert root.is_dir()
|
||||||
a, b, g, j = root.iterdir()
|
a, k, b, g, j = root.iterdir()
|
||||||
assert a.is_file()
|
assert a.is_file()
|
||||||
assert b.is_dir()
|
assert b.is_dir()
|
||||||
assert g.is_dir()
|
assert g.is_dir()
|
||||||
|
@ -111,7 +121,7 @@ class TestPath(unittest.TestCase):
|
||||||
@pass_alpharep
|
@pass_alpharep
|
||||||
def test_iterdir_on_file(self, alpharep):
|
def test_iterdir_on_file(self, alpharep):
|
||||||
root = zipfile.Path(alpharep)
|
root = zipfile.Path(alpharep)
|
||||||
a, b, g, j = root.iterdir()
|
a, k, b, g, j = root.iterdir()
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
a.iterdir()
|
a.iterdir()
|
||||||
|
|
||||||
|
@ -126,7 +136,7 @@ class TestPath(unittest.TestCase):
|
||||||
@pass_alpharep
|
@pass_alpharep
|
||||||
def test_open(self, alpharep):
|
def test_open(self, alpharep):
|
||||||
root = zipfile.Path(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:
|
with a.open(encoding="utf-8") as strm:
|
||||||
data = strm.read()
|
data = strm.read()
|
||||||
self.assertEqual(data, "content of a")
|
self.assertEqual(data, "content of a")
|
||||||
|
@ -230,7 +240,7 @@ class TestPath(unittest.TestCase):
|
||||||
@pass_alpharep
|
@pass_alpharep
|
||||||
def test_read(self, alpharep):
|
def test_read(self, alpharep):
|
||||||
root = zipfile.Path(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"
|
assert a.read_text(encoding="utf-8") == "content of a"
|
||||||
# Also check positional encoding arg (gh-101144).
|
# Also check positional encoding arg (gh-101144).
|
||||||
assert a.read_text("utf-8") == "content of a"
|
assert a.read_text("utf-8") == "content of a"
|
||||||
|
@ -296,7 +306,7 @@ class TestPath(unittest.TestCase):
|
||||||
reflect that change.
|
reflect that change.
|
||||||
"""
|
"""
|
||||||
root = zipfile.Path(alpharep)
|
root = zipfile.Path(alpharep)
|
||||||
a, b, g, j = root.iterdir()
|
a, k, b, g, j = root.iterdir()
|
||||||
alpharep.writestr('foo.txt', 'foo')
|
alpharep.writestr('foo.txt', 'foo')
|
||||||
alpharep.writestr('bar/baz.txt', 'baz')
|
alpharep.writestr('bar/baz.txt', 'baz')
|
||||||
assert any(child.name == 'foo.txt' for child in root.iterdir())
|
assert any(child.name == 'foo.txt' for child in root.iterdir())
|
||||||
|
@ -513,12 +523,9 @@ class TestPath(unittest.TestCase):
|
||||||
|
|
||||||
@pass_alpharep
|
@pass_alpharep
|
||||||
def test_is_symlink(self, alpharep):
|
def test_is_symlink(self, alpharep):
|
||||||
"""
|
|
||||||
See python/cpython#82102 for symlink support beyond this object.
|
|
||||||
"""
|
|
||||||
|
|
||||||
root = zipfile.Path(alpharep)
|
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
|
@pass_alpharep
|
||||||
def test_relative_to(self, alpharep):
|
def test_relative_to(self, alpharep):
|
||||||
|
|
|
@ -5,6 +5,7 @@ import itertools
|
||||||
import contextlib
|
import contextlib
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
import stat
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .glob import Translator
|
from .glob import Translator
|
||||||
|
@ -390,9 +391,11 @@ class Path:
|
||||||
|
|
||||||
def is_symlink(self):
|
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):
|
def glob(self, pattern):
|
||||||
if not pattern:
|
if not pattern:
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
``zipfile.Path.is_symlink`` now assesses if the given path is a symlink.
|
Loading…
Add table
Add a link
Reference in a new issue