From 5257a129a195ed29d91ed6874ef740194e07050f Mon Sep 17 00:00:00 2001
From: Pierre Raybaut
Date: Thu, 28 Sep 2023 12:34:18 +0200
Subject: [PATCH] Fixed/Added annotations/docstrings in some modules
---
guidata/dataset/qtitemwidgets.py | 78 ++------
guidata/dataset/qtwidgets.py | 330 +++++++++++++++++++++++--------
guidata/utils/encoding.py | 17 +-
guidata/utils/misc.py | 26 ++-
4 files changed, 293 insertions(+), 158 deletions(-)
diff --git a/guidata/dataset/qtitemwidgets.py b/guidata/dataset/qtitemwidgets.py
index e100ea8..539a40c 100644
--- a/guidata/dataset/qtitemwidgets.py
+++ b/guidata/dataset/qtitemwidgets.py
@@ -160,11 +160,7 @@ def set(self) -> None:
self.item.set_from_string(self.value())
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
pass
def value(self) -> Any:
@@ -218,11 +214,7 @@ def __init__(
self.group.setLayout(self.layout)
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
self.edit.update_widgets()
def set(self) -> None:
@@ -298,11 +290,7 @@ def __init__(
self.widgets.append(widget)
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
for widget in self.widgets:
widget.get()
@@ -367,11 +355,7 @@ def __init__(
self.edit.textChanged.connect(self.line_edit_changed) # type:ignore
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
value = self.item.get()
old_value = str(self.value())
if value is not None:
@@ -438,11 +422,7 @@ def __get_text(self) -> str:
return str(self.edit.toPlainText()).replace("\u2029", os.linesep)
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
value = self.item.get()
if value is not None:
self.edit.setPlainText(value)
@@ -496,11 +476,7 @@ def __init__(
self.checkbox.stateChanged.connect(self.state_changed) # type:ignore
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
value = self.item.get()
if value is not None:
self.checkbox.setChecked(value)
@@ -576,11 +552,7 @@ def date_changed(self, value):
self.notify_value_change()
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
value = self.item.get()
if value:
if not isinstance(value, datetime.date):
@@ -629,11 +601,7 @@ def date_changed(self, value):
self.notify_value_change()
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
value = self.item.get()
if value:
if not isinstance(value, datetime.datetime):
@@ -1004,11 +972,7 @@ def get_widget_value(self) -> Optional[int]:
return self.combobox.currentIndex()
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
self.initialize_widget()
value = self.item.get()
if value is not None:
@@ -1074,11 +1038,7 @@ def __init__(
self.groupbox.setLayout(layout)
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
value = self.item.get()
_choices = self.item.get_prop_value("data", "choices")
for (i, _choice, _img), checkbox in zip(_choices, self.boxes):
@@ -1172,11 +1132,7 @@ def edit_array(self) -> None:
self.update(self.arr)
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
self.arr = numpy.array(self.item.get(), copy=False)
if self.item.get_prop_value("display", "transpose"):
self.arr = self.arr.T
@@ -1276,11 +1232,7 @@ def __init__(
self.cb_value = None
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
self.cb_value = self.item.get()
def set(self) -> None:
@@ -1355,11 +1307,7 @@ def __init__(
)
def get(self) -> None:
- """Update widget contents from data item value
-
- Returns:
- Widget value
- """
+ """Update widget contents from data item value"""
self.get_dataset()
for widget in self.edit.widgets:
widget.get()
diff --git a/guidata/dataset/qtwidgets.py b/guidata/dataset/qtwidgets.py
index 0a104cc..a3b4397 100644
--- a/guidata/dataset/qtwidgets.py
+++ b/guidata/dataset/qtwidgets.py
@@ -34,7 +34,10 @@
:members:
"""
-from typing import *
+from __future__ import annotations
+
+from collections.abc import Callable
+from typing import TYPE_CHECKING, Any, Type
from qtpy.compat import getopenfilename, getopenfilenames, getsavefilename
from qtpy.QtCore import QRect, QSize, Qt, Signal
@@ -73,20 +76,27 @@
class DataSetEditDialog(QDialog):
- """
- Dialog box for DataSet editing
+ """Dialog box for DataSet editing
+
+ Args:
+ instance: DataSet instance to edit
+ icon: icon name (default: "guidata.svg")
+ parent: parent widget
+ apply: function called when Apply button is clicked
+ wordwrap: if True, comment text is wordwrapped
+ size: dialog size (default: None)
"""
def __init__(
self,
- instance: Union["DataSet", "DataSetGroup"],
- icon: Union[str, QIcon] = "",
- parent: Optional[QWidget] = None,
- apply: Optional[Callable] = None,
+ instance: DataSet | DataSetGroup,
+ icon: str | QIcon = "",
+ parent: QWidget | None = None,
+ apply: Callable | None = None,
wordwrap: bool = True,
- size: Optional[Union[QSize, Tuple[int, int]]] = None,
+ size: QSize | tuple[int, int] | None = None,
) -> None:
- QDialog.__init__(self, parent)
+ super().__init__(parent)
win32_fix_title_bar_background(self)
self.wordwrap = wordwrap
self.apply_func = apply
@@ -96,7 +106,7 @@ def __init__(
label.setWordWrap(wordwrap)
self._layout.addWidget(label)
self.instance = instance
- self.edit_layout: List["DataSetEditLayout"] = []
+ self.edit_layout: list[DataSetEditLayout] = []
self.setup_instance(instance)
@@ -129,7 +139,12 @@ def __init__(
else:
self.resize(*size)
- def button_clicked(self, button: "QAbstractButton") -> None:
+ def button_clicked(self, button: QAbstractButton) -> None:
+ """Handle button click
+
+ Args:
+ button: button that was clicked
+ """
role = self.bbox.buttonRole(button)
if (
role == QDialogButtonBox.ApplyRole # type:ignore
@@ -141,29 +156,49 @@ def button_clicked(self, button: "QAbstractButton") -> None:
self.apply_func(self.instance)
def setup_instance(self, instance: Any) -> None:
- """Construct main layout"""
+ """Construct main layout
+
+ Args:
+ instance: DataSet instance to edit
+ """
grid = QGridLayout()
grid.setAlignment(Qt.AlignTop) # type:ignore
self._layout.addLayout(grid)
self.edit_layout.append(self.layout_factory(instance, grid))
- def layout_factory(
- self, instance: "DataSet", grid: "QGridLayout"
- ) -> "DataSetEditLayout":
+ def layout_factory(self, instance: DataSet, grid: QGridLayout) -> DataSetEditLayout:
"""A factory method that produces instances of DataSetEditLayout
-
or derived classes (see DataSetShowDialog)
+
+ Args:
+ instance: DataSet instance to edit
+ grid: grid layout
+
+ Returns:
+ DataSetEditLayout instance
"""
return DataSetEditLayout(self, instance, grid)
- def child_title(self, item: "DataItemVariable") -> str:
- """Return data item title combined with QApplication title"""
+ def child_title(self, item: DataItemVariable) -> str:
+ """Return data item title combined with QApplication title
+
+ Args:
+ item: data item
+
+ Returns:
+ title
+ """
app_name = QApplication.applicationName()
if not app_name:
app_name = self.instance.get_title()
return "%s - %s" % (app_name, item.label())
def check(self) -> bool:
+ """Check input of all widgets
+
+ Returns:
+ True if all widgets are valid
+ """
is_ok = True
for edl in self.edit_layout:
if not edl.check_all_values():
@@ -188,12 +223,23 @@ def accept(self) -> None:
class DataSetGroupEditDialog(DataSetEditDialog):
+ """Tabbed dialog box for DataSet editing
+
+ Args:
+ instance: DataSetGroup instance to edit
+ icon: icon name (default: "guidata.svg")
+ parent: parent widget
+ apply: function called when Apply button is clicked
+ wordwrap: if True, comment text is wordwrapped
+ size: dialog size (default: None)
"""
- Tabbed dialog box for DataSet editing
- """
- def setup_instance(self, instance: "DataSetGroup") -> None:
- """Override DataSetEditDialog method"""
+ def setup_instance(self, instance: DataSetGroup) -> None:
+ """Construct main layout
+
+ Args:
+ instance: DataSetGroup instance to edit
+ """
from guidata.dataset.datatypes import DataSetGroup
assert isinstance(instance, DataSetGroup)
@@ -220,43 +266,60 @@ def setup_instance(self, instance: "DataSetGroup") -> None:
class DataSetEditLayout:
- """
- Layout in which data item widgets are placed
+ """Layout in which data item widgets are placed
+
+ Args:
+ parent: parent widget
+ instance: DataSet instance to edit
+ layout: grid layout
+ items: list of data items
+ first_line: first line of grid layout
+ change_callback: function called when any widget's value has changed
"""
- _widget_factory: Dict[Any, Any] = {}
+ _widget_factory: dict[Any, Any] = {}
@classmethod
def register(cls: Type, item_type: Type, factory: Any) -> None:
- """Register a factory for a new item_type"""
+ """Register a factory for a new item_type
+
+ Args:
+ item_type: item type
+ factory: factory function
+ """
cls._widget_factory[item_type] = factory
def __init__(
self,
- parent: Optional[QWidget],
- instance: "DataSet",
+ parent: QWidget | None,
+ instance: DataSet,
layout: QGridLayout,
- items: Optional[List["DataItem"]] = None,
+ items: list[DataItem] | None = None,
first_line: int = 0,
- change_callback: Optional[Callable] = None,
+ change_callback: Callable | None = None,
) -> None:
self.parent = parent
self.instance = instance
self.layout = layout
self.first_line = first_line
self.change_callback = change_callback
- self.widgets: List["AbstractDataSetWidget"] = []
+ self.widgets: list[AbstractDataSetWidget] = []
# self.linenos = {} # prochaine ligne à remplir par colonne
- self.items_pos: Dict["DataItem", List[int]] = {}
+ self.items_pos: dict[DataItem, list[int]] = {}
if not items:
items = self.instance._items
items = self.transform_items(items) # type:ignore
self.setup_layout(items)
- def transform_items(self, items: List["DataItem"]) -> List["DataItem"]:
- """
- Handle group of items: transform items into a GroupItem instance
+ def transform_items(self, items: list[DataItem]) -> list[DataItem]:
+ """Handle group of items: transform items into a GroupItem instance
if they are located between BeginGroup and EndGroup
+
+ Args:
+ items: list of data items
+
+ Returns:
+ list of data items
"""
item_lists: Any = [[]]
for item in items:
@@ -272,7 +335,11 @@ def transform_items(self, items: List["DataItem"]) -> List["DataItem"]:
return item_lists[-1]
def check_all_values(self) -> bool:
- """Check input of all widgets"""
+ """Check input of all widgets
+
+ Returns:
+ True if all widgets are valid
+ """
for widget in self.widgets:
if widget.is_active() and not widget.check():
return False
@@ -282,8 +349,12 @@ def accept_changes(self) -> None:
"""Accept changes made to widget inputs"""
self.update_dataitems()
- def setup_layout(self, items: List["DataItem"]) -> None:
- """Place items on layout"""
+ def setup_layout(self, items: list[DataItem]) -> None:
+ """Place items on layout
+
+ Args:
+ items: list of data items
+ """
def last_col(col, span):
"""Return last column (which depends on column span)"""
@@ -302,7 +373,7 @@ def last_col(col, span):
)
# Check if specified rows are consistent
- sorted_items: List[Optional[DataItem]] = [None] * len(items)
+ sorted_items: list[DataItem | None] = [None] * len(items)
rows = []
other_items = []
for item in items:
@@ -350,14 +421,26 @@ def last_col(col, span):
self.refresh_widgets()
- def build_widget(self, item: "DataItem") -> "DataSetShowWidget":
+ def build_widget(self, item: DataItem) -> DataSetShowWidget:
+ """Build widget for item
+
+ Args:
+ item: data item
+
+ Returns:
+ widget
+ """
factory = self._widget_factory[type(item)]
widget = factory(item.bind(self.instance), self)
self.widgets.append(widget)
return widget
- def add_row(self, widget: "DataSetShowWidget") -> None:
- """Add widget to row"""
+ def add_row(self, widget: DataSetShowWidget) -> None:
+ """Add widget to row
+
+ Args:
+ widget: widget to add
+ """
item = widget.item
line, col, span = self.items_pos[item.item]
if col > 0:
@@ -382,9 +465,13 @@ def update_dataitems(self) -> None:
widget.set()
def update_widgets(
- self, except_this_one: Optional[Union[QWidget, "AbstractDataSetWidget"]] = None
+ self, except_this_one: QWidget | AbstractDataSetWidget | None = None
) -> None:
- """Refresh the content of all widgets"""
+ """Refresh the content of all widgets
+
+ Args:
+ except_this_one: widget to skip
+ """
for widget in self.widgets:
if widget is not except_this_one:
widget.get()
@@ -395,8 +482,6 @@ def widget_value_changed(self) -> None:
self.change_callback()
-from typing import Any
-
from guidata.dataset.dataitems import (
BoolItem,
ButtonItem,
@@ -475,12 +560,17 @@ def widget_value_changed(self) -> None:
class DataSetShowWidget(AbstractDataSetWidget):
- """Read-only base widget"""
+ """Read-only base widget
+
+ Args:
+ item: data item variable (``DataItemVariable``)
+ parent_layout: parent layout (``DataSetEditLayout``)
+ """
READ_ONLY = True
def __init__(
- self, item: "DataItemVariable", parent_layout: "DataSetEditLayout"
+ self, item: DataItemVariable, parent_layout: DataSetEditLayout
) -> None:
AbstractDataSetWidget.__init__(self, item, parent_layout)
self.group = QLabel()
@@ -492,27 +582,33 @@ def __init__(
# self.group.setEnabled(False)
def get(self) -> None:
- """Override AbstractDataSetWidget method"""
+ """Update widget contents from data item value"""
self.set_state()
text = self.item.get_string_value()
self.group.setText(text)
- def set(self) -> Any:
- """Read only..."""
+ def set(self) -> None:
+ """Update data item value from widget contents"""
+ # Do nothing: read-only widget
pass
class ShowColorWidget(DataSetShowWidget):
- """Read-only color item widget"""
+ """Read-only color item widget
+
+ Args:
+ item: data item variable (``DataItemVariable``)
+ parent_layout: parent layout (``DataSetEditLayout``)
+ """
def __init__(
- self, item: "DataItemVariable", parent_layout: "DataSetEditLayout"
+ self, item: DataItemVariable, parent_layout: DataSetEditLayout
) -> None:
DataSetShowWidget.__init__(self, item, parent_layout)
- self.picture: Optional[QPicture] = None
+ self.picture: QPicture | None = None
def get(self) -> None:
- """Override AbstractDataSetWidget method"""
+ """Update widget contents from data item value"""
value = self.item.get()
if value is not None:
color = QColor(value)
@@ -525,7 +621,12 @@ def get(self) -> None:
class ShowBooleanWidget(DataSetShowWidget):
- """Read-only bool item widget"""
+ """Read-only bool item widget
+
+ Args:
+ item: data item variable (``DataItemVariable``)
+ parent_layout: parent layout (``DataSetEditLayout``)
+ """
def place_on_grid(
self,
@@ -536,7 +637,16 @@ def place_on_grid(
row_span: int = 1,
column_span: int = 1,
):
- """Override AbstractDataSetWidget method"""
+ """Place widget on layout at specified position
+
+ Args:
+ layout: parent layout
+ row: row index
+ label_column: column index for label
+ widget_column: column index for widget
+ row_span: number of rows to span
+ column_span: number of columns to span
+ """
if not self.item.get_prop_value("display", "label"):
widget_column = label_column
column_span += 1
@@ -545,7 +655,7 @@ def place_on_grid(
layout.addWidget(self.group, row, widget_column, row_span, column_span)
def get(self) -> None:
- """Override AbstractDataSetWidget method"""
+ """Update widget contents from data item value"""
DataSetShowWidget.get(self)
text = self.item.get_prop_value("display", "text")
self.group.setText(text)
@@ -558,16 +668,43 @@ def get(self) -> None:
class DataSetShowLayout(DataSetEditLayout):
- """Read-only layout"""
+ """Read-only layout
+
+ Args:
+ parent: parent widget
+ instance: DataSet instance to edit
+ layout: grid layout
+ items: list of data items
+ first_line: first line of grid layout
+ change_callback: function called when any widget's value has changed
+ """
_widget_factory = {}
class DataSetShowDialog(DataSetEditDialog):
- """Read-only dialog box"""
+ """Read-only dialog box
+
+ Args:
+ instance: DataSet instance to edit
+ icon: icon name (default: "guidata.svg")
+ parent: parent widget
+ apply: function called when Apply button is clicked
+ wordwrap: if True, comment text is wordwrapped
+ size: dialog size (default: None)
+ """
def layout_factory(self, instance: DataSet, grid: QGridLayout) -> DataSetShowLayout:
- """Override DataSetEditDialog method"""
+ """A factory method that produces instances of DataSetEditLayout
+ or derived classes (see DataSetShowDialog)
+
+ Args:
+ instance: DataSet instance to edit
+ grid: grid layout
+
+ Returns:
+ DataSetEditLayout instance
+ """
return DataSetShowLayout(self, instance, grid)
@@ -593,13 +730,20 @@ def layout_factory(self, instance: DataSet, grid: QGridLayout) -> DataSetShowLay
class DataSetShowGroupBox(QGroupBox):
- """Group box widget showing a read-only DataSet"""
+ """Group box widget showing a read-only DataSet
+
+ Args:
+ label: group box label (string)
+ klass: guidata.DataSet class
+ wordwrap: if True, comment text is wordwrapped
+ kwargs: keyword arguments passed to DataSet constructor
+ """
def __init__(
self, label: QLabel, klass: Type, wordwrap: bool = False, **kwargs
) -> None:
QGroupBox.__init__(self, label)
- self.apply_button: Optional[QPushButton] = None
+ self.apply_button: QPushButton | None = None
self.klass = klass
self.dataset = klass(**kwargs)
self._layout = QVBoxLayout()
@@ -613,7 +757,11 @@ def __init__(
self.edit = self.get_edit_layout()
def get_edit_layout(self) -> DataSetEditLayout:
- """Return edit layout"""
+ """Return edit layout
+
+ Returns:
+ edit layout
+ """
return DataSetShowLayout(self, self.dataset, self.grid_layout)
def get(self) -> None:
@@ -625,13 +773,16 @@ def get(self) -> None:
class DataSetEditGroupBox(DataSetShowGroupBox):
- """
- Group box widget including a DataSet
-
- label: group box label (string)
- klass: guidata.DataSet class
- button_text: action button text (default: "Apply")
- button_icon: QIcon object or string (default "apply.png")
+ """Group box widget including a DataSet
+
+ Args:
+ label: group box label (string)
+ klass: guidata.DataSet class
+ button_text: text of apply button (default: "Apply")
+ button_icon: icon of apply button (default: "apply.png")
+ show_button: if True, show apply button (default: True)
+ wordwrap: if True, comment text is wordwrapped
+ kwargs: keyword arguments passed to DataSet constructor
"""
#: Signal emitted when Apply button is clicked
@@ -641,11 +792,11 @@ def __init__(
self,
label: QLabel,
klass: Type,
- button_text: Optional[str] = None,
- button_icon: Optional[Union[QIcon, str]] = None,
+ button_text: str | None = None,
+ button_icon: QIcon | str | None = None,
show_button: bool = True,
wordwrap: bool = False,
- **kwargs
+ **kwargs,
):
DataSetShowGroupBox.__init__(self, label, klass, wordwrap=wordwrap, **kwargs)
if show_button:
@@ -663,7 +814,11 @@ def __init__(
)
def get_edit_layout(self) -> DataSetEditLayout:
- """Return edit layout"""
+ """Return edit layout
+
+ Returns:
+ edit layout
+ """
return DataSetEditLayout(
self, self.dataset, self.grid_layout, change_callback=self.change_callback
)
@@ -672,8 +827,12 @@ def change_callback(self) -> None:
"""Method called when any widget's value has changed"""
self.set_apply_button_state(True)
- def set(self, check=True) -> None:
- """Update data item values from layout contents"""
+ def set(self, check:bool=True) -> None:
+ """Update data item values from layout contents
+
+ Args:
+ check: if True, check input of all widgets
+ """
for widget in self.edit.widgets:
if widget.is_active():
if not check or widget.check():
@@ -682,12 +841,23 @@ def set(self, check=True) -> None:
self.set_apply_button_state(False)
def set_apply_button_state(self, state: bool) -> None:
- """Set apply button enable/disable state"""
+ """Set apply button enable/disable state
+
+ Args:
+ state: if True, enable apply button
+ """
if self.apply_button is not None:
self.apply_button.setEnabled(state)
- def child_title(self, item: "DataItemVariable") -> str:
- """Return data item title combined with QApplication title"""
+ def child_title(self, item: DataItemVariable) -> str:
+ """Return data item title combined with QApplication title
+
+ Args:
+ item: data item
+
+ Returns:
+ title
+ """
app_name = QApplication.applicationName()
if not app_name:
app_name = str(self.title())
diff --git a/guidata/utils/encoding.py b/guidata/utils/encoding.py
index b4d5f4f..6f97f07 100644
--- a/guidata/utils/encoding.py
+++ b/guidata/utils/encoding.py
@@ -11,6 +11,7 @@
source code (Utilities/__init___.py) Copyright © 2003-2009 Detlev Offenbach
"""
+from __future__ import annotations
import os
import re
@@ -47,7 +48,7 @@
]
-def get_coding(text):
+def get_coding(text: str) -> str | None:
"""
Function to get the coding of a text.
@param text text to inspect (string)
@@ -70,7 +71,7 @@ def get_coding(text):
return codec
-def decode(text):
+def decode(text: bytes) -> tuple[str, str] | tuple[str, str] | tuple[str, str]:
"""
Function to decode a text.
@param text text to decode (bytes)
@@ -100,7 +101,7 @@ def decode(text):
return str(text, "latin-1"), "latin-1-guessed"
-def encode(text, orig_coding):
+def encode(text: str, orig_coding: str) -> tuple[bytes, str] | tuple[bytes, str]:
"""
Function to encode a text.
@param text text to encode (string)
@@ -146,7 +147,7 @@ def encode(text, orig_coding):
return text.encode("utf-8"), "utf-8"
-def write(text, filename, encoding="utf-8", mode="wb"):
+def write(text: str, filename: str, encoding: str = "utf-8", mode: str = "wb") -> str:
"""
Write 'text' to file ('filename') assuming 'encoding'
Return (eventually new) encoding
@@ -157,7 +158,9 @@ def write(text, filename, encoding="utf-8", mode="wb"):
return encoding
-def writelines(lines, filename, encoding="utf-8", mode="wb"):
+def writelines(
+ lines: list[str], filename: str, encoding: str = "utf-8", mode: str = "wb"
+) -> str:
"""
Write 'lines' to file ('filename') assuming 'encoding'
Return (eventually new) encoding
@@ -165,7 +168,7 @@ def writelines(lines, filename, encoding="utf-8", mode="wb"):
return write(os.linesep.join(lines), filename, encoding, mode)
-def read(filename, encoding="utf-8"):
+def read(filename: str, encoding: str = "utf-8") -> tuple[str, str]:
"""
Read text from file ('filename')
Return text and encoding
@@ -174,7 +177,7 @@ def read(filename, encoding="utf-8"):
return text, encoding
-def readlines(filename, encoding="utf-8"):
+def readlines(filename: str, encoding: str = "utf-8") -> tuple[list[str], str]:
"""
Read lines from file ('filename')
Return lines and encoding
diff --git a/guidata/utils/misc.py b/guidata/utils/misc.py
index caa25f6..5ede9e2 100644
--- a/guidata/utils/misc.py
+++ b/guidata/utils/misc.py
@@ -34,7 +34,7 @@
import os.path as osp
import subprocess
import sys
-from typing import Any
+from typing import Any, Type
# Local imports
from guidata.userconfig import get_home_dir
@@ -84,8 +84,16 @@ def decode_fs_string(string: str) -> str:
# ==============================================================================
-def assert_interface_supported(klass, iface):
- """Makes sure a class supports an interface"""
+def assert_interface_supported(klass: Type, iface: Type) -> None:
+ """Makes sure a class supports an interface
+
+ Args:
+ klass (Type): The class.
+ iface (Type): The interface.
+
+ Raises:
+ AssertionError: If the class does not support the interface.
+ """
for name, func in list(iface.__dict__.items()):
if name == "__inherits__":
continue
@@ -107,9 +115,15 @@ def assert_interface_supported(klass, iface):
pass # should check class attributes for consistency
-def assert_interfaces_valid(klass):
- """Makes sure a class supports the interfaces
- it declares"""
+def assert_interfaces_valid(klass: Type) -> None:
+ """Makes sure a class supports the interfaces it declares
+
+ Args:
+ klass (Type): The class.
+
+ Raises:
+ AssertionError: If the class does not support the interfaces it declares.
+ """
assert hasattr(klass, "__implements__"), "Class doesn't implements anything"
for iface in klass.__implements__:
assert_interface_supported(klass, iface)