ObjDocumentArchive is a document delegate class for generating archive file listing preview icons. It reads the contents of ZIP, TAR, and GZ archives and renders the file listing as a syntax-highlighted text image using pygments, providing a visual preview of archive contents.
Module: factory.core/ObjDocumentArchive.py
Inherits from: ObjDocumentDelegate.ObjDocumentDelegate
Test file: resource.test/pytests/factory.core/test_ObjDocumentArchive.py
This module enables developers to:
| Format | Extension | Compression | Support |
|---|---|---|---|
| ZIP | .zip |
DEFLATE | Full |
| TAR | .tar |
None | Full |
| GZ | .gz |
Gzip | Full (tar.gz) |
__init__(DB: int = 0) -> NoneInitializes the ObjDocumentArchive instance.
Parameters:
DB (int, optional): Database connection identifier (default: 0)Initialization:
_IsA to "ObjDocumentArchive"list_contents(file_path: str, max_entries: int = 30) -> strReads archive contents and returns a formatted listing string.
Parameters:
file_path (str): Path to the archive filemax_entries (int, optional): Maximum number of entries to list (default: 30)Returns:
str: Formatted string with newline-separated entriesFormat:
filename.txt (1,234 bytes)
folder/file.dat (56,789 bytes)
image.png (123,456 bytes)
Behavior:
Example:
archive = ObjDocumentArchive()
# List ZIP contents
listing = archive.list_contents("/archives/data.zip")
print(listing)
# List first 10 entries only
listing = archive.list_contents("/archives/large.zip", max_entries=10)
# List tar.gz contents
listing = archive.list_contents("/archives/backup.tar.gz")
Output Example:
README.md (1,234 bytes)
src/main.py (5,678 bytes)
src/utils.py (2,345 bytes)
data/config.json (890 bytes)
_do_generate_icon(input_path: str, output_path: str, icon_size: int) -> NoneInternal method that generates a file listing icon from archive contents.
Parameters:
input_path (str): Path to the input archive fileoutput_path (str): Path where the icon will be savedicon_size (int): Desired size of the square icon in pixelsProcess Flow:
list_contents() to get file listinghighlight() to generate image bytesDocumentTools.resize_to_square() to resize to square thumbnailImageFormatter Settings:
Example:
archive = ObjDocumentArchive()
# Generate 128x128 thumbnail
archive._do_generate_icon(
"/archives/project.zip",
"/thumbnails/project_thumb.png",
128
)
# Generate 256x256 thumbnail
archive._do_generate_icon(
"/archives/backup.tar.gz",
"/thumbnails/backup_thumb.png",
256
)
from ObjDocumentArchive import ObjDocumentArchive
# Create instance
archive = ObjDocumentArchive()
# List ZIP contents
listing = archive.list_contents("/archives/project.zip")
print(listing)
# Output:
# README.md (2,345 bytes)
# src/main.py (8,901 bytes)
# data/config.json (567 bytes)
from ObjDocumentArchive import ObjDocumentArchive
archive = ObjDocumentArchive()
# Generate 200x200 thumbnail
archive._do_generate_icon(
"/archives/backup.tar.gz",
"/thumbnails/backup.png",
200
)
from ObjDocumentArchive import ObjDocumentArchive
import os
archive = ObjDocumentArchive()
archive_dir = "/archives"
output_dir = "/thumbnails"
archive_formats = (".zip", ".tar", ".gz")
for filename in os.listdir(archive_dir):
if filename.endswith(archive_formats):
input_path = os.path.join(archive_dir, filename)
output_name = os.path.splitext(filename)[0] + "_thumb.png"
output_path = os.path.join(output_dir, output_name)
try:
archive._do_generate_icon(input_path, output_path, 128)
print(f"Generated thumbnail for {filename}")
except Exception as e:
print(f"Failed for {filename}: {e}")
from ObjDocumentArchive import ObjDocumentArchive
archive = ObjDocumentArchive()
# Get full listing
listing = archive.list_contents("/archives/data.zip", max_entries=1000)
# Count files
file_count = len(listing.strip().split('\n'))
print(f"Archive contains {file_count} files")
# Check for errors
if "Error:" in listing:
print("Archive is corrupted or unreadable")
elif "(empty archive)" in listing:
print("Archive is empty")
from ObjDocumentArchive import ObjDocumentArchive
archive = ObjDocumentArchive()
archives = [
"/data/project.zip",
"/backups/data.tar",
"/releases/v1.0.tar.gz",
]
for archive_path in archives:
listing = archive.list_contents(archive_path)
print(f"\n{archive_path}:")
print(listing)
The module has comprehensive test coverage including:
| Test Case | Description | Status |
|---|---|---|
test_isa_set |
Validates _IsA attribute initialization | ✓ |
test_zip_lists_files |
Tests ZIP file listing | ✓ |
test_zip_shows_sizes |
Verifies size display for ZIP | ✓ |
test_tar_lists_files |
Tests TAR file listing | ✓ |
test_gz_lists_files |
Tests tar.gz file listing | ✓ |
test_empty_archive |
Tests empty archive handling | ✓ |
test_max_entries_limit |
Tests entry limit enforcement | ✓ |
test_corrupt_file_logs_error |
Tests corrupt file handling | ✓ |
test_unknown_extension_returns_empty |
Tests unknown format fallback | ✓ |
test_calls_list_contents_and_highlight |
Tests icon generation pipeline | ✓ |
test_uses_monokai_style |
Verifies monokai theme usage | ✓ |
test_writes_output_file |
Tests file writing | ✓ |
Test file location: resource.test/pytests/factory.core/test_ObjDocumentArchive.py
Run tests:
pytest resource.test/pytests/factory.core/test_ObjDocumentArchive.py -v
zipfile - ZIP archive handling (Python standard library)tarfile - TAR archive handling (Python standard library)pygments - Syntax highlighting libraryObjDocumentDelegate - Base delegate classObjDocumentTools.DocumentTools - Thumbnail resizing utilitiesPygments Components:
pygments.highlight - Main highlighting functionpygments.lexers.TextLexer - Plain text lexerpygments.formatters.ImageFormatter - Image output formatterpygments.styles.get_style_by_name - Style loadingModule: zipfile.ZipFile
Information Available:
filename - Full path of file in archivefile_size - Uncompressed file size in bytescompress_size - Compressed size in bytescompress_type - Compression method usedExample:
with zipfile.ZipFile("/archives/data.zip", "r") as zf:
for info in zf.infolist():
print(f"{info.filename} ({info.file_size:,} bytes)")
Module: tarfile.TarFile
Information Available:
name - Full path of member in archivesize - File size in bytesmode - File permissionsmtime - Modification timeExample:
with tarfile.open("/archives/backup.tar", "r") as tf:
for member in tf.getmembers():
print(f"{member.name} ({member.size:,} bytes)")
Module: tarfile.TarFile with gzip mode
Mode: "r:gz" for reading gzip-compressed tar
Example:
with tarfile.open("/archives/release.tar.gz", "r:gz") as tf:
for member in tf.getmembers():
print(f"{member.name} ({member.size:,} bytes)")
from ObjDocumentArchive import ObjDocumentArchive
import os
class ArchivePreviewGenerator:
def __init__(self):
self.archive_handler = ObjDocumentArchive()
self.supported_formats = {".zip", ".tar", ".gz"}
def is_supported(self, file_path):
"""Check if file format is supported."""
ext = os.path.splitext(file_path)[1].lower()
return ext in self.supported_formats
def generate_preview(self, archive_path, output_dir):
"""Generate listing preview for archive."""
listing = self.archive_handler.list_contents(archive_path)
filename = os.path.basename(archive_path)
output = os.path.join(output_dir, f"{filename}_preview.txt")
with open(output, 'w') as f:
f.write(listing)
return output
def generate_thumbnail(self, archive_path, output_dir, size=128):
"""Generate thumbnail icon for archive."""
filename = os.path.basename(archive_path)
output = os.path.join(output_dir, f"{filename}_thumb.png")
self.archive_handler._do_generate_icon(
archive_path, output, size
)
return output
def get_file_count(self, archive_path):
"""Count files in archive."""
listing = self.archive_handler.list_contents(
archive_path, max_entries=99999
)
if "(empty archive)" in listing or "Error:" in listing:
return 0
return len(listing.strip().split('\n'))
from ObjDocumentArchive import ObjDocumentArchive
def browse_backup_archives(backup_dir):
"""List contents of all backup archives."""
archive = ObjDocumentArchive()
for filename in os.listdir(backup_dir):
if filename.endswith((".zip", ".tar.gz", ".tar")):
path = os.path.join(backup_dir, filename)
print(f"\n{'='*60}")
print(f"Archive: {filename}")
print('='*60)
listing = archive.list_contents(path, max_entries=20)
print(listing)
if len(listing.split('\n')) >= 20:
print("\n... (more files not shown)")
from ObjDocumentArchive import ObjDocumentArchive
def validate_archive(archive_path):
"""Check if archive is valid and readable."""
archive = ObjDocumentArchive()
listing = archive.list_contents(archive_path)
if "Error:" in listing:
return False, "Archive is corrupted or unreadable"
elif "(empty archive)" in listing:
return True, "Archive is empty but valid"
else:
file_count = len(listing.strip().split('\n'))
return True, f"Archive is valid with {file_count} files"
# Usage
is_valid, message = validate_archive("/archives/data.zip")
print(message)
Issue: Error: Bad magic number for central directory
Cause: ZIP file is corrupted
Solution:
# Try to repair with zip utility
zip -FF corrupted.zip --out repaired.zip
# Or create new archive
zip -r new.zip files/
Issue: Error: unexpected end of data
Cause: Incomplete tar file (download interrupted)
Solution:
# Verify tar integrity
tar -tzf archive.tar
# If gz compressed, test with:
gunzip -t archive.tar.gz
Issue: High memory usage when listing large archives
Solution:
# Reduce max_entries for large archives
archive = ObjDocumentArchive()
listing = archive.list_contents(
"/large_archive.zip",
max_entries=50 # Limit to 50 entries
)
Issue: Returns "(empty archive)" for valid archive with unsupported extension
Solution:
Rename file with correct extension:
# If it's actually a ZIP
mv archive.xyz archive.zip
# If it's a tar.gz
mv archive.xyz archive.tar.gz
Error: Warning about missing DejaVu Sans Mono font
Solution:
Ubuntu/Debian:
sudo apt-get install fonts-dejavu
macOS:
brew install --cask font-dejavu
ObjDocumentDelegate.py - Base class for document delegatesObjDocumentTools.py - Shared thumbnail utilitiesObjDocumentCode.py - Code file syntax highlightingObjDocument.py - Main document handler that delegates to format-specific handlers