diff --git a/scrapy/settings/__init__.py b/scrapy/settings/__init__.py index 27b0f1187..5c5edf101 100644 --- a/scrapy/settings/__init__.py +++ b/scrapy/settings/__init__.py @@ -25,7 +25,9 @@ if TYPE_CHECKING: # typing.Self requires Python 3.11 from typing_extensions import Self - _SettingsInput: TypeAlias = SupportsItems[str, Any] | str | None + _SettingsInput: TypeAlias = ( + SupportsItems[str, Any] | Iterable[tuple[str, Any]] | str | None + ) SETTINGS_PRIORITIES: dict[str, int] = { @@ -560,7 +562,7 @@ class BaseSettings(MutableMapping[str, Any]): if key.isupper(): self.set(key, getattr(module, key), priority) - # BaseSettings.update() doesn't support all inputs that MutableMapping.update() supports + # BaseSettings.update() doesn't support kwargs input like MutableMapping.update(). def update(self, values: _SettingsInput, priority: int | str = "project") -> None: # type: ignore[override] """ Store key/value pairs with a given priority. @@ -577,7 +579,7 @@ class BaseSettings(MutableMapping[str, Any]): command. :param values: the settings names and values - :type values: dict or string or :class:`~scrapy.settings.BaseSettings` + :type values: dict, iterable, string or :class:`~scrapy.settings.BaseSettings` :param priority: the priority of the settings. Should be a key of :attr:`~scrapy.settings.SETTINGS_PRIORITIES` or an integer @@ -591,7 +593,12 @@ class BaseSettings(MutableMapping[str, Any]): for name, value in values.items(): self.set(name, value, cast("int", values.getpriority(name))) else: - for name, value in values.items(): + items: Iterable[tuple[str, Any]] + if hasattr(values, "items"): + items = cast("SupportsItems[str, Any]", values).items() + else: + items = values + for name, value in items: self.set(name, value, priority) def delete(self, name: str, priority: int | str = "project") -> None: diff --git a/tests/test_settings/__init__.py b/tests/test_settings/__init__.py index 3282b0591..97c095ae0 100644 --- a/tests/test_settings/__init__.py +++ b/tests/test_settings/__init__.py @@ -221,13 +221,11 @@ class TestBaseSettings: settings = BaseSettings({"key": 0}) settings.update(key=1) # pylint: disable=unexpected-keyword-arg - @pytest.mark.xfail( - raises=AttributeError, - reason="BaseSettings.update doesn't support iterable input", - ) def test_update_iterable(self): - settings = BaseSettings({"key": 0}) - settings.update([("key", 1)]) + settings = BaseSettings({"key": 0}, priority=0) + settings.update([("key", 1)], priority=10) + assert settings["key"] == 1 + assert settings.getpriority("key") == 10 def test_update_jsonstring(self): settings = BaseSettings({"number": 0, "dict": BaseSettings({"key": "val"})})