Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ The bindings provided in this package cover the `frame format
format bindings are the recommended ones to use, as this guarantees
interoperability with other implementations and language bindings.

A future release may implement support for the LZ4 stream format. Patches and
help are welcome.
The API provided by the frame format bindings follows that of the LZMA, zlib,
gzip and bzip2 compression libraries which are provided with the Python standard
library. As such, these LZ4 bindings should provide a drop-in alternative to the
compression libraries shipped with Python. The package provides context managers
and file handlers support.

Documenation
============
Expand All @@ -43,7 +46,14 @@ generated using Sphinx. Documentation is also hosted on readthedocs.

:master: http://python-lz4.readthedocs.io/en/stable/
:development: http://python-lz4.readthedocs.io/en/latest/


Homepage
========

The `project homepage <https://www.github.com/python-lz4/python-lz4>`_ is hosted
on Github. Please report any issues you find using the `issue tracker
<https://github.com/python-lz4/python-lz4/issues>`_.

Licensing
=========
Code specific to this project is covered by the `BSD 3-Clause License
Expand Down
40 changes: 40 additions & 0 deletions docs/lz4.frame.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,43 @@ equivalent functionalities in the Python standard library.
.. autofunction:: lz4.frame.open
.. autoclass:: lz4.frame.LZ4FrameFile
:members:

Module attributes
-----------------

A number of module attributes are defined for convenience. These are detailed below.

Compression level
~~~~~~~~~~~~~~~~~

The following module attributes can be used when setting the
``compression_level`` argument.

.. autodata:: lz4.frame.COMPRESSIONLEVEL_MIN
:annotation:

.. autodata:: lz4.frame.COMPRESSIONLEVEL_MINHC
:annotation:

.. autodata:: lz4.frame.COMPRESSIONLEVEL_MAX
:annotation:

Block size
~~~~~~~~~~

The following attributes can be used when setting the ``block_size`` argument.

.. autodata:: lz4.frame.BLOCKSIZE_DEFAULT
:annotation:

.. autodata:: lz4.frame.BLOCKSIZE_MAX64KB
:annotation:

.. autodata:: lz4.frame.BLOCKSIZE_MAX256KB
:annotation:

.. autodata:: lz4.frame.BLOCKSIZE_MAX1MB
:annotation:

.. autodata:: lz4.frame.BLOCKSIZE_MAX4MB
:annotation:
1 change: 1 addition & 0 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.. py:currentmodule:: lz4.frame
.. default-role:: obj

Quickstart
==========
Expand Down
126 changes: 114 additions & 12 deletions lz4/frame/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,25 @@
import os
import builtins
import sys
from ._frame import *
from ._frame import __doc__ as _doc
from ._frame import (
compress,
decompress,
create_compression_context,
compress_begin,
compress_chunk,
compress_flush,
create_decompression_context,
reset_decompression_context,
decompress_chunk,
get_frame_info,
BLOCKSIZE_DEFAULT as _BLOCKSIZE_DEFAULT,
BLOCKSIZE_MAX64KB as _BLOCKSIZE_MAX64KB,
BLOCKSIZE_MAX256KB as _BLOCKSIZE_MAX256KB,
BLOCKSIZE_MAX1MB as _BLOCKSIZE_MAX1MB,
BLOCKSIZE_MAX4MB as _BLOCKSIZE_MAX4MB,
__doc__ as _doc
)

__doc__ = _doc

try:
Expand All @@ -13,6 +30,61 @@
from . import _compression


BLOCKSIZE_DEFAULT = _BLOCKSIZE_DEFAULT
"""Specifying ``block_size=lz4.frame.BLOCKSIZE_DEFAULT`` will instruct the LZ4
library to use the default maximum blocksize.

"""

BLOCKSIZE_MAX64KB = _BLOCKSIZE_MAX64KB
"""Specifying ``block_size=lz4.frame.BLOCKSIZE_MAX64KB`` will instruct the LZ4
library to create blocks containing a maximum of 64 kB of uncompressed data.

"""

BLOCKSIZE_MAX256KB = _BLOCKSIZE_MAX256KB
"""Specifying ``block_size=lz4.frame.BLOCKSIZE_MAX256KB`` will instruct the LZ4
library to create blocks containing a maximum of 256 kB of uncompressed data.

"""
"""Specifying ``block_size=lz4.frame.BLOCKSIZE_DEFAULT`` will instruct the LZ4
library to use the default maximum blocksize.

"""

BLOCKSIZE_MAX1MB = _BLOCKSIZE_MAX1MB
"""Specifying ``block_size=lz4.frame.BLOCKSIZE_MAX1MB`` will instruct the LZ4
library to create blocks containing a maximum of 1 MB of uncompressed data.

"""

BLOCKSIZE_MAX4MB = _BLOCKSIZE_MAX4MB
"""Specifying ``block_size=lz4.frame.BLOCKSIZE_MAX4MB`` will instruct the LZ4
library to create blocks containing a maximum of 4 MB of uncompressed data.

"""

COMPRESSIONLEVEL_MIN = 0
"""Specifying ``compression_level=lz4.frame.COMPRESSIONLEVEL_MIN`` will instruct
the LZ4 library to use a compression level of 0

"""

COMPRESSIONLEVEL_MINHC = 3
"""Specifying ``compression_level=lz4.frame.COMPRESSIONLEVEL_MINHC`` will
instruct the LZ4 library to use a compression level of 3, the minimum for the
high compression mode.

"""

COMPRESSIONLEVEL_MAX = 16
"""Specifying ``compression_level=lz4.frame.COMPRESSIONLEVEL_MAX`` will instruct
the LZ4 library to use a compression level of 16, the highest compression level
available.

"""


class LZ4FrameCompressor(object):
"""Create a LZ4 compressor object, which can be used to compress data
incrementally.
Expand Down Expand Up @@ -92,9 +164,16 @@ def __enter__(self):
return self

def __exit__(self, exception_type, exception, traceback):
# The compression context is created with an appropriate destructor, so
# no need to del it here
pass
self.block_size = None
self.block_linked = None
self.compression_level = None
self.content_checksum = None
self.block_checksum = None
self.auto_flush = None
self.return_bytearray = None
self._context = None
self._started = False


def begin(self, source_size=0):
"""Begin a compression frame. The returned data contains frame header
Expand Down Expand Up @@ -235,9 +314,12 @@ def __enter__(self):
return self

def __exit__(self, exception_type, exception, traceback):
# The decompression context is created with an appropriate destructor,
# so no need to del it here
pass
self._context = None
self.eof = None
self.needs_input = None
self.unused_data = None
self._unconsumed_data = None
self._return_bytearray = None

def reset(self):
"""Reset the decompressor state. This is useful after an error occurs, allowing
Expand Down Expand Up @@ -447,34 +529,51 @@ def close(self):

@property
def closed(self):
"""True if this file is closed.
"""Returns ``True`` if this file is closed.

Returns:
bool: ``True`` if the file is closed, ``False`` otherwise.

"""
return self._mode == _MODE_CLOSED

def fileno(self):
"""Return the file descriptor for the underlying file.

Returns:
file object: file descriptor for file.

"""
self._check_not_closed()
return self._fp.fileno()

def seekable(self):
"""Return whether the file supports seeking.

Returns:
bool: ``True`` if the file supports seeking, ``False`` otherwise.

"""
return self.readable() and self._buffer.seekable()

def readable(self):
"""Return whether the file was opened for reading.

Returns:
bool: ``True`` if the file was opened for reading, ``False``
otherwise.

"""
self._check_not_closed()
return self._mode == _MODE_READ

def writable(self):
"""Return whether the file was opened for writing.

Returns:
bool: ``True`` if the file was opened for writing, ``False``
otherwise.

"""
self._check_not_closed()
return self._mode == _MODE_WRITE
Expand All @@ -485,6 +584,9 @@ def peek(self, size=-1):
Always returns at least one byte of data, unless at EOF. The exact
number of bytes returned is unspecified.

Returns:
bytes: uncompressed data

"""
self._check_can_read()
# Relies on the undocumented fact that BufferedReader.peek() always
Expand Down Expand Up @@ -572,9 +674,9 @@ def seek(self, offset, whence=io.SEEK_SET):
The new position is specified by ``offset``, relative to the position
indicated by ``whence``. Possible values for ``whence`` are:

- io.SEEK_SET or 0: start of stream (default): offset must not be negative
- io.SEEK_CUR or 1: current stream position
- io.SEEK_END or 2: end of stream; offset must not be positive
- ``io.SEEK_SET`` or 0: start of stream (default): offset must not be negative
- ``io.SEEK_CUR`` or 1: current stream position
- ``io.SEEK_END`` or 2: end of stream; offset must not be positive

Returns the new file position.

Expand Down
19 changes: 8 additions & 11 deletions lz4/frame/_frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -1400,11 +1400,11 @@ PyDoc_STRVAR(
#define COMPRESS_KWARGS_DOCSTRING \
" block_size (int): Sepcifies the maximum blocksize to use.\n" \
" Options:\n\n" \
" - ``lz4.frame.BLOCKSIZE_DEFAULT`` or 0: the lz4 library default\n" \
" - ``lz4.frame.BLOCKSIZE_MAX64KB`` or 4: 64 kB\n" \
" - ``lz4.frame.BLOCKSIZE_MAX256KB`` or 5: 256 kB\n" \
" - ``lz4.frame.BLOCKSIZE_MAX1MB`` or 6: 1 MB\n" \
" - ``lz4.frame.BLOCKSIZE_MAX4MB`` or 7: 4 MB\n\n" \
" - `lz4.frame.BLOCKSIZE_DEFAULT` or 0: the lz4 library default\n" \
" - `lz4.frame.BLOCKSIZE_MAX64KB` or 4: 64 kB\n" \
" - `lz4.frame.BLOCKSIZE_MAX256KB` or 5: 256 kB\n" \
" - `lz4.frame.BLOCKSIZE_MAX1MB` or 6: 1 MB\n" \
" - `lz4.frame.BLOCKSIZE_MAX4MB` or 7: 4 MB\n\n" \
" If unspecified, will default to ``lz4.frame.BLOCKSIZE_DEFAULT``\n" \
" which is currently equal to ``lz4.frame.BLOCKSIZE_MAX64KB``.\n" \
" block_linked (bool): Specifies whether to use block-linked\n" \
Expand All @@ -1416,11 +1416,11 @@ PyDoc_STRVAR(
" Values below 0 will enable \"fast acceleration\", proportional\n" \
" to the value. Values above 16 will be treated as 16.\n" \
" The following module constants are provided as a convenience:\n\n" \
" - lz4.frame.COMPRESSIONLEVEL_MIN: Minimum compression (0, the\n" \
" - `lz4.frame.COMPRESSIONLEVEL_MIN`: Minimum compression (0, the\n" \
" default)\n" \
" - lz4.frame.COMPRESSIONLEVEL_MINHC: Minimum high-compression\n" \
" - `lz4.frame.COMPRESSIONLEVEL_MINHC`: Minimum high-compression\n" \
" mode (3)\n" \
" - lz4.frame.COMPRESSIONLEVEL_MAX: Maximum compression (16)\n\n" \
" - `lz4.frame.COMPRESSIONLEVEL_MAX`: Maximum compression (16)\n\n" \
" content_checksum (bool): Specifies whether to enable checksumming\n" \
" of the uncompressed content. If True, a checksum is stored at the\n" \
" end of the frame, and checked during decompression. Default is\n" \
Expand Down Expand Up @@ -1706,9 +1706,6 @@ MODULE_INIT_FUNC (_frame)
PyModule_AddIntConstant (module, "BLOCKSIZE_MAX256KB", LZ4F_max256KB);
PyModule_AddIntConstant (module, "BLOCKSIZE_MAX1MB", LZ4F_max1MB);
PyModule_AddIntConstant (module, "BLOCKSIZE_MAX4MB", LZ4F_max4MB);
PyModule_AddIntConstant (module, "COMPRESSIONLEVEL_MIN", 0);
PyModule_AddIntConstant (module, "COMPRESSIONLEVEL_MINHC", 3);
PyModule_AddIntConstant (module, "COMPRESSIONLEVEL_MAX", 16);

return module;
}
20 changes: 20 additions & 0 deletions py3c/py3c/comparison.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@
return Py_INCREF(Py_NotImplemented), Py_NotImplemented
#endif

#ifndef Py_UNREACHABLE
#define Py_UNREACHABLE() abort()
#endif

#ifndef Py_RETURN_RICHCOMPARE
#define Py_RETURN_RICHCOMPARE(val1, val2, op) \
do { \
switch (op) { \
case Py_EQ: if ((val1) == (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
case Py_NE: if ((val1) != (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
case Py_LT: if ((val1) < (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
case Py_GT: if ((val1) > (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
case Py_LE: if ((val1) <= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
case Py_GE: if ((val1) >= (val2)) Py_RETURN_TRUE; Py_RETURN_FALSE; \
default: \
Py_UNREACHABLE(); \
} \
} while (0)
#endif

#define PY3C_RICHCMP(val1, val2, op) \
((op) == Py_EQ) ? PyBool_FromLong((val1) == (val2)) : \
((op) == Py_NE) ? PyBool_FromLong((val1) != (val2)) : \
Expand Down
6 changes: 3 additions & 3 deletions py3c/py3c/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#define PyStr_InternFromString PyUnicode_InternFromString
#define PyStr_Decode PyUnicode_Decode

#define PyStr_AsUTF8String PyUnicode_AsUTF8String // returns PyBytes
#define PyStr_AsUTF8String PyUnicode_AsUTF8String /* returns PyBytes */
#define PyStr_AsUTF8 PyUnicode_AsUTF8
#define PyStr_AsUTF8AndSize PyUnicode_AsUTF8AndSize

Expand Down Expand Up @@ -74,11 +74,11 @@
#define PyStr_Decode PyString_Decode

#ifdef __GNUC__
static PyObject * PyStr_Concat(PyObject *left, PyObject *right) __attribute__ ((unused));
static PyObject *PyStr_Concat(PyObject *left, PyObject *right) __attribute__ ((unused));
#endif
static PyObject *PyStr_Concat(PyObject *left, PyObject *right) {
PyObject *str = left;
Py_INCREF(left); // reference to old left will be stolen
Py_INCREF(left); /* reference to old left will be stolen */
PyString_Concat(&str, right);
if (str) {
return str;
Expand Down
Loading