Skip to content

Commit 5543d7b

Browse files
author
Kazuki Suzuki Przyborowski
committed
Update pycatfile.py
1 parent 2877145 commit 5543d7b

1 file changed

Lines changed: 31 additions & 18 deletions

File tree

pycatfile.py

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,16 +1212,29 @@ def __exit__(self, exc_type, exc_value, traceback):
12121212

12131213

12141214
class 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'\x89LZO\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

Comments
 (0)