diff --git a/construct_editor/core/commands.py b/construct_editor/core/commands.py index 17ae3b1..421f751 100644 --- a/construct_editor/core/commands.py +++ b/construct_editor/core/commands.py @@ -22,7 +22,7 @@ def __init__(self, max_commands: int) -> None: self._max_commands = max_commands self._history: t.List[Command] = [] - self._current_command_idx: t.Optional[int] = None + self._current_command_idx: int | None = None def can_undo(self) -> bool: """ @@ -131,7 +131,7 @@ def clear_commands(self): self._history.clear() self._current_command_idx = None - def get_current_command(self) -> t.Optional[Command]: + def get_current_command(self) -> Command | None: """ Returns the current command. """ @@ -140,7 +140,7 @@ def get_current_command(self) -> t.Optional[Command]: return self._history[self._current_command_idx] - def get_next_command(self) -> t.Optional[Command]: + def get_next_command(self) -> Command | None: """ Returns the next command. """ diff --git a/construct_editor/core/construct_editor.py b/construct_editor/core/construct_editor.py index 3427d00..647977d 100644 --- a/construct_editor/core/construct_editor.py +++ b/construct_editor/core/construct_editor.py @@ -11,7 +11,7 @@ class ConstructEditor: - def __init__(self, construct: cs.Construct, model: ConstructEditorModel): + def __init__(self, construct: cs.Construct[t.Any, t.Any], model: ConstructEditorModel): self._model = model self.change_construct(construct) @@ -30,7 +30,7 @@ def reload(self): """ @abc.abstractmethod - def show_parse_error_message(self, msg: t.Optional[str], ex: t.Optional[Exception]): + def show_parse_error_message(self, msg: str | None, ex: Exception | None): """ Show an parse error message to the user. @@ -38,7 +38,7 @@ def show_parse_error_message(self, msg: t.Optional[str], ex: t.Optional[Exceptio """ @abc.abstractmethod - def show_build_error_message(self, msg: t.Optional[str], ex: t.Optional[Exception]): + def show_build_error_message(self, msg: str | None, ex: Exception | None): """ Show an build error message to the user. @@ -78,7 +78,7 @@ def _put_to_clipboard(self, txt: str): """ @abc.abstractmethod - def _get_from_clipboard(self): + def _get_from_clipboard(self) -> str | None: """ Get text from the clipboard. @@ -113,7 +113,7 @@ def paste_entry_value_from_clipboard(self, entry: "entries.EntryConstruct"): # self.model.set_value(txt, entry, ConstructEditorColumn.Value) # self.on_root_obj_changed.fire(self.root_obj) - def change_construct(self, constr: cs.Construct) -> None: + def change_construct(self, constr: cs.Construct[t.Any, t.Any]) -> None: """ Change the construct format, that is used for building/parsing. """ @@ -309,14 +309,14 @@ def is_list_view_enabled(self, entry: "entries.EntryConstruct") -> bool: return False @property - def construct(self) -> cs.Construct: + def construct(self) -> cs.Construct[t.Any, t.Any]: """ Construct that is used for displaying. """ return self._construct @construct.setter - def construct(self, constr: cs.Construct): + def construct(self, constr: cs.Construct[t.Any, t.Any]): self.change_construct(constr) @property diff --git a/construct_editor/core/context_menu.py b/construct_editor/core/context_menu.py index 98635a9..cea410d 100644 --- a/construct_editor/core/context_menu.py +++ b/construct_editor/core/context_menu.py @@ -5,7 +5,10 @@ import construct_editor.core.construct_editor as construct_editor import construct_editor.core.entries as entries -from construct_editor.core.model import ConstructEditorModel, IntegerFormat +from construct_editor.core.model import ( + ConstructEditorModel, + IntegerFormat, +) COPY_LABEL = "Copy" PASTE_LABEL = "Paste" @@ -15,6 +18,7 @@ INTFORMAT_DEC_LABEL = "Dec" INTFORMAT_HEX_LABEL = "Hex" + # ##################################################################################################################### # Context Menu ######################################################################################################## # ##################################################################################################################### @@ -26,7 +30,7 @@ class SeparatorMenuItem: @dataclasses.dataclass class ButtonMenuItem: label: str - shortcut: t.Optional[str] + shortcut: str | None enabled: bool callback: t.Callable[[], None] @@ -34,7 +38,7 @@ class ButtonMenuItem: @dataclasses.dataclass class CheckboxMenuItem: label: str - shortcut: t.Optional[str] + shortcut: str | None enabled: bool checked: bool callback: t.Callable[[bool], None] @@ -53,13 +57,7 @@ class SubmenuItem: subitems: t.List["MenuItem"] -MenuItem = t.Union[ - ButtonMenuItem, - SeparatorMenuItem, - CheckboxMenuItem, - RadioGroupMenuItems, - SubmenuItem, -] +MenuItem = ButtonMenuItem | SeparatorMenuItem | CheckboxMenuItem | RadioGroupMenuItems | SubmenuItem class ContextMenu: @@ -161,7 +159,6 @@ def _init_intformat(self): def _init_list_viewed_entries(self): submenu = SubmenuItem("List Viewed Items", []) for e in self.model.list_viewed_entries: - def on_remove_list_viewed_item(checked: bool): self.parent.disable_list_view(e) diff --git a/construct_editor/core/entries.py b/construct_editor/core/entries.py index 4cde882..cc9b985 100644 --- a/construct_editor/core/entries.py +++ b/construct_editor/core/entries.py @@ -89,16 +89,16 @@ class ObjViewSettings_Timestamp: entry: "EntryTimestamp" -ObjViewSettings = t.Union[ - ObjViewSettings_Default, - ObjViewSettings_String, - ObjViewSettings_Integer, - ObjViewSettings_Flag, - ObjViewSettings_Bytes, - ObjViewSettings_Enum, - ObjViewSettings_FlagsEnum, - ObjViewSettings_Timestamp, -] +ObjViewSettings = ( + ObjViewSettings_Default + | ObjViewSettings_String + | ObjViewSettings_Integer + | ObjViewSettings_Flag + | ObjViewSettings_Bytes + | ObjViewSettings_Enum + | ObjViewSettings_FlagsEnum + | ObjViewSettings_Timestamp +) def _convert_restreamed(stream: cs.RestreamedBytesIO) -> io.BytesIO: @@ -109,7 +109,7 @@ def _convert_restreamed(stream: cs.RestreamedBytesIO) -> io.BytesIO: - `cs.BitsSwapped(cs.Bitwise(cs.GreedyRange(cs.Bit)))` """ - def reset_substream_recursively(stream: t.Union[io.BytesIO, cs.RestreamedBytesIO]): + def reset_substream_recursively(stream: io.BytesIO | cs.RestreamedBytesIO): if isinstance(stream, cs.RestreamedBytesIO): if stream.substream is None: raise RuntimeError( @@ -120,7 +120,7 @@ def reset_substream_recursively(stream: t.Union[io.BytesIO, cs.RestreamedBytesIO stream.seek(0) # check if there is already a cached version - bytes_io_stream: t.Optional[io.BytesIO] = getattr( + bytes_io_stream: io.BytesIO | None = getattr( stream, "_construct_bytes_io", None ) @@ -169,9 +169,9 @@ class ListIndexName(str): pass -NameType = t.Union[str, NameExcludedFromPath, ListIndexName] +NameType = str | NameExcludedFromPath | ListIndexName -PathType = t.List[t.Union[str, ListIndexName]] +PathType = t.List[str | ListIndexName] def create_path_str(path: PathType) -> str: @@ -197,7 +197,7 @@ def __init__( model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Construct[Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): self.model = model @@ -275,7 +275,7 @@ def obj_str(self) -> str: # default "obj_metadata" ################################################## @property - def obj_metadata(self) -> t.Optional[GuiMetaData]: + def obj_metadata(self) -> GuiMetaData | None: return get_gui_metadata(self.obj) # default "name" ########################################################## @@ -298,7 +298,7 @@ def typ_str(self) -> str: # default "subentries" #################################################### @property - def subentries(self) -> Optional[List["EntryConstruct"]]: + def subentries(self) -> List["EntryConstruct"] | None: return None # default "visible_row" ################################################### @@ -366,7 +366,7 @@ def path(self) -> PathType: return path def get_stream_infos( - self, child_stream: t.Optional[t.BinaryIO] = None + self, child_stream: t.BinaryIO | None = None ) -> t.List[StreamInfo]: """ Get infos about the current and parent streams. @@ -414,7 +414,7 @@ def __init__( model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Subconstruct[Any, Any, Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -435,7 +435,7 @@ def typ_str(self) -> str: # pass throught "subentries" to subentry ################################## @property - def subentries(self) -> Optional[List["EntryConstruct"]]: + def subentries(self) -> List["EntryConstruct"] | None: return self.subentry.subentries # pass throught "obj_view_settings" to subentry ########################### @@ -450,14 +450,12 @@ def modify_context_menu(self, menu: ContextMenu): # EntryStruct ######################################################################################################### class EntryStruct(EntryConstruct): - construct: "cs.Struct[Any, Any]" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], - construct: "cs.Struct[Any, Any]", - name: t.Optional[NameType], + construct: "cs.Struct", + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -471,7 +469,11 @@ def __init__( self._subentries.append(subentry) @property - def subentries(self) -> Optional[List["EntryConstruct"]]: + def construct(self) -> "cs.Struct": + return t.cast("cs.Struct", self._construct) + + @property + def subentries(self) -> List["EntryConstruct"] | None: return self._subentries @property @@ -514,26 +516,24 @@ def on_collapse_children_clicked(): # EntryArray ########################################################################################################## class EntryArray(EntrySubconstruct): - construct: t.Union[ - "cs.Array[Any, Any, Any, Any]", "cs.GreedyRange[Any, Any, Any, Any]" - ] - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], - construct: t.Union[ - "cs.Array[Any, Any, Any, Any]", "cs.GreedyRange[Any, Any, Any, Any]" - ], - name: t.Optional[NameType], + construct: "cs.Array[Any, Any] | cs.GreedyRange[Any, Any]", + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) - self._subentries = [] + self._subentries: list[EntryConstruct] = [] + + @property + def construct(self) -> "cs.Array[Any, Any] | cs.GreedyRange[Any, Any]": + return t.cast("cs.Array[Any, Any] | cs.GreedyRange[Any, Any]", self._construct) @property - def subentries(self) -> Optional[List["EntryConstruct"]]: + def subentries(self) -> List["EntryConstruct"] | None: # get length of array try: array_len = len(self.obj) @@ -631,14 +631,12 @@ def on_menu_item_clicked(checked: bool): # EntryIfThenElse ##################################################################################################### class EntryIfThenElse(EntryConstruct): - construct: "cs.IfThenElse[Any, Any]" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.IfThenElse[Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -665,7 +663,11 @@ def __init__( self._subentry_else, ] - def _get_subentry(self) -> "Optional[EntryConstruct]": + @property + def construct(self) -> "cs.IfThenElse[Any, Any]": + return t.cast("cs.IfThenElse[Any, Any]", self._construct) + + def _get_subentry(self) -> "EntryConstruct | None": """Evaluate the conditional function to detect the type of the subentry""" obj = self.obj if obj is None: @@ -699,7 +701,7 @@ def typ_str(self) -> str: return subentry.typ_str @property - def subentries(self) -> Optional[List["EntryConstruct"]]: + def subentries(self) -> List["EntryConstruct"] | None: subentry = self._get_subentry() if subentry is None: return self._subentries @@ -724,21 +726,19 @@ def modify_context_menu(self, menu: ContextMenu): # EntrySwitch ######################################################################################################### class EntrySwitch(EntryConstruct): - construct: "cs.Switch[Any, Any]" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Switch[Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) self._subentries: List[EntryConstruct] = [] self._subentry_cases: Dict[str, EntryConstruct] = {} - self._subentry_default: Optional[EntryConstruct] = None + self._subentry_default: EntryConstruct | None = None for key, value in self.construct.cases.items(): subentry_case = create_entry_from_construct( @@ -751,7 +751,7 @@ def __init__( self._subentry_cases[key] = subentry_case self._subentries.append(subentry_case) - if self.construct.default is not None: + if self.construct.default is not None: # type: ignore[reportUnnecessaryComparison] self._subentry_default = create_entry_from_construct( self.model, self, @@ -761,7 +761,11 @@ def __init__( ) self._subentries.append(self._subentry_default) - def _get_subentry(self) -> "Optional[EntryConstruct]": + @property + def construct(self) -> "cs.Switch[Any, Any]": + return t.cast("cs.Switch[Any, Any]", self._construct) + + def _get_subentry(self) -> "EntryConstruct | None": """Evaluate the conditional function to detect the type of the subentry""" obj = self.obj if obj is None: @@ -795,7 +799,7 @@ def typ_str(self) -> str: return subentry.typ_str @property - def subentries(self) -> Optional[List["EntryConstruct"]]: + def subentries(self) -> List["EntryConstruct"] | None: subentry = self._get_subentry() if subentry is None: return self._subentries @@ -832,8 +836,7 @@ class FormatFieldFloat: class EntryFormatField(EntryConstruct): - construct: "cs.FormatField[Any, Any]" - type_mapping: t.Dict[str, t.Union[FormatFieldInt, FormatFieldFloat]] = { + type_mapping: t.Dict[str, FormatFieldInt | FormatFieldFloat] = { ">B": FormatFieldInt("Int8ub", 8, False), ">H": FormatFieldInt("Int16ub", 16, False), ">L": FormatFieldInt("Int32ub", 32, False), @@ -874,7 +877,7 @@ def __init__( model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.FormatField[Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -884,6 +887,10 @@ def __init__( if construct.fmtstr in self.type_mapping: self.type_infos = self.type_mapping[construct.fmtstr] + @property + def construct(self) -> "cs.FormatField[Any, Any]": + return t.cast("cs.FormatField[Any, Any]", self._construct) + @property def obj_view_settings(self) -> ObjViewSettings: if isinstance(self.type_infos, FormatFieldInt): @@ -913,18 +920,20 @@ def typ_str(self) -> str: # EntryBytesInteger ################################################################################################### class EntryBytesInteger(EntryConstruct): - construct: "cs.BytesInteger[Any, Any]" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], - construct: "cs.BytesInteger[Any, Any]", - name: t.Optional[NameType], + construct: "cs.BytesInteger", + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) + @property + def construct(self) -> "cs.BytesInteger": + return t.cast("cs.BytesInteger", self._construct) + @property def typ_str(self) -> str: if self.construct.length == 3: @@ -959,18 +968,20 @@ def obj_view_settings(self) -> ObjViewSettings: # EntryBitsInteger #################################################################################################### class EntryBitsInteger(EntryConstruct): - construct: "cs.BitsInteger[Any, Any]" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], - construct: "cs.BitsInteger[Any, Any]", - name: t.Optional[NameType], + construct: "cs.BitsInteger", + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) + @property + def construct(self) -> "cs.BitsInteger": + return t.cast("cs.BitsInteger", self._construct) + @property def typ_str(self) -> str: # change default row infos @@ -1001,8 +1012,8 @@ def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], - construct: "cs.StringEncoded[Any, Any]", - name: t.Optional[NameType], + construct: "cs.StringEncoded", + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) # type: ignore @@ -1018,19 +1029,21 @@ def obj_view_settings(self) -> ObjViewSettings: # EntryBytes ########################################################################################################## class EntryBytes(EntryConstruct): - construct: t.Union["cs.Bytes[Any, Any]", "cs.Construct[bytes, bytes]"] - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], - construct: t.Union["cs.Bytes[Any, Any]", "cs.Construct[bytes, bytes]"], - name: t.Optional[NameType], + construct: "cs.Bytes | cs.Construct[bytes, bytes]", + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) self.ascii_view = False + @property + def construct(self) -> "cs.Bytes | cs.Construct[bytes, bytes]": + return t.cast("cs.Bytes | cs.Construct[bytes, bytes]", self._construct) + @property def obj_str(self) -> str: try: @@ -1086,7 +1099,7 @@ def __init__( model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Construct[Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -1098,18 +1111,20 @@ def typ_str(self) -> str: # EntrySeek ########################################################################################################### class EntrySeek(EntryConstruct): - construct: "cs.Seek" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Seek", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) + @property + def construct(self) -> "cs.Seek": + return t.cast("cs.Seek", self._construct) + @property def typ_str(self) -> str: return "Seek" @@ -1126,7 +1141,7 @@ def __init__( model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Construct[None, None]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -1146,8 +1161,8 @@ def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], - construct: "cs.Const[Any, Any, Any, Any]", - name: t.Optional[NameType], + construct: "cs.Const[Any, Any]", + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -1163,8 +1178,8 @@ def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], - construct: "cs.Computed[Any, Any]", - name: t.Optional[NameType], + construct: "cs.Computed[Any]", + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -1188,7 +1203,7 @@ def __init__( model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Subconstruct[Any, Any, Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -1212,14 +1227,12 @@ def on_default_clicked(): # EntryFocusedSeq ################################################################################################### class EntryFocusedSeq(EntryConstruct): - construct: "cs.FocusedSeq" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.FocusedSeq", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -1247,7 +1260,11 @@ def __init__( ) self._subentries[name] = subentry - def _get_subentry(self) -> "Optional[EntryConstruct]": + @property + def construct(self) -> "cs.FocusedSeq": + return t.cast("cs.FocusedSeq", self._construct) + + def _get_subentry(self) -> "EntryConstruct | None": """Evaluate the conditional function to detect the type of the subentry""" obj = self.obj if obj is None: @@ -1279,7 +1296,7 @@ def typ_str(self) -> str: return subentry.typ_str @property - def subentries(self) -> Optional[List["EntryConstruct"]]: + def subentries(self) -> List["EntryConstruct"] | None: subentry = self._get_subentry() if subentry is None: return list(self._subentries.values()) @@ -1304,14 +1321,12 @@ def modify_context_menu(self, menu: ContextMenu): # EntrySelect ################################################################################################### class EntrySelect(EntryConstruct): - construct: "cs.Select" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Select", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -1330,7 +1345,11 @@ def __init__( ) self._subentries[id(subentry.construct)] = subentry - def _get_subentry(self) -> "Optional[EntryConstruct]": + @property + def construct(self) -> "cs.Select": + return t.cast("cs.Select", self._construct) + + def _get_subentry(self) -> "EntryConstruct | None": """Evaluate the conditional function to detect the type of the subentry""" obj = self.obj if obj is None: @@ -1367,7 +1386,7 @@ def typ_str(self) -> str: return subentry.typ_str @property - def subentries(self) -> Optional[List["EntryConstruct"]]: + def subentries(self) -> List["EntryConstruct"] | None: subentry = self._get_subentry() if subentry is None: return list(self._subentries.values()) @@ -1397,7 +1416,7 @@ def __init__( model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.TimestampAdapter[Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -1418,7 +1437,7 @@ def __init__( model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Subconstruct[Any, Any, Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -1426,18 +1445,20 @@ def __init__( # EntryNullStripped ################################################################################################### class EntryNullStripped(EntrySubconstruct): - construct: "cs.NullStripped[Any, Any]" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.NullStripped[Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) + @property + def construct(self) -> "cs.NullStripped[Any, Any]": + return t.cast("cs.NullStripped[Any, Any]", self._construct) + @property def typ_str(self) -> str: return f"NullStripped[{self.subentry.typ_str}, Pad={self.construct.pad}]" @@ -1445,18 +1466,20 @@ def typ_str(self) -> str: # EntryNullTerminated ################################################################################################# class EntryNullTerminated(EntrySubconstruct): - construct: "cs.NullTerminated[Any, Any]" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.NullTerminated[Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) + @property + def construct(self) -> "cs.NullTerminated[Any, Any]": + return t.cast("cs.NullTerminated[Any, Any]", self._construct) + @property def typ_str(self) -> str: return f"NullTerminated[{self.subentry.typ_str}, Term={self.construct.term}]" @@ -1469,7 +1492,7 @@ def __init__( model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Checksum[Any, Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): # Don't access EntrySubconstruct's __init__() via super(), because "subcon" is no member of "cs.Checksum" @@ -1488,7 +1511,7 @@ def __init__( model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Compressed[Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -1500,18 +1523,20 @@ def typ_str(self) -> str: # EntryPeek ########################################################################################################### class EntryPeek(EntrySubconstruct): - construct: "cs.Peek" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], - construct: "cs.Peek", - name: t.Optional[NameType], + construct: "cs.Peek[Any, Any]", + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) + @property + def construct(self) -> "cs.Peek[Any, Any]": + return t.cast("cs.Peek[Any, Any]", self._construct) + # EntryRawCopy ####################################################################################################### class EntryRawCopy(EntrySubconstruct): @@ -1519,8 +1544,8 @@ def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], - construct: "cs.RawCopy[Any, Any, Any, Any]", - name: t.Optional[NameType], + construct: "cs.RawCopy[Any, Any]", + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @@ -1530,20 +1555,22 @@ def __init__( # EntryDataclassStruct ################################################################################################ class EntryDataclassStruct(EntrySubconstruct): - construct: "cst.DataclassStruct[Any]" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cst.DataclassStruct[Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) @property - def subentries(self) -> Optional[List["EntryConstruct"]]: + def construct(self) -> "cst.DataclassStruct[Any]": + return t.cast("cst.DataclassStruct[Any]", self._construct) + + @property + def subentries(self) -> List["EntryConstruct"] | None: subentries = super().subentries if (subentries is not None) and self.construct.reverse: return list(reversed(subentries)) @@ -1556,18 +1583,20 @@ def typ_str(self) -> str: # EntryFlag #################################################################################################### class EntryFlag(EntryConstruct): - construct: "cs.FormatField[Any, Any]" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.FormatField[Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) + @property + def construct(self) -> "cs.FormatField[Any, Any]": + return t.cast("cs.FormatField[Any, Any]", self._construct) + @property def obj_view_settings(self) -> ObjViewSettings: return ObjViewSettings_Flag(self) @@ -1584,18 +1613,20 @@ def typ_str(self) -> str: # EntryEnum ########################################################################################################### class EntryEnum(EntrySubconstruct): - construct: "cs.Enum" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.Enum", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) + @property + def construct(self) -> "cs.Enum": + return t.cast("cs.Enum", self._construct) + @property def typ_str(self) -> str: return super().typ_str + " as Enum" @@ -1641,28 +1672,29 @@ def conv_str_to_obj(self, s: str) -> Any: value = str_to_int(s) if value in self.construct.decmapping: - new_obj = self.construct.decmapping[value] + return self.construct.decmapping[value] else: - new_obj = cs.EnumInteger(value) + return cs.EnumInteger(value) except Exception: - new_obj = s # this will probably result in a binary-build-error - return new_obj + return s # this will probably result in a binary-build-error # EntryFlagsEnum ###################################################################################################### class EntryFlagsEnum(EntrySubconstruct): - construct: "cs.FlagsEnum" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cs.FlagsEnum", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) + @property + def construct(self) -> "cs.FlagsEnum": + return t.cast("cs.FlagsEnum", self._construct) + @property def typ_str(self) -> str: return super().typ_str + " as Flags" @@ -1700,7 +1732,7 @@ def get_flagsenum_items_from_obj(self) -> t.List[FlagsEnumItem]: def conv_flagsenum_items_to_obj(self, items: t.List[FlagsEnumItem]) -> Any: """Convert flagsenum items to object""" - new_obj = cs.Container() + new_obj: cs.Container[t.Any] = cs.Container[t.Any]() for item in items: if item.checked: new_obj[item.name] = True @@ -1715,18 +1747,20 @@ def get_enum_name(e: cst.EnumBase): class EntryTEnum(EntrySubconstruct): - construct: "cst.TEnum[Any]" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cst.TEnum[Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) + @property + def construct(self) -> "cst.TEnum[Any]": + return t.cast("cst.TEnum[Any]", self._construct) + @property def typ_str(self) -> str: return super().typ_str + " as Enum" @@ -1763,29 +1797,30 @@ def conv_str_to_obj(self, s: str) -> Any: enum_type: t.Type[cst.EnumBase] = self.construct.enum_type try: try: - new_obj = enum_type[s] + return enum_type[s] except KeyError: value = str_to_int(s) - new_obj = enum_type(value) + return enum_type(value) except Exception: - new_obj = s # this will probably result in a binary-build-error - return new_obj + return s # this will probably result in a binary-build-error # EntryTFlagsEnum ##################################################################################################### class EntryTFlagsEnum(EntrySubconstruct): - construct: "cst.TFlagsEnum[Any]" - def __init__( self, model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], construct: "cst.TFlagsEnum[Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ): super().__init__(model, parent, construct, name, docs) + @property + def construct(self) -> "cst.TFlagsEnum[Any]": + return t.cast("cst.TFlagsEnum[Any]", self._construct) + @property def typ_str(self) -> str: return super().typ_str + " as Flags" @@ -1957,7 +1992,7 @@ def create_entry_from_construct( model: "model.ConstructEditorModel", parent: Optional["EntryConstruct"], subcon: "cs.Construct[Any, Any]", - name: t.Optional[NameType], + name: NameType | None, docs: str, ) -> "EntryConstruct": diff --git a/construct_editor/core/model.py b/construct_editor/core/model.py index e20c5a8..4463915 100644 --- a/construct_editor/core/model.py +++ b/construct_editor/core/model.py @@ -49,8 +49,8 @@ class ConstructEditorModel: """ def __init__(self): - self.root_entry: t.Optional["entries.EntryConstruct"] = None - self.root_obj: t.Optional[t.Any] = None + self.root_entry: "entries.EntryConstruct | None" = None + self.root_obj: t.Any | None = None # Modelwide flag, if hidden entries should be shown (hidden means starting with an underscore) self.hide_protected = True @@ -90,7 +90,7 @@ def get_children( for subentry in entry.subentries: name = subentry.name - if (self.hide_protected == True) and (name.startswith("_") or name == ""): + if self.hide_protected and (name.startswith("_") or name == ""): subentry.visible_row = False continue diff --git a/construct_editor/core/preprocessor.py b/construct_editor/core/preprocessor.py index bf09e8f..d513e68 100644 --- a/construct_editor/core/preprocessor.py +++ b/construct_editor/core/preprocessor.py @@ -11,7 +11,7 @@ class GuiMetaData(t.TypedDict): byte_range: t.Tuple[int, int] - construct: cst.Construct + construct: cst.Construct[t.Any, t.Any] context: cst.Context stream: io.BytesIO child_gui_metadata: t.Optional["GuiMetaData"] @@ -41,7 +41,7 @@ class NoneWithGuiMetadata: pass -class ObjProxyWithGuiMetaData(wrapt.ObjectProxy): +class ObjProxyWithGuiMetaData(wrapt.ObjectProxy[t.Any]): __slots__ = "__construct_editor_metadata__" def __init__(self, wrapped: t.Any, gui_metadata: GuiMetaData): @@ -51,7 +51,7 @@ def __init__(self, wrapped: t.Any, gui_metadata: GuiMetaData): ) -def get_gui_metadata(obj: t.Any) -> t.Optional[GuiMetaData]: +def get_gui_metadata(obj: t.Any) -> GuiMetaData | None: """Get the GUI metadata if they are available""" try: return getattr(obj, "__construct_editor_metadata__") @@ -92,7 +92,7 @@ def add_gui_metadata(obj: t.Any, gui_metadata: GuiMetaData) -> t.Any: return obj -class IncludeGuiMetaData(cs.Subconstruct): +class IncludeGuiMetaData(cs.Subconstruct): # type: ignore[type-arg] """Include GUI metadata to the parsed object""" def __init__(self, subcon, bitwise: bool): @@ -122,7 +122,7 @@ def _parse(self, stream, context, path): return add_gui_metadata(obj, gui_metadata) def _build(self, obj, stream, context, path): - buildret = self.subcon._build(obj, stream, context, path) # type: ignore + _buildret = self.subcon._build(obj, stream, context, path) return obj # passthrought attribute access @@ -245,7 +245,7 @@ def include_metadata( for key, subcon in constr.cases.items(): new_cases[key] = include_metadata(subcon, bitwise) constr.cases = new_cases - if constr.default is not None: + if constr.default is not None: # type: ignore[reportUnnecessaryComparison] constr.default = include_metadata(constr.default, bitwise) return IncludeGuiMetaData(constr, bitwise) @@ -302,4 +302,4 @@ def include_metadata( raise ValueError(f"construct of type '{constr}' is not supported") -custom_subconstructs: t.List[t.Type[cs.Subconstruct]] = [] +custom_subconstructs: t.List[t.Type[cs.Subconstruct]] = [] # type: ignore[type-arg] diff --git a/construct_editor/gallery/__init__.py b/construct_editor/gallery/__init__.py index a482f8c..1d53228 100644 --- a/construct_editor/gallery/__init__.py +++ b/construct_editor/gallery/__init__.py @@ -1,7 +1,8 @@ import dataclasses -import construct as cs import typing as t +import construct as cs + @dataclasses.dataclass class GalleryItem: diff --git a/construct_editor/gallery/example_cmd_resp.py b/construct_editor/gallery/example_cmd_resp.py index 4d15d81..042a0d6 100644 --- a/construct_editor/gallery/example_cmd_resp.py +++ b/construct_editor/gallery/example_cmd_resp.py @@ -1,7 +1,9 @@ -import construct as cs -import construct_typed as cst import dataclasses import typing as t + +import construct as cs +import construct_typed as cst + from . import GalleryItem @@ -9,14 +11,14 @@ class DefaultSizedError(cs.ConstructError): pass -class DefaultSized(cs.Subconstruct): +class DefaultSized(cs.Subconstruct): # type: ignore[type-arg] r""" Returns a size when calling sizeof of GreedyBytes. Parsing and building is not changed. :param subcon: Construct instance :param default_size: size that should be returned - :raises DefaultSizedError: anouter GreedyBytes than GreedyBytes is passed + :raises DefaultSizedError: another GreedyBytes than GreedyBytes is passed Example:: @@ -63,11 +65,7 @@ class RespData_Error(cst.DataclassMixin): CmdCode.Command2: cst.DataclassStruct(RespData_Command2), } -RespDataType = t.Union[ - bytes, - RespData_Command1, - RespData_Command2, -] +RespDataType = bytes | RespData_Command1 | RespData_Command2 class StatusCode(cst.EnumBase): @@ -129,7 +127,6 @@ class Response(cst.DataclassMixin): # ###################################################################################### # ################## Adding new constructs to construct-editor ######################### # ###################################################################################### -import construct_editor.core.custom as custom - +import construct_editor.core.custom as custom # noqa: E402 custom.add_custom_transparent_subconstruct(DefaultSized) diff --git a/construct_editor/gallery/example_ipstack.py b/construct_editor/gallery/example_ipstack.py index 37e626e..4cc5dc8 100644 --- a/construct_editor/gallery/example_ipstack.py +++ b/construct_editor/gallery/example_ipstack.py @@ -4,11 +4,13 @@ WARNING: before parsing the application layer over a TCP stream, you must first combine all the TCP frames into a stream. See utils.tcpip for some solutions. """ + from construct import * # type: ignore from construct.lib import * # type: ignore + import construct_editor.core.custom as custom -from . import GalleryItem +from . import GalleryItem #=============================================================================== # layer 2, Ethernet @@ -612,17 +614,22 @@ # Domain Name System (TCP/IP protocol stack) #=============================================================================== -class DnsStringAdapter(Adapter): + +class DnsStringAdapter(Adapter): # type: ignore[type-arg] def _decode(self, obj, context, path): return u".".join(obj[:-1]) # type: ignore + def _encode(self, obj, context, path): return obj.split(u".") + [u""] # type: ignore -class DnsNamesAdapter(Adapter): + +class DnsNamesAdapter(Adapter): # type: ignore[type-arg] def _decode(self, obj, context, path): return [x.label if x.islabel else x.pointer & 0x3fff for x in obj] # type: ignore + def _encode(self, obj, context, path): - return [dict(ispointer=1,pointer=x|0xc000) if isinstance(x,int) else dict(islabel=1,label=x) for x in obj] # type: ignore + return [dict(ispointer=1, pointer=x | 0xc000) if isinstance(x,int) else dict(islabel=1, label=x) for x in obj] # type: ignore + dns_record_class = Enum(Int16ub, RESERVED = 0, diff --git a/construct_editor/gallery/example_pe32coff.py b/construct_editor/gallery/example_pe32coff.py index d0dfda4..f3c87f8 100644 --- a/construct_editor/gallery/example_pe32coff.py +++ b/construct_editor/gallery/example_pe32coff.py @@ -1,4 +1,5 @@ from construct import * # type: ignore + from . import GalleryItem docs = """ diff --git a/construct_editor/gallery/test_aligned.py b/construct_editor/gallery/test_aligned.py index c47c864..1a5d8b3 100644 --- a/construct_editor/gallery/test_aligned.py +++ b/construct_editor/gallery/test_aligned.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "before" / cs.Int8ub, diff --git a/construct_editor/gallery/test_array.py b/construct_editor/gallery/test_array.py index bfb6ebc..a2915c2 100644 --- a/construct_editor/gallery/test_array.py +++ b/construct_editor/gallery/test_array.py @@ -1,7 +1,9 @@ -import construct as cs -import construct_typed as cst import dataclasses import typing as t + +import construct as cs +import construct_typed as cst + from . import GalleryItem diff --git a/construct_editor/gallery/test_bits_swapped_bitwise.py b/construct_editor/gallery/test_bits_swapped_bitwise.py index ab9f4bb..5515aa4 100644 --- a/construct_editor/gallery/test_bits_swapped_bitwise.py +++ b/construct_editor/gallery/test_bits_swapped_bitwise.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.BitsSwapped(cs.Bitwise(cs.GreedyRange(cs.Bit))) diff --git a/construct_editor/gallery/test_bitwise.py b/construct_editor/gallery/test_bitwise.py index a587403..04f6de5 100644 --- a/construct_editor/gallery/test_bitwise.py +++ b/construct_editor/gallery/test_bitwise.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Bitwise(cs.GreedyRange(cs.Bit)) diff --git a/construct_editor/gallery/test_bytes_greedybytes.py b/construct_editor/gallery/test_bytes_greedybytes.py index aee7402..cc39979 100644 --- a/construct_editor/gallery/test_bytes_greedybytes.py +++ b/construct_editor/gallery/test_bytes_greedybytes.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "len" / cs.Int8ul, diff --git a/construct_editor/gallery/test_checksum.py b/construct_editor/gallery/test_checksum.py index 915cc02..0facdd8 100644 --- a/construct_editor/gallery/test_checksum.py +++ b/construct_editor/gallery/test_checksum.py @@ -1,16 +1,23 @@ import hashlib + import construct as cs + from . import GalleryItem +def _hashfunc(data: bytes) -> bytes: + return hashlib.sha512(data).digest() + + constr = cs.Struct( "checksum_start" / cs.Tell, "fields" / cs.Struct( cs.Padding(1000), ), "checksum_end" / cs.Tell, - "checksum" / cs.Checksum(cs.Bytes(64), - lambda data: hashlib.sha512(data).digest(), + "checksum" / cs.Checksum( + cs.Bytes(64), + _hashfunc, lambda ctx: ctx._io.getvalue()[ctx.checksum_start:ctx.checksum_end]), # type: ignore ) diff --git a/construct_editor/gallery/test_compressed.py b/construct_editor/gallery/test_compressed.py index 6a6c7d2..013803b 100644 --- a/construct_editor/gallery/test_compressed.py +++ b/construct_editor/gallery/test_compressed.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "compressed_bits" diff --git a/construct_editor/gallery/test_computed.py b/construct_editor/gallery/test_computed.py index 356ee7d..4bf3f3a 100644 --- a/construct_editor/gallery/test_computed.py +++ b/construct_editor/gallery/test_computed.py @@ -1,16 +1,23 @@ +import dataclasses + import construct as cs import construct_typed as cst -import dataclasses + from . import GalleryItem @dataclasses.dataclass class ComputedTest(cst.DataclassMixin): - type_int: int | None = cst.csfield_noinit(cs.Computed(lambda ctx: 50)) - type_float: float | None = cst.csfield_noinit(cs.Computed(lambda ctx: 80.0)) - type_bool: bool | None = cst.csfield_noinit(cs.Computed(lambda ctx: True)) - type_bytes: bytes | None = cst.csfield_noinit(cs.Computed(lambda ctx: bytes([0x00, 0xAB]))) - type_bytearray: bytearray | None = cst.csfield_noinit(cs.Computed(lambda ctx: bytearray([0x00, 0xAB, 0xFF]))) + type_int_const: int | None = cst.csfield_noinit(cs.Computed(50)) + type_int_lambda: int | None = cst.csfield_noinit(cs.Computed(lambda ctx: 50)) + type_float_const: float | None = cst.csfield_noinit(cs.Computed(80.0)) + type_float_lambda: float | None = cst.csfield_noinit(cs.Computed(lambda ctx: 80.0)) + type_bool_const: bool | None = cst.csfield_noinit(cs.Computed(True)) + type_bool_lambda: bool | None = cst.csfield_noinit(cs.Computed(lambda ctx: True)) + type_bytes_const: bytes | None = cst.csfield_noinit(cs.Computed(bytes([0x00, 0xAB]))) + type_bytes_lambda: bytes | None = cst.csfield_noinit(cs.Computed(lambda ctx: bytes([0x00, 0xAB]))) + type_bytearray_const: bytearray | None = cst.csfield_noinit(cs.Computed(bytearray([0x00, 0xAB, 0xFF]))) + type_bytearray_lambda: bytearray | None = cst.csfield_noinit(cs.Computed(lambda ctx: bytearray([0x00, 0xAB, 0xFF]))) constr = cst.DataclassStruct(ComputedTest) diff --git a/construct_editor/gallery/test_const.py b/construct_editor/gallery/test_const.py index 30634cf..973c694 100644 --- a/construct_editor/gallery/test_const.py +++ b/construct_editor/gallery/test_const.py @@ -1,6 +1,8 @@ +import dataclasses + import construct as cs import construct_typed as cst -import dataclasses + from . import GalleryItem diff --git a/construct_editor/gallery/test_dataclass_bit_struct.py b/construct_editor/gallery/test_dataclass_bit_struct.py index 9214562..57d8436 100644 --- a/construct_editor/gallery/test_dataclass_bit_struct.py +++ b/construct_editor/gallery/test_dataclass_bit_struct.py @@ -1,6 +1,8 @@ +import dataclasses + import construct as cs import construct_typed as cst -import dataclasses + from . import GalleryItem diff --git a/construct_editor/gallery/test_dataclass_struct.py b/construct_editor/gallery/test_dataclass_struct.py index ae95a27..b6d543b 100644 --- a/construct_editor/gallery/test_dataclass_struct.py +++ b/construct_editor/gallery/test_dataclass_struct.py @@ -1,7 +1,9 @@ -import construct as cs -import construct_typed as cst import dataclasses import typing as t + +import construct as cs +import construct_typed as cst + from . import GalleryItem diff --git a/construct_editor/gallery/test_default.py b/construct_editor/gallery/test_default.py index 8d00af1..34c7876 100644 --- a/construct_editor/gallery/test_default.py +++ b/construct_editor/gallery/test_default.py @@ -1,6 +1,8 @@ +import dataclasses + import construct as cs import construct_typed as cst -import dataclasses + from . import GalleryItem diff --git a/construct_editor/gallery/test_enum.py b/construct_editor/gallery/test_enum.py index f5239e8..838a54c 100644 --- a/construct_editor/gallery/test_enum.py +++ b/construct_editor/gallery/test_enum.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "brand" / cs.Enum(cs.Int8ul, Porsche=0, Audi=4, VW=7), diff --git a/construct_editor/gallery/test_fixedsized.py b/construct_editor/gallery/test_fixedsized.py index 7ae5a8f..cda2c7d 100644 --- a/construct_editor/gallery/test_fixedsized.py +++ b/construct_editor/gallery/test_fixedsized.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "choice" / cs.Int32ul, diff --git a/construct_editor/gallery/test_flag.py b/construct_editor/gallery/test_flag.py index 4d6ba0d..dff51a4 100644 --- a/construct_editor/gallery/test_flag.py +++ b/construct_editor/gallery/test_flag.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "flag0" / cs.Flag, diff --git a/construct_editor/gallery/test_flagsenum.py b/construct_editor/gallery/test_flagsenum.py index ab6e2e8..66dd5f0 100644 --- a/construct_editor/gallery/test_flagsenum.py +++ b/construct_editor/gallery/test_flagsenum.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "permissions" / cs.FlagsEnum(cs.Int8ul, R=4, W=2, X=1), diff --git a/construct_editor/gallery/test_greedyrange.py b/construct_editor/gallery/test_greedyrange.py index 1bee9c0..9075875 100644 --- a/construct_editor/gallery/test_greedyrange.py +++ b/construct_editor/gallery/test_greedyrange.py @@ -1,6 +1,8 @@ +import dataclasses + import construct as cs import construct_typed as cst -import dataclasses + from . import GalleryItem diff --git a/construct_editor/gallery/test_ifthenelse.py b/construct_editor/gallery/test_ifthenelse.py index 21e6954..92a24bf 100644 --- a/construct_editor/gallery/test_ifthenelse.py +++ b/construct_editor/gallery/test_ifthenelse.py @@ -1,7 +1,8 @@ +import dataclasses + import construct as cs import construct_typed as cst -import dataclasses -import typing as t + from . import GalleryItem @@ -20,7 +21,7 @@ class Else(cst.DataclassMixin): else_4: int = cst.csfield(cs.Int8sb) choice: int = cst.csfield(cs.Int8ub) - if_then_else: t.Union[Then, Else] = cst.csfield( + if_then_else: Then | Else = cst.csfield( cs.IfThenElse( cs.this.choice == 0, cst.DataclassStruct(Then), cst.DataclassStruct(Else) ) diff --git a/construct_editor/gallery/test_ifthenelse_nested_switch.py b/construct_editor/gallery/test_ifthenelse_nested_switch.py index 19ecb37..6d0503e 100644 --- a/construct_editor/gallery/test_ifthenelse_nested_switch.py +++ b/construct_editor/gallery/test_ifthenelse_nested_switch.py @@ -1,7 +1,8 @@ +import dataclasses + import construct as cs import construct_typed as cst -import dataclasses -import typing as t + from . import GalleryItem @@ -20,7 +21,7 @@ class Else(cst.DataclassMixin): else_4: int = cst.csfield(cs.Int8sb) choice: int = cst.csfield(cs.Int8ub) - if_then_else: t.Union[Then, Else, bytes] = cst.csfield( + if_then_else: Then | Else | bytes = cst.csfield( cs.IfThenElse( cs.this.choice == 0, cs.Switch( diff --git a/construct_editor/gallery/test_nullstripped.py b/construct_editor/gallery/test_nullstripped.py index 0ef45fc..e23d59f 100644 --- a/construct_editor/gallery/test_nullstripped.py +++ b/construct_editor/gallery/test_nullstripped.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "null_stripped" / cs.NullStripped(cs.GreedyBytes, pad=b"\x00"), diff --git a/construct_editor/gallery/test_nullterminated.py b/construct_editor/gallery/test_nullterminated.py index 3c8f932..538cc82 100644 --- a/construct_editor/gallery/test_nullterminated.py +++ b/construct_editor/gallery/test_nullterminated.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "null_terminated" / cs.NullTerminated(cs.Int16ul, term=b"\x00"), diff --git a/construct_editor/gallery/test_padded.py b/construct_editor/gallery/test_padded.py index ddad64b..8119591 100644 --- a/construct_editor/gallery/test_padded.py +++ b/construct_editor/gallery/test_padded.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "padded" / cs.Padded(5, cs.Bytes(3)), diff --git a/construct_editor/gallery/test_padded_string.py b/construct_editor/gallery/test_padded_string.py index 6eff295..4dcf022 100644 --- a/construct_editor/gallery/test_padded_string.py +++ b/construct_editor/gallery/test_padded_string.py @@ -1,6 +1,8 @@ +import dataclasses + import construct as cs import construct_typed as cst -import dataclasses + from . import GalleryItem diff --git a/construct_editor/gallery/test_pass.py b/construct_editor/gallery/test_pass.py index 1fd95e2..50fc8b1 100644 --- a/construct_editor/gallery/test_pass.py +++ b/construct_editor/gallery/test_pass.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "value1" / cs.Int8sb, diff --git a/construct_editor/gallery/test_pointer_peek_seek_tell.py b/construct_editor/gallery/test_pointer_peek_seek_tell.py index a736f19..9e9e751 100644 --- a/construct_editor/gallery/test_pointer_peek_seek_tell.py +++ b/construct_editor/gallery/test_pointer_peek_seek_tell.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "signature" / cs.Bytes(23), diff --git a/construct_editor/gallery/test_renamed.py b/construct_editor/gallery/test_renamed.py index 8490a65..093dbf7 100644 --- a/construct_editor/gallery/test_renamed.py +++ b/construct_editor/gallery/test_renamed.py @@ -1,6 +1,8 @@ +import dataclasses + import construct as cs import construct_typed as cst -import dataclasses + from . import GalleryItem diff --git a/construct_editor/gallery/test_select_complex.py b/construct_editor/gallery/test_select_complex.py index 0af674f..1854f59 100644 --- a/construct_editor/gallery/test_select_complex.py +++ b/construct_editor/gallery/test_select_complex.py @@ -1,5 +1,4 @@ import dataclasses -import typing as t import construct as cs import construct_typed as cst @@ -24,7 +23,7 @@ class SmallImage(cst.DataclassMixin): @dataclasses.dataclass class Image(cst.DataclassMixin): is_big: int = cst.csfield(cs.Int8ub) - data: t.Union[BigImage, SmallImage] = cst.csfield( + data: BigImage | SmallImage = cst.csfield( cs.Select( cs.IfThenElse( condfunc=cs.this.is_big == 1, diff --git a/construct_editor/gallery/test_stringencodded.py b/construct_editor/gallery/test_stringencodded.py index 0c5429d..885883c 100644 --- a/construct_editor/gallery/test_stringencodded.py +++ b/construct_editor/gallery/test_stringencodded.py @@ -1,6 +1,8 @@ +from typing import Any, Dict + import construct as cs + from . import GalleryItem -from typing import Dict, Any ENCODDINGS = dict( ASCII="ascii", diff --git a/construct_editor/gallery/test_switch.py b/construct_editor/gallery/test_switch.py index 2637d5e..91f4795 100644 --- a/construct_editor/gallery/test_switch.py +++ b/construct_editor/gallery/test_switch.py @@ -1,6 +1,6 @@ import construct as cs -from . import GalleryItem +from . import GalleryItem constr = cs.Struct( "choice" diff --git a/construct_editor/gallery/test_switch_dataclass.py b/construct_editor/gallery/test_switch_dataclass.py index 8e9fd5f..13f601b 100644 --- a/construct_editor/gallery/test_switch_dataclass.py +++ b/construct_editor/gallery/test_switch_dataclass.py @@ -1,6 +1,9 @@ +import dataclasses +import typing as t + import construct as cs import construct_typed as cst -import dataclasses + from . import GalleryItem @@ -40,28 +43,32 @@ def get_default(cls): return cls(0) choice: int = cst.csfield(cst.TEnum(cs.Int8ub, Choice)) - switch: Case1 | Case2 | CaseDefault | None = cst.csfield_noinit( - cs.Switch( - cs.this.choice, - cases={ - 1: cs.Default(cst.DataclassStruct(Case1), Case1.get_default()), - 2: cs.Default(cst.DataclassStruct(Case2), Case2.get_default()), - }, - default=cs.Default( - cst.DataclassStruct(CaseDefault), CaseDefault.get_default() - ), - ) + switch: Case1 | Case2 | CaseDefault | None = t.cast( + "Case1 | Case2 | CaseDefault | None", + cst.csfield_noinit( + cs.Switch( + cs.this.choice, + cases={ + 1: cs.Default(cst.DataclassStruct(Case1), Case1.get_default()), + 2: cs.Default(cst.DataclassStruct(Case2), Case2.get_default()), + }, + default=cs.Default(cst.DataclassStruct(CaseDefault), CaseDefault.get_default()), + ) + ), ) - switch_without_default: Case1 | Case2 | None = cst.csfield_noinit( - cs.Switch( - cs.this.choice, - cases={ - 1: cs.Default(cst.DataclassStruct(Case1), Case1.get_default()), - 2: cs.Default(cst.DataclassStruct(Case2), Case2.get_default()), - }, - default=cs.Default(cs.Pass, None), - ) + switch_without_default: Case1 | Case2 | None = t.cast( + "Case1 | Case2 | None", + cst.csfield_noinit( + cs.Switch( + cs.this.choice, + cases={ + 1: cs.Default(cst.DataclassStruct(Case1), Case1.get_default()), + 2: cs.Default(cst.DataclassStruct(Case2), Case2.get_default()), + }, + default=cs.Default(cs.Pass, None), + ) + ), ) diff --git a/construct_editor/gallery/test_tenum.py b/construct_editor/gallery/test_tenum.py index 159eed6..409c0c2 100644 --- a/construct_editor/gallery/test_tenum.py +++ b/construct_editor/gallery/test_tenum.py @@ -1,6 +1,8 @@ +import dataclasses + import construct as cs import construct_typed as cst -import dataclasses + from . import GalleryItem diff --git a/construct_editor/gallery/test_tflagsenum.py b/construct_editor/gallery/test_tflagsenum.py index 4f27151..0b5a8de 100644 --- a/construct_editor/gallery/test_tflagsenum.py +++ b/construct_editor/gallery/test_tflagsenum.py @@ -1,6 +1,8 @@ +import dataclasses + import construct as cs import construct_typed as cst -import dataclasses + from . import GalleryItem diff --git a/construct_editor/gallery/test_timestamp.py b/construct_editor/gallery/test_timestamp.py index 59598eb..ddfe33a 100644 --- a/construct_editor/gallery/test_timestamp.py +++ b/construct_editor/gallery/test_timestamp.py @@ -1,7 +1,9 @@ -import construct as cs -import construct_typed as cst import dataclasses + import arrow +import construct as cs +import construct_typed as cst + from . import GalleryItem diff --git a/construct_editor/main.py b/construct_editor/main.py index 9cbf240..4e21450 100644 --- a/construct_editor/main.py +++ b/construct_editor/main.py @@ -18,9 +18,9 @@ import construct_editor.gallery.test_compressed import construct_editor.gallery.test_computed import construct_editor.gallery.test_const -import construct_editor.gallery.test_default import construct_editor.gallery.test_dataclass_bit_struct import construct_editor.gallery.test_dataclass_struct +import construct_editor.gallery.test_default import construct_editor.gallery.test_enum import construct_editor.gallery.test_fixedsized import construct_editor.gallery.test_flag @@ -68,13 +68,13 @@ def __init__(self, *args, **kwargs): self.status_bar: wx.StatusBar = self.CreateStatusBar() -def on_uncaught_exception(etype: t.Type[BaseException], value: BaseException, trace: TracebackType | None): +def on_uncaught_exception(etype: t.Type[BaseException], value: BaseException, trace: TracebackType | None) -> None: """ Handler for all unhandled exceptions. :param etype: the exception type (`SyntaxError`, `ZeroDivisionError`, etc...); :param value: the exception error message; - :param trace: the traceback header, if any (otherwise, it prints the standard Python header: ``Traceback (most recent call last)``. + :param trace: the traceback header, if any (otherwise, it prints the standard Python header: ``Traceback (most recent call last)``). """ with WxExceptionDialog(None, "Uncaught Exception...", ExceptionInfo(etype, value, trace)) as dial: dial.ShowModal() @@ -181,6 +181,7 @@ def __init__(self, parent: ConstructGalleryFrame): self.gallery_selection = 1 default_gallery = list(self.construct_gallery.keys())[self.gallery_selection] default_gallery_item = self.construct_gallery[default_gallery] + assert default_gallery_item is not None # Define GUI elements ############################################# self.sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -296,7 +297,7 @@ def on_gallery_selection_changed(self, event): ) example = self.example_selector_lbx.GetStringSelection() - example_binary = self.construct_gallery[selection].example_binarys[example] + example_binary = gallery_item.example_binarys[example] else: example_binary = bytes(0) @@ -315,11 +316,12 @@ def on_clear_binary_clicked(self, event): def on_example_selection_changed(self, event): selection = self.gallery_selector_lbx.GetStringSelection() example = self.example_selector_lbx.GetStringSelection() - example_binary = self.construct_gallery[selection].example_binarys[example] - - # Set example binary - self.construct_hex_editor.binary = example_binary - self.construct_hex_editor.construct_editor.expand_all() + gallery_item = self.construct_gallery[selection] + if gallery_item is not None: + # Set example binary + example_binary = gallery_item.example_binarys[example] + self.construct_hex_editor.binary = example_binary + self.construct_hex_editor.construct_editor.expand_all() def on_load_binary_file_clicked(self, event): with wx.FileDialog( @@ -403,7 +405,7 @@ def main(): ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) inspect = False - if inspect is True: + if inspect is True: # type: ignore[reportUnnecessaryComparison] import wx.lib.mixins.inspection as wit app = wit.InspectableApp() diff --git a/construct_editor/wx_widgets/wx_construct_editor.py b/construct_editor/wx_widgets/wx_construct_editor.py index d777f61..ac45aa6 100644 --- a/construct_editor/wx_widgets/wx_construct_editor.py +++ b/construct_editor/wx_widgets/wx_construct_editor.py @@ -6,18 +6,19 @@ import construct as cs import wx import wx.dataview as dv +from wx.dataview import DataViewItem from construct_editor.core.construct_editor import ConstructEditor from construct_editor.core.entries import EntryConstruct from construct_editor.core.model import ConstructEditorColumn, ConstructEditorModel from construct_editor.wx_widgets.wx_context_menu import WxContextMenu +from construct_editor.wx_widgets.wx_exception_dialog import WxExceptionDialog from construct_editor.wx_widgets.wx_obj_view import ( WxObjEditor, WxObjRendererHelper, create_obj_editor, create_obj_renderer_helper, ) -from construct_editor.wx_widgets.wx_exception_dialog import WxExceptionDialog @dataclasses.dataclass @@ -34,8 +35,8 @@ class ValueFromEditorCtrl: class ObjectRenderer(dv.DataViewCustomRenderer): def __init__(self): super().__init__(varianttype="PyObject") - self.entry: t.Optional[EntryConstruct] = None - self.entry_renderer_helper: t.Optional[WxObjRendererHelper] = None + self.entry: EntryConstruct | None = None + self.entry_renderer_helper: WxObjRendererHelper | None = None self.EnableEllipsize(wx.ELLIPSIZE_END) def SetValue(self, value: EntryConstruct): @@ -53,10 +54,10 @@ def GetSize(self): raise ValueError("`entry_renderer_helper` not set") return self.entry_renderer_helper.get_size(self) - def Render(self, rect: wx.Rect, dc: wx.DC, state): + def Render(self, cell: wx.Rect, dc: wx.DC, state: int) -> bool: if self.entry_renderer_helper is None: raise ValueError("`entry_renderer_helper` not set") - return self.entry_renderer_helper.render(self, rect, dc, state) + return self.entry_renderer_helper.render(self, cell, dc, state) def GetMode(self) -> int: """ @@ -101,16 +102,16 @@ def GetMode(self) -> int: def ActivateCell( self, - rect: wx.Rect, + cell: wx.Rect, model: dv.DataViewModel, item: dv.DataViewItem, col: int, - mouseEvent: t.Optional[wx.MouseEvent], - ): + mouseEvent: wx.MouseEvent, + ) -> bool: if self.entry_renderer_helper is None: raise ValueError("`entry_renderer_helper` not set") return self.entry_renderer_helper.activate_cell( - self, rect, model, item, col, mouseEvent + self, cell, model, item, col, mouseEvent ) # The HasEditorCtrl, CreateEditorCtrl and GetValueFromEditorCtrl @@ -130,7 +131,8 @@ def CreateEditorCtrl( editor.SetSize(labelRect.GetSize()) return editor - def GetValueFromEditorCtrl(self, editor: WxObjEditor): + def GetValueFromEditorCtrl(self, editor: wx.Window) -> ValueFromEditorCtrl: + editor = t.cast("WxObjEditor", editor) new_obj = editor.get_new_obj() return ValueFromEditorCtrl(new_obj) @@ -191,7 +193,7 @@ def on_value_changed(self, entry: "EntryConstruct"): # ################################################################################################################# # dv.PyDataViewModel Interface #################################################################################### # ################################################################################################################# - def GetChildren(self, parent, children): + def GetChildren(self, item: dv.DataViewItem, children: t.List[dv.DataViewItem]) -> int: # The view calls this method to find the children of any node in the # control. There is an implicit hidden root node, and the top level # item(s) should be reported as children of this node. A List view @@ -206,11 +208,11 @@ def GetChildren(self, parent, children): if self.root_entry is None: return 0 - if not parent: + if not item: # hidden root entry = None else: - entry = self.dvc_item_to_entry(parent) + entry = self.dvc_item_to_entry(item) childs = self.get_children(entry) for child in childs: @@ -256,12 +258,12 @@ def GetValue(self, item: dv.DataViewItem, col: int): return self.get_value(entry, col) - def SetValue(self, value: ValueFromEditorCtrl, item: dv.DataViewItem, col: int): - if not isinstance(value, ValueFromEditorCtrl): - raise ValueError(f"value has the wrong type ({value})") + def SetValue(self, variant: ValueFromEditorCtrl, item: DataViewItem, col: int) -> bool: + if not isinstance(variant, ValueFromEditorCtrl): + raise ValueError(f"value has the wrong type ({variant})") entry = self.dvc_item_to_entry(item) - self.set_value(value.new_obj, entry, col) + self.set_value(variant.new_obj, entry, col) return True @@ -269,7 +271,7 @@ def GetAttr(self, item, col, attr): entry = self.dvc_item_to_entry(item) if entry is self.root_entry: - attr.SetColour("blue") + attr.SetColour(wx.BLUE) attr.SetBold(True) return True @@ -280,7 +282,7 @@ class WxConstructEditor(wx.Panel, ConstructEditor): def __init__( self, parent, - construct: cs.Construct, + construct: cs.Construct[t.Any, t.Any], ): wx.Panel.__init__(self, parent) self._init_gui() @@ -312,7 +314,7 @@ def _init_gui(self): self._parse_error_info_bar.Bind( wx.EVT_BUTTON, self._parse_error_info_bar_btn_clicked, id=btn_id ) - self._parse_error_ex: t.Optional[Exception] = None + self._parse_error_ex: Exception | None = None vsizer.Add(self._parse_error_info_bar, 0, wx.EXPAND) self._build_error_info_bar = wx.InfoBar(self) @@ -321,7 +323,7 @@ def _init_gui(self): self._build_error_info_bar.Bind( wx.EVT_BUTTON, self._build_error_info_bar_btn_clicked, id=btn_id ) - self._build_error_ex: t.Optional[Exception] = None + self._build_error_ex: Exception | None = None vsizer.Add(self._build_error_info_bar, 0, wx.EXPAND) # create status bar @@ -354,9 +356,7 @@ def _init_gui(self): self._dvc_main_window.Bind(wx.EVT_MOTION, self._on_dvc_motion) self._dvc_main_window.Bind(wx.EVT_KEY_DOWN, self._on_dvc_key_down) self._dvc_main_window.Bind(wx.EVT_CHAR, self._on_dvc_char) - self._last_tooltip: t.Optional[ - t.Tuple[EntryConstruct, ConstructEditorColumn] - ] = None + self._last_tooltip: t.Tuple[EntryConstruct, ConstructEditorColumn] | None = None def reload(self): """ @@ -386,7 +386,7 @@ def reload(self): finally: self.Thaw() - def show_parse_error_message(self, msg: t.Optional[str], ex: t.Optional[Exception]): + def show_parse_error_message(self, msg: str | None, ex: Exception | None): """ Show an message to the user. """ @@ -396,7 +396,7 @@ def show_parse_error_message(self, msg: t.Optional[str], ex: t.Optional[Exceptio self._parse_error_ex = ex self._parse_error_info_bar.ShowMessage(msg, wx.ICON_WARNING) - def show_build_error_message(self, msg: t.Optional[str], ex: t.Optional[Exception]): + def show_build_error_message(self, msg: str | None, ex: Exception | None): """ Show an build error message to the user. """ @@ -413,7 +413,7 @@ def show_status(self, path_info: str, bytes_info: str): self._status_bar.SetStatusText(path_info, 0) self._status_bar.SetStatusText(bytes_info, 1) - def get_selected_entry(self) -> t.Optional[EntryConstruct]: + def get_selected_entry(self) -> EntryConstruct | None: """ Get the currently selected entry (or None if nothing is selected). """ @@ -581,7 +581,7 @@ def _on_dvc_right_clicked(self, event: dv.DataViewEvent): Then a context menu is created """ item = event.GetItem() - entry: t.Optional["EntryConstruct"] + entry: "EntryConstruct | None" if item.ID is not None: entry = self._model.dvc_item_to_entry(item) else: @@ -666,7 +666,7 @@ def _put_to_clipboard(self, txt: str): wx.TheClipboard.SetData(wx.TextDataObject(txt)) wx.TheClipboard.Close() - def _get_from_clipboard(self): + def _get_from_clipboard(self) -> str | None: """ Get text from the clipboard. """ diff --git a/construct_editor/wx_widgets/wx_construct_hex_editor.py b/construct_editor/wx_widgets/wx_construct_hex_editor.py index ccbc7a3..89ee390 100644 --- a/construct_editor/wx_widgets/wx_construct_hex_editor.py +++ b/construct_editor/wx_widgets/wx_construct_hex_editor.py @@ -50,7 +50,7 @@ def __init__( self.Initialize(panel) - self.sub_panel: t.Optional["HexEditorPanel"] = None + self.sub_panel: "HexEditorPanel | None" = None def clear_sub_panels(self): """Clears all sub-panels recursivly""" @@ -62,11 +62,10 @@ def clear_sub_panels(self): def create_sub_panel(self, name: str, bitwise: bool) -> "HexEditorPanel": """Create a new sub-panel""" if self.sub_panel is None: - self.sub_panel = HexEditorPanel( - self, name, read_only=True, bitwiese=bitwise - ) - self.SplitHorizontally(self.GetWindow1(), self.sub_panel) - return self.sub_panel + new_panel = HexEditorPanel(self, name, read_only=True, bitwiese=bitwise) + self.sub_panel = new_panel + self.SplitHorizontally(self.GetWindow1(), new_panel) + return new_panel else: raise RuntimeError("sub-panel already created") @@ -75,13 +74,13 @@ class WxConstructHexEditor(wx.Panel): def __init__( self, parent, - construct: cs.Construct, - contextkw: dict = {}, + construct: cs.Construct[t.Any, t.Any], + contextkw: dict[t.Any, t.Any] | None = None, binary: bytes = b"", ): super().__init__(parent) - self._contextkw = contextkw + self._contextkw = contextkw or {} hsizer = wx.BoxSizer(wx.HORIZONTAL) self._init_gui_hex_editor_splitter(hsizer, binary) @@ -113,11 +112,9 @@ def _init_gui_hex_visibility(self, hsizer: wx.BoxSizer): self, wx.ID_ANY, "«", size=wx.Size(12, -1) ) hsizer.Add(self.toggle_hex_visibility_btn, 0, wx.EXPAND | wx.ALL, 0) - self.toggle_hex_visibility_btn.Bind( - wx.EVT_BUTTON, lambda evt: self.toggle_hex_visibility() - ) + self.toggle_hex_visibility_btn.Bind(wx.EVT_BUTTON, self.toggle_hex_visibility) - def _init_gui_construct_editor(self, hsizer: wx.BoxSizer, construct: cs.Construct): + def _init_gui_construct_editor(self, hsizer: wx.BoxSizer, construct: cs.Construct[t.Any, t.Any]): self.construct_editor: WxConstructEditor = WxConstructEditor( self, construct, @@ -150,13 +147,13 @@ def toggle_hex_visibility(self): self.Refresh() self.Thaw() - def change_construct(self, constr: cs.Construct) -> None: + def change_construct(self, constr: cs.Construct[t.Any, t.Any]) -> None: """ Change the construct format, that is used for building/parsing. """ self.construct_editor.change_construct(constr) - def change_contextkw(self, contextkw: dict): + def change_contextkw(self, contextkw: dict[t.Any, t.Any]) -> None: """ Change the contextkw used for building/parsing. """ @@ -170,25 +167,25 @@ def change_binary(self, binary: bytes): self.hex_panel.hex_editor.binary = binary @property - def construct(self) -> cs.Construct: + def construct(self) -> cs.Construct[t.Any, t.Any]: """ Construct that is used for displaying. """ return self.construct_editor.construct @construct.setter - def construct(self, constr: cs.Construct): + def construct(self, constr: cs.Construct[t.Any, t.Any]): self.construct_editor.construct = constr @property - def contextkw(self) -> dict: + def contextkw(self) -> dict[t.Any, t.Any]: """ Context used for building/parsing. """ return self._contextkw @contextkw.setter - def contextkw(self, contextkw: dict): + def contextkw(self, contextkw: dict[t.Any, t.Any]): self.change_contextkw(contextkw) @property @@ -257,7 +254,7 @@ def _convert_struct_to_binary(self): self.Thaw() self._converting = False - def _on_entry_selected(self, entry: t.Optional[EntryConstruct]): + def _on_entry_selected(self, entry: EntryConstruct | None): try: self.Freeze() self.hex_panel.clear_sub_panels() diff --git a/construct_editor/wx_widgets/wx_exception_dialog.py b/construct_editor/wx_widgets/wx_exception_dialog.py index 00b5a1b..461d113 100644 --- a/construct_editor/wx_widgets/wx_exception_dialog.py +++ b/construct_editor/wx_widgets/wx_exception_dialog.py @@ -10,12 +10,12 @@ class ExceptionInfo: etype: t.Type[BaseException] value: BaseException - trace: t.Optional[TracebackType] + trace: TracebackType | None class WxExceptionDialog(wx.Dialog): def __init__( - self, parent, title: str, exception: t.Union[ExceptionInfo, BaseException] + self, parent, title: str, exception: ExceptionInfo | BaseException ): wx.Dialog.__init__( self, diff --git a/construct_editor/wx_widgets/wx_hex_editor.py b/construct_editor/wx_widgets/wx_hex_editor.py index f56c2f2..873336e 100644 --- a/construct_editor/wx_widgets/wx_hex_editor.py +++ b/construct_editor/wx_widgets/wx_hex_editor.py @@ -7,6 +7,7 @@ import wx import wx.grid as Grid +from wx.grid import GridCellAttr from construct_editor.core.callbacks import CallbackList @@ -62,15 +63,16 @@ def __init__(self): True, f"Overwrite Range (Index: {idx}, Length: {len(byts)})" ) - def Do(self): + def Do(self) -> bool: self._range_backup = obj._binary[idx : idx + len(byts)] - if obj._binary[idx : idx + len(byts)] == byts: + byts_array = bytearray(byts) + if obj._binary[idx : idx + len(byts)] == byts_array: return False - obj._binary[idx : idx + len(byts)] = byts + obj._binary[idx : idx + len(byts)] = byts_array obj.on_binary_changed.fire(obj) return True - def Undo(self): + def Undo(self) -> bool: obj._binary[idx : idx + len(byts)] = self._range_backup obj.on_binary_changed.fire(obj) return True @@ -243,8 +245,8 @@ def GetAttr(self, row, col, kind): byte_idx = self.get_byte_idx(row, col) selected = False - for sel in self.selections: - if sel[0] <= byte_idx < sel[1]: + for sel0, sel1 in self.selections: + if sel0 <= byte_idx < sel1: selected = True break @@ -474,7 +476,7 @@ def SetSize(self, rect): rect.x - 4, rect.y, rect.width + 8, rect.height + 2, wx.SIZE_ALLOW_MINUS_ONE ) - def Show(self, show, attr): + def Show(self, show: bool, attr: GridCellAttr | None = None) -> None: """ Show or hide the edit control. You can use the attr (if not None) to set colours or fonts for the control. @@ -548,32 +550,32 @@ def Reset(self): self._tc.SetValue(self.startValue) self._tc.SetInsertionPointEnd() - def IsAcceptedKey(self, evt): + def IsAcceptedKey(self, event: wx.KeyEvent) -> bool: """ Return True to allow the given key to start editing: the base class version only checks that the event has no modifiers. F2 is special and will always start the editor. """ - logger.debug("keycode=%d" % (evt.GetKeyCode())) + logger.debug("keycode=%d" % (event.GetKeyCode())) # We can ask the base class to do it # return self.base_IsAcceptedKey(evt) # or do it ourselves return ( - not (evt.ControlDown() or evt.AltDown()) - and evt.GetKeyCode() != wx.WXK_SHIFT + not (event.ControlDown() or event.AltDown()) + and event.GetKeyCode() != wx.WXK_SHIFT ) - def StartingKey(self, evt): + def StartingKey(self, event: wx.KeyEvent) -> None: """ If the editor is enabled by pressing keys on the grid, this will be called to let the editor do something about that first key if desired. """ - logger.debug("keycode=%d" % evt.GetKeyCode()) - key = evt.GetKeyCode() + logger.debug("keycode=%d" % event.GetKeyCode()) + key = event.GetKeyCode() if not self._tc.insert_first_key(key): - evt.Skip() + event.Skip() def StartingClick(self): """ @@ -606,7 +608,7 @@ class ContextMenuItem: # None = option # True = toggle selected # False = toggle unselected - toggle_state: t.Optional[bool] + toggle_state: bool | None enabled: bool @@ -631,7 +633,7 @@ def __init__( self._table = table self._binary_data = binary_data self.read_only = read_only - self.on_selection_changed: "CallbackList[[int, t.Optional[int]]]" = ( + self.on_selection_changed: "CallbackList[[int, int | None]]" = ( CallbackList() ) @@ -665,7 +667,7 @@ def __init__( self.refresh() - self._selection: t.Tuple[t.Optional[int], t.Optional[int]] = (None, None) + self._selection: t.Tuple[int | None, int | None] = (None, None) def refresh(self): """ @@ -775,20 +777,20 @@ def _on_range_selecting_mouse(self, event): def _on_range_selecting_keyboard(self, row_diff: int = 0, col_diff: int = 0): """Change selection from the keyboard""" - sel = self._selection - if sel[0] is None: + sel0, sel1 = self._selection + if sel0 is None: return # nothing is currently selected cursor_row, cursor_col = self.GetGridCursorCoords() cursor_idx = self._table.get_byte_idx(cursor_row, cursor_col) - if sel[1] is None: + if sel1 is None: other_idx = cursor_idx else: - if sel[0] == cursor_idx: - other_idx = sel[1] + if sel0 == cursor_idx: + other_idx = sel1 else: - other_idx = sel[0] + other_idx = sel0 cursor_row += row_diff if cursor_row < 0: @@ -865,17 +867,16 @@ def _remove_selection(self) -> bool: if self.read_only is True: return False - sel = self._selection - if sel[0] is None: + sel0, sel1 = self._selection + if sel0 is None: return False - if sel[1] == None: + if sel1 is None: length = 1 else: - length = sel[1] - sel[0] + 1 - - byts = self._binary_data.remove_range(sel[0], length) + length = sel1 - sel0 + 1 + self._binary_data.remove_range(sel0, length) self.ClearSelection() self._selection = (None, None) @@ -899,11 +900,11 @@ def _insert_byte_at_selection(self) -> bool: if self.read_only is True: return False - sel = self._selection - if sel[0] is None: + sel0, _ = self._selection + if sel0 is None: return False - self._binary_data.insert_range(sel[0], b"\x00") + self._binary_data.insert_range(sel0, b"\x00") self._on_range_selecting_keyboard() return True @@ -915,16 +916,16 @@ def _copy_selection(self) -> bool: - true if copy is okay - false if an error occured """ - sel = self._selection - if sel[0] is None: + sel0, sel1 = self._selection + if sel0 is None: return False - if sel[1] == None: + if sel1 is None: length = 1 else: - length = sel[1] - sel[0] + 1 + length = sel1 - sel0 + 1 - byts = self._binary_data.get_range(sel[0], length) + byts = self._binary_data.get_range(sel0, length) if wx.TheClipboard.Open(): byts_str = byts.hex(" ") @@ -1108,11 +1109,11 @@ def _on_cell_right_click(self, event: Grid.GridEvent): """Show context menu""" # Check if the click is inside the current selection. # If not, select the current cell - sel = self._selection + sel0, sel1 = self._selection select_cell = True - if sel[0] is not None and sel[1] is not None: + if sel0 is not None and sel1 is not None: idx = self._table.get_byte_idx(event.GetRow(), event.GetCol()) - if sel[0] <= idx <= sel[1]: + if sel0 <= idx <= sel1: select_cell = False if select_cell: @@ -1123,11 +1124,12 @@ def _on_cell_right_click(self, event: Grid.GridEvent): if menu is None: popup_menu.AppendSeparator() continue - if menu.toggle_state != None: # checkbox boolean state - item: wx.MenuItem = popup_menu.AppendCheckItem(menu.wx_id, menu.name) + item: wx.MenuItem + if menu.toggle_state is not None: # checkbox boolean state + item = popup_menu.AppendCheckItem(menu.wx_id, menu.name) item.Check(menu.toggle_state) else: - item: wx.MenuItem = popup_menu.Append(menu.wx_id, menu.name) + item = popup_menu.Append(menu.wx_id, menu.name) self.Bind(wx.EVT_MENU, menu.callback, id=item.Id) item.Enable(menu.enabled) @@ -1136,7 +1138,7 @@ def _on_cell_right_click(self, event: Grid.GridEvent): def build_context_menu( self, - ) -> t.List[t.Optional[ContextMenuItem]]: + ) -> t.List[ContextMenuItem | None]: """Build the context menu. Can be overridden.""" return [ @@ -1198,7 +1200,7 @@ def __init__( self, parent, binary: bytes = b"", - format: t.Optional[HexEditorFormat] = None, + format: HexEditorFormat | None = None, read_only: bool = False, bitwiese: bool = False, ): @@ -1246,7 +1248,7 @@ def _on_binary_changed(self, binary_data: HexEditorBinaryData): self._status_bar.SetStatusText(msg, 0) self.refresh() - def _on_selection_changed(self, idx1: int, idx2: t.Optional[int]): + def _on_selection_changed(self, idx1: int, idx2: int | None): if idx2 is None: msg = f"Selection: {idx1:n}" else: @@ -1305,7 +1307,7 @@ def on_binary_changed(self) -> "CallbackList[[HexEditorBinaryData]]": # Property: on_binary_changed ############################################# @property - def on_selection_changed(self) -> "CallbackList[[int, t.Optional[int]]]": + def on_selection_changed(self) -> "CallbackList[[int, int | None]]": return self._grid.on_selection_changed @@ -1320,7 +1322,7 @@ def __init__(self, parent, title): # Create an instance of our model... self.hex_editor = WxHexEditor(self) - self.hex_editor.binary = bytearray(500) + self.hex_editor.binary = bytes(500) self.Show(True) diff --git a/construct_editor/wx_widgets/wx_obj_view.py b/construct_editor/wx_widgets/wx_obj_view.py index cbae980..d8a283d 100644 --- a/construct_editor/wx_widgets/wx_obj_view.py +++ b/construct_editor/wx_widgets/wx_obj_view.py @@ -80,11 +80,9 @@ def get_new_obj(self) -> t.Any: try: # convert string to integer - new_obj = str_to_int(val_str) + return str_to_int(val_str) except Exception: - new_obj = val_str # this will probably result in a building error - - return new_obj + return val_str # this will probably result in a building error class WxObjEditor_Bytes(wx.TextCtrl): @@ -105,11 +103,9 @@ def get_new_obj(self) -> t.Any: try: # convert string to bytes - new_obj = str_to_bytes(val_str) + return str_to_bytes(val_str) except Exception: - new_obj = val_str # this will probably result in a building error - - return new_obj + return val_str # this will probably result in a building error class WxObjEditor_Enum(wx.ComboBox): @@ -326,15 +322,15 @@ def _on_kill_focus(self, event): evt_handler.ProcessEvent(event) -WxObjEditor = t.Union[ - WxObjEditor_Default, - WxObjEditor_String, - WxObjEditor_Integer, - WxObjEditor_Bytes, - WxObjEditor_Enum, - WxObjEditor_FlagsEnum, - WxObjEditor_Timestamp, -] +WxObjEditor = ( + WxObjEditor_Default + | WxObjEditor_String + | WxObjEditor_Integer + | WxObjEditor_Bytes + | WxObjEditor_Enum + | WxObjEditor_FlagsEnum + | WxObjEditor_Timestamp +) # ##################################################################################################################### @@ -391,7 +387,7 @@ def render( renderer.RenderText(obj_str, 0, rect, dc, state) return True - def get_mode(self): + def get_mode(self) -> int: return dv.DATAVIEW_CELL_EDITABLE def activate_cell( @@ -401,8 +397,8 @@ def activate_cell( model: dv.DataViewModel, item: dv.DataViewItem, col: int, - mouse_event: t.Optional[wx.MouseEvent], - ): + mouse_event: wx.MouseEvent | None, + ) -> bool: return False @@ -436,7 +432,7 @@ def render( native_renderer.DrawCheckBox(win, dc, rect, flags) return True - def get_mode(self): + def get_mode(self) -> int: return dv.DATAVIEW_CELL_ACTIVATABLE def activate_cell( @@ -446,8 +442,8 @@ def activate_cell( model: dv.DataViewModel, item: dv.DataViewItem, col: int, - mouse_event: t.Optional[wx.MouseEvent], - ): + mouse_event: wx.MouseEvent | None, + ) -> bool: # see wxWidgets: wxDataViewToggleRenderer::WXActivateCell if mouse_event is not None: @@ -469,10 +465,7 @@ def activate_cell( return True -WxObjRendererHelper = t.Union[ - WxObjRendererHelper_Default, - WxObjRendererHelper_Flag, -] +WxObjRendererHelper = WxObjRendererHelper_Default | WxObjRendererHelper_Flag def create_obj_renderer_helper(settings: ObjViewSettings) -> WxObjRendererHelper: diff --git a/construct_editor/wx_widgets/wx_python_code_editor.py b/construct_editor/wx_widgets/wx_python_code_editor.py index fac38f5..8ad3081 100644 --- a/construct_editor/wx_widgets/wx_python_code_editor.py +++ b/construct_editor/wx_widgets/wx_python_code_editor.py @@ -1,8 +1,8 @@ #!/usr/bin/env python import keyword - import sys + import wx import wx.stc as stc @@ -218,7 +218,7 @@ def OnMarginClick(self, evt): # fold and unfold as needed if evt.GetMargin() == 2: if evt.GetShift() and evt.GetControl(): - self.FoldAll() + self.FoldAll(1) else: lineClicked = self.LineFromPosition(evt.GetPosition()) @@ -236,7 +236,7 @@ def OnMarginClick(self, evt): else: self.ToggleFold(lineClicked) - def FoldAll(self): + def FoldAll(self, action: int): lineCount = self.GetLineCount() expanding = True @@ -349,8 +349,8 @@ def SetValue(self, value): self.SetSavePoint() self.SetReadOnly(val) - def SetEditable(self, val): - self.SetReadOnly(not val) + def SetEditable(self, editable: bool) -> None: + self.SetReadOnly(not editable) def IsModified(self): return self.GetModify() @@ -373,15 +373,15 @@ def GetLastPosition(self): def GetPositionFromLine(self, line): return self.PositionFromLine(line) - def GetRange(self, start, end): - return self.GetTextRange(start, end) + def GetRange(self, from_: int, to_: int) -> str: + return self.GetTextRange(from_, to_) def GetSelection(self): return self.GetAnchor(), self.GetCurrentPos() - def SetSelection(self, start, end): - self.SetSelectionStart(start) - self.SetSelectionEnd(end) + def SetSelection(self, from_: int, to_: int): + self.SetSelectionStart(from_) + self.SetSelectionEnd(to_) def SelectLine(self, line): start = self.PositionFromLine(line) diff --git a/doc/screenshot_scripts/bitwise.py b/doc/screenshot_scripts/bitwise.py index 0702f16..4778769 100644 --- a/doc/screenshot_scripts/bitwise.py +++ b/doc/screenshot_scripts/bitwise.py @@ -3,7 +3,6 @@ from doc.screenshot_scripts._helper import ScreenshotFrame - class Frame(ScreenshotFrame): screenshot_name = "bitwise" diff --git a/pyproject.toml b/pyproject.toml index 2c0548c..022df9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,20 +92,14 @@ strict = true # These errors should be activated in the future, but for now we don't want to refactor the entire codebase right now. disable_error_code = [ "arg-type", - "assignment", "attr-defined", "import-untyped", "misc", "no-any-return", "no-untyped-call", "no-untyped-def", - "no-redef", - "return-value", "str-bytes-safe", - "type-arg", - "union-attr", "unused-ignore", - "var-annotated", ] [tool.pyright] @@ -124,21 +118,14 @@ exclude = [ # These warnings should be activated as "error" in the future, but for now we don't want to refactor the entire codebase right now. # Valid values: "error", "warning", "information", "none" -reportArgumentType = "none" -reportAttributeAccessIssue = "none" -reportIncompatibleMethodOverride = "none" -reportInvalidTypeArguments = "none" -reportMissingParameterType = "none" -reportMissingTypeArgument = "none" -reportMissingTypeStubs = "none" -reportOptionalMemberAccess = "none" -reportUnknownArgumentType = "none" -reportUnknownLambdaType = "none" -reportUnknownMemberType = "none" -reportUnknownParameterType = "none" -reportUnknownVariableType = "none" -reportUnnecessaryComparison = "none" -reportUnusedVariable = "none" +reportArgumentType = "none" # 6 errors +reportAttributeAccessIssue = "none" # 8 errors +reportMissingParameterType = "none" # 130 errors +reportMissingTypeStubs = "none" # 11 errors +reportUnknownArgumentType = "none" # 132 errors +reportUnknownMemberType = "none" # 242 errors +reportUnknownParameterType = "none" # 64 errors +reportUnknownVariableType = "none" # 61 errors # These "unnecessary isinstance" checks often enhance readability or increase the future-proofness of the code, # since you can then explicitly "raise" for other types. So we don't want to complain about them. reportUnnecessaryIsInstance = "none" @@ -160,9 +147,7 @@ exclude = [ # Valid values: "error", "warn", "ignore" [tool.ty.rules] invalid-argument-type = "ignore" -invalid-assignment = "ignore" invalid-method-override = "ignore" -invalid-type-arguments = "ignore" unresolved-attribute = "ignore" unused-type-ignore-comment = "ignore" @@ -177,17 +162,17 @@ output-format = "concise" # "full" | "concise" | "grouped" | "json" | "junit" | [tool.ruff.lint] allowed-confusables = ["×", "–", "‘", "’"] - -# select = [] Use Default ruleset, cp. https://docs.astral.sh/ruff/rules/ +select = [ + "F", + "I", + "UP007", + "UP045", +] # These warnings should be activated as "error" in the future, but for now we don't want to refactor the entire codebase right now. ignore = [ - "E402", # Module level import not at top of file - "E711", # Comparison to `None` should be `cond is None` - "E712", # Avoid equality comparisons to `True` "F403", # `from ... import *` used; unable to detect undefined names "F405", # `...` may be undefined, or defined from star imports - "F841", # Local variable `...` is assigned to but never used ] [tool.poe.tasks.test]