diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 12d8580..6a2a8eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.9', '3.10', '3.11'] + python-version: ['3.9', '3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 15570f3..b24e4e1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,13 +25,13 @@ repos: args: - --fix=no - repo: https://github.com/asottile/add-trailing-comma - rev: v2.4.0 + rev: v3.1.0 hooks: - id: add-trailing-comma stages: - commit - repo: https://github.com/pre-commit/mirrors-autopep8 - rev: v2.0.2 + rev: v2.0.4 hooks: - id: autopep8 stages: @@ -39,7 +39,7 @@ repos: args: - --diff - repo: https://github.com/pycqa/flake8 - rev: 6.0.0 + rev: 6.1.0 hooks: - id: flake8 - repo: https://github.com/pycqa/isort @@ -63,7 +63,7 @@ repos: - --multi-line=9 - --project=generic_connection_pool - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.1.1 + rev: v1.5.1 hooks: - id: mypy stages: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6148f9c..46d70a3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,12 @@ Changelog ========= +0.6.0 (2023-10-05) +------------------ + +- python 3.12 support added. + + 0.5.0 (2023-08-17) ------------------ diff --git a/generic_connection_pool/common.py b/generic_connection_pool/common.py index 363e2dd..bcbe6df 100644 --- a/generic_connection_pool/common.py +++ b/generic_connection_pool/common.py @@ -6,7 +6,7 @@ from typing import Dict, Generic, Hashable, Optional, Tuple, TypeVar from . import exceptions -from .heap import ExtHeap +from .rankmap import RankMap logger = logging.getLogger(__package__) @@ -279,7 +279,7 @@ class BaseEventQueue(Generic[KeyType]): """ def __init__(self) -> None: - self._queue: ExtHeap[Event[KeyType]] = ExtHeap() + self._queue: RankMap[Event[KeyType]] = RankMap() def _insert(self, timestamp: float, key: KeyType) -> None: """ diff --git a/generic_connection_pool/contrib/unix.py b/generic_connection_pool/contrib/unix.py index 7a53bc3..b963925 100644 --- a/generic_connection_pool/contrib/unix.py +++ b/generic_connection_pool/contrib/unix.py @@ -27,9 +27,7 @@ class CheckSocketAlivenessMixin(Generic[EndpointT]): def check_aliveness(self, endpoint: EndpointT, conn: socket.socket, timeout: Optional[float] = None) -> bool: try: - with socket_timeout(conn, timeout): - resp = conn.recv(1, socket.MSG_PEEK | socket.MSG_DONTWAIT) - if resp == b'': + if conn.recv(1, socket.MSG_PEEK | socket.MSG_DONTWAIT) == b'': return False except BlockingIOError as exc: if exc.errno != errno.EAGAIN: diff --git a/generic_connection_pool/heap.py b/generic_connection_pool/rankmap.py similarity index 99% rename from generic_connection_pool/heap.py rename to generic_connection_pool/rankmap.py index 456a3e8..c91c8ff 100644 --- a/generic_connection_pool/heap.py +++ b/generic_connection_pool/rankmap.py @@ -13,7 +13,7 @@ class ComparableAndHashable(ComparableP, Protocol, Hashable): Item = TypeVar('Item', bound=ComparableAndHashable) -class ExtHeap(Generic[Item]): +class RankMap(Generic[Item]): """ Extended heap data structure implementation. Similar to `heapq` but supports remove and replace operations. diff --git a/pyproject.toml b/pyproject.toml index 6df25fb..2759ba4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "generic-connection-pool" -version = "0.5.0" +version = "0.6.0" description = "generic connection pool" authors = ["Dmitry Pershin "] license = "Unlicense" @@ -14,12 +14,17 @@ classifiers = [ "Intended Audience :: Developers", "Natural Language :: English", "License :: Public Domain", + "Operating System :: OS Independent", + "Topic :: Database", + "Topic :: Internet :: WWW/HTTP", "Topic :: Software Development :: Libraries", "Topic :: System :: Networking", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Typing :: Typed", ] [tool.poetry.dependencies] diff --git a/tests/test_heap.py b/tests/test_heap.py deleted file mode 100644 index c244842..0000000 --- a/tests/test_heap.py +++ /dev/null @@ -1,141 +0,0 @@ -import pytest - -from generic_connection_pool.heap import ExtHeap - - -def test_heap_top(): - heap = ExtHeap() - - heap.insert(3) - heap.insert(1) - heap.insert(2) - - assert heap.top() == 1 - heap.pop() - - assert heap.top() == 2 - heap.pop() - - assert heap.top() == 3 - heap.pop() - - assert heap.top() is None - - -def test_heap_push_pop(): - heap = ExtHeap() - - items = [0, 2, 1, 4, 3, 5, 6, 7, 8, 12, 11, 10, 9] - for item in items: - heap.insert(item) - - actual_result = [] - while heap: - actual_result.append(heap.pop()) - - expected_result = sorted(items) - assert actual_result == expected_result - assert heap.pop() is None - assert len(heap) == 0 - - -def test_heap_multiple_push_pop(): - heap = ExtHeap() - - heap.insert(0) - heap.insert(1) - heap.pop() - heap.insert(0) - - -def test_heap_replace(): - heap = ExtHeap() - - heap.insert(0) - heap.replace(0, 1) - assert len(heap) == 1 - - items = [3, 4, 6, 7, 8] - for item in items: - heap.insert(item) - - heap.replace(1, 2) - heap.replace(4, 5) - heap.replace(6, 0) - heap.replace(7, 9) - heap.replace(8, 1) - heap.replace(9, 4) - - actual_result = [] - while heap: - actual_result.append(heap.pop()) - - expected_result = [0, 1, 2, 3, 4, 5] - assert actual_result == expected_result - - -def test_heap_replace_by_copy(): - heap = ExtHeap() - - heap.insert(1) - heap.insert_or_replace(1) - heap.insert(2) - heap.insert_or_replace(2) - - actual_result = [] - while heap: - actual_result.append(heap.pop()) - - expected_result = [1, 2] - assert actual_result == expected_result - - -def test_heap_remove(): - heap = ExtHeap() - - heap.insert(1) - heap.remove(1) - - assert len(heap) == 0 - - heap.insert(1) - heap.insert(2) - heap.insert(3) - heap.insert(4) - - heap.remove(2) - assert heap.pop() == 1 - assert heap.pop() == 3 - - heap.remove(4) - assert len(heap) == 0 - - -def test_heap_duplicate_error(): - heap = ExtHeap() - - heap.insert(1) - with pytest.raises(KeyError): - heap.insert(1) - - heap.insert(2) - with pytest.raises(KeyError): - heap.replace(2, 1) - - -def test_heap_clear(): - heap = ExtHeap() - - items = [0, 1, 2] - for item in items: - heap.insert(item) - - heap.clear() - assert len(heap) == 0 - - -def test_heap_not_found_error(): - heap = ExtHeap() - - with pytest.raises(KeyError): - heap.replace(1, 2) diff --git a/tests/test_rankmap.py b/tests/test_rankmap.py new file mode 100644 index 0000000..c2cf800 --- /dev/null +++ b/tests/test_rankmap.py @@ -0,0 +1,141 @@ +import pytest + +from generic_connection_pool.rankmap import RankMap + + +def test_heap_top(): + rm = RankMap() + + rm.insert(3) + rm.insert(1) + rm.insert(2) + + assert rm.top() == 1 + rm.pop() + + assert rm.top() == 2 + rm.pop() + + assert rm.top() == 3 + rm.pop() + + assert rm.top() is None + + +def test_heap_push_pop(): + rm = RankMap() + + items = [0, 2, 1, 4, 3, 5, 6, 7, 8, 12, 11, 10, 9] + for item in items: + rm.insert(item) + + actual_result = [] + while rm: + actual_result.append(rm.pop()) + + expected_result = sorted(items) + assert actual_result == expected_result + assert rm.pop() is None + assert len(rm) == 0 + + +def test_heap_multiple_push_pop(): + rm = RankMap() + + rm.insert(0) + rm.insert(1) + rm.pop() + rm.insert(0) + + +def test_heap_replace(): + rm = RankMap() + + rm.insert(0) + rm.replace(0, 1) + assert len(rm) == 1 + + items = [3, 4, 6, 7, 8] + for item in items: + rm.insert(item) + + rm.replace(1, 2) + rm.replace(4, 5) + rm.replace(6, 0) + rm.replace(7, 9) + rm.replace(8, 1) + rm.replace(9, 4) + + actual_result = [] + while rm: + actual_result.append(rm.pop()) + + expected_result = [0, 1, 2, 3, 4, 5] + assert actual_result == expected_result + + +def test_heap_replace_by_copy(): + rm = RankMap() + + rm.insert(1) + rm.insert_or_replace(1) + rm.insert(2) + rm.insert_or_replace(2) + + actual_result = [] + while rm: + actual_result.append(rm.pop()) + + expected_result = [1, 2] + assert actual_result == expected_result + + +def test_heap_remove(): + rm = RankMap() + + rm.insert(1) + rm.remove(1) + + assert len(rm) == 0 + + rm.insert(1) + rm.insert(2) + rm.insert(3) + rm.insert(4) + + rm.remove(2) + assert rm.pop() == 1 + assert rm.pop() == 3 + + rm.remove(4) + assert len(rm) == 0 + + +def test_heap_duplicate_error(): + rm = RankMap() + + rm.insert(1) + with pytest.raises(KeyError): + rm.insert(1) + + rm.insert(2) + with pytest.raises(KeyError): + rm.replace(2, 1) + + +def test_heap_clear(): + rm = RankMap() + + items = [0, 1, 2] + for item in items: + rm.insert(item) + + rm.clear() + assert len(rm) == 0 + + +def test_heap_not_found_error(): + rm = RankMap() + + with pytest.raises(KeyError): + rm.replace(1, 2)