Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add exception-translations rule to quality_scale pytest validation #131914

Merged
merged 10 commits into from
Jan 9, 2025

Conversation

epenet
Copy link
Contributor

@epenet epenet commented Nov 29, 2024

Proposed change

If an integration marks exception-translations as done in the quality scale, then we should check them as they are raised in the tests

Sample (forced) failure:

============================================================
FAILED tests/components/renault/test_services.py::test_service_set_ac_cancel[zoe_40] - Failed:
Found untranslated HomeAssistantError exception: Didn't work

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue:
  • Link to documentation pull request:

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

@home-assistant home-assistant bot added cla-signed core new-feature small-pr PRs with less than 30 lines. labels Nov 29, 2024
@epenet
Copy link
Contributor Author

epenet commented Nov 29, 2024

@jbouwh already found one for mqtt:

FAILED tests/components/mqtt/test_vacuum.py::test_commands_without_supported_features[hass_config0] - Failed: 
Found untranslated HomeAssistantError exception: Entity vacuum.mqtttest does not support this service.

@epenet
Copy link
Contributor Author

epenet commented Nov 29, 2024

@zweckj this gets raised for tedee

ERROR tests/components/tedee/test_lock.py::test_lock_without_pullspring - Failed:
Found untranslated HomeAssistantError exception: Entity lock.lock_2c3d does not support this service.

@zweckj
Copy link
Member

zweckj commented Nov 29, 2024

ffs seems we missed one 😅

@zweckj
Copy link
Member

zweckj commented Nov 29, 2024

@epenet wait. I think this is coming from the base LockEntity?

@epenet
Copy link
Contributor Author

epenet commented Nov 29, 2024

@epenet wait. I think this is coming from the base LockEntity?

Ah, looks like it.
I think that's a shortcoming from core.
How do feel about fixing it?

@jbouwh
Copy link
Contributor

jbouwh commented Nov 29, 2024

test_commands_without_supported_features

It seems this is in the base component code as well. I found some other exceptions without translation though, I'll open a PR for it.

@jbouwh
Copy link
Contributor

jbouwh commented Nov 29, 2024

#131933

@zweckj
Copy link
Member

zweckj commented Nov 29, 2024

@jbouwh did you mean for mqtt or the other ones as well? Otherwise I'll take a look at the lock

@jbouwh
Copy link
Contributor

jbouwh commented Nov 29, 2024

@jbouwh did you mean for mqtt or the other ones as well? Otherwise I'll take a look at the lock

MQTT does not raise in the vacuum platform module. I am looking into a possible explanation for the vacuum translation error. There might be more cases.

A specialized MQTT value template exception derives from TemplateError, which behaves differently. I did not address translations with that one (yet).

@jbouwh
Copy link
Contributor

jbouwh commented Nov 29, 2024

@jbouwh already found one for mqtt:

FAILED tests/components/mqtt/test_vacuum.py::test_commands_without_supported_features[hass_config0] - Failed: 
Found untranslated HomeAssistantError exception: Entity vacuum.mqtttest does not support this service.

Caused by:

raise HomeAssistantError(
f"Entity {entity.entity_id} does not support this service."
)

There are more untranslated helper exceptions. I think we need to add translations for those too where they are missing.

@zweckj
Copy link
Member

zweckj commented Nov 29, 2024

There are more untranslated helper exceptions. I think we need to add translations for those too where they are missing.

Same source for the lock ones. We don't upload anything for those to lokalize yet, do we?

@jbouwh
Copy link
Contributor

jbouwh commented Nov 30, 2024

There are more untranslated helper exceptions. I think we need to add translations for those too where they are missing.

Same source for the lock ones. We don't upload anything for those to lokalize yet, do we?

We lokalize some of them. If they are generic, then they are placed under the homeassistant component.

@zweckj
Copy link
Member

zweckj commented Nov 30, 2024

We lokalize some of them. If they are generic, then they are placed under the homeassistant component.

I couldn't find a single translation in that entire folder.

It also looks like exception translations only works for components:

localize_key = (
f"component.{translation_domain}.exceptions.{translation_key}.message"
)

@jbouwh
Copy link
Contributor

jbouwh commented Nov 30, 2024

This should address the mqtt case: #131956

