-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Python] Implement TLV List type (#25238)
* [python] Implement TLV List type * Update * Revert "Update" This reverts commit 448c46f. * Fix * Update
- Loading branch information
Showing
4 changed files
with
245 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
#!/usr/bin/env python3 | ||
# coding=utf-8 | ||
|
||
# | ||
# Copyright (c) 2023 Project CHIP Authors | ||
# All rights reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
import dataclasses | ||
import enum | ||
from typing import Any, Iterator, List, Tuple, Union | ||
|
||
|
||
class TLVList: | ||
"""Represents a list in CHIP TLV. | ||
A TLVList can be constructed from a `list` of tuples of tag and value. `None` stands for "anonymous tag". | ||
e.g. | ||
``` | ||
l = TLVList([(1, 'a'), (2, 'b'), (None, 'c')]) | ||
``` | ||
Constructs a list of three items, tag 1 is 'a', tag 2 is 'b' and with an anonymous item 'c'. | ||
Since TLVLists are ordered, it is meanful to iterate over an list: | ||
e.g. | ||
``` | ||
for tag, val in l: | ||
print(f"tag={tag}, val={val}") | ||
``` | ||
Outputs: | ||
``` | ||
tag=1, val=a | ||
tag=2, val=b | ||
tag=None, val=c | ||
``` | ||
One can also append items into an list: | ||
e.g. | ||
``` | ||
l.append(3, 'd') | ||
``` | ||
The content of `l` will be `[(1, 'a'), (2, 'b'), (None, 'c'), (3, 'd')]` | ||
One can access an item in the list via the tag. | ||
e.g. | ||
``` | ||
val = l[1] | ||
# val is 'a' | ||
``` | ||
It is also possible to get an item via the index since it is ordered: | ||
e.g. | ||
``` | ||
tag, val = l[TLVList.IndexMethod.Tag:2] | ||
# tag is None, val is 'c' | ||
``` | ||
""" | ||
|
||
@dataclasses.dataclass | ||
class TLVListItem: | ||
tag: Union[None, int] | ||
value: Any | ||
|
||
def as_tuple(self): | ||
return (self.tag, self.value) | ||
|
||
def as_rich_repr_tuple(self): | ||
if self.tag is None: | ||
return "Anonymous", repr(self.value) | ||
else: | ||
return str(self.tag), repr(self.value) | ||
|
||
def __repr__(self): | ||
if self.tag is None: | ||
return "Anonymous: " + repr(self.value) | ||
else: | ||
return str(self.tag) + ": " + repr(self.value) | ||
|
||
def __rich_repr__(self): | ||
yield self.as_rich_repr_tuple() | ||
|
||
class IndexMethod(enum.Enum): | ||
Index = 0 | ||
Tag = 1 | ||
|
||
class Iterator: | ||
def __init__(self, iter: Iterator): | ||
self._iterator = iter | ||
|
||
def __iter__(self): | ||
return self | ||
|
||
def __next__(self): | ||
res = next(self._iterator) | ||
return res.tag, res.value | ||
|
||
def __init__(self, items: List[Tuple[Union[int, None], Any]] = []): | ||
"""Constructs a TLVList. | ||
items: A list of tuples for the tag and value for the items in the TLVList. | ||
""" | ||
self._data: List[TLVList.TLVListItem] = [] | ||
|
||
for tag, val in items: | ||
self.append(tag, val) | ||
|
||
def _get_item_by_tag(self, tag) -> Any: | ||
if not isinstance(tag, int): | ||
raise ValueError("Tag should be a integer for non-anonymous fields.") | ||
for data in self._data: | ||
if data.tag == tag: | ||
return data.value | ||
raise KeyError(f"Tag {tag} not found in the list.") | ||
|
||
def __getitem__(self, access) -> Any: | ||
"""Gets a item in the list by the tag or the index. | ||
Examples: | ||
``` | ||
tlv_list[1] # returns the item in the list with tag `1` | ||
tlv_list[TLVList.IndexMethod.Tag:2] # returns the item in the list with tag `2` | ||
tlv_list[TLVList.IndexMethod.Index:0] # returns the tag and value of the first item in the list | ||
``` | ||
""" | ||
if isinstance(access, slice): | ||
tag, index = access.start, access.stop | ||
if tag == TLVList.IndexMethod.Tag: | ||
return self._get_item_by_tag(index) | ||
elif tag == TLVList.IndexMethod.Index: | ||
return self._data[index].as_tuple() | ||
raise ValueError("Method should be TLVList.IndexMethod.Tag or TLVList.IndexMethod.Index") | ||
elif isinstance(access, int): | ||
return self._get_item_by_tag(access) | ||
raise ValueError("Invalid access method") | ||
|
||
def append(self, tag: Union[None, int], value: Any) -> None: | ||
"""Appends an item to the list.""" | ||
if (tag is not None) and (not isinstance(tag, int)): | ||
raise KeyError(f"Tag should be a integer or none for anonymous tag, {type(tag)} got") | ||
self._data.append(TLVList.TLVListItem(tag, value)) | ||
|
||
def __repr__(self): | ||
return "TLVList" + repr(self._data) | ||
|
||
def __rich_repr__(self): | ||
for items in self._data: | ||
yield items.as_rich_repr_tuple() | ||
|
||
def __iter__(self) -> """TLVList.Iterator""": | ||
return TLVList.Iterator(iter(self._data)) | ||
|
||
def __eq__(self, rhs: "TLVList") -> bool: | ||
if not isinstance(rhs, TLVList): | ||
return False | ||
return self._data == rhs._data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters