Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
764fc9bf
Commit
764fc9bf
authored
Mar 25, 2015
by
Serhiy Storchaka
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue #21717: The zipfile.ZipFile.open function now supports 'x' (exclusive
creation) mode.
parent
48919976
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
49 additions
and
21 deletions
+49
-21
Doc/library/zipfile.rst
Doc/library/zipfile.rst
+16
-9
Doc/whatsnew/3.5.rst
Doc/whatsnew/3.5.rst
+3
-0
Lib/test/test_zipfile.py
Lib/test/test_zipfile.py
+13
-0
Lib/zipfile.py
Lib/zipfile.py
+14
-12
Misc/NEWS
Misc/NEWS
+3
-0
No files found.
Doc/library/zipfile.rst
View file @
764fc9bf
...
@@ -134,8 +134,11 @@ ZipFile Objects
...
@@ -134,8 +134,11 @@ ZipFile Objects
Open a ZIP file, where *file* can be either a path to a file (a string) or a
Open a ZIP file, where *file* can be either a path to a file (a string) or a
file-like object. The *mode* parameter should be ``'
r
'`` to read an existing
file-like object. The *mode* parameter should be ``'
r
'`` to read an existing
file, ``'
w
'`` to truncate and write a new file, or ``'
a
'`` to append to an
file, ``'
w
'`` to truncate and write a new file, ``'
x
'`` to exclusive create
existing file. If *mode* is ``'
a
'`` and *file* refers to an existing ZIP
and write a new file, or ``'
a
'`` to append to an existing file.
If *mode* is ``'
x
'`` and *file* refers to an existing file,
a :exc:`FileExistsError` will be raised.
If *mode* is ``'
a
'`` and *file* refers to an existing ZIP
file, then additional files are added to it. If *file* does not refer to a
file, then additional files are added to it. If *file* does not refer to a
ZIP file, then a new ZIP archive is appended to the file. This is meant for
ZIP file, then a new ZIP archive is appended to the file. This is meant for
adding a ZIP archive to another file (such as :file:`python.exe`). If
adding a ZIP archive to another file (such as :file:`python.exe`). If
...
@@ -152,7 +155,7 @@ ZipFile Objects
...
@@ -152,7 +155,7 @@ ZipFile Objects
extensions when the zipfile is larger than 2 GiB. If it is false :mod:`zipfile`
extensions when the zipfile is larger than 2 GiB. If it is false :mod:`zipfile`
will raise an exception when the ZIP file would require ZIP64 extensions.
will raise an exception when the ZIP file would require ZIP64 extensions.
If the file is created with mode ``'
a
'`` or ``'
w
'`` and then
If the file is created with mode ``'
w
'``, ``'
x
'`` or ``'
a
'`` and then
:meth:`closed <close>` without adding any files to the archive, the appropriate
:meth:`closed <close>` without adding any files to the archive, the appropriate
ZIP structures for an empty archive will be written to the file.
ZIP structures for an empty archive will be written to the file.
...
@@ -174,6 +177,7 @@ ZipFile Objects
...
@@ -174,6 +177,7 @@ ZipFile Objects
..
versionchanged
::
3.5
..
versionchanged
::
3.5
Added
support
for
writing
to
unseekable
streams
.
Added
support
for
writing
to
unseekable
streams
.
Added
support
for
the
``
'x'
``
mode
.
..
method
::
ZipFile
.
close
()
..
method
::
ZipFile
.
close
()
...
@@ -310,7 +314,8 @@ ZipFile Objects
...
@@ -310,7 +314,8 @@ ZipFile Objects
*arcname* (by default, this will be the same as *filename*, but without a drive
*arcname* (by default, this will be the same as *filename*, but without a drive
letter and with leading path separators removed). If given, *compress_type*
letter and with leading path separators removed). If given, *compress_type*
overrides the value given for the *compression* parameter to the constructor for
overrides the value given for the *compression* parameter to the constructor for
the new entry. The archive must be open with mode ``'w'`` or ``'a'`` -- calling
the new entry.
The archive must be open with mode ``'w'``, ``'x'`` or ``'a'`` -- calling
:meth:`write` on a ZipFile created with mode ``'r'`` will raise a
:meth:`write` on a ZipFile created with mode ``'r'`` will raise a
:exc:`RuntimeError`. Calling :meth:`write` on a closed ZipFile will raise a
:exc:`RuntimeError`. Calling :meth:`write` on a closed ZipFile will raise a
:exc:`RuntimeError`.
:exc:`RuntimeError`.
...
@@ -337,10 +342,11 @@ ZipFile Objects
...
@@ -337,10 +342,11 @@ ZipFile Objects
Write the string *bytes* to the archive; *zinfo_or_arcname* is either the file
Write the string *bytes* to the archive; *zinfo_or_arcname* is either the file
name it will be given in the archive, or a :class:`ZipInfo` instance. If it's
name it will be given in the archive, or a :class:`ZipInfo` instance. If it's
an instance, at least the filename, date, and time must be given. If it's a
an instance, at least the filename, date, and time must be given. If it's a
name, the date and time is set to the current date and time. The archive must be
name, the date and time is set to the current date and time.
opened with mode ``'w'`` or ``'a'`` -- calling :meth:`writestr` on a ZipFile
The archive must be opened with mode ``'w'``, ``'x'`` or ``'a'`` -- calling
created with mode ``'r'`` will raise a :exc:`RuntimeError`. Calling
:meth:`writestr` on a ZipFile created with mode ``'r'`` will raise a
:meth:`writestr` on a closed ZipFile will raise a :exc:`RuntimeError`.
:exc:`RuntimeError`. Calling :meth:`writestr` on a closed ZipFile will
raise a :exc:`RuntimeError`.
If given, *compress_type* overrides the value given for the *compression*
If given, *compress_type* overrides the value given for the *compression*
parameter to the constructor for the new entry, or in the *zinfo_or_arcname*
parameter to the constructor for the new entry, or in the *zinfo_or_arcname*
...
@@ -368,7 +374,8 @@ The following data attributes are also available:
...
@@ -368,7 +374,8 @@ The following data attributes are also available:
.. attribute:: ZipFile.comment
.. attribute:: ZipFile.comment
The comment text associated with the ZIP file. If assigning a comment to a
The comment text associated with the ZIP file. If assigning a comment to a
:class:`ZipFile` instance created with mode 'a' or 'w', this should be a
:class:`ZipFile` instance created with mode ``'w'``, ``'x'`` or ``'a'``,
this should be a
string no longer than 65535 bytes. Comments longer than this will be
string no longer than 65535 bytes. Comments longer than this will be
truncated in the written archive when :meth:`close` is called.
truncated in the written archive when :meth:`close` is called.
...
...
Doc/whatsnew/3.5.rst
View file @
764fc9bf
...
@@ -454,6 +454,9 @@ zipfile
...
@@ -454,6 +454,9 @@ zipfile
* Added support for writing ZIP files to unseekable streams.
* Added support for writing ZIP files to unseekable streams.
(Contributed by Serhiy Storchaka in :issue:`23252`.)
(Contributed by Serhiy Storchaka in :issue:`23252`.)
* The :func:`zipfile.ZipFile.open` function now supports ``'x'`` (exclusive
creation) mode. (Contributed by Serhiy Storchaka in :issue:`21717`.)
Optimizations
Optimizations
=============
=============
...
...
Lib/test/test_zipfile.py
View file @
764fc9bf
...
@@ -1104,6 +1104,19 @@ class OtherTests(unittest.TestCase):
...
@@ -1104,6 +1104,19 @@ class OtherTests(unittest.TestCase):
self
.
assertEqual
(
zf
.
filelist
[
0
].
filename
,
"foo.txt"
)
self
.
assertEqual
(
zf
.
filelist
[
0
].
filename
,
"foo.txt"
)
self
.
assertEqual
(
zf
.
filelist
[
1
].
filename
,
"
\
xf6
.txt"
)
self
.
assertEqual
(
zf
.
filelist
[
1
].
filename
,
"
\
xf6
.txt"
)
def
test_exclusive_create_zip_file
(
self
):
"""Test exclusive creating a new zipfile."""
unlink
(
TESTFN2
)
filename
=
'testfile.txt'
content
=
b'hello, world. this is some content.'
with
zipfile
.
ZipFile
(
TESTFN2
,
"x"
,
zipfile
.
ZIP_STORED
)
as
zipfp
:
zipfp
.
writestr
(
filename
,
content
)
with
self
.
assertRaises
(
FileExistsError
):
zipfile
.
ZipFile
(
TESTFN2
,
"x"
,
zipfile
.
ZIP_STORED
)
with
zipfile
.
ZipFile
(
TESTFN2
,
"r"
)
as
zipfp
:
self
.
assertEqual
(
zipfp
.
namelist
(),
[
filename
])
self
.
assertEqual
(
zipfp
.
read
(
filename
),
content
)
def
test_create_non_existent_file_for_append
(
self
):
def
test_create_non_existent_file_for_append
(
self
):
if
os
.
path
.
exists
(
TESTFN
):
if
os
.
path
.
exists
(
TESTFN
):
os
.
unlink
(
TESTFN
)
os
.
unlink
(
TESTFN
)
...
...
Lib/zipfile.py
View file @
764fc9bf
...
@@ -962,7 +962,8 @@ class ZipFile:
...
@@ -962,7 +962,8 @@ class ZipFile:
file: Either the path to the file, or a file-like object.
file: Either the path to the file, or a file-like object.
If it is a path, the file will be opened and closed by ZipFile.
If it is a path, the file will be opened and closed by ZipFile.
mode: The mode can be either read "r", write "w" or append "a".
mode: The mode can be either read 'r', write 'w', exclusive create 'x',
or append 'a'.
compression: ZIP_STORED (no compression), ZIP_DEFLATED (requires zlib),
compression: ZIP_STORED (no compression), ZIP_DEFLATED (requires zlib),
ZIP_BZIP2 (requires bz2) or ZIP_LZMA (requires lzma).
ZIP_BZIP2 (requires bz2) or ZIP_LZMA (requires lzma).
allowZip64: if True ZipFile will create files with ZIP64 extensions when
allowZip64: if True ZipFile will create files with ZIP64 extensions when
...
@@ -975,9 +976,10 @@ class ZipFile:
...
@@ -975,9 +976,10 @@ class ZipFile:
_windows_illegal_name_trans_table
=
None
_windows_illegal_name_trans_table
=
None
def
__init__
(
self
,
file
,
mode
=
"r"
,
compression
=
ZIP_STORED
,
allowZip64
=
True
):
def
__init__
(
self
,
file
,
mode
=
"r"
,
compression
=
ZIP_STORED
,
allowZip64
=
True
):
"""Open the ZIP file with mode read "r", write "w" or append "a"."""
"""Open the ZIP file with mode read 'r', write 'w', exclusive create 'x',
if
mode
not
in
(
"r"
,
"w"
,
"a"
):
or append 'a'."""
raise
RuntimeError
(
'ZipFile() requires mode "r", "w", or "a"'
)
if
mode
not
in
(
'r'
,
'w'
,
'x'
,
'a'
):
raise
RuntimeError
(
"ZipFile requires mode 'r', 'w', 'x', or 'a'"
)
_check_compression
(
compression
)
_check_compression
(
compression
)
...
@@ -996,8 +998,8 @@ class ZipFile:
...
@@ -996,8 +998,8 @@ class ZipFile:
# No, it's a filename
# No, it's a filename
self
.
_filePassed
=
0
self
.
_filePassed
=
0
self
.
filename
=
file
self
.
filename
=
file
modeDict
=
{
'r'
:
'rb'
,
'w'
:
'w+b'
,
'a'
:
'r+b'
,
modeDict
=
{
'r'
:
'rb'
,
'w'
:
'w+b'
,
'
x'
:
'x+b'
,
'
a'
:
'r+b'
,
'r+b'
:
'w+b'
,
'w+b'
:
'wb'
}
'r+b'
:
'w+b'
,
'w+b'
:
'wb'
,
'x+b'
:
'xb'
}
filemode
=
modeDict
[
mode
]
filemode
=
modeDict
[
mode
]
while
True
:
while
True
:
try
:
try
:
...
@@ -1019,7 +1021,7 @@ class ZipFile:
...
@@ -1019,7 +1021,7 @@ class ZipFile:
try
:
try
:
if
mode
==
'r'
:
if
mode
==
'r'
:
self
.
_RealGetContents
()
self
.
_RealGetContents
()
elif
mode
==
'w'
:
elif
mode
in
(
'w'
,
'x'
)
:
# set the modified flag so central directory gets written
# set the modified flag so central directory gets written
# even if no files are added to the archive
# even if no files are added to the archive
self
.
_didModify
=
True
self
.
_didModify
=
True
...
@@ -1050,7 +1052,7 @@ class ZipFile:
...
@@ -1050,7 +1052,7 @@ class ZipFile:
self
.
_didModify
=
True
self
.
_didModify
=
True
self
.
start_dir
=
self
.
fp
.
tell
()
self
.
start_dir
=
self
.
fp
.
tell
()
else
:
else
:
raise
RuntimeError
(
'Mode must be "r", "w" or "a"'
)
raise
RuntimeError
(
"Mode must be 'r', 'w', 'x', or 'a'"
)
except
:
except
:
fp
=
self
.
fp
fp
=
self
.
fp
self
.
fp
=
None
self
.
fp
=
None
...
@@ -1400,8 +1402,8 @@ class ZipFile:
...
@@ -1400,8 +1402,8 @@ class ZipFile:
if
zinfo
.
filename
in
self
.
NameToInfo
:
if
zinfo
.
filename
in
self
.
NameToInfo
:
import
warnings
import
warnings
warnings
.
warn
(
'Duplicate name: %r'
%
zinfo
.
filename
,
stacklevel
=
3
)
warnings
.
warn
(
'Duplicate name: %r'
%
zinfo
.
filename
,
stacklevel
=
3
)
if
self
.
mode
not
in
(
"w"
,
"a"
):
if
self
.
mode
not
in
(
'w'
,
'x'
,
'a'
):
raise
RuntimeError
(
'write() requires mode "w" or "a"'
)
raise
RuntimeError
(
"write() requires mode 'w', 'x', or 'a'"
)
if
not
self
.
fp
:
if
not
self
.
fp
:
raise
RuntimeError
(
raise
RuntimeError
(
"Attempt to write ZIP archive that was already closed"
)
"Attempt to write ZIP archive that was already closed"
)
...
@@ -1588,13 +1590,13 @@ class ZipFile:
...
@@ -1588,13 +1590,13 @@ class ZipFile:
self
.
close
()
self
.
close
()
def
close
(
self
):
def
close
(
self
):
"""Close the file, and for mode
"w" and "a"
write the ending
"""Close the file, and for mode
'w', 'x' and 'a'
write the ending
records."""
records."""
if
self
.
fp
is
None
:
if
self
.
fp
is
None
:
return
return
try
:
try
:
if
self
.
mode
in
(
"w"
,
"a"
)
and
self
.
_didModify
:
# write ending records
if
self
.
mode
in
(
'w'
,
'x'
,
'a'
)
and
self
.
_didModify
:
# write ending records
with
self
.
_lock
:
with
self
.
_lock
:
if
self
.
_seekable
:
if
self
.
_seekable
:
self
.
fp
.
seek
(
self
.
start_dir
)
self
.
fp
.
seek
(
self
.
start_dir
)
...
...
Misc/NEWS
View file @
764fc9bf
...
@@ -30,6 +30,9 @@ Core and Builtins
...
@@ -30,6 +30,9 @@ Core and Builtins
Library
Library
-------
-------
-
Issue
#
21717
:
The
zipfile
.
ZipFile
.
open
function
now
supports
'x'
(
exclusive
creation
)
mode
.
-
Issue
#
21802
:
The
reader
in
BufferedRWPair
now
is
closed
even
when
closing
-
Issue
#
21802
:
The
reader
in
BufferedRWPair
now
is
closed
even
when
closing
writer
failed
in
BufferedRWPair
.
close
().
writer
failed
in
BufferedRWPair
.
close
().
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment