From 4c523a252ab81320c68f9e235c860cb796a94643 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Wed, 12 Aug 2026 11:07:19 +0200 Subject: [PATCH 1/2] fix(storage-clients): restore `DatasetClient._is_sequence_of_items` for released Apify SDK versions --- .../storage_clients/_base/_dataset_client.py | 16 +++++++++- .../test_base_dataset_client.py | 30 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/unit/storage_clients/test_base_dataset_client.py diff --git a/src/crawlee/storage_clients/_base/_dataset_client.py b/src/crawlee/storage_clients/_base/_dataset_client.py index e1a3c4b468..bd13144e52 100644 --- a/src/crawlee/storage_clients/_base/_dataset_client.py +++ b/src/crawlee/storage_clients/_base/_dataset_client.py @@ -1,10 +1,11 @@ from __future__ import annotations from abc import ABC, abstractmethod +from collections.abc import Sequence from typing import TYPE_CHECKING if TYPE_CHECKING: - from collections.abc import AsyncIterator, Mapping, Sequence + from collections.abc import AsyncIterator, Mapping from crawlee._types import JsonSerializable from crawlee.storage_clients.models import DatasetItemsListPage, DatasetMetadata @@ -91,3 +92,16 @@ async def iterate_items( raise NotImplementedError if False: yield {} + + @staticmethod + def _is_sequence_of_items( + data: Sequence[Mapping[str, JsonSerializable]] | Mapping[str, JsonSerializable], + ) -> bool: + """Tell whether the `push_data` payload is a sequence of items rather than a single item. + + No `push_data` implementation in this repository uses this helper - it is inlined there instead. It stays + on the base class because the Apify SDK up to 4.0.1 calls it from `ApifyDatasetClient.push_data`, so + removing it breaks every Actor that resolves such an SDK version against this package. Drop it in the + next major version, once those SDK versions are out of the supported range. + """ + return isinstance(data, Sequence) diff --git a/tests/unit/storage_clients/test_base_dataset_client.py b/tests/unit/storage_clients/test_base_dataset_client.py new file mode 100644 index 0000000000..4f16628e2a --- /dev/null +++ b/tests/unit/storage_clients/test_base_dataset_client.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import pytest + +from crawlee.storage_clients._base import DatasetClient +from crawlee.storage_clients._memory import MemoryDatasetClient + + +@pytest.mark.parametrize( + ('data', 'expected'), + [ + pytest.param([{'a': 1}, {'a': 2}], True, id='sequence of items'), + pytest.param([], True, id='empty sequence'), + pytest.param({'a': 1}, False, id='single item'), + pytest.param({}, False, id='empty single item'), + ], +) +def test_is_sequence_of_items_distinguishes_payload_shapes( + data: list[dict[str, int]] | dict[str, int], + *, + expected: bool, +) -> None: + """`_is_sequence_of_items` reports whether a `push_data` payload holds many items or just one.""" + assert DatasetClient._is_sequence_of_items(data) is expected + + +def test_is_sequence_of_items_is_inherited_by_concrete_clients() -> None: + """Concrete clients inherit `_is_sequence_of_items`, the way the Apify SDK up to 4.0.1 calls it on itself.""" + assert MemoryDatasetClient._is_sequence_of_items([{'a': 1}]) is True + assert MemoryDatasetClient._is_sequence_of_items({'a': 1}) is False From dbb267d10dfa0d83473b8da168c800b74e17be90 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Wed, 12 Aug 2026 11:21:01 +0200 Subject: [PATCH 2/2] test: drop the base dataset client shim tests --- .../test_base_dataset_client.py | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 tests/unit/storage_clients/test_base_dataset_client.py diff --git a/tests/unit/storage_clients/test_base_dataset_client.py b/tests/unit/storage_clients/test_base_dataset_client.py deleted file mode 100644 index 4f16628e2a..0000000000 --- a/tests/unit/storage_clients/test_base_dataset_client.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import annotations - -import pytest - -from crawlee.storage_clients._base import DatasetClient -from crawlee.storage_clients._memory import MemoryDatasetClient - - -@pytest.mark.parametrize( - ('data', 'expected'), - [ - pytest.param([{'a': 1}, {'a': 2}], True, id='sequence of items'), - pytest.param([], True, id='empty sequence'), - pytest.param({'a': 1}, False, id='single item'), - pytest.param({}, False, id='empty single item'), - ], -) -def test_is_sequence_of_items_distinguishes_payload_shapes( - data: list[dict[str, int]] | dict[str, int], - *, - expected: bool, -) -> None: - """`_is_sequence_of_items` reports whether a `push_data` payload holds many items or just one.""" - assert DatasetClient._is_sequence_of_items(data) is expected - - -def test_is_sequence_of_items_is_inherited_by_concrete_clients() -> None: - """Concrete clients inherit `_is_sequence_of_items`, the way the Apify SDK up to 4.0.1 calls it on itself.""" - assert MemoryDatasetClient._is_sequence_of_items([{'a': 1}]) is True - assert MemoryDatasetClient._is_sequence_of_items({'a': 1}) is False