Assorted docstring fixes (#7698)

* Smaller fixes.

* Add more code blocks in docstrings.

* Queue stuff.

* Response stuff.

* Round 2.

* Address feedback.
This commit is contained in:
Andrey Rakhmatullin 2026-06-30 18:27:32 +05:00 committed by GitHub
parent deb7e2861e
commit 00098cb596
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 151 additions and 139 deletions

View File

@ -64,7 +64,7 @@ class AddonManager:
:param settings: The :class:`~scrapy.settings.BaseSettings` object from \ :param settings: The :class:`~scrapy.settings.BaseSettings` object from \
which to read the early add-on configuration which to read the early add-on configuration
:type settings: :class:`~scrapy.settings.Settings` :type settings: :class:`~scrapy.settings.BaseSettings`
""" """
for clspath in build_component_list(settings["ADDONS"]): for clspath in build_component_list(settings["ADDONS"]):
addoncls = load_object(clspath) addoncls = load_object(clspath)

View File

@ -73,7 +73,7 @@ class ScrapyCommand(ABC):
def long_desc(self) -> str: def long_desc(self) -> str:
"""A long description of the command. Return short description when not """A long description of the command. Return short description when not
available. It cannot contain newlines since contents will be formatted available. It cannot contain newlines since contents will be formatted
by optparser which removes newlines and wraps text. by argparse which removes newlines and wraps text.
""" """
return self.short_desc() return self.short_desc()

View File

@ -47,8 +47,13 @@ class _ScrapyClientContextFactory(BrowserLikePolicyForHTTPS):
instance. instance.
The purpose of this custom class is to provide a ``creatorForNetloc()`` The purpose of this custom class is to provide a ``creatorForNetloc()``
method that returns a ``_ScrapyClientTLSOptions`` instance configured based method that returns:
on TLS settings provided to the factory.
- a ``_ScrapyClientTLSOptions26`` or ``_ScrapyClientTLSOptions`` instance
configured based on TLS settings provided to the factory (when the
certificate verification is disabled);
- a result of ``optionsForClientTLS()`` called with those TLS settings
(when the certificate verification is enabled).
""" """
def __init__( def __init__(

View File

@ -2,9 +2,9 @@
An asynchronous FTP file download handler for scrapy which somehow emulates an http response. An asynchronous FTP file download handler for scrapy which somehow emulates an http response.
FTP connection parameters are passed using the request meta field: FTP connection parameters are passed using the request meta field:
- ftp_user (required) - ftp_user (optional, falls back to FTP_USER)
- ftp_password (required) - ftp_password (optional, falls back to FTP_PASSWORD)
- ftp_passive (by default, enabled) sets FTP connection passive mode - ftp_passive (optional, falls back to FTP_PASSIVE_MODE) sets FTP connection passive mode
- ftp_local_filename - ftp_local_filename
- If not given, file data will come in the response.body, as a normal scrapy Response, - If not given, file data will come in the response.body, as a normal scrapy Response,
which will imply that the entire file will be on memory. which will imply that the entire file will be on memory.

View File

@ -104,7 +104,6 @@ class HTTP11DownloadHandler(BaseHttpDownloadHandler):
self._disconnect_timeout: int = 1 self._disconnect_timeout: int = 1
async def download_request(self, request: Request) -> Response: async def download_request(self, request: Request) -> Response:
"""Return a deferred for the HTTP download"""
if hasattr(self._crawler.spider, "download_maxsize"): # pragma: no cover if hasattr(self._crawler.spider, "download_maxsize"): # pragma: no cover
warn_on_deprecated_spider_attribute("download_maxsize", "DOWNLOAD_MAXSIZE") warn_on_deprecated_spider_attribute("download_maxsize", "DOWNLOAD_MAXSIZE")
if hasattr(self._crawler.spider, "download_warnsize"): # pragma: no cover if hasattr(self._crawler.spider, "download_warnsize"): # pragma: no cover
@ -283,7 +282,7 @@ def _tunnel_request_data(
class _TunnelingAgent(Agent): class _TunnelingAgent(Agent):
"""An agent that uses a L{TunnelingTCP4ClientEndpoint} to make HTTPS """An agent that uses a ``_TunnelingTCP4ClientEndpoint`` to make HTTPS
downloads. It may look strange that we have chosen to subclass Agent and not downloads. It may look strange that we have chosen to subclass Agent and not
ProxyAgent but consider that after the tunnel is opened the proxy is ProxyAgent but consider that after the tunnel is opened the proxy is
transparent to the client; thus the agent should behave like there is no transparent to the client; thus the agent should behave like there is no

View File

@ -114,11 +114,7 @@ class H2ConnectionPool:
d.errback(ResponseFailed(errors)) d.errback(ResponseFailed(errors))
def close_connections(self) -> None: def close_connections(self) -> None:
"""Close all the HTTP/2 connections and remove them from pool """Close all the HTTP/2 connections and remove them from pool."""
Returns:
Deferred that fires when all connections have been closed
"""
for conn in self._connections.values(): for conn in self._connections.values():
assert conn.transport is not None # typing assert conn.transport is not None # typing
conn.transport.abortConnection() conn.transport.abortConnection()

View File

@ -101,7 +101,7 @@ class H2ClientProtocol(Protocol, TimeoutMixin):
uri is used to verify that incoming client requests have correct uri is used to verify that incoming client requests have correct
base URL. base URL.
settings -- Scrapy project settings settings -- Scrapy project settings
conn_lost_deferred -- Deferred fires with the reason: Failure to notify conn_lost_deferred -- Deferred that fires with the list of underlying exceptions to notify
that connection was lost that connection was lost
tls_verbose_logging -- Whether to log TLS details tls_verbose_logging -- Whether to log TLS details
""" """
@ -375,7 +375,7 @@ class H2ClientProtocol(Protocol, TimeoutMixin):
def _handle_events(self, events: list[Event]) -> None: def _handle_events(self, events: list[Event]) -> None:
"""Private method which acts as a bridge between the events """Private method which acts as a bridge between the events
received from the HTTP/2 data and IH2EventsHandler received from the HTTP/2 data and the handlers in this class.
Arguments: Arguments:
events -- A list of events that the remote peer triggered by sending data events -- A list of events that the remote peer triggered by sending data

View File

@ -131,10 +131,10 @@ class Scheduler(BaseScheduler):
(:setting:`SCHEDULER_PRIORITY_QUEUE`) that sort requests by (:setting:`SCHEDULER_PRIORITY_QUEUE`) that sort requests by
:attr:`~scrapy.http.Request.priority`. :attr:`~scrapy.http.Request.priority`.
By default, a single, memory-based priority queue is used for all requests. By default, memory-based priority queues are used for all requests.
When using :setting:`JOBDIR`, a disk-based priority queue is also created, When using :setting:`JOBDIR`, disk-based priority queues are also created,
and only unserializable requests are stored in the memory-based priority and only unserializable requests are stored in the memory-based priority
queue. For a given priority value, requests in memory take precedence over queues. For a given priority value, requests in memory take precedence over
requests in disk. requests in disk.
Each priority queue stores requests in separate internal queues, one per Each priority queue stores requests in separate internal queues, one per
@ -209,8 +209,8 @@ class Scheduler(BaseScheduler):
------------------------- -------------------------
While pending requests are below the configured values of While pending requests are below the configured values of
:setting:`CONCURRENT_REQUESTS`, :setting:`CONCURRENT_REQUESTS_PER_DOMAIN` :setting:`CONCURRENT_REQUESTS` or
or :setting:`CONCURRENT_REQUESTS_PER_IP`, those requests are sent :setting:`CONCURRENT_REQUESTS_PER_DOMAIN`, those requests are sent
concurrently. concurrently.
As a result, the first few requests of a crawl may not follow the desired As a result, the first few requests of a crawl may not follow the desired
@ -342,7 +342,7 @@ class Scheduler(BaseScheduler):
def open(self, spider: Spider) -> Deferred[None] | None: def open(self, spider: Spider) -> Deferred[None] | None:
""" """
(1) initialize the memory queue (1) initialize the memory queue
(2) initialize the disk queue if the ``jobdir`` attribute is a valid directory (2) initialize the disk queue if the ``jobdir`` argument wasn't empty
(3) return the result of the dupefilter's ``open`` method (3) return the result of the dupefilter's ``open`` method
""" """
self.spider: Spider = spider self.spider: Spider = spider

View File

@ -441,7 +441,7 @@ class Scraper:
self, output: Any, response: Response | Failure self, output: Any, response: Response | Failure
) -> Deferred[None]: ) -> Deferred[None]:
"""Process each Request/Item (given in the output parameter) returned """Process each Request/Item (given in the output parameter) returned
from the given spider. from the spider.
Items are sent to the item pipelines, requests are scheduled. Items are sent to the item pipelines, requests are scheduled.
""" """
@ -451,7 +451,7 @@ class Scraper:
self, output: Any, response: Response | Failure self, output: Any, response: Response | Failure
) -> None: ) -> None:
"""Process each Request/Item (given in the output parameter) returned """Process each Request/Item (given in the output parameter) returned
from the given spider. from the spider.
Items are sent to the item pipelines, requests are scheduled. Items are sent to the item pipelines, requests are scheduled.
""" """

View File

@ -531,8 +531,8 @@ class AsyncCrawlerRunner(CrawlerRunnerBase):
""" """
Run a crawler with the provided arguments. Run a crawler with the provided arguments.
It will call the given Crawler's :meth:`~Crawler.crawl` method, while It will call the given Crawler's :meth:`~Crawler.crawl_async` method,
keeping track of it so it can be stopped later. while keeping track of it so it can be stopped later.
If ``crawler_or_spidercls`` isn't a :class:`~scrapy.crawler.Crawler` If ``crawler_or_spidercls`` isn't a :class:`~scrapy.crawler.Crawler`
instance, this method will try to create one using this parameter as instance, this method will try to create one using this parameter as
@ -773,7 +773,7 @@ class CrawlerProcess(CrawlerProcessBase, CrawlerRunner):
""" """
This method starts a :mod:`~twisted.internet.reactor`, adjusts its pool This method starts a :mod:`~twisted.internet.reactor`, adjusts its pool
size to :setting:`REACTOR_THREADPOOL_MAXSIZE`, and installs a DNS size to :setting:`REACTOR_THREADPOOL_MAXSIZE`, and installs a DNS
resolver based on :setting:`DNSCACHE_ENABLED`. resolver based on :setting:`TWISTED_DNS_RESOLVER`.
If ``stop_after_crawl`` is True, the reactor will be stopped after all If ``stop_after_crawl`` is True, the reactor will be stopped after all
crawlers have finished, using :meth:`join`. crawlers have finished, using :meth:`join`.
@ -875,10 +875,10 @@ class AsyncCrawlerProcess(CrawlerProcessBase, AsyncCrawlerRunner):
When using a reactor it adjusts its pool size to When using a reactor it adjusts its pool size to
:setting:`REACTOR_THREADPOOL_MAXSIZE` and installs a DNS resolver based :setting:`REACTOR_THREADPOOL_MAXSIZE` and installs a DNS resolver based
on :setting:`DNSCACHE_ENABLED`. on :setting:`TWISTED_DNS_RESOLVER`.
If ``stop_after_crawl`` is True, the reactor will be stopped after all If ``stop_after_crawl`` is True, the reactor/event loop will be stopped
crawlers have finished, using :meth:`join`. after all crawlers have finished, using :meth:`join`.
:param bool stop_after_crawl: stop or not the reactor when all :param bool stop_after_crawl: stop or not the reactor when all
crawlers have finished crawlers have finished

