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 strict typing to ring integration #115276

Merged
merged 9 commits into from
Apr 11, 2024

Conversation

sdb9696
Copy link
Contributor

@sdb9696 sdb9696 commented Apr 9, 2024

Proposed change

Add ring integration to .strict-typing and associated type hint changes.

  • Most changes are either minor type hints or using the ring_doorbell library typed get functions.
  • sensor.py has been refactored to support the value_fn for native values.
  • The RingDeviceData class has been dropped from the coordinator as the library now stores history with the device.

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.
  • Untested files have been added to .coveragerc.

To help with the load of incoming pull requests:

homeassistant/components/ring/camera.py Show resolved Hide resolved
Comment on lines +34 to +35
ON = auto()
OFF = auto()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The StrEnum was so that I could type hint the possible state values. The auto() sets the string value to the lower case value of the member name.

@@ -15,4 +15,4 @@ async def setup_platform(hass, platform):
)
with patch("homeassistant.components.ring.PLATFORMS", [platform]):
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
await hass.async_block_till_done(wait_background_tasks=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this change here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a test failing after I added the unique_id migration code in a separate PR and I added it as per this PR #112726 suggesting it should be in place for tests. In the end the failure was something else so I could pull it out but it seems useful to have it there.

Copy link
Member

@cdce8p cdce8p left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot @sdb9696! Took some time to go through the changes and left some comments to make the annotations even better. Please let me know if you have any questions.

homeassistant/components/ring/__init__.py Outdated Show resolved Hide resolved
homeassistant/components/ring/__init__.py Outdated Show resolved Hide resolved
homeassistant/components/ring/binary_sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/binary_sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/binary_sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/sensor.py Outdated Show resolved Hide resolved
@home-assistant home-assistant bot marked this pull request as draft April 9, 2024 22:04
@home-assistant
Copy link

home-assistant bot commented Apr 9, 2024

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

@bdraco
Copy link
Member

bdraco commented Apr 9, 2024

Merged in dev to see if CI is fixed

@sdb9696
Copy link
Contributor Author

sdb9696 commented Apr 10, 2024

Many thanks for the excellent review @cdce8p, some really great suggestions!

I've implemented them all except I couldn't get the default TypeVar working properly as per my comments above.

@sdb9696 sdb9696 marked this pull request as ready for review April 10, 2024 09:57
@home-assistant home-assistant bot requested a review from cdce8p April 10, 2024 09:57
@sdb9696
Copy link
Contributor Author

sdb9696 commented Apr 10, 2024

Removed the prefixes. Any thoughts on the default TypeVar not working or should I just leave it as-is?

@sdb9696 sdb9696 requested a review from cdce8p April 10, 2024 11:07
Copy link
Member

@cdce8p cdce8p left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few more suggestions to get the TypeVar default working. It can be a bit tricky sometimes.

See also my other comments regarding your questions on my earlier comments.

homeassistant/components/ring/binary_sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/binary_sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/binary_sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/sensor.py Outdated Show resolved Hide resolved
@@ -59,183 +62,170 @@ async def async_setup_entry(
class RingSensor(RingEntity, SensorEntity):
"""A sensor implementation for Ring device."""

entity_description: RingSensorEntityDescription
entity_description: RingSensorEntityDescription[RingGeneric]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
entity_description: RingSensorEntityDescription[RingGeneric]
entity_description: RingSensorEntityDescription[_RingGenericT]
_device: _RingGenericT

Ideally _device in RingBaseEntity would already be generic but that will likely break a few more things. If you want to explore that, I suggest to do it afterwards. For now setting _device: _RingGenericT here works fine.

Feel free to ping me once you do 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's done, I previously tried to get the _device generic in RingBaseEntity but ran into a few issues. I've reached out on discord to ask about this.

homeassistant/components/ring/sensor.py Outdated Show resolved Hide resolved
Comment on lines 86 to 88
self._device = self._get_coordinator_data().get_device(
self._device.device_api_id
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self._device = self._get_coordinator_data().get_device(
self._device.device_api_id
)
self._device = cast(
_RingGenericT,
self._get_coordinator_data().get_device(self._device.device_api_id),
)

This is a limitation from the library. get_device always returns RingGeneric. Ideally it would return the correct subclass for a specific device_api_id.

Possible, but that is advanced typing territory. Message me on discord if you're interested. Then we can see if we're able to get this to work together.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am very interested, have reached out.

homeassistant/components/ring/sensor.py Outdated Show resolved Hide resolved
homeassistant/components/ring/sensor.py Show resolved Hide resolved
@home-assistant home-assistant bot marked this pull request as draft April 10, 2024 11:09
@sdb9696 sdb9696 marked this pull request as ready for review April 10, 2024 11:52
@home-assistant home-assistant bot requested a review from cdce8p April 10, 2024 11:52
Comment on lines 139 to 144
SENSOR_TYPES: tuple[
RingSensorEntityDescription[RingGeneric]
| RingSensorEntityDescription[RingDoorBell | RingChime]
| RingSensorEntityDescription[RingOther],
...,
] = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SENSOR_TYPES: tuple[
RingSensorEntityDescription[RingGeneric]
| RingSensorEntityDescription[RingDoorBell | RingChime]
| RingSensorEntityDescription[RingOther],
...,
] = (
SENSOR_TYPES: tuple[RingSensorEntityDescription[Any, ...] = (

Quite a tricky situation here. This actually needs to be Any, ... for the RingSensor(..., description) call to be correct. As you noticed though, it causes the inference for value_fn to break. Not sure there is a good solution here.

It doesn't work correctly with the current annotations either. Just an example, for the battery description, device inside the value_fn lambda would be inferred as RingDoorBell | RingChime by both mypy and pyright. I honestly don't know why.

Anyway, we somehow have to make it work now. I would suggest using Any but then for each RingSensorEntityDescription adding a subscript even if it should have been provided by the TypeVar default. That should do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, so it did, that's strange. Anyway I've added the subscripts, reverted to Any and put a comment to explain.

@home-assistant home-assistant bot marked this pull request as draft April 10, 2024 22:01
@sdb9696 sdb9696 marked this pull request as ready for review April 11, 2024 07:54
@home-assistant home-assistant bot requested a review from cdce8p April 11, 2024 07:54
Copy link
Member

@cdce8p cdce8p left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks @sdb9696 👍🏻

@cdce8p cdce8p merged commit 6954fcc into home-assistant:dev Apr 11, 2024
24 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Apr 12, 2024
@sdb9696 sdb9696 deleted the update_ring_typing branch April 19, 2024 10:36
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.

None yet

4 participants