@@ -131,14 +131,37 @@ def create_archive(source, target=None, interpreter=None, main=None,
131131 elif not hasattr (target , 'write' ):
132132 target = pathlib .Path (target )
133133
134+ # Create the list of files to add to the archive now, in case
135+ # the target is being created in the source directory - we
136+ # don't want the target being added to itself
137+ files_to_add = sorted (source .rglob ('*' ))
138+
139+ # The target cannot be in the list of files to add. If it were, we'd
140+ # end up overwriting the source file and writing the archive into
141+ # itself, which is an error. We therefore check for that case and
142+ # provide a helpful message for the user.
143+
144+ # Note that we only do a simple path equality check. This won't
145+ # catch every case, but it will catch the common case where the
146+ # source is the CWD and the target is a file in the CWD. More
147+ # thorough checks don't provide enough value to justify the extra
148+ # cost.
149+
150+ # If target is a file-like object, it will simply fail to compare
151+ # equal to any of the entries in files_to_add, so there's no need
152+ # to add a special check for that.
153+ if target in files_to_add :
154+ raise ZipAppError (
155+ f"The target archive { target } overwrites one of the source files." )
156+
134157 with _maybe_open (target , 'wb' ) as fd :
135158 _write_file_prefix (fd , interpreter )
136159 compression = (zipfile .ZIP_DEFLATED if compressed else
137160 zipfile .ZIP_STORED )
138161 with zipfile .ZipFile (fd , 'w' , compression = compression ) as z :
139- for child in sorted ( source . rglob ( '*' )) :
162+ for child in files_to_add :
140163 arcname = child .relative_to (source )
141- if filter is None or filter (arcname ) and child . resolve () != arcname . resolve () :
164+ if filter is None or filter (arcname ):
142165 z .write (child , arcname .as_posix ())
143166 if main_py :
144167 z .writestr ('__main__.py' , main_py .encode ('utf-8' ))
0 commit comments