Skip to content

Commit

Permalink
Fix incorrect super().__getattr__() use on devicestatus
Browse files Browse the repository at this point in the history
  • Loading branch information
rytilahti committed Jan 15, 2023
1 parent d4b939c commit 3b65ddb
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 6 deletions.
1 change: 1 addition & 0 deletions miio/click_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ def wrap(*args, **kwargs):
echo(json.dumps(ex.args[0], indent=indent))
return

# TODO: __json__ is not used anywhere and could be removed
get_json_data_func = getattr(result, "__json__", None)
data_variable = getattr(result, "data", None)
if get_json_data_func is not None:
Expand Down
13 changes: 7 additions & 6 deletions miio/devicestatus.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
get_type_hints,
)

import attr

from .descriptors import (
ActionDescriptor,
BooleanSettingDescriptor,
Expand Down Expand Up @@ -115,7 +117,6 @@ def embed(self, other: "DeviceStatus"):

for name, sensor in other.sensors().items():
final_name = f"{other_name}__{name}"
import attr

self._sensors[final_name] = attr.evolve(sensor, property=final_name)

Expand All @@ -125,13 +126,13 @@ def embed(self, other: "DeviceStatus"):

def __getattr__(self, item):
"""Overridden to lookup properties from embedded containers."""
if "__" not in item:
return super().__getattr__(item)
if item.startswith("__") and item.endswith("__"):
return super().__getattribute__(item)

if item == "__json__": # special handling for custom json dunder
return None
embed, prop = item.split("__", maxsplit=1)
if not embed or not prop:
return super().__getattribute__(item)

embed, prop = item.split("__")
return getattr(self._embedded[embed], prop)


Expand Down
30 changes: 30 additions & 0 deletions miio/tests/test_devicestatus.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,36 @@ def return_none(self):
assert repr(NoneStatus()) == "<NoneStatus return_none=None>"


def test_json_dunder():
"""Make sure that getattr on __json__ returns None when not defined."""

class JsonDunder(DeviceStatus):
def __json__(self):
return "{}"

class NoJsonDunder(DeviceStatus):
pass

assert getattr(JsonDunder(), "__json__", None)() == "{}"
assert getattr(NoJsonDunder(), "__json__", None) is None


def test_get_attribute(mocker):
"""Make sure that __get_attribute__ works as expected."""

class TestStatus(DeviceStatus):
@property
def existing_attribute(self):
pass

status = TestStatus()
with pytest.raises(AttributeError):
_ = status.__missing_attribute

with pytest.raises(AttributeError):
_ = status.__missing_dunder__


def test_sensor_decorator():
class DecoratedProps(DeviceStatus):
@property
Expand Down

0 comments on commit 3b65ddb

Please sign in to comment.