From 1fa641cabca97fdc32bf4d174964b841a0235931 Mon Sep 17 00:00:00 2001 From: Mark Koch Date: Mon, 13 Nov 2023 21:30:03 +0000 Subject: [PATCH] Mypy compliance --- zxlive/animations.py | 3 ++- zxlive/app.py | 2 +- zxlive/base_panel.py | 3 ++- zxlive/commands.py | 5 +++-- zxlive/common.py | 2 +- zxlive/custom_rule.py | 4 ++-- zxlive/editor_base_panel.py | 12 ++++++------ zxlive/eitem.py | 1 + zxlive/graphscene.py | 3 ++- zxlive/graphview.py | 6 ++++-- zxlive/mainwindow.py | 4 ++-- zxlive/parse_poly.py | 2 +- zxlive/proof.py | 9 ++++++--- zxlive/proof_actions.py | 2 +- zxlive/proof_panel.py | 14 ++++++++------ zxlive/settings_dialog.py | 27 ++++++++++++++------------- zxlive/vitem.py | 1 + 17 files changed, 57 insertions(+), 43 deletions(-) diff --git a/zxlive/animations.py b/zxlive/animations.py index f901a79c..6c3b776d 100644 --- a/zxlive/animations.py +++ b/zxlive/animations.py @@ -183,7 +183,8 @@ def fuse(dragged: VItem, target: VItem, meet_halfway: bool = False) -> QAbstract if not meet_halfway: group.addAnimation(move(dragged, target=target.pos(), duration=100, ease=QEasingCurve(QEasingCurve.Type.OutQuad))) else: - halfway_pos = (dragged.pos() + target.pos()) / 2 + sum_pos = dragged.pos() + target.pos() + halfway_pos = QPointF(sum_pos.x() / 2, sum_pos.y() / 2) group.addAnimation(move(dragged, target=halfway_pos, duration=100, ease=QEasingCurve(QEasingCurve.Type.OutQuad))) group.addAnimation(move(target, target=halfway_pos, duration=100, ease=QEasingCurve(QEasingCurve.Type.OutQuad))) group.addAnimation(scale(target, target=1, duration=100, ease=QEasingCurve(QEasingCurve.Type.InBack))) diff --git a/zxlive/app.py b/zxlive/app.py index 2b9fba83..e525f4fc 100644 --- a/zxlive/app.py +++ b/zxlive/app.py @@ -30,7 +30,7 @@ if os.name == 'nt': import ctypes myappid = 'quantomatic.zxlive.zxlive.1.0.0' # arbitrary string - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) + ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) # type: ignore class ZXLive(QApplication): diff --git a/zxlive/base_panel.py b/zxlive/base_panel.py index f08c3044..e971b0ed 100644 --- a/zxlive/base_panel.py +++ b/zxlive/base_panel.py @@ -96,7 +96,8 @@ def deselect_all(self) -> None: def copy_selection(self) -> GraphT: selection = list(self.graph_scene.selected_vertices) copied_graph = self.graph.subgraph_from_vertices(selection) - assert isinstance(copied_graph, GraphT) + # Mypy issue: https://github.com/python/mypy/issues/11673 + assert isinstance(copied_graph, GraphT) # type: ignore return copied_graph def update_colors(self) -> None: diff --git a/zxlive/commands.py b/zxlive/commands.py index 0303e9ea..880142f3 100644 --- a/zxlive/commands.py +++ b/zxlive/commands.py @@ -15,6 +15,7 @@ from .common import ET, VT, W_INPUT_OFFSET, GraphT from .graphview import GraphView +from .poly import Poly from .proof import ProofModel, Rewrite @@ -304,9 +305,9 @@ def redo(self) -> None: class ChangePhase(BaseCommand): """Updates the phase of a spider.""" v: VT - new_phase: Union[Fraction, int] + new_phase: Union[Fraction, Poly, complex] - _old_phase: Optional[Union[Fraction, int]] = field(default=None, init=False) + _old_phase: Optional[Union[Fraction, Poly, complex]] = field(default=None, init=False) def undo(self) -> None: assert self._old_phase is not None diff --git a/zxlive/common.py b/zxlive/common.py index 6b858e88..a5d7ac91 100644 --- a/zxlive/common.py +++ b/zxlive/common.py @@ -158,7 +158,7 @@ def _get_synonyms(key: str, default: list[str]) -> list[str]: def to_tikz(g: GraphT) -> str: - return pyzx.tikz.to_tikz(g) + return pyzx.tikz.to_tikz(g) # type: ignore def from_tikz(s: str) -> GraphT: try: diff --git a/zxlive/custom_rule.py b/zxlive/custom_rule.py index 9d3e5a23..e3dee9f0 100644 --- a/zxlive/custom_rule.py +++ b/zxlive/custom_rule.py @@ -93,8 +93,8 @@ def from_json(cls, json_str: str) -> "CustomRule": d = json.loads(json_str) lhs_graph = GraphT.from_json(d['lhs_graph']) rhs_graph = GraphT.from_json(d['rhs_graph']) - assert (isinstance(lhs_graph, GraphT) and - isinstance(rhs_graph, GraphT)) + # Mypy issue: https://github.com/python/mypy/issues/11673 + assert (isinstance(lhs_graph, GraphT) and isinstance(rhs_graph, GraphT)) # type: ignore return cls(lhs_graph, rhs_graph, d['name'], d['description']) def to_proof_action(self) -> "ProofAction": diff --git a/zxlive/editor_base_panel.py b/zxlive/editor_base_panel.py index 70d20882..9230491d 100644 --- a/zxlive/editor_base_panel.py +++ b/zxlive/editor_base_panel.py @@ -39,7 +39,7 @@ class ShapeType(Enum): class DrawPanelNodeType(TypedDict): text: str - icon: tuple[ShapeType, str] + icon: tuple[ShapeType, QColor] def vertices_data() -> dict[VertexType.Type, DrawPanelNodeType]: @@ -54,8 +54,8 @@ def vertices_data() -> dict[VertexType.Type, DrawPanelNodeType]: def edges_data() -> dict[EdgeType.Type, DrawPanelNodeType]: return { - EdgeType.SIMPLE: {"text": "Simple", "icon": (ShapeType.LINE, BLACK)}, - EdgeType.HADAMARD: {"text": "Hadamard", "icon": (ShapeType.DASHED_LINE, HAD_EDGE_BLUE)}, + EdgeType.SIMPLE: {"text": "Simple", "icon": (ShapeType.LINE, QColor(BLACK))}, + EdgeType.HADAMARD: {"text": "Hadamard", "icon": (ShapeType.DASHED_LINE, QColor(HAD_EDGE_BLUE))}, } @@ -211,7 +211,7 @@ def __init__(self, variable_types: dict[str, bool]) -> None: self._variable_types = variable_types self._widget = QWidget() - lpal = QApplication.palette("QListWidget") + lpal = QApplication.palette("QListWidget") # type: ignore palette = QPalette() palette.setBrush(QPalette.ColorRole.Window, lpal.base()) self._widget.setAutoFillBackground(True) @@ -354,14 +354,14 @@ def populate_list_widget(list_widget: QListWidget, list_widget.setCurrentRow(row) -def create_icon(shape: ShapeType, color: str) -> QIcon: +def create_icon(shape: ShapeType, color: QColor) -> QIcon: icon = QIcon() pixmap = QPixmap(64, 64) pixmap.fill(Qt.GlobalColor.transparent) painter = QPainter(pixmap) painter.setRenderHint(QPainter.RenderHint.Antialiasing) painter.setPen(QPen(QColor(BLACK), 6)) - painter.setBrush(QColor(color)) + painter.setBrush(color) if shape == ShapeType.CIRCLE: painter.drawEllipse(4, 4, 56, 56) elif shape == ShapeType.SQUARE: diff --git a/zxlive/eitem.py b/zxlive/eitem.py index 20d85ee7..d6b6a03e 100644 --- a/zxlive/eitem.py +++ b/zxlive/eitem.py @@ -93,6 +93,7 @@ def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: Opt # By default, Qt draws a dashed rectangle around selected items. # We have our own implementation to draw selected vertices, so # we intercept the selected option here. + assert hasattr(option, "state") option.state &= ~QStyle.StateFlag.State_Selected super().paint(painter, option, widget) diff --git a/zxlive/graphscene.py b/zxlive/graphscene.py index 2fda49fc..e14b8250 100644 --- a/zxlive/graphscene.py +++ b/zxlive/graphscene.py @@ -121,7 +121,8 @@ def update_graph(self, new: GraphT, select_new: bool = False) -> None: self.removeItem(e_item) new_g = diff.apply_diff(self.g) - assert isinstance(new_g, GraphT) + # Mypy issue: https://github.com/python/mypy/issues/11673 + assert isinstance(new_g, GraphT) # type: ignore self.g = new_g # g now contains the new graph, # but we still need to update the scene diff --git a/zxlive/graphview.py b/zxlive/graphview.py index b9d13cb1..b4415956 100644 --- a/zxlive/graphview.py +++ b/zxlive/graphview.py @@ -119,7 +119,7 @@ def mousePressEvent(self, e: QMouseEvent) -> None: elif self.tool == GraphTool.MagicWand: pos = self.mapToScene(e.pos()) shift = e.modifiers() & Qt.KeyboardModifier.ShiftModifier - self.wand_trace = WandTrace(pos, shift) + self.wand_trace = WandTrace(pos, bool(shift)) self.wand_path = QGraphicsPathItem() self.graph_scene.addItem(self.wand_path) pen = QPen(QColor(WAND_COLOR), WAND_WIDTH) @@ -302,6 +302,7 @@ def mousePressEvent(self, e: QMouseEvent) -> None: class Sparkles(QObject): + def __init__(self, graph_scene: GraphScene) -> None: super().__init__() self.graph_scene = graph_scene @@ -314,7 +315,7 @@ def __init__(self, graph_scene: GraphScene) -> None: vx = speed * math.cos(angle) / SPARKLE_STEPS vy = speed * math.sin(angle) / SPARKLE_STEPS self.sparkle_deltas.append((vx, vy)) - self.timer_id = None + self.timer_id: Optional[int] = None def emit_sparkles(self, pos: QPointF, mult: int) -> None: if not self.timer_id: @@ -335,6 +336,7 @@ def timerEvent(self, event: QTimerEvent) -> None: sparkle.timer_step() def stop(self) -> None: + assert self.timer_id is not None self.killTimer(self.timer_id) self.timer_id = None for sparkle in reversed(self.sparkles): diff --git a/zxlive/mainwindow.py b/zxlive/mainwindow.py index a55feb0c..6f363a81 100644 --- a/zxlive/mainwindow.py +++ b/zxlive/mainwindow.py @@ -214,7 +214,7 @@ def _new_action(self, name: str, trigger: Callable, shortcut: QKeySequence | QKe if not action.shortcuts(): action.setShortcut(alt_shortcut) elif alt_shortcut not in action.shortcuts(): - action.setShortcuts([shortcut, alt_shortcut]) + action.setShortcuts([shortcut, alt_shortcut]) # type: ignore return action @property @@ -501,7 +501,7 @@ def format_str(c: complex) -> str: for i in range(matrix.shape[0]): for j in range(matrix.shape[1]): entry = QTableWidgetItem(format_str(matrix[i, j])) - entry.setFlags(entry.flags() & ~Qt.ItemIsEditable) + entry.setFlags(entry.flags() & ~Qt.ItemFlag.ItemIsEditable) table.setItem(i, j, entry) table.resizeColumnsToContents() table.resizeRowsToContents() diff --git a/zxlive/parse_poly.py b/zxlive/parse_poly.py index f70a33ab..42e9da97 100644 --- a/zxlive/parse_poly.py +++ b/zxlive/parse_poly.py @@ -24,7 +24,7 @@ parser='lalr', maybe_placeholders=True) -class PolyTransformer(Transformer): +class PolyTransformer(Transformer[Poly]): def __init__(self, new_var: Callable[[str], Poly]): super().__init__() diff --git a/zxlive/proof.py b/zxlive/proof.py index 93b74634..5649aa8d 100644 --- a/zxlive/proof.py +++ b/zxlive/proof.py @@ -113,7 +113,8 @@ def pop_rewrite(self) -> tuple[Rewrite, GraphT]: def get_graph(self, index: int) -> GraphT: """Returns the grap at a given position in the proof.""" copy = self.graphs[index].copy() - assert isinstance(copy, GraphT) + # Mypy issue: https://github.com/python/mypy/issues/11673 + assert isinstance(copy, GraphT) # type: ignore return copy def to_json(self) -> str: @@ -132,11 +133,13 @@ def from_json(json_str: str) -> "ProofModel": """Deserializes the model from JSON.""" d = json.loads(json_str) initial_graph = GraphT.from_tikz(d["initial_graph"]) - assert isinstance(initial_graph, GraphT) + # Mypy issue: https://github.com/python/mypy/issues/11673 + assert isinstance(initial_graph, GraphT) # type: ignore model = ProofModel(initial_graph) for step in d["proof_steps"]: rewrite = Rewrite.from_json(step) rewritten_graph = rewrite.diff.apply_diff(model.graphs[-1]) - assert isinstance(rewritten_graph, GraphT) + # Mypy issue: https://github.com/python/mypy/issues/11673 + assert isinstance(rewritten_graph, GraphT) # type: ignore model.add_rewrite(rewrite, rewritten_graph) return model diff --git a/zxlive/proof_actions.py b/zxlive/proof_actions.py index 004479dd..46df31bc 100644 --- a/zxlive/proof_actions.py +++ b/zxlive/proof_actions.py @@ -208,7 +208,7 @@ def rule(g: GraphT, matches: list) -> pyzx.rules.RewriteOutputType[ET,VT]: return ({}, [], [], True) return rule -def _extract_circuit(graph, matches): +def _extract_circuit(graph: GraphT, matches: list) -> GraphT: graph.auto_detect_io() simplify.full_reduce(graph) return extract_circuit(graph).to_graph() diff --git a/zxlive/proof_panel.py b/zxlive/proof_panel.py index a518ac8f..266c1c22 100644 --- a/zxlive/proof_panel.py +++ b/zxlive/proof_panel.py @@ -51,7 +51,7 @@ def __init__(self, graph: GraphT, *actions: QAction) -> None: self.graph_view.set_graph(graph) self.actions_bar = QTabWidget(self) - self.layout().insertWidget(1, self.actions_bar) + self.layout().insertWidget(1, self.actions_bar) # type: ignore self.init_action_groups() self.actions_bar.currentChanged.connect(self.update_on_selection) @@ -131,7 +131,7 @@ def init_action_groups(self) -> None: widget = QWidget() widget.setLayout(hlayout) - widget.action_group = group + setattr(widget, "action_group", group) self.actions_bar.addTab(widget, group.name) def parse_selection(self) -> tuple[list[VT], list[ET]]: @@ -148,7 +148,8 @@ def parse_selection(self) -> tuple[list[VT], list[ET]]: def update_on_selection(self) -> None: selection, edges = self.parse_selection() g = self.graph_scene.g - self.actions_bar.currentWidget().action_group.update_active(g, selection, edges) + action_group = getattr(self.actions_bar.currentWidget(), "action_group") + action_group.update_active(g, selection, edges) def _vert_moved(self, vs: list[tuple[VT, float, float]]) -> None: cmd = MoveNodeInStep(self.graph_view, vs, self.step_view) @@ -244,7 +245,7 @@ def cross(a: QPointF, b: QPointF) -> float: if not ok: return False try: - def new_var(_): + def new_var(_: str) -> Poly: raise ValueError() phase = string_to_complex(text) if phase_is_complex else string_to_fraction(text, new_var) except ValueError: @@ -367,7 +368,7 @@ def _proof_step_selected(self, selected: QItemSelection, deselected: QItemSelect cmd = GoToRewriteStep(self.graph_view, self.step_view, deselected.first().topLeft().row(), selected.first().topLeft().row()) self.undo_stack.push(cmd) - def _refresh_rules(self): + def _refresh_rules(self) -> None: self.actions_bar.removeTab(self.actions_bar.count() - 1) custom_rules = [] for root, dirs, files in os.walk(get_custom_rules_path()): @@ -386,7 +387,7 @@ def _refresh_rules(self): hlayout.addStretch() widget = QWidget() widget.setLayout(hlayout) - widget.action_group = group + setattr(widget, "action_group", group) self.actions_bar.addTab(widget, group.name) @@ -406,6 +407,7 @@ class ProofStepItemDelegate(QStyledItemDelegate): def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: Union[QModelIndex, QPersistentModelIndex]) -> None: painter.save() + assert hasattr(option, "state") and hasattr(option, "rect") and hasattr(option, "font") # Draw background painter.setPen(Qt.GlobalColor.transparent) diff --git a/zxlive/settings_dialog.py b/zxlive/settings_dialog.py index 20b7adc8..28cd980f 100644 --- a/zxlive/settings_dialog.py +++ b/zxlive/settings_dialog.py @@ -69,9 +69,9 @@ } class SettingsDialog(QDialog): - def __init__(self, parent: MainWindow) -> None: - super().__init__(parent) - self.parent = parent + def __init__(self, main_window: MainWindow) -> None: + super().__init__(main_window) + self.main_window = main_window self.setWindowTitle("Settings") self.settings = QSettings("zxlive", "zxlive") self.value_dict: Dict[str,QWidget] = {} @@ -173,6 +173,7 @@ def __init__(self, parent: MainWindow) -> None: def add_setting(self,form:QFormLayout, name:str, label:str, ty:str, data:Any=None) -> None: val = self.settings.value(name) + widget: QWidget if val is None: val = defaults[name] if ty == 'str': widget = QLineEdit() @@ -180,12 +181,10 @@ def add_setting(self,form:QFormLayout, name:str, label:str, ty:str, data:Any=Non widget.setText(val) elif ty == 'int': widget = QSpinBox() - val = int(val) - widget.setValue(val) + widget.setValue(int(val)) # type: ignore elif ty == 'float': widget = QDoubleSpinBox() - val = float(val) - widget.setValue(val) + widget.setValue(float(val)) # type: ignore elif ty == 'folder': widget = QWidget() hlayout = QHBoxLayout() @@ -194,10 +193,10 @@ def add_setting(self,form:QFormLayout, name:str, label:str, ty:str, data:Any=Non val = str(val) widget_line.setText(val) def browse() -> None: - directory = QFileDialog.getExistingDirectory(self,"Pick folder",options=QFileDialog.ShowDirsOnly) + directory = QFileDialog.getExistingDirectory(self,"Pick folder",options=QFileDialog.Option.ShowDirsOnly) if directory: widget_line.setText(directory) - widget.text_value = directory + setattr(widget, "text_value", directory) hlayout.addWidget(widget_line) button = QPushButton("Browse") button.clicked.connect(browse) @@ -206,9 +205,9 @@ def browse() -> None: widget = QComboBox() val = str(val) assert isinstance(data, dict) - widget.addItems(data.values()) + widget.addItems(list(data.values())) widget.setCurrentText(data[val]) - widget.data = data + setattr(widget, "data", data) form.addRow(label, widget) @@ -231,8 +230,10 @@ def okay(self) -> None: self.settings.setValue(name, widget.text_value) set_pyzx_tikz_settings() if self.settings.value("color-scheme") != self.prev_color_scheme: - colors.set_color_scheme(self.settings.value("color-scheme")) - self.parent.update_colors() + theme = self.settings.value("color-scheme") + assert isinstance(theme, str) + colors.set_color_scheme(theme) + self.main_window.update_colors() self.accept() def cancel(self) -> None: diff --git a/zxlive/vitem.py b/zxlive/vitem.py index 9df61b5b..02c89f66 100644 --- a/zxlive/vitem.py +++ b/zxlive/vitem.py @@ -222,6 +222,7 @@ def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: Opt # By default, Qt draws a dashed rectangle around selected items. # We have our own implementation to draw selected vertices, so # we intercept the selected option here. + assert hasattr(option, "state") option.state &= ~QStyle.StateFlag.State_Selected super().paint(painter, option, widget)