View File

@ -60,7 +60,7 @@ else:
class HttpCompressionMiddleware: class HttpCompressionMiddleware:
"""This middleware allows compressed (gzip, deflate) traffic to be """This middleware allows compressed (gzip, deflate etc.) traffic to be
sent/received from websites""" sent/received from websites"""
def __init__( def __init__(

View File

@ -196,10 +196,7 @@ class BaseRedirectMiddleware:
class RedirectMiddleware(BaseRedirectMiddleware): class RedirectMiddleware(BaseRedirectMiddleware):
""" """Handle redirection of requests based on response status."""
Handle redirection of requests based on response status
and meta-refresh html tag.
"""
@_warn_spider_arg @_warn_spider_arg
def process_response( def process_response(
@ -251,6 +248,8 @@ class RedirectMiddleware(BaseRedirectMiddleware):
class MetaRefreshMiddleware(BaseRedirectMiddleware): class MetaRefreshMiddleware(BaseRedirectMiddleware):
"""Handle redirection of requests based on meta-refresh html tag."""
enabled_setting = "METAREFRESH_ENABLED" enabled_setting = "METAREFRESH_ENABLED"
def __init__(self, settings: BaseSettings): def __init__(self, settings: BaseSettings):

View File

@ -5,9 +5,6 @@ problems such as a connection timeout or HTTP 500 error.
You can change the behaviour of this middleware by modifying the scraping settings: You can change the behaviour of this middleware by modifying the scraping settings:
RETRY_TIMES - how many times to retry a failed page RETRY_TIMES - how many times to retry a failed page
RETRY_HTTP_CODES - which HTTP response codes to retry RETRY_HTTP_CODES - which HTTP response codes to retry
Failed pages are collected on the scraping process and rescheduled at the end,
once the spider has finished crawling all regular (non-failed) pages.
""" """
from __future__ import annotations from __future__ import annotations
@ -70,8 +67,9 @@ def get_retry_request(
and :ref:`stats <topics-stats>`, and to provide extra logging context (see and :ref:`stats <topics-stats>`, and to provide extra logging context (see
:func:`logging.debug`). :func:`logging.debug`).
*reason* is a string or an :class:`Exception` object that indicates the *reason* is a string, an :class:`Exception` subclass or an
reason why the request needs to be retried. It is used to name retry stats. :class:`Exception` object that indicates the reason why the request needs
to be retried. It is used to name retry stats.
*max_retry_times* is a number that determines the maximum number of times *max_retry_times* is a number that determines the maximum number of times
that *request* can be retried. If not specified or ``None``, the number is that *request* can be retried. If not specified or ``None``, the number is

View File

@ -115,7 +115,7 @@ class UsageError(Exception):
class ScrapyDeprecationWarning(Warning): class ScrapyDeprecationWarning(Warning):
"""Warning category for deprecated features, since the default """Warning category for deprecated features, since the default
DeprecationWarning is silenced on Python 2.7+ :exc:`DeprecationWarning` is silenced.
""" """

View File

@ -22,7 +22,7 @@ logger = logging.getLogger(__name__)
class LogStats: class LogStats:
"""Log basic scraping stats periodically like: """Log basic scraping stats periodically like:
* RPM - Requests per Minute * RPM - Responses per Minute
* IPM - Items per Minute * IPM - Items per Minute
""" """

View File

@ -116,7 +116,7 @@ class AutoThrottle:
# It works better with problematic sites. # It works better with problematic sites.
new_delay = max(target_delay, new_delay) new_delay = max(target_delay, new_delay)
# Make sure self.mindelay <= new_delay <= self.max_delay # Make sure self.mindelay <= new_delay <= self.maxdelay
new_delay = min(max(self.mindelay, new_delay), self.maxdelay) new_delay = min(max(self.mindelay, new_delay), self.maxdelay)
# Dont adjust delay if response status != 200 and new delay is smaller # Dont adjust delay if response status != 200 and new delay is smaller

View File

@ -136,9 +136,9 @@ class _DummyLock:
class WrappedRequest: class WrappedRequest:
"""Wraps a scrapy Request class with methods defined by urllib2.Request class to interact with CookieJar class """Wraps a :class:`scrapy.Request` class with methods defined by
the :class:`urllib.request.Request` class to interact with
see http://docs.python.org/library/urllib2.html#urllib2.Request the :class:`http.cookiejar.CookieJar` class.
""" """
def __init__(self, request: Request): def __init__(self, request: Request):

View File

@ -1,6 +1,7 @@
""" """
This module implements the HtmlResponse class which adds encoding This module implements the :class:`HtmlResponse` class which is used as a
discovering through HTML encoding declarations to the TextResponse class. content type marker by :class:`~scrapy.selector.Selector` and can be used in
``isinstance()`` checks.
See documentation in docs/topics/request-response.rst See documentation in docs/topics/request-response.rst
""" """

View File

@ -1,6 +1,7 @@
""" """
This module implements the XmlResponse class which adds encoding This module implements the :class:`XmlResponse` class which is used as a
discovering through XML encoding declarations to the TextResponse class. content type marker by :class:`~scrapy.selector.Selector` and can be used in
``isinstance()`` checks.
See documentation in docs/topics/request-response.rst See documentation in docs/topics/request-response.rst
""" """

View File

@ -1,7 +1,7 @@
""" """
Scrapy Item Scrapy Item
See documentation in docs/topics/item.rst See documentation in docs/topics/items.rst
""" """
from __future__ import annotations from __future__ import annotations

View File

@ -9,7 +9,9 @@ its documentation in: docs/topics/link-extractors.rst
class Link: class Link:
"""Link objects represent an extracted link by the LinkExtractor. """Link objects represent an extracted link by the LinkExtractor.
Using the anchor tag sample below to illustrate the parameters:: Using the anchor tag sample below to illustrate the parameters:
.. code-block:: html
<a href="https://example.com/nofollow.html#foo" rel="nofollow">Dont follow this one</a> <a href="https://example.com/nofollow.html#foo" rel="nofollow">Dont follow this one</a>

View File

@ -58,18 +58,20 @@ class LogFormatter:
logging an action the method must return ``None``. logging an action the method must return ``None``.
Here is an example on how to create a custom log formatter to lower the severity level of Here is an example on how to create a custom log formatter to lower the severity level of
the log message when an item is dropped from the pipeline:: the log message when an item is dropped from the pipeline:
class PoliteLogFormatter(logformatter.LogFormatter): .. code-block:: python
def dropped(self, item, exception, response, spider):
return { class PoliteLogFormatter(logformatter.LogFormatter):
'level': logging.INFO, # lowering the level from logging.WARNING def dropped(self, item, exception, response, spider):
'msg': "Dropped: %(exception)s" + os.linesep + "%(item)s", return {
'args': { "level": logging.INFO, # lowering the level from logging.WARNING
'exception': exception, "msg": "Dropped: %(exception)s" + os.linesep + "%(item)s",
'item': item, "args": {
} "exception": exception,
} "item": item,
},
}
""" """
def crawled( def crawled(

View File

@ -1,7 +1,5 @@
""" """
Mail sending helpers Mail sending helpers
See documentation in docs/topics/email.rst
""" """
from __future__ import annotations from __future__ import annotations

View File

@ -1,7 +1,7 @@
""" """
Item pipeline Item pipeline
See documentation in docs/item-pipeline.rst See documentation in docs/topics/item-pipeline.rst
""" """
from __future__ import annotations from __future__ import annotations

View File

@ -423,7 +423,7 @@ class FTPFilesStore:
class FilesPipeline(MediaPipeline): class FilesPipeline(MediaPipeline):
"""Abstract pipeline that implement the file downloading """Pipeline that implements file downloading.
This pipeline tries to minimize network transfers and file processing, This pipeline tries to minimize network transfers and file processing,
doing stat of the files and determining if file is new, up-to-date or doing stat of the files and determining if file is new, up-to-date or

View File

@ -47,7 +47,7 @@ class ImageException(FileException):
class ImagesPipeline(FilesPipeline): class ImagesPipeline(FilesPipeline):
"""Abstract pipeline that implement the image thumbnail generation logic""" """Pipeline that implements the handling logic specific to images."""
MEDIA_NAME: str = "image" MEDIA_NAME: str = "image"

View File

@ -92,9 +92,10 @@ class ScrapyPriorityQueue:
- The :data:`~scrapy.Request.priority` of the request. - The :data:`~scrapy.Request.priority` of the request.
For each combination of the above seen, this class creates an instance of For each combination of the above seen, this class creates an instance of
*downstream_queue_cls* with *key* set to a subdirectory of the persistence *downstream_queue_cls* (or *start_queue_cls* for start requests if it was
directory, named as the request priority (e.g. ``1``), with an ``s`` suffix passed) with *key* set to a subdirectory of the persistence directory,
in case of a start request (e.g. ``1s``). named as the negated request priority (e.g. ``-1``), with an ``s`` suffix
in case of a start request (e.g. ``-1s``).
""" """
@classmethod @classmethod

View File

@ -454,9 +454,10 @@ class BaseSettings(MutableMapping[str, Any]):
""" """
Store a key/value attribute with a given priority. Store a key/value attribute with a given priority.
Settings should be populated *before* configuring the Crawler object Settings should be populated *before* the Crawler object applies them
(through the :meth:`~scrapy.crawler.Crawler.configure` method), (in the :meth:`~scrapy.crawler.Crawler.crawl_async` or
otherwise they won't have any effect. :meth:`~scrapy.crawler.Crawler.crawl` method), otherwise they won't
have any effect.
:param name: the setting name :param name: the setting name
:type name: str :type name: str
@ -613,7 +614,7 @@ class BaseSettings(MutableMapping[str, Any]):
""" """
Make a deep copy of current settings. Make a deep copy of current settings.
This method returns a new instance of the :class:`Settings` class, This method returns a new instance of this class,
populated with the same values and their priorities. populated with the same values and their priorities.
Modifications to the new object won't be reflected on the original Modifications to the new object won't be reflected on the original
@ -658,7 +659,7 @@ class BaseSettings(MutableMapping[str, Any]):
Make a copy of current settings and convert to a dict. Make a copy of current settings and convert to a dict.
This method returns a new dict populated with the same values This method returns a new dict populated with the same values
and their priorities as the current settings. as the current settings.
Modifications to the returned dict won't be reflected on the original Modifications to the returned dict won't be reflected on the original
settings. settings.

View File

@ -75,7 +75,7 @@ if TYPE_CHECKING:
# running event loop. # running event loop.
# #
# Side note: it should be possible to remove _request_deferred() by using # Side note: it should be possible to remove _request_deferred() by using
# engine.download_async() instead of engine.schedule(), losing the usual stuff # engine.download_async() instead of engine.crawl(), losing the usual stuff
# like spider middlewares (none of which should be important). # like spider middlewares (none of which should be important).
# #
# Other architecture problems: # Other architecture problems:
@ -188,7 +188,8 @@ class Shell:
async def _schedule(self, request: Request, spider: Spider | None) -> Response: async def _schedule(self, request: Request, spider: Spider | None) -> Response:
"""Send the request to the engine, wait for the result. """Send the request to the engine, wait for the result.
Runs in the reactor thread. Runs in the reactor thread when using the reactor, or in the asyncio
event loop thread otherwise.
""" """
if not self.spider: if not self.spider:
await self._open_spider(spider) await self._open_spider(spider)

View File

@ -75,7 +75,7 @@ class BaseSpiderMiddleware:
) -> Request | None: ) -> Request | None:
"""Return a processed request from the spider output. """Return a processed request from the spider output.
This method is called with a single request from the start seeds or the This method is called with a single request from ``start()`` or the
spider output. It should return the same or a different request, or spider output. It should return the same or a different request, or
``None`` to ignore it. ``None`` to ignore it.
@ -84,7 +84,7 @@ class BaseSpiderMiddleware:
:param response: the response being processed :param response: the response being processed
:type response: :class:`~scrapy.http.Response` object or ``None`` for :type response: :class:`~scrapy.http.Response` object or ``None`` for
start seeds start requests
:return: the processed request or ``None`` :return: the processed request or ``None``
""" """
@ -93,7 +93,7 @@ class BaseSpiderMiddleware:
def get_processed_item(self, item: Any, response: Response | None) -> Any: def get_processed_item(self, item: Any, response: Response | None) -> Any:
"""Return a processed item from the spider output. """Return a processed item from the spider output.
This method is called with a single item from the start seeds or the This method is called with a single item from ``start()`` or the
spider output. It should return the same or a different item, or spider output. It should return the same or a different item, or
``None`` to ignore it. ``None`` to ignore it.
@ -102,7 +102,7 @@ class BaseSpiderMiddleware:
:param response: the response being processed :param response: the response being processed
:type response: :class:`~scrapy.http.Response` object or ``None`` for :type response: :class:`~scrapy.http.Response` object or ``None`` for
start seeds start items
:return: the processed item or ``None`` :return: the processed item or ``None``
""" """

View File

@ -28,7 +28,7 @@ logger = logging.getLogger(__name__)
class HttpError(IgnoreRequest): class HttpError(IgnoreRequest):
"""A non-200 response was filtered""" """A non-2xx response was filtered"""
def __init__(self, response: Response, *args: Any, **kwargs: Any): def __init__(self, response: Response, *args: Any, **kwargs: Any):
self.response = response self.response = response

View File

@ -92,7 +92,7 @@ class ReferrerPolicy(ABC):
) )
def origin(self, url: str) -> str | None: def origin(self, url: str) -> str | None:
"""Return serialized origin (scheme, host, path) for a request or response URL.""" """Return serialized origin (scheme, host, port) for a request or response URL."""
return self.strip_url(url, origin_only=True) return self.strip_url(url, origin_only=True)
def potentially_trustworthy(self, url: str) -> bool: def potentially_trustworthy(self, url: str) -> bool:

View File

@ -54,17 +54,21 @@ class XMLFeedSpider(Spider):
return response return response
def parse_node(self, response: Response, selector: Selector) -> Any: def parse_node(self, response: Response, selector: Selector) -> Any:
"""This method must be overridden with your custom spider functionality""" """This method is called for the nodes matching the provided tag name
(itertag). Receives the response and an Selector for each node.
This method must return either an item, a request, or a list
containing any of them.
This method must be overridden with your custom spider functionality.
"""
if hasattr(self, "parse_item"): # backward compatibility if hasattr(self, "parse_item"): # backward compatibility
return self.parse_item(response, selector) return self.parse_item(response, selector)
raise NotImplementedError raise NotImplementedError
def parse_nodes(self, response: Response, nodes: Iterable[Selector]) -> Any: def parse_nodes(self, response: Response, nodes: Iterable[Selector]) -> Any:
"""This method is called for the nodes matching the provided tag name """This method is called for the nodes matching the provided tag name
(itertag). Receives the response and an Selector for each node. (itertag). Receives the response and an iterable of Selectors.
Overriding this method is mandatory. Otherwise, you spider won't work.
This method must return either an item, a request, or a list
containing any of them.
""" """
for selector in nodes: for selector in nodes:
@ -113,6 +117,9 @@ class CSVFeedSpider(Spider):
It receives a CSV file in a response; iterates through each of its rows, It receives a CSV file in a response; iterates through each of its rows,
and calls parse_row with a dict containing each field's data. and calls parse_row with a dict containing each field's data.
This spider also gives the opportunity to override adapt_response and
process_results methods for pre and post-processing purposes.
You can set some options regarding the CSV file, such as the delimiter, quotechar You can set some options regarding the CSV file, such as the delimiter, quotechar
and the file's headers. and the file's headers.
""" """
@ -136,16 +143,14 @@ class CSVFeedSpider(Spider):
return response return response
def parse_row(self, response: Response, row: dict[str, str]) -> Any: def parse_row(self, response: Response, row: dict[str, str]) -> Any:
"""This method must be overridden with your custom spider functionality""" """Receives a response and a dict (representing each row) with a key for
each provided (or detected) header of the CSV file.
This method must be overridden with your custom spider functionality.
"""
raise NotImplementedError raise NotImplementedError
def parse_rows(self, response: Response) -> Any: def parse_rows(self, response: Response) -> Any:
"""Receives a response and a dict (representing each row) with a key for
each provided (or detected) header of the CSV file. This spider also
gives the opportunity to override adapt_response and
process_results methods for pre and post-processing purposes.
"""
for row in csviter( for row in csviter(
response, self.delimiter, self.headers, quotechar=self.quotechar response, self.delimiter, self.headers, quotechar=self.quotechar
): ):

View File

@ -1,8 +1,6 @@
""" """
This module contains data types used by Scrapy which are not included in the This module contains data types used by Scrapy which are not included in the
Python Standard Library. Python Standard Library.
This module must not depend on any module outside the Standard Library.
""" """
from __future__ import annotations from __future__ import annotations

View File

@ -103,7 +103,7 @@ async def _defer_sleep_async() -> None:
def defer_result(result: Any) -> Deferred[Any]: # pragma: no cover def defer_result(result: Any) -> Deferred[Any]: # pragma: no cover
warnings.warn( warnings.warn(
"scrapy.utils.defer.defer_result() is deprecated, use" "scrapy.utils.defer.defer_result() is deprecated, use"
" twisted.internet.defer.success() and twisted.internet.defer.fail()," " twisted.internet.defer.succeed() and twisted.internet.defer.fail(),"
" plus an explicit sleep if needed, or explicit reactor.callLater().", " plus an explicit sleep if needed, or explicit reactor.callLater().",
category=ScrapyDeprecationWarning, category=ScrapyDeprecationWarning,
stacklevel=2, stacklevel=2,
@ -469,22 +469,22 @@ def _maybeDeferred_coro(
def deferred_to_future(d: Deferred[_T]) -> Future[_T]: def deferred_to_future(d: Deferred[_T]) -> Future[_T]:
"""Return an :class:`asyncio.Future` object that wraps *d*. """Return an :class:`asyncio.Future` object that wraps *d*.
This function requires This function requires an installed asyncio reactor or a running asyncio
:class:`~twisted.internet.asyncioreactor.AsyncioSelectorReactor` to be event loop, see :ref:`using-asyncio`.
installed.
When :ref:`using the asyncio reactor <install-asyncio>`, you cannot await In this state you cannot await on :class:`~twisted.internet.defer.Deferred`
on :class:`~twisted.internet.defer.Deferred` objects from :ref:`Scrapy objects from :ref:`Scrapy callables defined as coroutines
callables defined as coroutines <coroutine-support>`, you can only await on <coroutine-support>`, you can only await on ``Future`` objects. Wrapping
``Future`` objects. Wrapping ``Deferred`` objects into ``Future`` objects ``Deferred`` objects into ``Future`` objects allows you to wait on them:
allows you to wait on them::
.. code-block:: python
class MySpider(Spider): class MySpider(Spider):
... ...
async def parse(self, response): async def parse(self, response):
additional_request = scrapy.Request('https://example.org/price') deferred = some_dfd_helper()
deferred = self.crawler.engine.download(additional_request) result = await deferred_to_future(deferred)
additional_response = await deferred_to_future(deferred)
.. versionchanged:: 2.14 .. versionchanged:: 2.14
This function no longer installs an asyncio loop if called before the This function no longer installs an asyncio loop if called before the
@ -492,7 +492,10 @@ def deferred_to_future(d: Deferred[_T]) -> Future[_T]:
in this case. in this case.
""" """
if not is_asyncio_available(): if not is_asyncio_available():
raise RuntimeError("deferred_to_future() requires AsyncioSelectorReactor.") raise RuntimeError(
"deferred_to_future() requires an installed asyncio reactor"
" or a running asyncio event loop."
)
return d.asFuture(asyncio.get_event_loop()) return d.asFuture(asyncio.get_event_loop())
@ -501,23 +504,26 @@ def maybe_deferred_to_future(d: Deferred[_T]) -> Deferred[_T] | Future[_T]:
defined as a coroutine <coroutine-support>`. defined as a coroutine <coroutine-support>`.
What you can await in Scrapy callables defined as coroutines depends on the What you can await in Scrapy callables defined as coroutines depends on the
value of :setting:`TWISTED_REACTOR`: value of :setting:`TWISTED_REACTOR` and :setting:`TWISTED_REACTOR_ENABLED`:
- When :ref:`using the asyncio reactor <install-asyncio>`, you can only - When :ref:`using the asyncio reactor <install-asyncio>`, or :ref:`not
await on :class:`asyncio.Future` objects. using a reactor at all <asyncio-without-reactor>`, you can only await
on :class:`asyncio.Future` objects.
- When not using the asyncio reactor, you can only await on - When :ref:`using a non-asyncio reactor <disable-asyncio>`, you can only
:class:`~twisted.internet.defer.Deferred` objects. await on :class:`~twisted.internet.defer.Deferred` objects.
If you want to write code that uses ``Deferred`` objects but works with any If you want to write code that uses ``Deferred`` objects but works in both
reactor, use this function on all ``Deferred`` objects:: of these states, use this function on all ``Deferred`` objects:
.. code-block:: python
class MySpider(Spider): class MySpider(Spider):
... ...
async def parse(self, response): async def parse(self, response):
additional_request = scrapy.Request('https://example.org/price') deferred = some_dfd_helper()
deferred = self.crawler.engine.download(additional_request) result = await maybe_deferred_to_future(deferred)
additional_response = await maybe_deferred_to_future(deferred)
""" """
if not is_asyncio_available(): if not is_asyncio_available():
return d return d

View File

@ -43,15 +43,18 @@ def create_deprecated_class(
It can be used to rename a base class in a library. For example, if we It can be used to rename a base class in a library. For example, if we
have have
class OldName(SomeClass): .. code-block:: python
# ...
and we want to rename it to NewName, we can do the following:: class OldName(SomeClass): ...
class NewName(SomeClass): and we want to rename it to NewName, we can do the following:
# ...
OldName = create_deprecated_class('OldName', NewName) .. code-block:: python
class NewName(SomeClass): ...
OldName = create_deprecated_class("OldName", NewName)
Then, if user class inherits from OldName, warning is issued. Also, if Then, if user class inherits from OldName, warning is issued. Also, if
some code uses ``issubclass(sub, OldName)`` or ``isinstance(sub(), OldName)`` some code uses ``issubclass(sub, OldName)`` or ``isinstance(sub(), OldName)``

View File

@ -248,8 +248,7 @@ def logformatter_adapter(
) -> tuple[int, str, dict[str, Any] | tuple[Any, ...]]: ) -> tuple[int, str, dict[str, Any] | tuple[Any, ...]]:
""" """
Helper that takes the dictionary output from the methods in LogFormatter Helper that takes the dictionary output from the methods in LogFormatter
and adapts it into a tuple of positional arguments for logger.log calls, and adapts it into a tuple of positional arguments for logger.log calls.
handling backward compatibility as well.
""" """
level = logkws.get("level", logging.INFO) level = logkws.get("level", logging.INFO)

View File

@ -88,7 +88,7 @@ def open_in_browser(
def parse_details(self, response): def parse_details(self, response):
if "item name" not in response.body: if "item name" not in response.text:
open_in_browser(response) open_in_browser(response)
""" """
# circular imports # circular imports

View File

@ -39,7 +39,7 @@ def send_catch_log(
*arguments: TypingAny, *arguments: TypingAny,
**named: TypingAny, **named: TypingAny,
) -> list[tuple[TypingAny, TypingAny]]: ) -> list[tuple[TypingAny, TypingAny]]:
"""Like ``pydispatcher.robust.sendRobust()`` but it also logs errors and returns """Like ``pydispatch.robust.sendRobust()`` but it also logs errors and returns
Failures instead of exceptions. Failures instead of exceptions.
""" """
dont_log = named.pop("dont_log", ()) dont_log = named.pop("dont_log", ())
@ -172,9 +172,8 @@ async def _send_catch_log_asyncio(
Returns a coroutine that completes once all signal handlers have finished. Returns a coroutine that completes once all signal handlers have finished.
This function requires This function requires an installed asyncio reactor or a running asyncio
:class:`~twisted.internet.asyncioreactor.AsyncioSelectorReactor` to be event loop.
installed.
.. versionadded:: 2.14 .. versionadded:: 2.14
""" """

View File

@ -4,9 +4,7 @@ references to live object instances.
If you want live objects for a particular class to be tracked, you only have to If you want live objects for a particular class to be tracked, you only have to
subclass from object_ref (instead of object). subclass from object_ref (instead of object).
About performance: This library has a minimal performance impact when enabled, This library has a minimal performance impact.
and no performance penalty at all when disabled (as object_ref becomes just an
alias to object in that case).
.. note:: PyPy uses a tracing garbage collector, so objects may .. note:: PyPy uses a tracing garbage collector, so objects may
remain in the ``live_refs`` longer than expected, even after they remain in the ``live_refs`` longer than expected, even after they

View File

@ -117,8 +117,8 @@ def strip_url(
- ``strip_credentials`` removes "user:password@" - ``strip_credentials`` removes "user:password@"
- ``strip_default_port`` removes ":80" (resp. ":443", ":21") - ``strip_default_port`` removes ":80" (resp. ":443", ":21")
from http:// (resp. https://, ftp://) URLs from http:// (resp. https://, ftp://) URLs
- ``origin_only`` replaces path component with "/", also dropping - ``origin_only`` replaces the path component with "/", also dropping
query and fragment components ; it also strips credentials the query component; it also strips credentials
- ``strip_fragment`` drops any #fragment component - ``strip_fragment`` drops any #fragment component
""" """