D4Vinci/Scrapling · error · ValueError

Storage class must be wrapped with lru_cache decorator, see

Error message

Storage class must be wrapped with lru_cache decorator, see docs for info

What it means

ValueError raised when adaptive mode is enabled and a custom `storage` class is supplied that is not wrapped with functools.lru_cache. Scrapling requires `storage` to be an lru_cache-wrapped callable (it checks for `storage.__wrapped__`, which lru_cache sets) so the class can be cached/reused and later unwrapped to inspect the true type. A bare class or function fails the `hasattr(storage, '__wrapped__')` check.

Source

Thrown at scrapling/parser.py:176

            if self._is_text_node(root):
                self.__adaptive_enabled = False
                return

        self.__adaptive_enabled = bool(adaptive)

        if self.__adaptive_enabled:
            if _storage is not None:
                self._storage = _storage
            else:
                if not storage_args:
                    storage_args = {
                        "storage_file": __DEFAULT_DB_FILE__,
                        "url": url,
                    }

                if not hasattr(storage, "__wrapped__"):
                    raise ValueError("Storage class must be wrapped with lru_cache decorator, see docs for info")

                if not issubclass(storage.__wrapped__, StorageSystemMixin):  # pragma: no cover
                    raise ValueError("Storage system must be inherited from class `StorageSystemMixin`")

                self._storage = storage(**storage_args)

    def __getitem__(self, key: str) -> TextHandler:
        if self._is_text_node(self._root):
            raise TypeError("Text nodes do not have attributes")
        return self.attrib[key]

    def __contains__(self, key: str) -> bool:
        if self._is_text_node(self._root):
            return False
        return key in self.attrib

    # Node functionalities, I wanted to move to a separate Mixin class, but it had a slight impact on performance
    @staticmethod

View on GitHub (pinned to 5d213a2d47)

Solutions

  1. Wrap the storage class with functools.lru_cache before passing: from functools import lru_cache; @lru_cache class MyStorage(StorageSystemMixin): ...
  2. Or use the library's built-in storage class as shown in the docs, which is already wrapped.
  3. Double-check you are passing the class (wrapped), not an instance.

Example fix

# before
class MyStorage(StorageSystemMixin):
    ...
Selector(html, adaptive=True, storage=MyStorage)  # ValueError

# after
from functools import lru_cache
@lru_cache
class MyStorage(StorageSystemMixin):
    ...
Selector(html, adaptive=True, storage=MyStorage)
Defensive patterns

Strategy: validation

Validate before calling

from functools import lru_cache

def is_lru_wrapped(storage) -> bool:
    return callable(storage) and hasattr(storage, "__wrapped__")

assert is_lru_wrapped(storage), "decorate the storage class with @lru_cache first"
Selector(content=html, adaptive=True, storage=storage)

Type guard

def is_valid_storage_class(storage: object) -> bool:
    return callable(storage) and hasattr(storage, "__wrapped__")

Try / catch

try:
    sel = Selector(content=html, adaptive=True, storage=storage)
except ValueError as e:
    if "lru_cache" in str(e):
        storage = lru_cache(storage)
        sel = Selector(content=html, adaptive=True, storage=storage)
    else:
        raise

Prevention

When it happens

Trigger: Selector(content=html, adaptive=True, storage=SQLiteStorage) where SQLiteStorage is a plain class; defining a custom StorageSystemMixin subclass and passing it directly without decorating it with @lru_cache; passing a lambda or instance instead of the decorated class.

Common situations: Writing a custom storage backend and missing the documented decorator step; upgrading scrapling versions where the lru_cache requirement was introduced; copying example code that omitted the decorator.

Related errors


AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14). Data as JSON: /api/errors/b6d7b7832f98d77c. Report an issue: GitHub.