@@ -1212,16 +1212,29 @@ def __exit__(self, exc_type, exc_value, traceback):
12121212
12131213
12141214class LzopFile (object ):
1215+ """
1216+ A file-like wrapper around LZO compression/decompression using python-lzo.
1217+
1218+ - In read mode (r): Reads the entire file, checks for LZOP magic bytes,
1219+ then decompresses into memory.
1220+ - In write mode (w/a/x): Buffers all data in memory. On close, writes
1221+ the LZOP magic bytes + compressed data.
1222+ - Supports a 'level' parameter (default=9). python-lzo commonly accepts only
1223+ level=1 or level=9 for LZO1X_1 or LZO1X_999.
1224+ """
12151225 # LZOP magic bytes: b'\x89LZO\x0D\x0A\x1A\n'
1216- # If your files use a different LZO wrapper, you can adjust the magic or remove it entirely.
12171226 LZOP_MAGIC = b'\x89 LZO\x0D \x0A \x1A \n '
12181227
12191228 def __init__ (self , file_path = None , fileobj = None , mode = 'rb' ,
1220- encoding = None , errors = None , newline = None ):
1229+ level = 9 , encoding = None , errors = None , newline = None ):
12211230 """
1222- A file-like wrapper around LZO (via python-lzo).
1223- - For reading: reads entire file, verifies LZOP magic, then decompresses.
1224- - For writing: buffers all data in memory until close(), then writes the LZOP magic + compressed data.
1231+ :param file_path: Path to the file (if any)
1232+ :param fileobj: An existing file object (if any)
1233+ :param mode: File mode, e.g., 'rb', 'wb', 'rt', 'wt', etc.
1234+ :param level: Compression level (int). python-lzo typically supports 1 or 9.
1235+ :param encoding: Text encoding (for text mode)
1236+ :param errors: Error handling for encoding/decoding (e.g., 'strict')
1237+ :param newline: Placeholder to mimic built-in open() signature
12251238 """
12261239 if file_path is None and fileobj is None :
12271240 raise ValueError ("Either file_path or fileobj must be provided" )
@@ -1231,13 +1244,14 @@ def __init__(self, file_path=None, fileobj=None, mode='rb',
12311244 self .file_path = file_path
12321245 self .fileobj = fileobj
12331246 self .mode = mode
1247+ self .level = level
12341248 self .encoding = encoding
12351249 self .errors = errors
12361250 self .newline = newline
12371251 self ._decompressed_data = b''
12381252 self ._position = 0
12391253
1240- # For writing, we'll store uncompressed data in memory until close()
1254+ # For writing, store uncompressed data in memory until close()
12411255 self ._write_buffer = b''
12421256
12431257 # Track whether we're doing text mode
@@ -1271,8 +1285,8 @@ def __init__(self, file_path=None, fileobj=None, mode='rb',
12711285
12721286 def _load_file (self ):
12731287 """
1274- Reads the entire compressed file into memory. Expects an LZOP-style header
1275- (with magic bytes). Decompresses the remainder into _decompressed_data.
1288+ Read the entire compressed file into memory. Expects LZOP magic bytes
1289+ at the start. Decompress the remainder into _decompressed_data.
12761290 """
12771291 self .file .seek (0 )
12781292 compressed_data = self .file .read ()
@@ -1281,9 +1295,7 @@ def _load_file(self):
12811295 if not compressed_data .startswith (self .LZOP_MAGIC ):
12821296 raise ValueError ("Invalid LZOP file header (magic bytes missing)" )
12831297
1284- # Strip the magic from the front; the rest is actual LZO-compressed data
1285- # In a real lzop file, there may be more fields in the header, but
1286- # this simplistic approach just strips the magic bytes.
1298+ # Strip the magic; everything after is LZO-compressed data.
12871299 compressed_data = compressed_data [len (self .LZOP_MAGIC ):]
12881300
12891301 # Decompress the remainder
@@ -1300,7 +1312,7 @@ def _load_file(self):
13001312
13011313 def write (self , data ):
13021314 """
1303- Write data to our internal buffer. The actual compression + file writing
1315+ Write data into an internal buffer. The actual compression + file write
13041316 happens on close().
13051317 """
13061318 if 'r' in self .mode :
@@ -1346,14 +1358,13 @@ def seek(self, offset, whence=0):
13461358
13471359 def tell (self ):
13481360 """
1349- Returns the current read position in the decompressed buffer.
1361+ Return the current read position in the decompressed buffer.
13501362 """
13511363 return self ._position
13521364
13531365 def flush (self ):
13541366 """
1355- Flush the underlying file, if any. (No partial flush for LZO
1356- because we only compress on close.)
1367+ Flush the underlying file if supported. (No partial compression flush for LZO.)
13571368 """
13581369 if hasattr (self .file , 'flush' ):
13591370 self .file .flush ()
@@ -1385,17 +1396,19 @@ def truncate(self, size=None):
13851396 def close (self ):
13861397 """
13871398 If in write mode, compress the entire accumulated buffer using LZO
1388- and write it (with the LZOP magic header) to the file. Then close
1389- if we opened it ourselves.
1399+ (with the specified level) and write it (with the LZOP magic) to the file.
13901400 """
13911401 if any (x in self .mode for x in ('w' , 'a' , 'x' )):
13921402 # Write the LZOP magic
13931403 self .file .write (self .LZOP_MAGIC )
1404+
13941405 # Compress the entire buffer
13951406 try :
1396- compressed = lzo .compress (self ._write_buffer )
1407+ # python-lzo supports level=1 or level=9 for LZO1X
1408+ compressed = lzo .compress (self ._write_buffer , self .level )
13971409 except lzo .error as e :
13981410 raise ValueError ("LZO compression failed: {}" .format (str (e )))
1411+
13991412 self .file .write (compressed )
14001413
14011414 if self .file_path :
0 commit comments