From e5e48883b580b2fcfe8d30b301675f1f3ef3d67c Mon Sep 17 00:00:00 2001 From: Vishal Gawade Date: Fri, 19 Jun 2026 00:49:32 -0400 Subject: [PATCH] Deprecate ScrapyCommand.help() (#7633) Co-authored-by: Vishal Gawade --- scrapy/commands/__init__.py | 19 ++++++++++++---- tests/test_commands.py | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/scrapy/commands/__init__.py b/scrapy/commands/__init__.py index 19b6f6681..598e8060e 100644 --- a/scrapy/commands/__init__.py +++ b/scrapy/commands/__init__.py @@ -16,6 +16,8 @@ from twisted.python import failure from scrapy.exceptions import ScrapyDeprecationWarning, UsageError from scrapy.utils.conf import arglist_to_dict, feed_process_params_from_cli +from scrapy.utils.deprecate import method_is_overridden +from scrapy.utils.python import global_object_name if TYPE_CHECKING: from collections.abc import Iterable @@ -36,6 +38,14 @@ class ScrapyCommand(ABC): def __init__(self) -> None: self.settings: Settings | None = None # set in scrapy.cmdline + if method_is_overridden(self.__class__, ScrapyCommand, "help"): + warnings.warn( + "The ScrapyCommand.help() method is deprecated and overriding " + f"it, as the {global_object_name(self.__class__)} class does, " + "has no effect; override long_desc() instead.", + ScrapyDeprecationWarning, + stacklevel=2, + ) def set_crawler(self, crawler: Crawler) -> None: # pragma: no cover warnings.warn( @@ -68,10 +78,11 @@ class ScrapyCommand(ABC): return self.short_desc() def help(self) -> str: - """An extensive help for the command. It will be shown when using the - "help" command. It can contain newlines since no post-formatting will - be applied to its contents. - """ + warnings.warn( + "ScrapyCommand.help() is deprecated, use long_desc() instead.", + ScrapyDeprecationWarning, + stacklevel=2, + ) return self.long_desc() def add_options(self, parser: argparse.ArgumentParser) -> None: diff --git a/tests/test_commands.py b/tests/test_commands.py index 0657f393c..d7b7a9ff2 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -12,6 +12,7 @@ import pytest import scrapy from scrapy.cmdline import _pop_command_name, _print_unknown_command_msg from scrapy.commands import ScrapyCommand, ScrapyHelpFormatter, view +from scrapy.exceptions import ScrapyDeprecationWarning from scrapy.settings import Settings from scrapy.utils.reactor import _asyncio_reactor_path from tests.utils.cmdline import call, proc @@ -28,6 +29,50 @@ class EmptyCommand(ScrapyCommand): pass +class TestHelpDeprecation: + def test_calling_help_is_deprecated(self) -> None: + command = EmptyCommand() + with pytest.warns( + ScrapyDeprecationWarning, + match=r"ScrapyCommand\.help\(\) is deprecated, use long_desc\(\) instead\.", + ): + result = command.help() + # help() still delegates to long_desc() for backward compatibility. + assert result == command.long_desc() + + def test_overriding_help_is_deprecated(self) -> None: + class HelpCommand(ScrapyCommand): + def short_desc(self) -> str: + return "" + + def run(self, args: list[str], opts: argparse.Namespace) -> None: + pass + + def help(self) -> str: + return "custom help" + + with pytest.warns( + ScrapyDeprecationWarning, + match=r"The ScrapyCommand\.help\(\) method is deprecated and " + r"overriding it, as the .*HelpCommand class does, has no effect; " + r"override long_desc\(\) instead\.", + ): + HelpCommand() + + def test_not_overriding_help_does_not_warn(self, recwarn) -> None: + # Commands that do not override help() must not emit the + # override-deprecation warning when instantiated, including subclasses + # several levels below ScrapyCommand (as the built-in commands are). + class SubCommand(EmptyCommand): + pass + + EmptyCommand() + SubCommand() + assert not [ + w for w in recwarn.list if issubclass(w.category, ScrapyDeprecationWarning) + ] + + class TestCommandSettings: def setup_method(self): self.command = EmptyCommand()