mirror of
https://github.com/pyapp-kit/superqt.git
synced 2025-07-21 04:01:07 +02:00
build: drop py38 (#263)
* build: drop py38 * bump min typing ext * ignore cleanup warning from pyside6 * change minreq * bump min * fix for pint again
This commit is contained in:
4
.github/workflows/test_and_deploy.yml
vendored
4
.github/workflows/test_and_deploy.yml
vendored
@@ -68,9 +68,9 @@ jobs:
|
||||
test-qt-minreqs:
|
||||
uses: pyapp-kit/workflows/.github/workflows/test-pyrepo.yml@v2
|
||||
with:
|
||||
python-version: "3.8"
|
||||
python-version: "3.9"
|
||||
qt: pyqt5
|
||||
pip-post-installs: "qtpy==1.1.0 typing-extensions==3.7.4.3"
|
||||
pip-post-installs: "qtpy==1.1.0 typing-extensions==4.5.0" # 4.5.0 is just for pint
|
||||
pip-install-flags: -e
|
||||
coverage-upload: artifact
|
||||
|
||||
|
@@ -26,7 +26,7 @@ pytest
|
||||
|
||||
All widgets must be well-tested, and should work on:
|
||||
|
||||
- Python 3.8 and above
|
||||
- Python 3.9 and above
|
||||
- PyQt5 (5.11 and above) & PyQt6
|
||||
- PySide2 (5.11 and above) & PySide6
|
||||
- macOS, Windows, & Linux
|
||||
|
@@ -15,7 +15,7 @@ that are not provided in the native QtWidgets module.
|
||||
Components are tested on:
|
||||
|
||||
- macOS, Windows, & Linux
|
||||
- Python 3.8 and above
|
||||
- Python 3.9 and above
|
||||
- PyQt5 (5.11 and above) & PyQt6
|
||||
- PySide2 (5.11 and above) & PySide6
|
||||
|
||||
|
@@ -10,7 +10,7 @@ QtWidgets module.
|
||||
Components are tested on:
|
||||
|
||||
- macOS, Windows, & Linux
|
||||
- Python 3.8 and above
|
||||
- Python 3.9 and above
|
||||
- PyQt5 (5.11 and above) & PyQt6
|
||||
- PySide2 (5.11 and above) & PySide6
|
||||
|
||||
|
@@ -27,7 +27,7 @@ SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
from typing import Deque
|
||||
from collections import deque
|
||||
|
||||
from qtpy.QtCore import QRect, QSize, Qt, QTimer, Signal
|
||||
from qtpy.QtGui import QPainter, QPen
|
||||
@@ -65,8 +65,8 @@ class DrawSignalsWidget(QWidget):
|
||||
self._scrollTimer.timeout.connect(self._scroll)
|
||||
self._scrollTimer.start()
|
||||
|
||||
self._signalActivations: Deque[int] = Deque()
|
||||
self._throttledSignalActivations: Deque[int] = Deque()
|
||||
self._signalActivations: deque[int] = deque()
|
||||
self._throttledSignalActivations: deque[int] = deque()
|
||||
|
||||
def sizeHint(self):
|
||||
return QSize(400, 200)
|
||||
@@ -84,7 +84,7 @@ class DrawSignalsWidget(QWidget):
|
||||
|
||||
self.update()
|
||||
|
||||
def scrollAndCut(self, v: Deque[int], cutoff: int):
|
||||
def scrollAndCut(self, v: deque[int], cutoff: int):
|
||||
L = len(v)
|
||||
for p in range(L):
|
||||
v[p] += 1
|
||||
@@ -121,7 +121,7 @@ class DrawSignalsWidget(QWidget):
|
||||
p.drawLine(0, h2, w, h2)
|
||||
p.restore()
|
||||
|
||||
def _drawSignals(self, p: QPainter, v: Deque[int], color, yStart, yEnd):
|
||||
def _drawSignals(self, p: QPainter, v: deque[int], color, yStart, yEnd):
|
||||
p.save()
|
||||
pen = QPen()
|
||||
pen.setWidthF(2.0)
|
||||
|
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
|
||||
name = "superqt"
|
||||
description = "Missing widgets and components for PyQt/PySide"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
requires-python = ">=3.9"
|
||||
license = { text = "BSD 3-Clause License" }
|
||||
authors = [{ email = "talley.lambert@gmail.com", name = "Talley Lambert" }]
|
||||
keywords = [
|
||||
@@ -28,7 +28,6 @@ classifiers = [
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
@@ -41,13 +40,21 @@ dynamic = ["version"]
|
||||
dependencies = [
|
||||
"pygments>=2.4.0",
|
||||
"qtpy>=1.1.0",
|
||||
"typing-extensions >=3.7.4.3,!=3.10.0.0",
|
||||
"typing-extensions >=3.7.4.3,!=3.10.0.0", # however, pint requires >4.5.0
|
||||
]
|
||||
|
||||
# extras
|
||||
# https://peps.python.org/pep-0621/#dependencies-optional-dependencies
|
||||
[project.optional-dependencies]
|
||||
test = ["pint", "pytest", "pytest-cov", "pytest-qt", "numpy", "cmap", "pyconify"]
|
||||
test = [
|
||||
"pint",
|
||||
"pytest",
|
||||
"pytest-cov",
|
||||
"pytest-qt",
|
||||
"numpy",
|
||||
"cmap",
|
||||
"pyconify",
|
||||
]
|
||||
dev = [
|
||||
"ipython",
|
||||
"ruff",
|
||||
@@ -58,7 +65,13 @@ dev = [
|
||||
"rich",
|
||||
"types-Pygments",
|
||||
]
|
||||
docs = ["mkdocs-macros-plugin", "mkdocs-material", "mkdocstrings[python]", "pint", "cmap"]
|
||||
docs = [
|
||||
"mkdocs-macros-plugin",
|
||||
"mkdocs-material",
|
||||
"mkdocstrings[python]",
|
||||
"pint",
|
||||
"cmap",
|
||||
]
|
||||
quantity = ["pint"]
|
||||
cmap = ["cmap >=0.1.1"]
|
||||
pyside2 = ["pyside2"]
|
||||
@@ -100,21 +113,30 @@ python = ["3.11"]
|
||||
|
||||
[[tool.hatch.envs.test.matrix]]
|
||||
qt = ["pyside2", "pyqt5", "pyqt5.12"]
|
||||
python = ["3.8"]
|
||||
python = ["3.9"]
|
||||
|
||||
[tool.hatch.envs.test.overrides]
|
||||
matrix.qt.extra-dependencies = [
|
||||
{value = "pyside2", if = ["pyside2"]},
|
||||
{value = "pyside6", if = ["pyside6"]},
|
||||
{value = "pyqt5", if = ["pyqt5"]},
|
||||
{value = "pyqt6", if = ["pyqt6"]},
|
||||
{value = "pyqt5==5.12", if = ["pyqt5.12"]},
|
||||
{ value = "pyside2", if = [
|
||||
"pyside2",
|
||||
] },
|
||||
{ value = "pyside6", if = [
|
||||
"pyside6",
|
||||
] },
|
||||
{ value = "pyqt5", if = [
|
||||
"pyqt5",
|
||||
] },
|
||||
{ value = "pyqt6", if = [
|
||||
"pyqt6",
|
||||
] },
|
||||
{ value = "pyqt5==5.12", if = [
|
||||
"pyqt5.12",
|
||||
] },
|
||||
]
|
||||
|
||||
# https://github.com/charliermarsh/ruff
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
target-version = "py38"
|
||||
target-version = "py39"
|
||||
src = ["src", "tests"]
|
||||
|
||||
# https://docs.astral.sh/ruff/rules
|
||||
@@ -132,7 +154,7 @@ select = [
|
||||
"B", # flake8-bugbear
|
||||
"A001", # flake8-builtins
|
||||
"RUF", # ruff-specific rules
|
||||
"TCH", # flake8-type-checking
|
||||
"TC", # flake8-type-checking
|
||||
"TID", # flake8-tidy-imports
|
||||
]
|
||||
ignore = [
|
||||
@@ -159,6 +181,7 @@ filterwarnings = [
|
||||
"ignore:QPixmapCache.find:DeprecationWarning:",
|
||||
"ignore:SelectableGroups dict interface:DeprecationWarning",
|
||||
"ignore:The distutils package is deprecated:DeprecationWarning",
|
||||
"ignore:.*Skipping callback call set_result",
|
||||
]
|
||||
|
||||
# https://mypy.readthedocs.io/en/stable/config_file.html
|
||||
|
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Container
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from cmap import Colormap
|
||||
from qtpy.QtCore import Qt, Signal
|
||||
@@ -11,6 +11,8 @@ from ._cmap_line_edit import QColormapLineEdit
|
||||
from ._cmap_utils import try_cast_colormap
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Container
|
||||
|
||||
from cmap._catalog import Category, Interpolation
|
||||
from qtpy.QtGui import QKeyEvent
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Sequence
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from cmap import Colormap
|
||||
from qtpy.QtCore import Qt, Signal
|
||||
@@ -23,6 +23,8 @@ from ._cmap_line_edit import QColormapLineEdit
|
||||
from ._cmap_utils import try_cast_colormap
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Sequence
|
||||
|
||||
from cmap._colormap import ColorStopsLike
|
||||
|
||||
|
||||
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import warnings
|
||||
from contextlib import suppress
|
||||
from enum import IntEnum, auto
|
||||
from typing import Any, Literal, Sequence, cast
|
||||
from typing import TYPE_CHECKING, Any, Literal, cast
|
||||
|
||||
from qtpy.QtCore import QModelIndex, QPersistentModelIndex, QRect, QSize, Qt, Signal
|
||||
from qtpy.QtGui import QColor, QPainter
|
||||
@@ -19,6 +19,9 @@ from qtpy.QtWidgets import (
|
||||
|
||||
from superqt.utils import signals_blocked
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Sequence
|
||||
|
||||
_NAME_MAP = {QColor(x).name(): x for x in QColor.colorNames()}
|
||||
|
||||
COLOR_ROLE = Qt.ItemDataRole.BackgroundRole
|
||||
|
@@ -3,7 +3,7 @@ from enum import Enum, EnumMeta, Flag
|
||||
from functools import reduce
|
||||
from itertools import combinations
|
||||
from operator import or_
|
||||
from typing import Optional, Tuple, TypeVar
|
||||
from typing import Optional, TypeVar
|
||||
|
||||
from qtpy.QtCore import Signal
|
||||
from qtpy.QtWidgets import QComboBox
|
||||
@@ -47,7 +47,7 @@ def _get_name(enum_value: Enum):
|
||||
return name
|
||||
|
||||
|
||||
def _get_name_with_value(enum_value: Enum) -> Tuple[str, Enum]:
|
||||
def _get_name_with_value(enum_value: Enum) -> tuple[str, Enum]:
|
||||
return _get_name(enum_value), enum_value
|
||||
|
||||
|
||||
|
@@ -1,5 +1,3 @@
|
||||
from typing import List
|
||||
|
||||
from qtpy.QtCore import Qt
|
||||
from qtpy.QtGui import QFont, QFontMetrics, QTextLayout
|
||||
|
||||
@@ -36,7 +34,7 @@ class _GenericEliding:
|
||||
self._ellipses_width = width
|
||||
|
||||
@staticmethod
|
||||
def wrapText(text, width, font=None) -> List[str]:
|
||||
def wrapText(text, width, font=None) -> list[str]:
|
||||
"""Returns `text`, split as it would be wrapped for `width`, given `font`.
|
||||
|
||||
Static method.
|
||||
@@ -74,5 +72,5 @@ class _GenericEliding:
|
||||
# join them
|
||||
return "".join(text[:nlines] + [last_line])
|
||||
|
||||
def _wrappedText(self) -> List[str]:
|
||||
def _wrappedText(self) -> list[str]:
|
||||
return _GenericEliding.wrapText(self._text, self.width(), self.font())
|
||||
|
@@ -1,4 +1,5 @@
|
||||
from typing import Mapping, Type, Union
|
||||
from collections.abc import Mapping
|
||||
from typing import Union
|
||||
|
||||
FONTFILE_ATTR = "__font_file__"
|
||||
|
||||
@@ -69,7 +70,7 @@ class IconFont(metaclass=IconFontMeta):
|
||||
__font_file__ = "..."
|
||||
|
||||
|
||||
def namespace2font(namespace: Union[Mapping, Type], name: str) -> Type[IconFont]:
|
||||
def namespace2font(namespace: Union[Mapping, type], name: str) -> type[IconFont]:
|
||||
"""Convenience to convert a namespace (class, module, dict) into an IconFont."""
|
||||
if isinstance(namespace, type):
|
||||
if not isinstance(getattr(namespace, FONTFILE_ATTR), str):
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import contextlib
|
||||
from typing import ClassVar, Dict, List, Set, Tuple
|
||||
from typing import ClassVar
|
||||
|
||||
from ._iconfont import IconFontMeta, namespace2font
|
||||
|
||||
@@ -11,9 +11,9 @@ except ImportError:
|
||||
|
||||
class FontIconManager:
|
||||
ENTRY_POINT: ClassVar[str] = "superqt.fonticon"
|
||||
_PLUGINS: ClassVar[Dict[str, EntryPoint]] = {}
|
||||
_LOADED: ClassVar[Dict[str, IconFontMeta]] = {}
|
||||
_BLOCKED: ClassVar[Set[EntryPoint]] = set()
|
||||
_PLUGINS: ClassVar[dict[str, EntryPoint]] = {}
|
||||
_LOADED: ClassVar[dict[str, IconFontMeta]] = {}
|
||||
_BLOCKED: ClassVar[set[EntryPoint]] = set()
|
||||
|
||||
def _discover_fonts(self) -> None:
|
||||
self._PLUGINS.clear()
|
||||
@@ -86,15 +86,15 @@ _manager = FontIconManager()
|
||||
get_font_class = _manager._get_font_class
|
||||
|
||||
|
||||
def discover() -> Tuple[str]:
|
||||
def discover() -> tuple[str]:
|
||||
_manager._discover_fonts()
|
||||
|
||||
|
||||
def available() -> Tuple[str]:
|
||||
def available() -> tuple[str]:
|
||||
return tuple(_manager._PLUGINS)
|
||||
|
||||
|
||||
def loaded(load_all=False) -> Dict[str, List[str]]:
|
||||
def loaded(load_all=False) -> dict[str, list[str]]:
|
||||
if load_all:
|
||||
discover()
|
||||
for x in available():
|
||||
|
@@ -2,9 +2,10 @@ from __future__ import annotations
|
||||
|
||||
import warnings
|
||||
from collections import abc, defaultdict
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, ClassVar, DefaultDict, Sequence, Tuple, Union, cast
|
||||
from typing import TYPE_CHECKING, ClassVar, Union, cast
|
||||
|
||||
from qtpy import QT_VERSION
|
||||
from qtpy.QtCore import QObject, QPoint, QRect, QSize, Qt
|
||||
@@ -47,8 +48,8 @@ ValidColor = Union[
|
||||
int,
|
||||
str,
|
||||
Qt.GlobalColor,
|
||||
Tuple[int, int, int, int],
|
||||
Tuple[int, int, int],
|
||||
tuple[int, int, int, int],
|
||||
tuple[int, int, int],
|
||||
None,
|
||||
]
|
||||
|
||||
@@ -159,7 +160,7 @@ class _QFontIconEngine(QIconEngine):
|
||||
def __init__(self, options: _IconOptions):
|
||||
super().__init__()
|
||||
self._opts: defaultdict[QIcon.State, dict[QIcon.Mode, _IconOptions | None]] = (
|
||||
DefaultDict(dict)
|
||||
defaultdict(dict)
|
||||
)
|
||||
self._opts[QIcon.State.Off][QIcon.Mode.Normal] = options
|
||||
self.update_hash()
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import logging
|
||||
from typing import Any, Iterable, Mapping
|
||||
from collections.abc import Iterable, Mapping
|
||||
from typing import Any
|
||||
|
||||
from qtpy.QtCore import QRegularExpression
|
||||
from qtpy.QtWidgets import QLineEdit, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
|
||||
|
@@ -1,4 +1,5 @@
|
||||
from typing import List, Optional, Sequence, Tuple, TypeVar, Union
|
||||
from collections.abc import Sequence
|
||||
from typing import Optional, TypeVar, Union
|
||||
|
||||
from qtpy import QtGui
|
||||
from qtpy.QtCore import Property, QEvent, QPoint, QPointF, QRect, QRectF, Qt, Signal
|
||||
@@ -42,11 +43,11 @@ class _GenericRangeSlider(_GenericSlider):
|
||||
self.valueChanged = self._valuesChanged
|
||||
self.sliderMoved = self._slidersMoved
|
||||
# list of values
|
||||
self._value: List[_T] = [20, 80]
|
||||
self._value: list[_T] = [20, 80]
|
||||
|
||||
# list of current positions of each handle. same length as _value
|
||||
# If tracking is enabled (the default) this will be identical to _value
|
||||
self._position: List[_T] = [20, 80]
|
||||
self._position: list[_T] = [20, 80]
|
||||
|
||||
# which handle is being pressed/hovered
|
||||
self._pressedIndex = 0
|
||||
@@ -113,7 +114,7 @@ class _GenericRangeSlider(_GenericSlider):
|
||||
|
||||
# ############### QtOverrides #######################
|
||||
|
||||
def value(self) -> Tuple[_T, ...]:
|
||||
def value(self) -> tuple[_T, ...]:
|
||||
"""Get current value of the widget as a tuple of integers."""
|
||||
return tuple(self._value)
|
||||
|
||||
@@ -332,7 +333,7 @@ class _GenericRangeSlider(_GenericSlider):
|
||||
# NOTE: this is very much tied to mousepress... not a generic "get control"
|
||||
def _getControlAtPos(
|
||||
self, pos: QPoint, opt: Optional[QStyleOptionSlider] = None
|
||||
) -> Tuple[QStyle.SubControl, int]:
|
||||
) -> tuple[QStyle.SubControl, int]:
|
||||
"""Update self._pressedControl based on ev.pos()."""
|
||||
opt = opt or self._styleOption
|
||||
|
||||
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import contextlib
|
||||
from enum import IntEnum, IntFlag, auto
|
||||
from functools import partial
|
||||
from typing import Any, Iterable, overload
|
||||
from typing import TYPE_CHECKING, Any, overload
|
||||
|
||||
from qtpy import QtGui
|
||||
from qtpy.QtCore import Property, QPoint, QSize, Qt, Signal
|
||||
@@ -25,6 +25,9 @@ from superqt.utils import signals_blocked
|
||||
|
||||
from ._sliders import QDoubleRangeSlider, QDoubleSlider, QRangeSlider
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Iterable
|
||||
|
||||
|
||||
class LabelPosition(IntEnum):
|
||||
NoLabel = 0
|
||||
|
@@ -1,5 +1,6 @@
|
||||
from collections.abc import Iterator
|
||||
from contextlib import contextmanager
|
||||
from typing import TYPE_CHECKING, Iterator
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qtpy.QtCore import QObject
|
||||
|
@@ -9,9 +9,7 @@ from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
ClassVar,
|
||||
Generator,
|
||||
Generic,
|
||||
Sequence,
|
||||
TypeVar,
|
||||
overload,
|
||||
)
|
||||
@@ -19,6 +17,8 @@ from typing import (
|
||||
from qtpy.QtCore import QObject, QRunnable, QThread, QThreadPool, QTimer, Signal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Generator, Sequence
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
class SigInst(Generic[_T]):
|
||||
|
@@ -1,5 +1,3 @@
|
||||
from typing import List, Tuple
|
||||
|
||||
import pytest
|
||||
from pytestqt.qtbot import QtBot
|
||||
from qtpy.QtCore import Qt
|
||||
@@ -30,15 +28,15 @@ def widget(qtbot: QtBot, data: dict) -> QSearchableTreeWidget:
|
||||
return widget
|
||||
|
||||
|
||||
def columns(item: QTreeWidgetItem) -> Tuple[str, str]:
|
||||
def columns(item: QTreeWidgetItem) -> tuple[str, str]:
|
||||
return item.text(0), item.text(1)
|
||||
|
||||
|
||||
def all_items(tree: QTreeWidget) -> List[QTreeWidgetItem]:
|
||||
def all_items(tree: QTreeWidget) -> list[QTreeWidgetItem]:
|
||||
return tree.findItems("", Qt.MatchContains | Qt.MatchRecursive)
|
||||
|
||||
|
||||
def shown_items(tree: QTreeWidget) -> List[QTreeWidgetItem]:
|
||||
def shown_items(tree: QTreeWidget) -> list[QTreeWidgetItem]:
|
||||
items = all_items(tree)
|
||||
return [item for item in items if not item.isHidden()]
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
from typing import Any, Iterable
|
||||
from collections.abc import Iterable
|
||||
from typing import Any
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import math
|
||||
from collections.abc import Iterable
|
||||
from itertools import product
|
||||
from typing import Any, Iterable
|
||||
from typing import Any
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
Reference in New Issue
Block a user