diff --git a/homeassistant/core.py b/homeassistant/core.py index 8c12e2764ec0..7e563587668e 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -207,7 +207,7 @@ def validate_state(state: str) -> str: def callback[_CallableT: Callable[..., Any]](func: _CallableT) -> _CallableT: """Annotation to mark method as safe to call from within the event loop.""" - setattr(func, "_hass_callback", True) + setattr(func, "_hass_callback", True) # noqa: B010 return func diff --git a/homeassistant/core_config.py b/homeassistant/core_config.py index e6cdb1067391..3c45d096b739 100644 --- a/homeassistant/core_config.py +++ b/homeassistant/core_config.py @@ -367,9 +367,7 @@ async def async_process_ha_core_config(hass: HomeAssistant, config: dict) -> Non [{"type": "totp", "id": "totp", "name": "Authenticator app"}], ) - setattr( - hass, "auth", await auth.auth_manager_from_config(hass, auth_conf, mfa_conf) - ) + hass.auth = await auth.auth_manager_from_config(hass, auth_conf, mfa_conf) await hass.config.async_load() diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index c822a5b12689..3ab9612a8ad7 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -1411,7 +1411,7 @@ def _make_entity_service_schema(schema: dict, extra: int) -> VolSchemaType: ), _HAS_ENTITY_SERVICE_FIELD, ) - setattr(validator, "_entity_service_schema", True) + setattr(validator, "_entity_service_schema", True) # noqa: B010 return validator diff --git a/homeassistant/helpers/deprecation.py b/homeassistant/helpers/deprecation.py index 0be8dfeb2b15..a0287aa8af1c 100644 --- a/homeassistant/helpers/deprecation.py +++ b/homeassistant/helpers/deprecation.py @@ -43,7 +43,7 @@ def deprecated_substitute[_ObjectT: object]( inspect.getfile(self.__class__), ) warnings[module_name] = True - setattr(func, "_deprecated_substitute_warnings", warnings) + setattr(func, "_deprecated_substitute_warnings", warnings) # noqa: B010 # Return the old property return getattr(self, substitute_name) diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 104d7046c9b2..5190f51035c2 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -475,7 +475,7 @@ class SchemaOptionsFlowHandler(OptionsFlow): ) if async_setup_preview: - setattr(self, "async_setup_preview", async_setup_preview) + setattr(self, "async_setup_preview", async_setup_preview) # noqa: B010 @property def options(self) -> dict[str, Any]: diff --git a/pyproject.toml b/pyproject.toml index c993f6c5e20a..d8f24bb8543c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -658,6 +658,7 @@ select = [ "B006", # Do not use mutable data structures for argument defaults "B007", # Loop control variable {name} not used within loop body "B009", # Do not call getattr with a constant attribute value. It is not any safer than normal property access. + "B010", # Do not call setattr with a constant attribute value. It is not any safer than normal property access. "B011", # Do not call assert False since python -O removes these calls "B012", # Use of break/continue/return inside a finally block "B013", # A length-one tuple literal is redundant in exception handlers diff --git a/tests/common.py b/tests/common.py index 79149433599a..52c03e7e9ed7 100644 --- a/tests/common.py +++ b/tests/common.py @@ -1248,7 +1248,7 @@ def patch_yaml_files(files_dict, endswith=True): if fname in files_dict: _LOGGER.debug("patch_yaml_files match %s", fname) res = StringIO(files_dict[fname]) - setattr(res, "name", fname) + res.name = fname return res # Match using endswith @@ -1256,7 +1256,7 @@ def patch_yaml_files(files_dict, endswith=True): if fname.endswith(ends): _LOGGER.debug("patch_yaml_files end match %s: %s", ends, fname) res = StringIO(files_dict[ends]) - setattr(res, "name", fname) + res.name = fname return res # Fallback for hass.components (i.e. services.yaml) diff --git a/tests/components/balboa/test_switch.py b/tests/components/balboa/test_switch.py index 7e443075bd70..7553b5f313a5 100644 --- a/tests/components/balboa/test_switch.py +++ b/tests/components/balboa/test_switch.py @@ -41,7 +41,7 @@ async def test_switch(hass: HomeAssistant, client: MagicMock) -> None: await common.async_turn_off(hass, ENTITY_SWITCH) client.configure_filter_cycle.assert_called_with(2, enabled=False) - setattr(client, "filter_cycle_2_enabled", False) + client.filter_cycle_2_enabled = False client.emit("") await hass.async_block_till_done() diff --git a/tests/components/google_pubsub/test_init.py b/tests/components/google_pubsub/test_init.py index 5f160054da77..590f2615a077 100644 --- a/tests/components/google_pubsub/test_init.py +++ b/tests/components/google_pubsub/test_init.py @@ -45,11 +45,7 @@ async def test_nested() -> None: def mock_client_fixture() -> Generator[MagicMock]: """Mock the pubsub client.""" with patch(f"{GOOGLE_PUBSUB_PATH}.PublisherClient") as client: - setattr( - client, - "from_service_account_json", - MagicMock(return_value=MagicMock()), - ) + client.from_service_account_json = MagicMock(return_value=MagicMock()) yield client diff --git a/tests/components/light/common.py b/tests/components/light/common.py index b29ac0c7c892..fb53bc13d845 100644 --- a/tests/components/light/common.py +++ b/tests/components/light/common.py @@ -198,6 +198,6 @@ class MockLight(MockToggleEntity, LightEntity): ]: setattr(self, key, value) if key == "white": - setattr(self, "brightness", value) + self.brightness = value if key in TURN_ON_ARG_TO_COLOR_MODE: self._attr_color_mode = TURN_ON_ARG_TO_COLOR_MODE[key] diff --git a/tests/components/prometheus/test_init.py b/tests/components/prometheus/test_init.py index 5466c2e977cb..1d5761c28994 100644 --- a/tests/components/prometheus/test_init.py +++ b/tests/components/prometheus/test_init.py @@ -2836,7 +2836,7 @@ def mock_client_fixture(): with mock.patch(f"{PROMETHEUS_PATH}.prometheus_client") as client: counter_client = mock.MagicMock() client.Counter = mock.MagicMock(return_value=counter_client) - setattr(counter_client, "labels", mock.MagicMock(return_value=mock.MagicMock())) + counter_client.labels = mock.MagicMock(return_value=mock.MagicMock()) yield counter_client diff --git a/tests/components/roborock/conftest.py b/tests/components/roborock/conftest.py index e1146639efbc..d73468f16231 100644 --- a/tests/components/roborock/conftest.py +++ b/tests/components/roborock/conftest.py @@ -279,11 +279,11 @@ def make_dnd_timer(dataclass_template: RoborockBase) -> AsyncMock: ) async def set_dnd_timer(timer: DnDTimer) -> None: - setattr(dnd_trait, "start_hour", timer.start_hour) - setattr(dnd_trait, "start_minute", timer.start_minute) - setattr(dnd_trait, "end_hour", timer.end_hour) - setattr(dnd_trait, "end_minute", timer.end_minute) - setattr(dnd_trait, "enabled", timer.enabled) + dnd_trait.start_hour = timer.start_hour + dnd_trait.start_minute = timer.start_minute + dnd_trait.end_hour = timer.end_hour + dnd_trait.end_minute = timer.end_minute + dnd_trait.enabled = timer.enabled dnd_trait.set_dnd_timer = AsyncMock() dnd_trait.set_dnd_timer.side_effect = set_dnd_timer @@ -298,11 +298,11 @@ def make_valley_electric_timer(dataclass_template: RoborockBase) -> AsyncMock: ) async def set_timer(timer: ValleyElectricityTimer) -> None: - setattr(valley_electric_timer_trait, "start_hour", timer.start_hour) - setattr(valley_electric_timer_trait, "start_minute", timer.start_minute) - setattr(valley_electric_timer_trait, "end_hour", timer.end_hour) - setattr(valley_electric_timer_trait, "end_minute", timer.end_minute) - setattr(valley_electric_timer_trait, "enabled", timer.enabled) + valley_electric_timer_trait.start_hour = timer.start_hour + valley_electric_timer_trait.start_minute = timer.start_minute + valley_electric_timer_trait.end_hour = timer.end_hour + valley_electric_timer_trait.end_minute = timer.end_minute + valley_electric_timer_trait.enabled = timer.enabled valley_electric_timer_trait.set_timer = AsyncMock() valley_electric_timer_trait.set_timer.side_effect = set_timer diff --git a/tests/components/totalconnect/conftest.py b/tests/components/totalconnect/conftest.py index 803fc0521294..51d88fab40dc 100644 --- a/tests/components/totalconnect/conftest.py +++ b/tests/components/totalconnect/conftest.py @@ -214,7 +214,7 @@ def mock_client(mock_location: TotalConnectLocation) -> Generator[TotalConnectCl "can_bypass_zones": True, "can_clear_bypass": True, } - setattr(client, "_user", user_mock) + client._user = user_mock yield client diff --git a/tests/components/water_heater/test_init.py b/tests/components/water_heater/test_init.py index 485dd844e978..33870bf14d9c 100644 --- a/tests/components/water_heater/test_init.py +++ b/tests/components/water_heater/test_init.py @@ -85,13 +85,13 @@ async def test_sync_turn_on(hass: HomeAssistant) -> None: water_heater.hass = hass # Test with turn_on method defined - setattr(water_heater, "turn_on", MagicMock()) + water_heater.turn_on = MagicMock() await water_heater.async_turn_on() assert water_heater.turn_on.call_count == 1 # Test with async_turn_on method defined - setattr(water_heater, "async_turn_on", AsyncMock()) + water_heater.async_turn_on = AsyncMock() await water_heater.async_turn_on() assert water_heater.async_turn_on.call_count == 1 @@ -103,13 +103,13 @@ async def test_sync_turn_off(hass: HomeAssistant) -> None: water_heater.hass = hass # Test with turn_off method defined - setattr(water_heater, "turn_off", MagicMock()) + water_heater.turn_off = MagicMock() await water_heater.async_turn_off() assert water_heater.turn_off.call_count == 1 # Test with async_turn_off method defined - setattr(water_heater, "async_turn_off", AsyncMock()) + water_heater.async_turn_off = AsyncMock() await water_heater.async_turn_off() assert water_heater.async_turn_off.call_count == 1 diff --git a/tests/conftest.py b/tests/conftest.py index dcab490cd60a..52328d2b3d5d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -229,9 +229,9 @@ def pytest_runtest_setup() -> None: _validate_host(host) return (host, [], [host]) - setattr(socket, "getaddrinfo", getaddrinfo_patched) - setattr(socket, "gethostbyname", gethostbyname_patched) - setattr(socket, "gethostbyname_ex", gethostbyname_ex_patched) + socket.getaddrinfo = getaddrinfo_patched + socket.gethostbyname = gethostbyname_patched + socket.gethostbyname_ex = gethostbyname_ex_patched pytest_socket.SocketBlockedError = HASocketBlockedError diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index b730a07a7184..cc449beb99a0 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -1128,8 +1128,8 @@ def test_deprecated_or_removed_logger_with_config_attributes( option_status = "is deprecated" replacement = f"'mars' option near {file}:{line} {option_status}, please replace it with '{replacement_key}'" config = OrderedDict([("mars", "blah")]) - setattr(config, "__config_file__", file) - setattr(config, "__line__", line) + config.__config_file__ = file + config.__line__ = line validated = cv.deprecated("mars", replacement_key=replacement_key, default=False)( config @@ -1146,8 +1146,8 @@ def test_deprecated_or_removed_logger_with_config_attributes( option_status = "has been removed" replacement = f"'mars' option near {file}:{line} {option_status}, please remove it from your configuration" config = OrderedDict([("mars", "blah")]) - setattr(config, "__config_file__", file) - setattr(config, "__line__", line) + config.__config_file__ = file + config.__line__ = line validated = cv.removed("mars", default=False, raise_if_present=False)(config) assert "mars" not in validated # Removed because by cv.removed @@ -1167,7 +1167,7 @@ def test_deprecated_logger_with_one_config_attribute( line: int = 54 replacement = f"'mars' option near {file}:{line} is deprecated" config = OrderedDict([("mars", "blah")]) - setattr(config, "__config_file__", file) + config.__config_file__ = file cv.deprecated("mars", replacement_key="jupiter", default=False)(config) @@ -1181,7 +1181,7 @@ def test_deprecated_logger_with_one_config_attribute( assert len(caplog.records) == 0 config = OrderedDict([("mars", "blah")]) - setattr(config, "__line__", line) + config.__line__ = line cv.deprecated("mars", replacement_key="jupiter", default=False)(config) diff --git a/tests/testing_config/custom_components/test/light.py b/tests/testing_config/custom_components/test/light.py index d9fad11655e7..541872664931 100644 --- a/tests/testing_config/custom_components/test/light.py +++ b/tests/testing_config/custom_components/test/light.py @@ -98,6 +98,6 @@ class MockLight(MockToggleEntity, LightEntity): ]: setattr(self, key, value) if key == "white": - setattr(self, "brightness", value) + self.brightness = value if key in TURN_ON_ARG_TO_COLOR_MODE: self._attr_color_mode = TURN_ON_ARG_TO_COLOR_MODE[key]