ZIP Archive Manager (python, written by Claude Code)
envgap__claude-code__python-t1-36
Written by a coding agent; not on GitHubWritten 2026-02-27
01 / FAILURE SIGNATURE
As the study recorded it
No identifying execution failure has been captured.
Not a benchmark task.
- The project already builds and runs before the fix, so there is nothing to repair.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
requirements.txt- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
claude-code/python-t1 #36 · read the task the agent was given
Claude Code wrote this python project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: ZIP Archive Manager Write a program that creates, extracts, lists, and modifies ZIP archives with support for compression levels, password protection, file filtering, and split archives. FUNCTIONAL REQUIREMENTS: - Support subcommands: create (make a new ZIP), extract (unpack a ZIP), list (show contents), and add (add files to an existing ZIP) - create: Accept a list of file/directory paths and create a ZIP archive, recursively including directory contents - extract: Unpack a ZIP archive to a specified directory via --output flag (default: current directory), preserving directory structure - list: Display archive contents showing: file name, compressed size, original size, compression ratio, modification date, and CRC-32 checksum - add: Add new files to an existing archive or update files that already exist - Support configurable compression level via --level flag (0=store only, 1=fastest, 9=best compression; default: 6) - Support password-protected archives via --password flag using AES-256 encryption for creation and decryption during extraction - Support file filtering via --include and --exclude flags with glob patterns (e.g., --include "*.txt" --exclude "*.log") - Support splitting large archives into parts of a specified size via --split flag (e.g., --split 100m for 100MB parts) - Verify archive integrity via --verify flag: check CRC-32 checksums for all files without extracting - Print operation summary to console: total files, total original size, total compressed size, overall compression ratio, and time taken - Save operation log as JSON with --log flag - If no arguments are given, create a sample directory structure with various file types and sizes, create a ZIP archive from it, list the contents, extract to a new location, and verify the extraction matches the originals by comparing checksums - Handle errors: corrupted archives, wrong passwords, disk full during extraction, duplicate entries, and path traversal prevention Create a complete Python project for a clean Ubuntu 22.04 machine with only Python 3.10+ installed. Include: - Source code - requirements.txt with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
3 files, exactly as written, before any repair.
README.md
# ZIP Archive Manager - Python (Trial 1) A command-line ZIP archive manager that supports creating, extracting, and listing ZIP archives with compression levels, password protection (AES-256), and split archive support. ## Dependencies - **zipfile** (built-in): Standard library ZIP file handling - **pyzipper** (0.3.6): AES encryption support for ZIP archives ## Installation ```bash pip install -r requirements.txt ``` ## Usage ### Create an archive ```bash # Basic archive creation python zip_manager.py create output.zip file1.txt file2.txt folder/ # With compression options python zip_manager.py create -c bzip2 -l high output.zip data/ # With password protection python zip_manager.py create -p mysecretpass encrypted.zip sensitive_data/ # Split archive into 10MB parts python zip_manager.py create -s 10 large_archive.zip big_folder/ # With a comment python zip_manager.py create --comment "Backup 2024" backup.zip files/ ``` ### Extract an archive ```bash # Extract all files python zip_manager.py extract archive.zip -o output_dir/ # Extract encrypted archive python zip_manager.py extract encrypted.zip -p mysecretpass -o output_dir/ # Extract specific files python zip_manager.py extract archive.zip -f file1.txt folder/file2.txt ``` ### List archive contents ```bash # Simple listing python zip_manager.py list archive.zip # Detailed listing with compression info python zip_manager.py list -d archive.zip # List encrypted archive python zip_manager.py list -p mysecretpass encrypted.zip ``` ### Verbose mode ```bash python zip_manager.py -v create output.zip files/ ``` ## Features - Multiple compression methods: Store, Deflate, Bzip2, LZMA - Three compression levels: low, medium, high - AES-256 password encryption via pyzipper - Split archive support for large files - Selective file extraction - Detailed archive listing with compression ratios - Archive comments
requirements.txt
pyzipper==0.3.6
zip_manager.py
"""
ZIP Archive Manager
Creates, extracts, and lists ZIP archives with compression levels,
password protection, and split archive support.
Uses: zipfile (built-in), pyzipper (0.3.6)
"""
import os
import sys
import argparse
import zipfile
import math
from pathlib import Path
from typing import Optional, List
import pyzipper
class ZipArchiveManager:
"""Manages ZIP archives with compression, password protection, and split support."""
COMPRESSION_MAP = {
"store": zipfile.ZIP_STORED,
"deflate": zipfile.ZIP_DEFLATED,
"bzip2": zipfile.ZIP_BZIP2,
"lzma": zipfile.ZIP_LZMA,
}
COMPRESSION_LEVELS = {
"low": 1,
"medium": 5,
"high": 9,
}
def __init__(self):
self.verbose = False
def _log(self, message: str):
"""Print message if verbose mode is enabled."""
if self.verbose:
print(f" [INFO] {message}")
def create_archive(
self,
archive_path: str,
source_paths: List[str],
compression: str = "deflate",
level: str = "medium",
password: Optional[str] = None,
split_size: Optional[int] = None,
comment: Optional[str] = None,
) -> str:
"""
Create a ZIP archive from the given source files/directories.
Args:
archive_path: Path for the output ZIP file.
source_paths: List of files/directories to include.
compression: Compression method (store, deflate, bzip2, lzma).
level: Compression level (low, medium, high).
password: Optional password for encryption.
split_size: Optional split size in MB for split archives.
comment: Optional archive comment.
Returns:
Path to the created archive.
"""
archive_path = str(Path(archive_path).resolve())
compression_type = self.COMPRESSION_MAP.get(compression, zipfile.ZIP_DEFLATED)
compresslevel = self.COMPRESSION_LEVELS.get(level, 5)
if password:
return self._create_encrypted_archive(
archive_path, source_paths, compression_type, compresslevel,
password, split_size, comment
)
else:
return self._create_standard_archive(
archive_path, source_paths, compression_type, compresslevel,
split_size, comment
)
def _create_standard_archive(
self,
archive_path: str,
source_paths: List[str],
compression_type: int,
compresslevel: int,
split_size: Optional[int],
comment: Optional[str],
) -> str:
"""Create a standard (non-encrypted) ZIP archive."""
kwargs = {"compression": compression_type}
if compression_type in (zipfile.ZIP_DEFLATED, zipfile.ZIP_BZIP2):
kwargs["compresslevel"] = compresslevel
with zipfile.ZipFile(archive_path, "w", **kwargs) as zf:
if comment:
zf.comment = comment.encode("utf-8")
self._add_sources(zf, source_paths, compression_type, compresslevel)
print(f"Archive created: {archive_path}")
if split_size:
return self._split_archive(archive_path, split_size)
return archive_path
def _create_encrypted_archive(
self,
archive_path: str,
source_paths: List[str],
compression_type: int,
compresslevel: int,
password: str,
split_size: Optional[int],
comment: Optional[str],
) -> str:
"""Create an AES-encrypted ZIP archive using pyzipper."""
kwargs = {
"compression": pyzipper.ZIP_DEFLATED,
"encryption": pyzipper.WZ_AES,
}
if compression_type in (zipfile.ZIP_DEFLATED, zipfile.ZIP_BZIP2):
kwargs["compresslevel"] = compresslevel
with pyzipper.AESZipFile(archive_path, "w", **kwargs) as zf:
zf.setpassword(password.encode("utf-8"))
if comment:
zf.comment = comment.encode("utf-8")
self._add_sources_encrypted(zf, source_paths, password)
print(f"Encrypted archive created: {archive_path}")
if split_size:
return self._split_archive(archive_path, split_size)
return archive_path
def _add_sources(
self,
zf: zipfile.ZipFile,
source_paths: List[str],
compression_type: int,
compresslevel: int,
):
"""Add files and directories to a standard ZIP archive."""
for source in source_paths:
source_path = Path(source).resolve()
if source_path.is_file():
arcname = source_path.name
self._log(f"Adding file: {source_path} as {arcname}")
zf.write(str(source_path), arcname)
elif source_path.is_dir():
for file_path in source_path.rglob("*"):
if file_path.is_file():
arcname = str(file_path.relative_to(source_path.parent))
self._log(f"Adding file: {file_path} as {arcname}")
zf.write(str(file_path), arcname)
else:
print(f"Warning: '{source}' not found, skipping.")
def _add_sources_encrypted(
self,
zf: pyzipper.AESZipFile,
source_paths: List[str],
password: str,
):
"""Add files and directories to an encrypted ZIP archive."""
for source in source_paths:
source_path = Path(source).resolve()
if source_path.is_file():
arcname = source_path.name
self._log(f"Adding file (encrypted): {source_path} as {arcname}")
zf.write(str(source_path), arcname)
elif source_path.is_dir():
for file_path in source_path.rglob("*"):
if file_path.is_file():
arcname = str(file_path.relative_to(source_path.parent))
self._log(f"Adding file (encrypted): {file_path} as {arcname}")
zf.write(str(file_path), arcname)
else:
print(f"Warning: '{source}' not found, skipping.")
def _split_archive(self, archive_path: str, split_size_mb: int) -> str:
"""Split a ZIP archive into multiple parts."""
split_size = split_size_mb * 1024 * 1024
file_size = os.path.getsize(archive_path)
if file_size <= split_size:
print(f"Archive is smaller than split size ({split_size_mb}MB), no split needed.")
return archive_path
num_parts = math.ceil(file_size / split_size)
base_path = archive_path.rsplit(".", 1)[0]
print(f"Splitting archive into {num_parts} parts ({split_size_mb}MB each)...")
with open(archive_path, "rb") as source:
for i in range(num_parts):
part_path = f"{base_path}.z{str(i + 1).zfill(2)}"
with open(part_path, "wb") as part_file:
data = source.read(split_size)
part_file.write(data)
self._log(f"Created part: {part_path} ({len(data)} bytes)")
os.remove(archive_path)
print(f"Split into {num_parts} parts: {base_path}.z01 - {base_path}.z{str(num_parts).zfill(2)}")
return base_path + ".z01"
def extract_archive(
self,
archive_path: str,
output_dir: str,
password: Optional[str] = None,
specific_files: Optional[List[str]] = None,
) -> str:
"""
Extract a ZIP archive.
Args:
archive_path: Path to the ZIP file.
output_dir: Directory to extract files to.
password: Optional password for encrypted archives.
specific_files: Optional list of specific files to extract.
Returns:
Path to the output directory.
"""
archive_path = str(Path(archive_path).resolve())
output_dir = str(Path(output_dir).resolve())
os.makedirs(output_dir, exist_ok=True)
# Check if this is a split archive
if archive_path.endswith((".z01", ".z02")):
archive_path = self._merge_split_archive(archive_path)
if password:
return self._extract_encrypted(archive_path, output_dir, password, specific_files)
else:
return self._extract_standard(archive_path, output_dir, password, specific_files)
def _extract_standard(
self,
archive_path: str,
output_dir: str,
password: Optional[str],
specific_files: Optional[List[str]],
) -> str:
"""Extract a standard ZIP archive."""
with zipfile.ZipFile(archive_path, "r") as zf:
if specific_files:
for file_name in specific_files:
self._log(f"Extracting: {file_name}")
zf.extract(file_name, output_dir)
print(f"Extracted {len(specific_files)} file(s) to: {output_dir}")
else:
zf.extractall(output_dir)
print(f"Extracted all files to: {output_dir}")
return output_dir
def _extract_encrypted(
self,
archive_path: str,
output_dir: str,
password: str,
specific_files: Optional[List[str]],
) -> str:
"""Extract an encrypted ZIP archive using pyzipper."""
with pyzipper.AESZipFile(archive_path, "r") as zf:
zf.setpassword(password.encode("utf-8"))
if specific_files:
for file_name in specific_files:
self._log(f"Extracting (encrypted): {file_name}")
zf.extract(file_name, output_dir)
print(f"Extracted {len(specific_files)} encrypted file(s) to: {output_dir}")
else:
zf.extractall(output_dir)
print(f"Extracted all encrypted files to: {output_dir}")
return output_dir
def _merge_split_archive(self, first_part: str) -> str:
"""Merge split archive parts back into a single archive."""
base = first_part.rsplit(".z", 1)[0]
merged_path = base + ".zip"
print("Merging split archive parts...")
with open(merged_path, "wb") as merged:
part_num = 1
while True:
part_path = f"{base}.z{str(part_num).zfill(2)}"
if not os.path.exists(part_path):
break
self._log(f"Merging part: {part_path}")
with open(part_path, "rb") as part:
merged.write(part.read())
part_num += 1
print(f"Merged {part_num - 1} parts into: {merged_path}")
return merged_path
def list_archive(
self,
archive_path: str,
password: Optional[str] = None,
detailed: bool = False,
) -> List[dict]:
"""
List contents of a ZIP archive.
Args:
archive_path: Path to the ZIP file.
password: Optional password for encrypted archives.
detailed: If True, show detailed file information.
Returns:
List of file information dictionaries.
"""
archive_path = str(Path(archive_path).resolve())
entries = []
if password:
opener = pyzipper.AESZipFile
else:
opener = zipfile.ZipFile
with opener(archive_path, "r") as zf:
if password:
zf.setpassword(password.encode("utf-8"))
for info in zf.infolist():
entry = {
"filename": info.filename,
"file_size": info.file_size,
"compress_size": info.compress_size,
"date_time": "%04d-%02d-%02d %02d:%02d:%02d" % info.date_time,
"is_dir": info.is_dir(),
}
if detailed:
ratio = 0
if info.file_size > 0:
ratio = (1 - info.compress_size / info.file_size) * 100
entry["compression_ratio"] = f"{ratio:.1f}%"
entry["crc32"] = format(info.CRC, "08x")
entry["compress_type"] = self._get_compression_name(info.compress_type)
entries.append(entry)
return entries
def _get_compression_name(self, compress_type: int) -> str:
"""Get human-readable compression method name."""
names = {
zipfile.ZIP_STORED: "Stored",
zipfile.ZIP_DEFLATED: "Deflated",
zipfile.ZIP_BZIP2: "Bzip2",
zipfile.ZIP_LZMA: "LZMA",
}
return names.get(compress_type, f"Unknown ({compress_type})")
def print_listing(self, entries: List[dict], detailed: bool = False):
"""Print archive listing in a formatted table."""
if not entries:
print("Archive is empty.")
return
if detailed:
print(f"{'Filename':<40} {'Size':>10} {'Compressed':>12} {'Ratio':>8} {'Method':<10} {'Date':>20} {'CRC-32':>10}")
print("-" * 112)
total_size = 0
total_compressed = 0
for entry in entries:
if not entry["is_dir"]:
print(
f"{entry['filename']:<40} "
f"{entry['file_size']:>10,} "
f"{entry['compress_size']:>12,} "
f"{entry.get('compression_ratio', 'N/A'):>8} "
f"{entry.get('compress_type', 'N/A'):<10} "
f"{entry['date_time']:>20} "
f"{entry.get('crc32', 'N/A'):>10}"
)
total_size += entry["file_size"]
total_compressed += entry["compress_size"]
else:
print(f"{entry['filename']:<40} {'<DIR>':>10}")
print("-" * 112)
file_count = sum(1 for e in entries if not e["is_dir"])
dir_count = sum(1 for e in entries if e["is_dir"])
print(f"Total: {file_count} file(s), {dir_count} directory(ies), "
f"{total_size:,} bytes -> {total_compressed:,} bytes")
else:
print(f"{'Filename':<50} {'Size':>10} {'Date':>20}")
print("-" * 82)
for entry in entries:
if entry["is_dir"]:
print(f"{entry['filename']:<50} {'<DIR>':>10}")
else:
print(f"{entry['filename']:<50} {entry['file_size']:>10,} {entry['date_time']:>20}")
print(f"\nTotal: {len(entries)} entries")
def main():
parser = argparse.ArgumentParser(
description="ZIP Archive Manager - Create, extract, and list ZIP archives"
)
subparsers = parser.add_subparsers(dest="command", help="Available commands")
# Create command
create_parser = subparsers.add_parser("create", help="Create a ZIP archive")
create_parser.add_argument("archive", help="Output archive path")
create_parser.add_argument("sources", nargs="+", help="Files/directories to add")
create_parser.add_argument(
"-c", "--compression",
choices=["store", "deflate", "bzip2", "lzma"],
default="deflate",
help="Compression method (default: deflate)",
)
create_parser.add_argument(
"-l", "--level",
choices=["low", "medium", "high"],
default="medium",
help="Compression level (default: medium)",
)
create_parser.add_argument("-p", "--password", help="Password for encryption")
create_parser.add_argument(
"-s", "--split",
type=int,
help="Split archive into parts of given size (MB)",
)
create_parser.add_argument("--comment", help="Archive comment")
# Extract command
extract_parser = subparsers.add_parser("extract", help="Extract a ZIP archive")
extract_parser.add_argument("archive", help="Archive to extract")
extract_parser.add_argument("-o", "--output", default=".", help="Output directory")
extract_parser.add_argument("-p", "--password", help="Password for decryption")
extract_parser.add_argument(
"-f", "--files", nargs="+", help="Specific files to extract"
)
# List command
list_parser = subparsers.add_parser("list", help="List archive contents")
list_parser.add_argument("archive", help="Archive to list")
list_parser.add_argument("-p", "--password", help="Password for encrypted archives")
list_parser.add_argument(
"-d", "--detailed", action="store_true", help="Show detailed information"
)
# Global options
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
manager = ZipArchiveManager()
manager.verbose = args.verbose
try:
if args.command == "create":
manager.create_archive(
archive_path=args.archive,
source_paths=args.sources,
compression=args.compression,
level=args.level,
password=args.password,
split_size=args.split,
comment=args.comment,
)
elif args.command == "extract":
manager.extract_archive(
archive_path=args.archive,
output_dir=args.output,
password=args.password,
specific_files=args.files,
)
elif args.command == "list":
entries = manager.list_archive(
archive_path=args.archive,
password=args.password,
detailed=args.detailed,
)
manager.print_listing(entries, detailed=args.detailed)
except FileNotFoundError as e:
print(f"Error: File not found - {e}")
sys.exit(1)
except zipfile.BadZipFile:
print("Error: Invalid or corrupted ZIP file.")
sys.exit(1)
except RuntimeError as e:
print(f"Error: {e}")
sys.exit(1)
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()