Skip to content

Commit ba96fab

Browse files
jaracoclaude
andauthored
gh-100609: Render an unnamed zipfile as ":zipfile:" in zipfile.Path (#160103)
Synchronizes the fix from zipp 3.23.1. `str()` of a `zipfile.Path` whose zipfile has no filename previously raised `TypeError`. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent cf69000 commit ba96fab

4 files changed

Lines changed: 35 additions & 3 deletions

File tree

‎Lib/test/test_zipfile/_path/test_path.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,21 @@ class Subclass(zipfile.Path):
347347
root = Subclass(alpharep)
348348
assert isinstance(root / 'b', Subclass)
349349

350+
def test_at(self):
351+
zip_file = self.enterContext(zipfile.ZipFile(io.BytesIO(), 'w'))
352+
root = zipfile.Path(zip_file)
353+
assert root.at == ''
354+
assert (root / 'foo' / 'bar').at == 'foo/bar'
355+
356+
def test_str(self):
357+
"""
358+
An unnamed zip file is indicated by ":zipfile:"
359+
"""
360+
zip_file = self.enterContext(zipfile.ZipFile(io.BytesIO(), 'w'))
361+
root = zipfile.Path(zip_file)
362+
assert str(root) == ':zipfile:'
363+
assert str(root / 'foo' / 'bar') == ':zipfile:/foo/bar'
364+
350365
@pass_alpharep
351366
def test_filename(self, alpharep):
352367
root = zipfile.Path(alpharep)

‎Lib/zipfile/_path/__init__.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import sys
1818
import zipfile
1919

20-
from ._functools import save_method_args
20+
from ._functools import none_as, save_method_args
2121
from .glob import Translator
2222

2323
__all__ = ['Path']
@@ -276,7 +276,7 @@ class Path:
276276
resolve to the zipfile.
277277
278278
>>> str(path)
279-
'mem/abcde.zip/'
279+
'mem/abcde.zip'
280280
>>> path.name
281281
'abcde.zip'
282282
>>> path.filename == pathlib.Path('mem/abcde.zip')
@@ -434,7 +434,8 @@ def relative_to(self, other, *extra):
434434
return posixpath.relpath(str(self), str(other.joinpath(*extra)))
435435

436436
def __str__(self):
437-
return posixpath.join(self.root.filename, self.at)
437+
root = none_as(self.root.filename, ':zipfile:')
438+
return posixpath.join(root, self.at) if self.at else root
438439

439440
def __repr__(self):
440441
return self.__repr.format(self=self)

‎Lib/zipfile/_path/_functools.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,14 @@ def wrapper(self, /, *args, **kwargs):
1818
return method(self, *args, **kwargs)
1919

2020
return wrapper
21+
22+
23+
# from jaraco.functools 4.3
24+
def none_as(value, replacement=None):
25+
"""
26+
>>> none_as(None, 'foo')
27+
'foo'
28+
>>> none_as('bar', 'foo')
29+
'bar'
30+
"""
31+
return replacement if value is None else value
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
``str()`` of a :class:`zipfile.Path` whose underlying zipfile has no
2+
filename, such as one constructed from an :class:`io.BytesIO`, now renders
3+
that filename as ``:zipfile:`` instead of raising :exc:`TypeError`. The root
4+
of a zipfile is also no longer rendered with a trailing slash. Synchronized
5+
with :pypi:`zipp` 3.23.1.

0 commit comments

Comments
 (0)