Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a hashlib-esque wrapper class + slight general cleanup which was necessary #50

Merged
merged 8 commits into from
Aug 9, 2024
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
8 changes: 5 additions & 3 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ jobs:
architecture: x64
- name: Checkout
uses: actions/checkout@v3
- name: Install mypy & wheel
run: pip install mypy wheel
- name: Install dev requirements
run: pip install -r requirements-dev.txt
- name: Install package
run: python setup.py bdist_wheel && pip install dist/crc32c*.whl
- name: Run mypy
run: mypy --strict test/test_crc32c.py
run: mypy --strict src test
- name: Run black
run: black --check src/ test/
27 changes: 0 additions & 27 deletions .travis.yml

This file was deleted.

4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Development

* Added new `crc32c.CRC32CHash` class
modelled after the stdlib `hashlib` hash objects.
* Drop support for Python < 3.7.

## [2.5]

* Made this package PEP 561 compliant (#49).
Expand Down
18 changes: 17 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ by older compiler versions.
Usage
-----

The only method exposed by this module is ``crc32c(data, value=0, gil_release_mode=-1)``.
The core function exposed by this module is ``crc32c(data, value=0, gil_release_mode=-1)``.
It computes the CRC32C checksum of ``data``
starting with an initial ``value`` checksum,
similarly to how the built-in ``binascii.crc32`` works.
Expand All @@ -56,6 +56,22 @@ It can be set to the following values:
* 0: Never release the GIL
* Positive: Always release the GIL

On top of the ``crc32c`` function,
a ``CRC32CHash`` class is also offered.
It is modelled after the "hash objects" of the ``hashlib`` module
of the standard library:

.. code-block:: python

crc32c_hash = crc32c.CRC32CHash()
crc32c_hash.update(b'hello')
crc32c_hash.update(b' world')
print(crc32c_hash.digest())
# b'\xc9\x94e\xaa'

For more details see
the documentation on `hash objects <https://docs.python.org/3/library/hashlib.html#hash-objects>`_.

Additionally one can consult
the following module-level values:

Expand Down
5 changes: 5 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Keep alphasorted
black
mypy
pytest
wheel
7 changes: 1 addition & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@
"License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)",
"Operating System :: OS Independent",
"Programming Language :: C",
"Programming Language :: Python :: 3.2",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
Expand All @@ -66,7 +61,7 @@
classifiers=classifiers,
packages=['crc32c'],
package_dir={'': 'src'},
python_requires=">=3.2",
python_requires=">=3.7",
include_package_data=True,
ext_modules=[crcmod_ext],
test_suite="test")
76 changes: 75 additions & 1 deletion src/crc32c/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,75 @@
from ._crc32c import big_endian, crc32, crc32c, hardware_based
from __future__ import annotations

from typing import TYPE_CHECKING

# Explicitly "import ... as" to make mypy --strict happy
from ._crc32c import big_endian as big_endian
from ._crc32c import crc32 as crc32
from ._crc32c import crc32c as crc32c
from ._crc32c import hardware_based as hardware_based

if TYPE_CHECKING:
from typing_extensions import Buffer, Self


class CRC32CHash:
"""Wrapper class for crc32c. Tries to conform to the interface of `hashlib` classes."""

@property
def digest_size(self) -> int:
"""
The size of the resulting hash in bytes.
"""
return 4

@property
def block_size(self) -> int:
"""
The internal block size of the hash algorithm in bytes.
"""
return 1

@property
def name(self) -> str:
"""
The canonical name of this hash,
"""
return "crc32c"

def __init__(self, data: Buffer = b"") -> None:
"""
Initialise the hash object with an optional bytes-like object.
"""
self._checksum = crc32c(data)

def update(self, data: Buffer) -> None:
"""
Update the hash object with the bytes-like object.
Repeated calls are equivalent to a single call with the concatenation of all the arguments:
m.update(a); m.update(b) is equivalent to m.update(a+b).
"""
self._checksum = crc32c(data, self._checksum)

def digest(self) -> bytes:
"""
Return the digest of the data passed to the update() method so far.
This is a bytes object of size digest_size which may contain bytes in the whole range from 0 to 255.
"""
return self._checksum.to_bytes(4, "big")

def hexdigest(self) -> str:
"""
Like digest() except the digest is returned as a string object of double length,
containing only hexadecimal digits.
This may be used to exchange the value safely in email or other non-binary environments.
"""
return self.digest().hex()

def copy(self) -> Self:
"""
Return a copy (“clone”) of the hash object. This can be used to efficiently compute
the digests of data sharing a common initial substring.
"""
res = type(self)()
res._checksum = self._checksum
return res
2 changes: 1 addition & 1 deletion src/crc32c/__init__.pyi → src/crc32c/_crc32c.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from collections.abc import Buffer
from typing_extensions import Buffer

big_endian: int
hardware_based: bool
Expand Down
Loading
Loading