D4Vinci/Scrapling · error · AttributeError
module {__name__!r} has no attribute {name!r}
Error message
module {__name__!r} has no attribute {name!r} What it means
Standard AttributeError raised by the module-level `__getattr__` in scrapling/fetchers/__init__.py. This module lazily imports its fetcher classes; when you access an attribute that is not in the `_LAZY_IMPORTS` mapping (e.g. a misspelled class name or a name that moved), the fallback `__getattr__` raises with the exact module and attribute names. Note this is normal Python behavior for unknown attributes, not a corruption of the package.
Source
Thrown at scrapling/fetchers/__init__.py:43
"AsyncFetcher",
"ProxyRotator",
"FetcherSession",
"DynamicFetcher",
"DynamicSession",
"AsyncDynamicSession",
"StealthyFetcher",
"StealthySession",
"AsyncStealthySession",
]
def __getattr__(name: str) -> Any:
if name in _LAZY_IMPORTS:
module_path, class_name = _LAZY_IMPORTS[name]
module = __import__(module_path, fromlist=[class_name])
return getattr(module, class_name)
else:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
def __dir__() -> list[str]:
"""Support for dir() and autocomplete."""
return sorted(list(_LAZY_IMPORTS.keys()))
View on GitHub (pinned to 5d213a2d47)
Solutions
- Run `dir(scrapling.fetchers)` — the __dir__ hook lists exactly what is exported — and use one of those names.
- Check the changelog/release notes if the name worked in a previous version; it may have been renamed.
- Import the module directly and use getattr with a guarded default if you support multiple versions.
- Verify you are not looking for a helper that lives elsewhere (e.g. scrapling.engines...) rather than in scrapling.fetchers.
Example fix
# before from scrapling.fetchers import AsyncFetcher # AttributeError # after import scrapling.fetchers as sf print(dir(sf)) # see valid names, e.g. StealthyFetcher, StealthySession, AsyncStealthySession from scrapling.fetchers import StealthyFetcher
Defensive patterns
Strategy: type-guard
Validate before calling
import scrapling.fetchers as sf
name = "StealthyFetcher"
fetcher_cls = getattr(sf, name, None) or getattr(sf, "DynamicFetcher") # explicit fallback
if fetcher_cls is None:
raise ImportError(f"scrapling.fetchers has no {name}; available: {dir(sf)}") Type guard
def fetcher_exists(name: str) -> bool:
import scrapling.fetchers as sf
return name in dir(sf) # __dir__ lists lazily exported names Try / catch
try:
from scrapling.fetchers import StealthyFetcher
except AttributeError as e:
raise ImportError(f"name moved/renamed in this scrapling version: {e}") from e Prevention
- Call dir(scrapling.fetchers) to see the real export list.
- Pin the scrapling version in requirements to avoid renames.
- Check the changelog when upgrading before keeping old class names.
When it happens
Trigger: `from scrapling.fetchers import Fetcher` when the class is actually named something else (e.g. `Fetcher` vs the real exported names like StealthyFetcher/StealthySession), `scrapling.fetchers.AsyncFetcher` for a name not in _LAZY_IMPORTS, or accessing a name that was renamed across a version bump.
Common situations: Upgrading scrapling and using an old class name that was removed/renamed; IDE autocompleting a plausible-but-wrong name; tutorials targeting a different scrapling version; `dir()` shows only lazily exported names.
Related errors
AI-assisted analysis of D4Vinci/Scrapling@5d213a2d47 (2026-08-14).
Data as JSON: /api/errors/f07c0418603182ba.
Report an issue: GitHub.