Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']

steps:
- uses: actions/checkout@v6
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Minor breaking changes; mismatched Bloom filters raise a `SimilarityError` inste
* Add ability to read and write as bytes
* Add abilitt to export
* Updated typing to be more consistent and correct
* Drop python 3.9 support


### Version 0.6.2
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ To install `pyprobables`, simply clone the `repository on GitHub

$ python setup.py install

`pyprobables` supports python 3.6 - 3.11+
`pyprobables` supports python 3.10 - 3.14+

For *python 2.7* support, install `release 0.3.2 <https://github.com/barrust/pyprobables/releases/tag/v0.3.2>`__

::

$ pip install pyprobables==0.3.2
$ pip install pyprobables==0.7.0


API Documentation
Expand Down
52 changes: 26 additions & 26 deletions probables/blooms/bloom.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

def _verify_not_type_mismatch(second: SimpleBloomT) -> bool:
"""verify that there is not a type mismatch"""
return isinstance(second, (BloomFilter, BloomFilterOnDisk))
return isinstance(second, BloomFilter | BloomFilterOnDisk)


class BloomFilter:
Expand Down Expand Up @@ -68,11 +68,11 @@ class BloomFilter:

def __init__(
self,
est_elements: Union[int, None] = None,
false_positive_rate: Union[float, None] = None,
filepath: Union[str, Path, None] = None,
hex_string: Union[str, None] = None,
hash_function: Union[HashFuncT, None] = None,
est_elements: int | None = None,
false_positive_rate: float | None = None,
filepath: str | Path | None = None,
hex_string: str | None = None,
hash_function: HashFuncT | None = None,
):
# set some things up
self._on_disk = False
Expand Down Expand Up @@ -110,7 +110,7 @@ def _load_init(self, filepath, hash_function, hex_string, est_elements, false_po
_FPR_STRUCT = Struct("f")
_IMPT_STRUCT = Struct("B")

def __contains__(self, key: KeyT) -> Union[int, bool]:
def __contains__(self, key: KeyT) -> int | bool:
"""setup the `in` keyword"""
return self.check(key)

Expand Down Expand Up @@ -220,7 +220,7 @@ def clear(self) -> None:
for idx in range(self._bloom_length):
self._bloom[idx] = 0

def hashes(self, key: KeyT, depth: Union[int, None] = None) -> HashResultsT:
def hashes(self, key: KeyT, depth: int | None = None) -> HashResultsT:
"""Return the hashes based on the provided key

Args:
Expand Down Expand Up @@ -284,12 +284,12 @@ def export_hex(self) -> str:
bytes_string = hexlify(bytearray(self._bloom[: self.bloom_length])) + hexlify(footer_bytes)
return str(bytes_string, "utf-8")

def export(self, file: Union[Path, str, IOBase, mmap]) -> None:
def export(self, file: Path | str | IOBase | mmap) -> None:
"""Export the Bloom Filter to disk

Args:
file (str): The file or filepath to which the Bloom Filter will be written."""
if not isinstance(file, (IOBase, mmap)):
if not isinstance(file, IOBase | mmap):
file = resolve_path(file)
with open(file, "wb") as filepointer:
self.export(filepointer)
Expand All @@ -303,7 +303,7 @@ def export(self, file: Union[Path, str, IOBase, mmap]) -> None:
)
)

def export_c_header(self, filename: Union[str, Path]) -> None:
def export_c_header(self, filename: str | Path) -> None:
"""Export the Bloom Filter to disk as a C header file.

Args:
Expand All @@ -322,7 +322,7 @@ def export_c_header(self, filename: Union[str, Path]) -> None:
print("const unsigned char bloom[] = {", *data, "};", sep="\n", file=file)

@classmethod
def frombytes(cls, b: ByteString, hash_function: Union[HashFuncT, None] = None) -> "BloomFilter":
def frombytes(cls, b: ByteString, hash_function: HashFuncT | None = None) -> "BloomFilter":
"""
Args:
b (ByteString): The bytes to load as a Bloom Filter
Expand Down Expand Up @@ -488,7 +488,7 @@ def _set_values(
fpr: float,
n_hashes: int,
n_bits: int,
hash_func: Union[HashFuncT, None],
hash_func: HashFuncT | None,
) -> None:
self._est_elements = est_els
self._fpr = fpr
Expand All @@ -501,7 +501,7 @@ def _set_values(
self._number_hashes = n_hashes
self._num_bits = n_bits

def _load_hex(self, hex_string: str, hash_function: Union[HashFuncT, None] = None) -> None:
def _load_hex(self, hex_string: str, hash_function: HashFuncT | None = None) -> None:
"""placeholder for loading from hex string"""
offset = self._FOOTER_STRUCT_BE.size * 2
est_els, els_added, fpr, n_hashes, n_bits = self._parse_footer(
Expand All @@ -513,11 +513,11 @@ def _load_hex(self, hex_string: str, hash_function: Union[HashFuncT, None] = Non

def _load(
self,
file: Union[Path, str, IOBase, mmap, ByteString],
hash_function: Union[HashFuncT, None] = None,
file: Path | str | IOBase | mmap | ByteString,
hash_function: HashFuncT | None = None,
) -> None:
"""load the Bloom Filter from file or bytes"""
if not isinstance(file, (IOBase, mmap, bytes, bytearray, memoryview)):
if not isinstance(file, IOBase | mmap | bytes | bytearray | memoryview):
file = resolve_path(file)
with MMap(file) as filepointer:
self._load(filepointer, hash_function)
Expand Down Expand Up @@ -594,15 +594,15 @@ class BloomFilterOnDisk(BloomFilter):

def __init__(
self,
filepath: Union[str, Path],
est_elements: Union[int, None] = None,
false_positive_rate: Union[float, None] = None,
hex_string: Union[str, None] = None,
hash_function: Union[HashFuncT, None] = None,
filepath: str | Path,
est_elements: int | None = None,
false_positive_rate: float | None = None,
hex_string: str | None = None,
hash_function: HashFuncT | None = None,
) -> None:
# set some things up
self._filepath = resolve_path(filepath)
self.__file_pointer: Union[BufferedRandom, None] = None
self.__file_pointer: BufferedRandom | None = None
super().__init__(est_elements, false_positive_rate, filepath, hex_string, hash_function)

def _load_init(self, filepath, hash_function, hex_string, est_elements, false_positive_rate):
Expand Down Expand Up @@ -641,7 +641,7 @@ def close(self) -> None:
self.__file_pointer.close()
self.__file_pointer = None

def export(self, file: Union[str, Path]) -> None: # type: ignore
def export(self, file: str | Path) -> None: # type: ignore
"""Export to disk if a different location

Args:
Expand All @@ -653,7 +653,7 @@ def export(self, file: Union[str, Path]) -> None: # type: ignore
copyfile(self._filepath, str(file))
# otherwise, nothing to do!

def _load(self, file: Union[str, Path], hash_function: Union[HashFuncT, None] = None): # type: ignore
def _load(self, file: str | Path, hash_function: HashFuncT | None = None): # type: ignore
"""load the Bloom Filter on disk"""
# read the file, set the optimal params
# mmap everything
Expand All @@ -675,7 +675,7 @@ def add_alt(self, hashes: HashResultsT) -> None:
self.__update()

@classmethod
def frombytes(cls, b: ByteString, hash_function: Union[HashFuncT, None] = None) -> "BloomFilterOnDisk":
def frombytes(cls, b: ByteString, hash_function: HashFuncT | None = None) -> "BloomFilterOnDisk":
"""
Raises: NotSupportedError
"""
Expand Down
13 changes: 6 additions & 7 deletions probables/blooms/countingbloom.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from collections.abc import ByteString
from pathlib import Path
from struct import Struct
from typing import Union

from probables.blooms.bloom import BloomFilter
from probables.constants import UINT32_T_MAX, UINT64_T_MAX
Expand Down Expand Up @@ -48,11 +47,11 @@ class CountingBloomFilter(BloomFilter):

def __init__(
self,
est_elements: Union[int, None] = None,
false_positive_rate: Union[float, None] = None,
filepath: Union[str, Path, None] = None,
hex_string: Union[str, None] = None,
hash_function: Union[HashFuncT, None] = None,
est_elements: int | None = None,
false_positive_rate: float | None = None,
filepath: str | Path | None = None,
hex_string: str | None = None,
hash_function: HashFuncT | None = None,
) -> None:
"""setup the basic values needed"""
self._filepath = None
Expand Down Expand Up @@ -81,7 +80,7 @@ def _load_init(self, filepath, hash_function, hex_string, est_elements, false_po
_IMPT_STRUCT = Struct("I")

@classmethod
def frombytes(cls, b: ByteString, hash_function: Union[HashFuncT, None] = None) -> "CountingBloomFilter":
def frombytes(cls, b: ByteString, hash_function: HashFuncT | None = None) -> "CountingBloomFilter":
"""
Args:
b (ByteString): the bytes to load as a Counting Bloom Filter
Expand Down
29 changes: 14 additions & 15 deletions probables/blooms/expandingbloom.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from mmap import mmap
from pathlib import Path
from struct import Struct
from typing import Union

from probables.blooms.bloom import BloomFilter
from probables.exceptions import RotatingBloomFilterError
Expand Down Expand Up @@ -46,10 +45,10 @@ class ExpandingBloomFilter:

def __init__(
self,
est_elements: Union[int, None] = None,
false_positive_rate: Union[float, None] = None,
filepath: Union[str, Path, None] = None,
hash_function: Union[HashFuncT, None] = None,
est_elements: int | None = None,
false_positive_rate: float | None = None,
filepath: str | Path | None = None,
hash_function: HashFuncT | None = None,
):
"""initialize"""
self._blooms = [] # type: ignore
Expand All @@ -74,7 +73,7 @@ def __init__(
_BLOOM_ELEMENT_SIZE = Struct("B").size

@classmethod
def frombytes(cls, b: ByteString, hash_function: Union[HashFuncT, None] = None) -> "ExpandingBloomFilter":
def frombytes(cls, b: ByteString, hash_function: HashFuncT | None = None) -> "ExpandingBloomFilter":
"""
Args:
b (ByteString): The bytes to load as a Expanding Bloom Filter
Expand Down Expand Up @@ -183,12 +182,12 @@ def __check_for_growth(self):
if self._blooms[-1].elements_added >= self.__est_elements:
self.__add_bloom_filter()

def export(self, file: Union[Path, str, IOBase, mmap]) -> None:
def export(self, file: Path | str | IOBase | mmap) -> None:
"""Export an expanding Bloom Filter, or subclass, to disk

Args:
filepath (str): The path to the file to import"""
if not isinstance(file, (IOBase, mmap)):
if not isinstance(file, IOBase | mmap):
file = resolve_path(file)
with open(file, "wb") as filepointer:
self.export(filepointer) # type:ignore
Expand All @@ -207,9 +206,9 @@ def export(self, file: Union[Path, str, IOBase, mmap]) -> None:
)
)

def __load(self, file: Union[Path, str, IOBase, mmap]):
def __load(self, file: Path | str | IOBase | mmap):
"""load a file"""
if not isinstance(file, (IOBase, mmap)):
if not isinstance(file, IOBase | mmap):
file = resolve_path(file)
with MMap(file) as filepointer:
self.__load(filepointer)
Expand Down Expand Up @@ -272,11 +271,11 @@ class RotatingBloomFilter(ExpandingBloomFilter):

def __init__(
self,
est_elements: Union[int, None] = None,
false_positive_rate: Union[float, None] = None,
est_elements: int | None = None,
false_positive_rate: float | None = None,
max_queue_size: int = 10,
filepath: Union[str, Path, None] = None,
hash_function: Union[HashFuncT, None] = None,
filepath: str | Path | None = None,
hash_function: HashFuncT | None = None,
) -> None:
"""initialize"""
super().__init__(
Expand All @@ -289,7 +288,7 @@ def __init__(

@classmethod
def frombytes( # type:ignore
cls, b: ByteString, max_queue_size: int, hash_function: Union[HashFuncT, None] = None
cls, b: ByteString, max_queue_size: int, hash_function: HashFuncT | None = None
) -> "RotatingBloomFilter":
"""
Args:
Expand Down
Loading
Loading