-
-
Notifications
You must be signed in to change notification settings - Fork 28.5k
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
Conversation
ON = auto() | ||
OFF = auto() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍 |
Merged in dev to see if CI is fixed |
Many thanks for the excellent review @cdce8p, some really great suggestions! I've implemented them all except I couldn't get the |
Removed the prefixes. Any thoughts on the |
There was a problem hiding this 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.
@@ -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] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 😄
There was a problem hiding this comment.
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.
self._device = self._get_coordinator_data().get_device( | ||
self._device.device_api_id | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
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.
SENSOR_TYPES: tuple[ | ||
RingSensorEntityDescription[RingGeneric] | ||
| RingSensorEntityDescription[RingDoorBell | RingChime] | ||
| RingSensorEntityDescription[RingOther], | ||
..., | ||
] = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks @sdb9696 👍🏻
Proposed change
Add ring integration to
.strict-typing
and associated type hint changes.get
functions.sensor.py
has been refactored to support thevalue_fn
for native values.RingDeviceData
class has been dropped from the coordinator as the library now stores history with the device.Type of change
Additional information
Checklist
ruff format homeassistant tests
)If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
Updated and included derived files by running:
python3 -m script.hassfest
.requirements_all.txt
.Updated by running
python3 -m script.gen_requirements_all
..coveragerc
.To help with the load of incoming pull requests: