From 39c9ee09e6742dbb22a55aebcf693e4f2974d970 Mon Sep 17 00:00:00 2001 From: MatrixEditor <58256046+MatrixEditor@users.noreply.github.com> Date: Sun, 10 Dec 2023 20:35:41 +0100 Subject: [PATCH 1/2] Documentation updates + Fixed errors with quote() calls + Some typing hints modified --- README.md | 12 ++++++------ fsapi/isu/format.py | 6 +++--- fsapi/isu/update.py | 2 +- fsapi/net/_wrap.py | 17 ++++++++--------- fsapi/net/base.py | 14 +++++++------- fsapi/net/cli.py | 2 +- fsapi/net/device.py | 25 ++++++++++++++----------- nodes/fsapi_nodes.py | 2 +- 8 files changed, 41 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 81e59c2..9df8683 100644 --- a/README.md +++ b/README.md @@ -210,19 +210,19 @@ api = wrap(device) friendly_name = api.friendly_name # or manually response = device.get(nodes / "netRemote.sys.info.friendlyName") -if response.status == FS_OK: +if response.success: #_ Again, type(content) = nodes.BaseSysInfoFriendlyName friendly_name = response.content.value - # Apply a new name via wrapper - api.friendly_name = "FooBar" - # or manually - device.put(nodes / "netRemote.sys.info.friendlyName", value="FooBar") +# Apply a new name via wrapper +api.friendly_name = "FooBar" +# or manually +device.put(nodes / "netRemote.sys.info.friendlyName", value="FooBar") # get all elements of a list valid_modes = api.ls_valid_modes() # get a certain amount of elements beginning at index 3 -valid_mpdes = api.ls_valid_modes(_pos=3, max_items=10) +valid_modes = api.ls_valid_modes(_pos=3, max_items=10) ``` ## Software Update diff --git a/fsapi/isu/format.py b/fsapi/isu/format.py index 920d882..d7836ec 100644 --- a/fsapi/isu/format.py +++ b/fsapi/isu/format.py @@ -137,7 +137,7 @@ def is_compressed(self) -> bool: @dataclass_struct class ISUArchiveIndexDirectoryEntry: entry_count: int = csfield(cs.Int8ul) - entries: list[ISUArchiveIndexEntry] = subcsfield( + entries: t.List[ISUArchiveIndexEntry] = subcsfield( ISUArchiveIndexEntry, cs.Array(cs.this.entry_count, ISUArchiveIndexEntry.struct) ) @@ -147,7 +147,7 @@ class ISUArchiveIndex: length: int = csfield(cs.Int8ul) name: bytes = csfield(cs.Bytes(cs.this.length)) # always 0 entry_count: int = csfield(cs.Int8ul) - entries: list[ISUArchiveIndexEntry] = subcsfield( + entries: t.List[ISUArchiveIndexEntry] = subcsfield( ISUArchiveIndexEntry, cs.Array(cs.this.entry_count, ISUArchiveIndexEntry.struct) ) @@ -381,7 +381,7 @@ def __init__(self, isu: ISU) -> None: self.isu = isu self._fields = self._parse_fields() - def _parse_fields(self) -> list[ISUDataField]: + def _parse_fields(self) -> t.List[ISUDataField]: index = self.isu.stream.find(b"DecompBuffer") if index == -1: # Rather return an empty list than raising an error diff --git a/fsapi/isu/update.py b/fsapi/isu/update.py index fe96874..ebfe0ad 100644 --- a/fsapi/isu/update.py +++ b/fsapi/isu/update.py @@ -153,7 +153,7 @@ class UpdateRequest: """ status: Union[UpdateStatus, int] = UpdateStatus.ERROR - updates: list[ISUSoftwareElement] = dataclasses.field(default_factory=list) + updates: t.List[ISUSoftwareElement] = dataclasses.field(default_factory=list) error: Optional[Exception] = None @property diff --git a/fsapi/net/_wrap.py b/fsapi/net/_wrap.py index b5a2ce6..15238be 100644 --- a/fsapi/net/_wrap.py +++ b/fsapi/net/_wrap.py @@ -82,7 +82,7 @@ class APICall(t.Generic[_T]): def __init__(self, node_path: str) -> None: self.node_path = NodePath(node_path) - def __get__(self, __instance: t.Any, __owner: type | None = None) -> _T: + def __get__(self, __instance: t.Any, __owner: t.Optional[type] = None) -> _T: return self.get(__instance) def __set__(self, __instance: t.Any, __value: _T) -> None: @@ -119,7 +119,7 @@ def put(self, wrapper: _Wrapper, value: _T) -> None: wrapper.device.put(self.node_path, value=value) -class ListAPICall(APICall["list[NodeListItem]"]): +class ListAPICall(APICall[t.List[NodeListItem]]): """Representation for a list API call. This class behaves differently compared to standard API call variables @@ -137,22 +137,22 @@ def __init__(self, node_path: str) -> None: super().__init__(node_path) self.__instance = None - def __get__(self, __instance: t.Any, __owner: type | None = None) -> ListAPICall: + def __get__(self, __instance: t.Any, __owner: t.Optional[type] = None) -> ListAPICall: self.__instance = __instance return self - def __call__(self, _pos=-1, max_items=0xFFFF, **argv) -> list[NodeListItem] | None: + def __call__(self, _pos=-1, max_items=0xFFFF, **argv) -> t.List[NodeListItem]: argv["_pos"] = _pos argv["maxItems"] = max_items return self.get(self.__instance, **argv) - def get(self, wrapper: _Wrapper, **argv) -> list[NodeListItem] | None: + def get(self, wrapper: _Wrapper, **argv) -> t.List[NodeListItem]: """Returns a list of items. :param wrapper: the api wrapper instance :type wrapper: _Wrapper :return: the list returned by the device - :rtype: list[NodeListItem] | None + :rtype: t.List[NodeListItem] """ response = wrapper.device.list_get_next(self.node_path, **argv) if response.status != Status.FS_OK: @@ -226,13 +226,13 @@ def device(self) -> FSDevice: return self.__device() return self.__device - def get_field(self, name: str) -> APICall | ListAPICall: + def get_field(self, name: str) -> t.Union[APICall, ListAPICall]: """Returns a field of this instance named by the provided string. :param name: the field's name :type name: str :return: the api call wrapper instance - :rtype: APICall | ListAPICall + :rtype: t.Union[APICall, ListAPICall] """ return getattr(self, name) @@ -287,4 +287,3 @@ def get_field(self, name: str) -> APICall | ListAPICall: ls_incident_reports: _lac = ListAPICall("netRemote.debug.incidentReport.list") ls_clock_sources: _lac = ListAPICall("netRemote.sys.caps.clockSourceList") ls_languages: _lac = ListAPICall("netRemote.sys.caps.validLang") - ls_: _lac = ListAPICall("netRemote.sys.caps.clockSourceList") \ No newline at end of file diff --git a/fsapi/net/base.py b/fsapi/net/base.py index 1ac3096..09eb132 100644 --- a/fsapi/net/base.py +++ b/fsapi/net/base.py @@ -352,7 +352,7 @@ def cacheable(cls) -> bool: return cls._meta["cacheable"] @property - def prototype(cls) -> list[Argument] | _Dynamic: + def prototype(cls) -> t.List[Argument] | _Dynamic: """Returns the prototype if this node.""" return cls._meta["prototype"] @@ -512,11 +512,11 @@ def __eq__(self, __value: object) -> bool: return super().__eq__(__value) @property - def value(self) -> int | str | list[NodeListItem] | NodeValue: + def value(self) -> int | str | t.List[NodeListItem] | NodeValue: """The node's value :return: the currently applied node value or None - :rtype: int | str | list[NodeListItem] | NodeValue + :rtype: int | str | t.List[NodeListItem] | NodeValue """ return self.__value @@ -731,11 +731,11 @@ def __repr__(self) -> str: return f"NodeListItem({repr(self.attrib)})" @property - def fields(self) -> list[str]: + def fields(self) -> t.List[str]: """Returns a list of field names. :return: all field names stored by this item. - :rtype: list[str] + :rtype: t.List[str] """ return list(self.attrib) @@ -776,11 +776,11 @@ def __iter__(self) -> Iterator[NodeListItem]: return iter(self.items) @property - def items(self) -> list[NodeListItem]: + def items(self) -> t.List[NodeListItem]: """Returns the stored items as a list. :return: the stored list items - :rtype: list[NodeListItem] + :rtype: t.List[NodeListItem] """ return self.value diff --git a/fsapi/net/cli.py b/fsapi/net/cli.py index 2c58863..b0feeef 100644 --- a/fsapi/net/cli.py +++ b/fsapi/net/cli.py @@ -213,7 +213,7 @@ def View(value: str, search=False, disable_color=False, **kwds) -> None: def Get( host: str, pin: str, - nodes: list[str], + nodes: t.List[str], force_session=False, disable_color=False, simulate=False, diff --git a/fsapi/net/device.py b/fsapi/net/device.py index 62f01da..b9d7de7 100644 --- a/fsapi/net/device.py +++ b/fsapi/net/device.py @@ -172,7 +172,7 @@ def __init__( # The new value will be mapped automatically self.new_session() - def get(self, *__nodes: t.Iterable[_NodeType]) -> FSResponse | list[FSResponse]: + def get(self, *__nodes: t.Iterable[_NodeType]) -> t.Union[FSResponse, t.List[FSResponse]]: nodes = list(__nodes) if len(nodes) == 1: return self.node_request(Method.GET, nodes[0]) @@ -194,7 +194,7 @@ def list_get_next(self, node: _NodeType, **argv) -> FSResponse: def list_get_prev(self, node: _NodeType, **argv) -> FSResponse: return self.node_request(Method.LIST_GET_PREV, node, **argv) - def get_notifies(self) -> list[Node]: + def get_notifies(self) -> t.List[Node]: if self.sid is None or self.sid == -1: # NOTE: The unit will only execute this command if the user sends # a valid Session ID @@ -288,20 +288,20 @@ def node_request( def node_request_multiple( self, method: Method, - nodes: list[_NodeType], + nodes: t.List[_NodeType], config: FSNetConfiguration = None, **argv, - ) -> list[FSResponse]: + ) -> t.List[FSResponse]: """Performs a multiple node request. :param method: the API method :type method: Method :param nodes: the list of nodes to query - :type nodes: list[_NodeType] + :type nodes: t.List[_NodeType] :param config: the network configuration, defaults to None :type config: FSNetConfiguration, optional :return: a list of response objects storing the de-serialized data. - :rtype: list[FSResponse] + :rtype: t.List[FSResponse] """ node_paths = list(map(self._to_node_path, nodes)) path_elements = ["fsapi", method.name] @@ -387,18 +387,18 @@ def _create_url(self, path: str, parameters: dict) -> str: def _build_parameters(self, parameters: dict) -> str: # NOTE: These parameters must occur before any other custom # parameter as GET_MULTIPLE would result in errors. - query_params = [f"pin={quote(self.pin)}"] + query_params = [f"pin={self._quote(self.pin)}"] if self.sid is not None and self.sid != -1: - query_params.append(f"sid={quote(self.sid)}") + query_params.append(f"sid={self._quote(self.sid)}") for key in parameters: - name = quote(key) + name = self._quote(key) value = parameters[key] if isinstance(value, list): for list_element in value: - query_params.append(f"{name}={quote(str(list_element))}") + query_params.append(f"{name}={self._quote(list_element)}") else: - query_params.append(f"{name}={quote(str(value))}") + query_params.append(f"{name}={self._quote(value)}") return "&".join(query_params) def _unmarshal(self, node_path: NodePath, tree: ElementTree.ElementTree) -> Node: @@ -428,3 +428,6 @@ def _to_node_path(self, node: _NodeType) -> NodePath: raise TypeError(f"Invalid node path type: {type(node)}") return node_path + + def _quote(self, obj: t.Any) -> str: + return quote(str(obj)) diff --git a/nodes/fsapi_nodes.py b/nodes/fsapi_nodes.py index 5ef502e..47d9278 100644 --- a/nodes/fsapi_nodes.py +++ b/nodes/fsapi_nodes.py @@ -83,7 +83,7 @@ def get_enum_class(java_class: str) -> str: return content.replace("\n", "").replace(" ", "") -def get_list_prototype(java_class) -> list[str]: +def get_list_prototype(java_class) -> t.List[str]: prototype = java_class[java_class.find("Prototype == null") :] return prototype[prototype.find("{") + 1 : prototype.find("}")].split("\n") From 254c7ca8ba3646b53d44c1f85c5ab18d609427ee Mon Sep 17 00:00:00 2001 From: MatrixEditor <58256046+MatrixEditor@users.noreply.github.com> Date: Sun, 10 Dec 2023 20:47:32 +0100 Subject: [PATCH 2/2] Fixed code errors + Renamed "netRemote_debug_incidentReport_lastCreatedKey_nt" -> "netRemote_debug_incidentReport_list_nt" + Removed unnecesary classes + FIxed some imports --- docs/api-examples.rst | 2 +- docs/conf.py | 2 +- fsapi/__init__.py | 2 +- fsapi/__main__.py | 2 +- fsapi/isu/cli.py | 2 +- fsapi/isu/format.py | 11 ----------- fsapi/isu/update.py | 1 + fsapi/net/_enum.py | 10 ++++++---- fsapi/net/_nodes.py | 2 +- fsapi/net/cli.py | 2 +- pyproject.toml | 2 +- 11 files changed, 15 insertions(+), 23 deletions(-) diff --git a/docs/api-examples.rst b/docs/api-examples.rst index eb4d018..d12ea14 100644 --- a/docs/api-examples.rst +++ b/docs/api-examples.rst @@ -109,7 +109,7 @@ devices: friendly_name = api.friendly_name # or manually response = device.get(nodes / "netRemote.sys.info.friendlyName") - if response.status == FS_OK: + if response.success: #_ Again, type(content) = nodes.BaseSysInfoFriendlyName friendly_name = response.content.value diff --git a/docs/conf.py b/docs/conf.py index eb732f0..77f7b79 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,7 +11,7 @@ project = 'fsapi-tools' copyright = '2022, MatrixEditor' author = 'MatrixEditor' -release = '2.0.1' +release = '2.0.2' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/fsapi/__init__.py b/fsapi/__init__.py index f768e52..b7ead4d 100644 --- a/fsapi/__init__.py +++ b/fsapi/__init__.py @@ -26,5 +26,5 @@ >>> python3 -m fsapi --help """ -__version__ = "2.0.1" +__version__ = "2.0.2" __author__ = 'MatrixEditor' diff --git a/fsapi/__main__.py b/fsapi/__main__.py index e23e20f..8a4bafe 100644 --- a/fsapi/__main__.py +++ b/fsapi/__main__.py @@ -19,7 +19,7 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from fsapi.netremote.cli import main +from fsapi.net.cli import main if __name__ == '__main__': main() \ No newline at end of file diff --git a/fsapi/isu/cli.py b/fsapi/isu/cli.py index 0d50397..344ec8f 100644 --- a/fsapi/isu/cli.py +++ b/fsapi/isu/cli.py @@ -180,7 +180,7 @@ def _run(argv, target: pathlib.Path, pp: DataclassPrinter): # Don't include the data section if no explicitly activated values["archive"].pop("data") - with open(str(out_path), "w") as fp: + with open(str(out_path), "w", encoding="utf-8") as fp: json.dump(values, fp, cls=BytesJSONEncoder) pp.print_msg(Fore.LIGHTBLACK_EX, "[out] JSON saved to", str(out_path)) else: diff --git a/fsapi/isu/format.py b/fsapi/isu/format.py index d7836ec..3e7d2ff 100644 --- a/fsapi/isu/format.py +++ b/fsapi/isu/format.py @@ -162,17 +162,6 @@ class ISUArchive: data: bytes = csfield(cs.Bytes(cs.this.size - cs.this.index_size - 4)) -@dataclass_struct -class ISUDataField: - length: int = csfield(cs.Int16ul) - unknown_1: int = csfield(cs.Int16ul) - name_length: int = csfield(cs.Int16ul) - flags: int = csfield(cs.Int16ul) - name: str = csfield(cs.PaddedString(16, "utf-8")) - value: int | None = csfield(cs.If(cs.this.length == 32, cs.Int32ul)) - unknown_2: int | None = csfield(cs.If(cs.this.length == 32, cs.Int32ul)) - - @dataclass_struct class ISUDataSection: magic: int = csfield(cs.Int8ul) diff --git a/fsapi/isu/update.py b/fsapi/isu/update.py index ebfe0ad..7fe4d2a 100644 --- a/fsapi/isu/update.py +++ b/fsapi/isu/update.py @@ -36,6 +36,7 @@ import dataclasses import enum import xml.etree.ElementTree as xmltree +import typing as t from typing import Optional, Union from fsapi.netconfig import FSNetConfiguration diff --git a/fsapi/net/_enum.py b/fsapi/net/_enum.py index 75cdcf5..6941f6c 100644 --- a/fsapi/net/_enum.py +++ b/fsapi/net/_enum.py @@ -25,6 +25,7 @@ import sys import enum +from typing import Optional class FSAlarmSource(enum.IntEnum): BUZZER = 0 @@ -121,14 +122,15 @@ class FSEQPreset(enum.IntEnum): NEWS = enum.auto() -def enum_value_name(value: int, enum: str) -> str | None: - member = get_enum(enum) +def enum_value_name(value: int, enum_: str) -> Optional[str]: + member = get_enum(enum_) if member is None: return None - return member._value2member_map_[value] + return getattr(member, "_value2member_map_")[value] -def get_enum(field_name: str, path: str = None) -> type | None: + +def get_enum(field_name: str, path: str = None) -> Optional[type]: module = sys.modules[__name__] internal_name = f"FS_{field_name.upper()}" diff --git a/fsapi/net/_nodes.py b/fsapi/net/_nodes.py index 71c73f2..b61f24e 100644 --- a/fsapi/net/_nodes.py +++ b/fsapi/net/_nodes.py @@ -62,7 +62,7 @@ # ============================================================================ -class netRemote_debug_incidentReport_lastCreatedKey_nt(NodeList): +class netRemote_debug_incidentReport_list_nt(NodeList): class Meta: path = "netRemote.debug.incidentReport.list" name = "BaseDebugIncidentReportList" diff --git a/fsapi/net/cli.py b/fsapi/net/cli.py index b0feeef..497e0c2 100644 --- a/fsapi/net/cli.py +++ b/fsapi/net/cli.py @@ -423,7 +423,7 @@ def Scan( method = Method.GET node_cls = (nodes / node).get_node_type() if node_cls.is_list: - method = method.LIST_GET_NEXT + method = Method.LIST_GET_NEXT if simulate: pp.print_url(device.get_url(method, node)) diff --git a/pyproject.toml b/pyproject.toml index 137fb53..809aecf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "fsapi-tools" -version = "2.0.1" +version = "2.0.2" description="Frontier Smart Firmware Tools and FSAPI Implementation." authors = [ { name="MatrixEditor", email="not@supported.com" },