Skip to content

Commit

Permalink
feat: first version (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco committed Jan 27, 2024
1 parent 71e343d commit 72f0ee7
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 10 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,32 @@

A wrapper to use the fastest available zlib

zlib is be a bottleneck for creating backups with `tarfile`. `zlib-fast` replaces usage of `zlib` with [`isal`](https://github.com/pycompression/python-isal) which is a drop-in faster replacement.

Unlike the base isal implementation, this module maps zlib compression
levels for `compressobj` to their nearest `isal` equivalent which allows
it to be used as a drop in replacement with `tarfile`.

## Installation

Install this via pip (or your favourite package manager):

`pip install zlib-fast`

## Usage

Enable zlib-fast support by calling `zlib_fast.enable()`.

```python

import zlib_fast

zlib_fast.enable()
zlib_fast.disable()
```

The most common use case is for use with `tarfile` which does a late import. `enable` will only be effective for future imports of `zlib`, and will not redirect existing imports. If you need to use this module for other use cases, be sure to call `zlib_fast.enable()` before any code that loads `zlib`.

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Expand Down
54 changes: 53 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ packages = [

[tool.poetry.dependencies]
python = "^3.8"
isal = ">=1.5.3"

[tool.poetry.group.dev.dependencies]
pytest = "^7.0"
Expand Down
15 changes: 15 additions & 0 deletions src/zlib_fast/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
__version__ = "0.0.0"

import sys
import zlib as zlib_original

from . import zlib_adapter as best_zlib


def enable() -> None:
"""Enable the adapter."""
sys.modules["zlib"] = best_zlib


def disable() -> None:
"""Disable the adapter restore the original zlib."""
sys.modules["zlib"] = zlib_original
3 changes: 0 additions & 3 deletions src/zlib_fast/main.py

This file was deleted.

98 changes: 98 additions & 0 deletions src/zlib_fast/zlib_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from typing import Any, Optional

from isal import isal_zlib
from isal.isal_zlib import (
DEF_BUF_SIZE,
DEF_MEM_LEVEL,
DEFLATED,
MAX_WBITS,
Z_BEST_COMPRESSION,
Z_BEST_SPEED,
Z_DEFAULT_COMPRESSION,
Z_DEFAULT_STRATEGY,
Z_FILTERED,
Z_FINISH,
Z_FIXED,
Z_FULL_FLUSH,
Z_HUFFMAN_ONLY,
Z_NO_FLUSH,
Z_RLE,
Z_SYNC_FLUSH,
Compress,
Decompress,
adler32,
compress,
crc32,
crc32_combine,
decompress,
decompressobj,
error,
)


def compressobj(
level: int = isal_zlib.Z_DEFAULT_COMPRESSION,
method: int = isal_zlib.DEFLATED,
wbits: int = isal_zlib.MAX_WBITS,
memLevel: int = isal_zlib.DEF_MEM_LEVEL,
strategy: int = isal_zlib.Z_DEFAULT_STRATEGY,
zdict: Optional[Any] = None,
) -> isal_zlib.Compress:
"""Compressobj adapter to convert zlib level to isal compression level."""
if level < 0 or level > 9:
raise ValueError(f"Invalid compression level: {level}")

if level <= 3:
level = isal_zlib.Z_BEST_SPEED
elif level <= 6:
level = isal_zlib.Z_DEFAULT_COMPRESSION
else:
level = isal_zlib.Z_BEST_COMPRESSION

if zdict is not None:
return isal_zlib.compressobj(
level,
method,
wbits,
memLevel,
strategy,
zdict,
)

return isal_zlib.compressobj(
level,
method,
wbits,
memLevel,
strategy,
)


__all__ = (
"DEF_BUF_SIZE",
"DEF_MEM_LEVEL",
"DEFLATED",
"MAX_WBITS",
"Z_BEST_COMPRESSION",
"Z_BEST_SPEED",
"Z_DEFAULT_COMPRESSION",
"Z_DEFAULT_STRATEGY",
"Z_FILTERED",
"Z_FINISH",
"Z_FIXED",
"Z_FULL_FLUSH",
"Z_HUFFMAN_ONLY",
"Z_NO_FLUSH",
"Z_RLE",
"Z_SYNC_FLUSH",
"Compress",
"Decompress",
"adler32",
"compress",
"crc32",
"crc32_combine",
"decompress",
"decompressobj",
"error",
"compressobj",
)
19 changes: 19 additions & 0 deletions tests/test_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import zlib as zlib_original

from zlib_fast import disable, enable
from zlib_fast import zlib_adapter as best_zlib


def test_enable_disable():
"""Test enable/disable."""
import zlib

assert zlib is zlib_original
enable()
import zlib

assert zlib is best_zlib
disable()
import zlib

assert zlib is zlib_original
6 changes: 0 additions & 6 deletions tests/test_main.py

This file was deleted.

16 changes: 16 additions & 0 deletions tests/test_zlib_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest

from zlib_fast import zlib_adapter


def test_compressobj():
assert zlib_adapter.compressobj() is not None
assert zlib_adapter.compressobj(zdict=b"") is not None
with pytest.raises(ValueError, match="Invalid compression level: -1"):
zlib_adapter.compressobj(level=-1)
for level in range(10):
zlib_adapter.compressobj(level=level)


def test_decompressobj():
assert zlib_adapter.decompressobj() is not None

0 comments on commit 72f0ee7

Please sign in to comment.