@epenet epenet force-pushed the epenet-20241129-1507 branch from 0a84191 to ba12605 Compare December 1, 2024 16:09
@epenet epenet marked this pull request as ready for review December 1, 2024 16:50
@epenet epenet force-pushed the epenet-20241129-1507 branch from ba12605 to dfb6ced Compare December 5, 2024 06:39
@epenet
Copy link
Contributor Author

epenet commented Dec 5, 2024

Rebased to check for new violations

@epenet epenet force-pushed the epenet-20241129-1507 branch 2 times, most recently from 4cc5fee to 5ffd51f Compare December 12, 2024 21:21
tests/common.py Outdated Show resolved Hide resolved
@epenet epenet force-pushed the epenet-20241129-1507 branch from 8178e1a to 02edf62 Compare January 2, 2025 21:03
@epenet
Copy link
Contributor Author

epenet commented Jan 3, 2025

@starkillerOG it seems that reolink does not translate all errors, despite what #132301 implies.
Could you please take a look at the errors in this CI run?
https://github.com/home-assistant/core/actions/runs/12588734557/job/35087555075?pr=131914

@starkillerOG
Copy link
Contributor

@epenet this is due to the way I wrote the test files:
https://github.com/home-assistant/core/blob/66b4b24612a88ca7fd4fa88a0e58d9fe927aae54/tests/components/reolink/test_switch.py#L277C45-L277C71

And the way I do the translations:

try:
return await func(*args, **kwargs)
except InvalidParameterError as err:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_parameter",
translation_placeholders={"err": str(err)},
) from err
except ApiError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="api_error",
translation_placeholders={"err": str(err)},
) from err
except InvalidContentTypeError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="invalid_content_type",
translation_placeholders={"err": str(err)},
) from err
except CredentialsInvalidError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="invalid_credentials",
translation_placeholders={"err": str(err)},
) from err
except LoginError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="login_error",
translation_placeholders={"err": str(err)},
) from err
except NoDataError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="no_data",
translation_placeholders={"err": str(err)},
) from err
except UnexpectedDataError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="unexpected_data",
translation_placeholders={"err": str(err)},
) from err
except NotSupportedError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="not_supported",
translation_placeholders={"err": str(err)},
) from err
except SubscriptionError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="subscription_error",
translation_placeholders={"err": str(err)},
) from err
except ReolinkConnectionError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="connection_error",
translation_placeholders={"err": str(err)},
) from err
except ReolinkTimeoutError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="timeout",
translation_placeholders={"err": str(err)},
) from err
except ReolinkError as err:
raise HomeAssistantError(err) from err

All errors from the upstream reolink-aio library are derived from ReolinkError, but during runtime no ReolinkError is ever expected. Instead more specific errors like InvalidParameterError or ReolinkConnectionError (which are derived from ReolinkError) are expected.

The last catch of ReolinkError is just to catch anything that might have sliped through the cracks, but is not expected.

except ReolinkError as err:
raise HomeAssistantError(err) from err

In the tests I often use ReolinkError because it is easy and general, but I could simply replace them all by ReolinkConnectionError or any other error you prefer.

Not sure what the best way forward is with this:

  1. adjust the tests to throw more specific errors
  2. make a translation for the ReolinkError which is something like f"Unexpected Reolink error {error_string}" but Honestly that translation makes little sence to me...

@epenet
Copy link
Contributor Author

epenet commented Jan 5, 2025

We recommend that unexpected errors still use translations, so IMO it makes sense to have the extra one with the placeholder.

@starkillerOG
Copy link
Contributor

@epenet PR for the missing translation is here: #134807
I checked the upstream library and the ReolinkError is indeed never raised. It is only used as the "catch all errors".

tests/components/conftest.py Outdated Show resolved Hide resolved
tests/components/conftest.py Outdated Show resolved Hide resolved
tests/components/conftest.py Outdated Show resolved Hide resolved
tests/components/conftest.py Outdated Show resolved Hide resolved
tests/components/conftest.py Outdated Show resolved Hide resolved
@epenet epenet merged commit ee865d2 into dev Jan 9, 2025
64 checks passed
@epenet epenet deleted the epenet-20241129-1507 branch January 9, 2025 20:21
@github-actions github-actions bot locked and limited conversation to collaborators Jan 10, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants