mirror of https://github.com/scrapy/scrapy.git
67 lines
2.1 KiB
Python
67 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
import contextlib
|
|
import sys
|
|
from importlib.abc import MetaPathFinder
|
|
from typing import TYPE_CHECKING
|
|
|
|
from scrapy.utils.asyncio import is_asyncio_available
|
|
from scrapy.utils.reactor import is_reactor_installed
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Sequence
|
|
from importlib.machinery import ModuleSpec
|
|
from types import ModuleType
|
|
|
|
|
|
def is_reactorless() -> bool:
|
|
"""Check if we are running in the reactorless mode, i.e. with
|
|
:setting:`TWISTED_REACTOR_ENABLED` set to ``False``.
|
|
|
|
As this checks the runtime state and not the setting itself, it can be
|
|
wrong when executed very early, before the reactor and/or the asyncio event
|
|
loop are initialized.
|
|
|
|
.. note:: As this function uses
|
|
:func:`scrapy.utils.asyncio.is_asyncio_available()`, it has the same
|
|
limitations for detecting a running asyncio event loop as that one.
|
|
|
|
.. versionadded:: 2.15.0
|
|
"""
|
|
return is_asyncio_available() and not is_reactor_installed()
|
|
|
|
|
|
class ReactorImportHook(MetaPathFinder):
|
|
"""Hook that prevents importing :mod:`twisted.internet.reactor`."""
|
|
|
|
def find_spec(
|
|
self,
|
|
fullname: str,
|
|
path: Sequence[str] | None,
|
|
target: ModuleType | None = None,
|
|
) -> ModuleSpec | None:
|
|
if fullname == "twisted.internet.reactor":
|
|
raise ImportError(
|
|
f"Import of {fullname} is forbidden when running without a Twisted reactor,"
|
|
f" as importing it installs the reactor, which can lead to unexpected behavior."
|
|
)
|
|
return None
|
|
|
|
|
|
def install_reactor_import_hook() -> ReactorImportHook:
|
|
"""Prevent importing :mod:`twisted.internet.reactor`.
|
|
|
|
The hook is returned and can later be uninstalled with
|
|
:func:`uninstall_reactor_import_hook()`.
|
|
"""
|
|
|
|
hook = ReactorImportHook()
|
|
sys.meta_path.insert(0, hook)
|
|
return hook
|
|
|
|
|
|
def uninstall_reactor_import_hook(hook: ReactorImportHook) -> None:
|
|
"""Uninstall the hook installed with :func:`install_reactor_import_hook()`."""
|
|
with contextlib.suppress(ValueError):
|
|
sys.meta_path.remove(hook)
|