-
Notifications
You must be signed in to change notification settings - Fork 17
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
feat: optimize update_from_dict #75
Changes from 4 commits
681d0a4
29c931e
ae318ee
2903ad2
2784c1d
b8f01c4
6367dd1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -485,68 +485,54 @@ | |
|
||
return new_data | ||
|
||
def _inject_api( | ||
self, | ||
data: dict[str, Any], | ||
api: ProtectApiClient | None, | ||
) -> dict[str, Any]: | ||
data["api"] = api | ||
unifi_objs_sets = self._get_protect_objs_set() | ||
has_unifi_objs = bool(unifi_objs_sets) | ||
unifi_lists_sets = self._get_protect_lists_set() | ||
has_unifi_lists = bool(unifi_lists_sets) | ||
unifi_dicts_sets = self._get_protect_dicts_set() | ||
has_unifi_dicts = bool(unifi_dicts_sets) | ||
for key, value in data.items(): | ||
if has_unifi_objs and key in unifi_objs_sets and isinstance(value, dict): | ||
value["api"] = api | ||
elif ( | ||
has_unifi_lists and key in unifi_lists_sets and isinstance(value, list) | ||
): | ||
for item in value: | ||
if isinstance(item, dict): | ||
item["api"] = api | ||
elif ( | ||
has_unifi_dicts and key in unifi_dicts_sets and isinstance(value, dict) | ||
): | ||
for item in value.values(): | ||
if isinstance(item, dict): | ||
item["api"] = api | ||
def update_from_dict(cls: ProtectObject, data: dict[str, Any]) -> ProtectObject: | ||
""" | ||
Updates current object from a cleaned UFP JSON dict. | ||
|
||
return data | ||
The api client is injected into each dict for any child | ||
UFP objects that are detected. | ||
""" | ||
unifi_objs = cls._get_protect_objs() | ||
has_unifi_objs = bool(unifi_objs) | ||
unifi_lists = cls._get_protect_lists() | ||
has_unifi_lists = bool(unifi_lists) | ||
unifi_dicts_sets = cls._get_protect_dicts_set() | ||
has_unifi_dicts = bool(unifi_dicts_sets) | ||
|
||
def update_from_dict(self: ProtectObject, data: dict[str, Any]) -> ProtectObject: | ||
"""Updates current object from a cleaned UFP JSON dict""" | ||
data_set = set(data) | ||
for key in self._get_protect_objs_set().intersection(data_set): | ||
unifi_obj: Any | None = getattr(self, key) | ||
if unifi_obj is not None and isinstance(unifi_obj, ProtectBaseObject): | ||
item = data.pop(key) | ||
if item is not None: | ||
item = unifi_obj.update_from_dict(item) | ||
setattr(self, key, item) | ||
|
||
data = self._inject_api(data, self._api) | ||
unifi_lists = self._get_protect_lists() | ||
for key in self._get_protect_lists_set().intersection(data_set): | ||
if not isinstance(data[key], list): | ||
continue | ||
klass = unifi_lists[key] | ||
new_items = [] | ||
for item in data.pop(key): | ||
if item is not None and isinstance(item, ProtectBaseObject): | ||
new_items.append(item) | ||
elif isinstance(item, dict): | ||
new_items.append(klass(**item)) | ||
setattr(self, key, new_items) | ||
|
||
# Always injected above | ||
del data["api"] | ||
|
||
for key in data: | ||
setattr(self, key, convert_unifi_data(data[key], self.__fields__[key])) | ||
|
||
return self | ||
api = cls._api | ||
_fields = cls.__fields__ | ||
unifi_obj: ProtectBaseObject | ||
if "api" in data: | ||
del data["api"] | ||
value: Any | ||
|
||
for key, item in data.items(): | ||
if has_unifi_objs and key in unifi_objs and isinstance(item, dict): | ||
item["api"] = api | ||
unifi_obj = getattr(cls, key) | ||
value = unifi_obj.update_from_dict(item) | ||
elif has_unifi_lists and key in unifi_lists and isinstance(item, list): | ||
klass = unifi_lists[key] | ||
value = [ | ||
klass(**i, api=api) if isinstance(i, dict) else i | ||
for i in item | ||
if i is not None and isinstance(i, (dict, ProtectBaseObject)) | ||
] | ||
else: | ||
# Inject the api if the key is in the unifi_dicts_sets | ||
if ( | ||
has_unifi_dicts | ||
and key in unifi_dicts_sets | ||
and isinstance(item, dict) | ||
): | ||
for i in item.values(): | ||
if isinstance(i, dict): | ||
i["api"] = api | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the previous comment, this line where the API client is injected into dictionary values was not covered by tests. Given that this is a key part of the API injection logic, comprehensive testing is essential to ensure that the API client is correctly and safely injected into all applicable objects. I can help draft these unit tests or open a GitHub issue to track this task. Let me know how you would like to proceed. ToolsGitHub Check: codecov/patch
|
||
value = convert_unifi_data(item, _fields[key]) | ||
|
||
setattr(cls, key, value) | ||
|
||
return cls | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The refactored
Consider adding error handling and working with a copy of the data to avoid side-effects. + data = data.copy() # Work with a copy to avoid side-effects
+ try:
+ if unifi_obj is None:
+ continue # Skip if the attribute does not exist
+ except Exception as e:
+ _LOGGER.error("Failed to update from dict: %s", e)
+ raise
ToolsGitHub Check: codecov/patch
|
||
|
||
def dict_with_excludes(self) -> dict[str, Any]: | ||
"""Returns a dict of the current object without any UFP objects converted to dicts.""" | ||
|
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 line where a new instance of a class is created inside a list comprehension was not covered by tests. This is a crucial part of the functionality as it involves data transformation and API injection. It is recommended to add unit tests to cover this scenario to ensure that the creation and initialization of objects in lists are handled correctly.
Would you like assistance in writing these unit tests?
Tools
GitHub Check: codecov/patch