Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-superqt for openSUSE:Factory 
checked in at 2025-03-12 15:27:37
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-superqt (Old)
 and      /work/SRC/openSUSE:Factory/.python-superqt.new.19136 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-superqt"

Wed Mar 12 15:27:37 2025 rev:3 rq:1252357 version:0.7.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-superqt/python-superqt.changes    
2024-12-02 16:59:20.473721958 +0100
+++ /work/SRC/openSUSE:Factory/.python-superqt.new.19136/python-superqt.changes 
2025-03-12 15:27:41.415398084 +0100
@@ -1,0 +2,15 @@
+Tue Mar 11 12:35:39 UTC 2025 - Markéta Machová <[email protected]>
+
+- Update to 0.7.1
+  * fix: minimum size hint for QElidingLabel
+  * fix: End painter when drawing colormap
+  * build: drop py38
+  * fix: KeyError in CodeSyntaxHighlight
+  * build: support py313
+  * feat: allow chaining of QIconifyIcon.addKey
+  * feat: Improve CodeSyntaxHighlight object
+  * feat: add QFlowLayout, for variable width widgets
+  * Lazy-import pyconify
+- Skip testing on pyside2, broken with python313
+
+-------------------------------------------------------------------

Old:
----
  superqt-0.6.7.tar.gz

New:
----
  superqt-0.7.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-superqt.spec ++++++
--- /var/tmp/diff_new_pack.E7fBdw/_old  2025-03-12 15:27:42.523444490 +0100
+++ /var/tmp/diff_new_pack.E7fBdw/_new  2025-03-12 15:27:42.527444657 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-superqt
 #
-# Copyright (c) 2024 SUSE LLC
+# Copyright (c) 2025 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           python-superqt
-Version:        0.6.7
+Version:        0.7.1
 Release:        0
 Summary:        Missing widgets and components for PyQt/PySide
 License:        BSD-3-Clause
@@ -65,6 +65,8 @@
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 
 %check
+# test_iconify needs an optional module pyconify, which is not in the 
distribution
+rm tests/test_iconify.py
 # skip flaky tests marked @skip_on_ci by upstream
 export CI=1
 for PYTEST_QT_API in pyqt5 pyqt6; do
@@ -72,7 +74,9 @@
   %pytest
 done
 # The pysides are only for the primary python
-for PYTEST_QT_API in pyside2 pyside6; do
+# pyside2 is currently broken on python313
+# for PYTEST_QT_API in pyside2 pyside6; do
+for PYTEST_QT_API in pyside6; do
   export PYTEST_QT_API
   %python3_pytest
 done

++++++ superqt-0.6.7.tar.gz -> superqt-0.7.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/CHANGELOG.md 
new/superqt-0.7.1/CHANGELOG.md
--- old/superqt-0.6.7/CHANGELOG.md      2020-02-02 01:00:00.000000000 +0100
+++ new/superqt-0.7.1/CHANGELOG.md      2020-02-02 01:00:00.000000000 +0100
@@ -1,5 +1,53 @@
 # Changelog
 
+## [v0.7.1](https://github.com/pyapp-kit/superqt/tree/v0.7.1) (2025-01-05)
+
+[Full Changelog](https://github.com/pyapp-kit/superqt/compare/v0.7.0...v0.7.1)
+
+**Implemented enhancements:**
+
+- feat: add QFlowLayout, for variable width widgets 
[\#271](https://github.com/pyapp-kit/superqt/pull/271) 
([tlambert03](https://github.com/tlambert03))
+- feat: Improve CodeSyntaxHighlight object 
[\#268](https://github.com/pyapp-kit/superqt/pull/268) 
([tlambert03](https://github.com/tlambert03))
+- feat: allow chaining of QIconifyIcon.addKey 
[\#267](https://github.com/pyapp-kit/superqt/pull/267) 
([tlambert03](https://github.com/tlambert03))
+
+**Fixed bugs:**
+
+- fix: better warning for download error 
[\#266](https://github.com/pyapp-kit/superqt/pull/266) 
([tlambert03](https://github.com/tlambert03))
+
+**Merged pull requests:**
+
+- Lazy-import `pyconify` 
[\#270](https://github.com/pyapp-kit/superqt/pull/270) 
([hanjinliu](https://github.com/hanjinliu))
+
+## [v0.7.0](https://github.com/pyapp-kit/superqt/tree/v0.7.0) (2024-12-14)
+
+[Full Changelog](https://github.com/pyapp-kit/superqt/compare/v0.6.8...v0.7.0)
+
+**Fixed bugs:**
+
+- fix: End painter when drawing colormap 
[\#262](https://github.com/pyapp-kit/superqt/pull/262) 
([gselzer](https://github.com/gselzer))
+- fix: minimum size hint for QElidingLabel 
[\#260](https://github.com/pyapp-kit/superqt/pull/260) 
([gselzer](https://github.com/gselzer))
+- fix: KeyError in CodeSyntaxHighlight 
[\#258](https://github.com/pyapp-kit/superqt/pull/258) 
([hanjinliu](https://github.com/hanjinliu))
+
+**Refactors:**
+
+- chore: Revert "remove stylesheet on sliderLabel \(\#254\)" 
[\#265](https://github.com/pyapp-kit/superqt/pull/265) 
([tlambert03](https://github.com/tlambert03))
+- refactor: remove stylesheet on sliderLabel 
[\#254](https://github.com/pyapp-kit/superqt/pull/254) 
([tlambert03](https://github.com/tlambert03))
+
+**Merged pull requests:**
+
+- build: support py313 [\#264](https://github.com/pyapp-kit/superqt/pull/264) 
([tlambert03](https://github.com/tlambert03))
+- build: drop py38 [\#263](https://github.com/pyapp-kit/superqt/pull/263) 
([tlambert03](https://github.com/tlambert03))
+- ci: \[pre-commit.ci\] autoupdate 
[\#257](https://github.com/pyapp-kit/superqt/pull/257) 
([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
+- ci: \[pre-commit.ci\] autoupdate 
[\#253](https://github.com/pyapp-kit/superqt/pull/253) 
([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci))
+
+## [v0.6.8](https://github.com/pyapp-kit/superqt/tree/v0.6.8) (2024-06-15)
+
+[Full Changelog](https://github.com/pyapp-kit/superqt/compare/v0.6.7...v0.6.8)
+
+**Implemented enhancements:**
+
+- feat: graceful offline fallback for qiconify 
[\#251](https://github.com/pyapp-kit/superqt/pull/251) 
([tlambert03](https://github.com/tlambert03))
+
 ## [v0.6.7](https://github.com/pyapp-kit/superqt/tree/v0.6.7) (2024-06-07)
 
 [Full Changelog](https://github.com/pyapp-kit/superqt/compare/v0.6.6...v0.6.7)
@@ -464,13 +512,21 @@
 
 ## [v0.2.1](https://github.com/pyapp-kit/superqt/tree/v0.2.1) (2021-07-10)
 
-[Full Changelog](https://github.com/pyapp-kit/superqt/compare/v0.2.0...v0.2.1)
+[Full 
Changelog](https://github.com/pyapp-kit/superqt/compare/v0.2.0rc1...v0.2.1)
 
 **Fixed bugs:**
 
 - Fix QLabeledRangeSlider API \(fix slider proxy\) 
[\#10](https://github.com/pyapp-kit/superqt/pull/10) 
([tlambert03](https://github.com/tlambert03))
 - Fix range slider with negative min range 
[\#9](https://github.com/pyapp-kit/superqt/pull/9) 
([tlambert03](https://github.com/tlambert03))
 
+## [v0.2.0rc1](https://github.com/pyapp-kit/superqt/tree/v0.2.0rc1) 
(2021-06-26)
+
+[Full 
Changelog](https://github.com/pyapp-kit/superqt/compare/v0.2.0rc0...v0.2.0rc1)
+
+## [v0.2.0rc0](https://github.com/pyapp-kit/superqt/tree/v0.2.0rc0) 
(2021-06-26)
+
+[Full 
Changelog](https://github.com/pyapp-kit/superqt/compare/v0.2.0...v0.2.0rc0)
+
 
 
 \* *This Changelog was automatically generated by 
[github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/PKG-INFO new/superqt-0.7.1/PKG-INFO
--- old/superqt-0.6.7/PKG-INFO  2020-02-02 01:00:00.000000000 +0100
+++ new/superqt-0.7.1/PKG-INFO  2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 2.3
+Metadata-Version: 2.4
 Name: superqt
-Version: 0.6.7
+Version: 0.7.1
 Summary: Missing widgets and components for PyQt/PySide
 Project-URL: Documentation, https://pyapp-kit.github.io/superqt/
 Project-URL: Source, https://github.com/pyapp-kit/superqt
@@ -17,15 +17,15 @@
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
 Classifier: Topic :: Desktop Environment
 Classifier: Topic :: Software Development :: User Interfaces
 Classifier: Topic :: Software Development :: Widget Sets
-Requires-Python: >=3.8
+Requires-Python: >=3.9
 Requires-Dist: pygments>=2.4.0
 Requires-Dist: qtpy>=1.1.0
 Requires-Dist: typing-extensions!=3.10.0.0,>=3.7.4.3
@@ -63,7 +63,7 @@
 Provides-Extra: pyside2
 Requires-Dist: pyside2; extra == 'pyside2'
 Provides-Extra: pyside6
-Requires-Dist: pyside6!=6.5.0,!=6.5.1,!=6.6.2; extra == 'pyside6'
+Requires-Dist: pyside6!=6.5.0,!=6.5.1,!=6.6.2,<6.8; extra == 'pyside6'
 Provides-Extra: quantity
 Requires-Dist: pint; extra == 'quantity'
 Provides-Extra: test
@@ -93,7 +93,7 @@
 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
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/README.md new/superqt-0.7.1/README.md
--- old/superqt-0.6.7/README.md 2020-02-02 01:00:00.000000000 +0100
+++ new/superqt-0.7.1/README.md 2020-02-02 01:00:00.000000000 +0100
@@ -15,7 +15,7 @@
 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
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/pyproject.toml 
new/superqt-0.7.1/pyproject.toml
--- old/superqt-0.6.7/pyproject.toml    2020-02-02 01:00:00.000000000 +0100
+++ new/superqt-0.7.1/pyproject.toml    2020-02-02 01:00:00.000000000 +0100
@@ -8,7 +8,7 @@
 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 = "[email protected]", name = "Talley Lambert" }]
 keywords = [
@@ -28,11 +28,11 @@
     "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",
     "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
     "Topic :: Desktop Environment",
     "Topic :: Software Development :: User Interfaces",
     "Topic :: Software Development :: Widget Sets",
@@ -41,13 +41,21 @@
 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 +66,13 @@
     "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"]
@@ -66,7 +80,7 @@
 # https://github.com/pyapp-kit/superqt/pull/177
 # https://github.com/pyapp-kit/superqt/pull/164
 # https://bugreports.qt.io/browse/PYSIDE-2627
-pyside6 = ["pyside6 !=6.5.0,!=6.5.1,!=6.6.2"]
+pyside6 = ["pyside6 !=6.5.0,!=6.5.1,!=6.6.2,<6.8"]
 pyqt5 = ["pyqt5"]
 pyqt6 = ["pyqt6<6.7"]
 font-fa5 = ["fonticon-fontawesome5"]
@@ -100,21 +114,30 @@
 
 [[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 +155,7 @@
     "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 +182,7 @@
     "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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/__init__.py 
new/superqt-0.7.1/src/superqt/__init__.py
--- old/superqt-0.6.7/src/superqt/__init__.py   2020-02-02 01:00:00.000000000 
+0100
+++ new/superqt-0.7.1/src/superqt/__init__.py   2020-02-02 01:00:00.000000000 
+0100
@@ -11,7 +11,6 @@
 from .collapsible import QCollapsible
 from .combobox import QColorComboBox, QEnumComboBox, QSearchableComboBox
 from .elidable import QElidingLabel, QElidingLineEdit
-from .iconify import QIconifyIcon
 from .selection import QSearchableListWidget, QSearchableTreeWidget
 from .sliders import (
     QDoubleRangeSlider,
@@ -23,11 +22,14 @@
     QRangeSlider,
 )
 from .spinbox import QLargeIntSpinBox
-from .utils import QMessageHandler, ensure_main_thread, ensure_object_thread
+from .utils import (
+    QFlowLayout,
+    QMessageHandler,
+    ensure_main_thread,
+    ensure_object_thread,
+)
 
 __all__ = [
-    "ensure_main_thread",
-    "ensure_object_thread",
     "QCollapsible",
     "QColorComboBox",
     "QColormapComboBox",
@@ -36,8 +38,9 @@
     "QElidingLabel",
     "QElidingLineEdit",
     "QEnumComboBox",
-    "QLabeledDoubleRangeSlider",
+    "QFlowLayout",
     "QIconifyIcon",
+    "QLabeledDoubleRangeSlider",
     "QLabeledDoubleSlider",
     "QLabeledRangeSlider",
     "QLabeledSlider",
@@ -48,20 +51,27 @@
     "QSearchableComboBox",
     "QSearchableListWidget",
     "QSearchableTreeWidget",
+    "ensure_main_thread",
+    "ensure_object_thread",
 ]
 
 if TYPE_CHECKING:
-    from .combobox import QColormapComboBox  # noqa: TCH004
-    from .spinbox._quantity import QQuantity  # noqa: TCH004
+    from .combobox import QColormapComboBox  # noqa: TC004
+    from .iconify import QIconifyIcon  # noqa: TC004
+    from .spinbox._quantity import QQuantity  # noqa: TC004
 
 
 def __getattr__(name: str) -> Any:
-    if name == "QQuantity":
-        from .spinbox._quantity import QQuantity
-
-        return QQuantity
     if name == "QColormapComboBox":
         from .cmap import QColormapComboBox
 
         return QColormapComboBox
+    if name == "QIconifyIcon":
+        from .iconify import QIconifyIcon
+
+        return QIconifyIcon
+    if name == "QQuantity":
+        from .spinbox._quantity import QQuantity
+
+        return QQuantity
     raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/cmap/__init__.py 
new/superqt-0.7.1/src/superqt/cmap/__init__.py
--- old/superqt-0.6.7/src/superqt/cmap/__init__.py      2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/cmap/__init__.py      2020-02-02 
01:00:00.000000000 +0100
@@ -15,9 +15,9 @@
 from ._cmap_utils import draw_colormap
 
 __all__ = [
-    "QColormapItemDelegate",
-    "draw_colormap",
-    "QColormapLineEdit",
     "CmapCatalogComboBox",
     "QColormapComboBox",
+    "QColormapItemDelegate",
+    "QColormapLineEdit",
+    "draw_colormap",
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/cmap/_catalog_combo.py 
new/superqt-0.7.1/src/superqt/cmap/_catalog_combo.py
--- old/superqt-0.6.7/src/superqt/cmap/_catalog_combo.py        2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/cmap/_catalog_combo.py        2020-02-02 
01:00:00.000000000 +0100
@@ -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_utils import try_cast_colormap
 
 if TYPE_CHECKING:
+    from collections.abc import Container
+
     from cmap._catalog import Category, Interpolation
     from qtpy.QtGui import QKeyEvent
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/cmap/_cmap_combo.py 
new/superqt-0.7.1/src/superqt/cmap/_cmap_combo.py
--- old/superqt-0.6.7/src/superqt/cmap/_cmap_combo.py   2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/cmap/_cmap_combo.py   2020-02-02 
01:00:00.000000000 +0100
@@ -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_utils import try_cast_colormap
 
 if TYPE_CHECKING:
+    from collections.abc import Sequence
+
     from cmap._colormap import ColorStopsLike
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/cmap/_cmap_utils.py 
new/superqt-0.7.1/src/superqt/cmap/_cmap_utils.py
--- old/superqt-0.6.7/src/superqt/cmap/_cmap_utils.py   2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/cmap/_cmap_utils.py   2020-02-02 
01:00:00.000000000 +0100
@@ -121,6 +121,10 @@
         painter.setBrush(gradient)
         painter.drawRect(rect)
 
+    # If we created a new Painter, free its resources
+    if isinstance(painter_or_device, QPaintDevice):
+        painter.end()
+
 
 def _draw_checkerboard(
     painter: QPainter, rect: QRect | QRectF, checker_size: int
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/combobox/__init__.py 
new/superqt-0.7.1/src/superqt/combobox/__init__.py
--- old/superqt-0.6.7/src/superqt/combobox/__init__.py  2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/combobox/__init__.py  2020-02-02 
01:00:00.000000000 +0100
@@ -13,7 +13,7 @@
 
 
 if TYPE_CHECKING:
-    from superqt.cmap import QColormapComboBox  # noqa: TCH004
+    from superqt.cmap import QColormapComboBox  # noqa: TC004
 
 
 def __getattr__(name: str) -> Any:  # pragma: no cover
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/superqt-0.6.7/src/superqt/combobox/_color_combobox.py 
new/superqt-0.7.1/src/superqt/combobox/_color_combobox.py
--- old/superqt-0.6.7/src/superqt/combobox/_color_combobox.py   2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/combobox/_color_combobox.py   2020-02-02 
01:00:00.000000000 +0100
@@ -3,7 +3,7 @@
 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 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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/combobox/_enum_combobox.py 
new/superqt-0.7.1/src/superqt/combobox/_enum_combobox.py
--- old/superqt-0.6.7/src/superqt/combobox/_enum_combobox.py    2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/combobox/_enum_combobox.py    2020-02-02 
01:00:00.000000000 +0100
@@ -3,7 +3,7 @@
 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 @@
     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
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/elidable/_eliding.py 
new/superqt-0.7.1/src/superqt/elidable/_eliding.py
--- old/superqt-0.6.7/src/superqt/elidable/_eliding.py  2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/elidable/_eliding.py  2020-02-02 
01:00:00.000000000 +0100
@@ -1,5 +1,3 @@
-from typing import List
-
 from qtpy.QtCore import Qt
 from qtpy.QtGui import QFont, QFontMetrics, QTextLayout
 
@@ -36,7 +34,7 @@
         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 @@
         # 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())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/elidable/_eliding_label.py 
new/superqt-0.7.1/src/superqt/elidable/_eliding_label.py
--- old/superqt-0.6.7/src/superqt/elidable/_eliding_label.py    2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/elidable/_eliding_label.py    2020-02-02 
01:00:00.000000000 +0100
@@ -73,3 +73,10 @@
         flags = int(self.alignment() | Qt.TextFlag.TextWordWrap)
         r = fm.boundingRect(QRect(QPoint(0, 0), self.size()), flags, 
self._text)
         return QSize(self.width(), r.height())
+
+    def minimumSizeHint(self) -> QSize:
+        # The smallest that self._elidedText can be is just the ellipsis.
+        fm = QFontMetrics(self.font())
+        flags = int(self.alignment() | Qt.TextFlag.TextWordWrap)
+        r = fm.boundingRect(QRect(QPoint(0, 0), self.size()), flags, "...")
+        return QSize(r.width(), r.height())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/fonticon/__init__.py 
new/superqt-0.7.1/src/superqt/fonticon/__init__.py
--- old/superqt-0.6.7/src/superqt/fonticon/__init__.py  2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/fonticon/__init__.py  2020-02-02 
01:00:00.000000000 +0100
@@ -1,16 +1,16 @@
 from __future__ import annotations
 
 __all__ = [
-    "addFont",
-    "Animation",
     "ENTRY_POINT",
-    "font",
-    "icon",
+    "Animation",
     "IconFont",
     "IconFontMeta",
     "IconOpts",
-    "pulse",
     "QIconifyIcon",
+    "addFont",
+    "font",
+    "icon",
+    "pulse",
     "setTextIcon",
     "spin",
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/fonticon/_iconfont.py 
new/superqt-0.7.1/src/superqt/fonticon/_iconfont.py
--- old/superqt-0.6.7/src/superqt/fonticon/_iconfont.py 2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/fonticon/_iconfont.py 2020-02-02 
01:00:00.000000000 +0100
@@ -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 @@
     __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):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/fonticon/_plugins.py 
new/superqt-0.7.1/src/superqt/fonticon/_plugins.py
--- old/superqt-0.6.7/src/superqt/fonticon/_plugins.py  2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/fonticon/_plugins.py  2020-02-02 
01:00:00.000000000 +0100
@@ -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 @@
 
 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 @@
 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():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/fonticon/_qfont_icon.py 
new/superqt-0.7.1/src/superqt/fonticon/_qfont_icon.py
--- old/superqt-0.6.7/src/superqt/fonticon/_qfont_icon.py       2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/fonticon/_qfont_icon.py       2020-02-02 
01:00:00.000000000 +0100
@@ -2,9 +2,10 @@
 
 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 @@
     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 @@
     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()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/iconify/__init__.py 
new/superqt-0.7.1/src/superqt/iconify/__init__.py
--- old/superqt-0.6.7/src/superqt/iconify/__init__.py   2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/iconify/__init__.py   2020-02-02 
01:00:00.000000000 +0100
@@ -1,9 +1,20 @@
 from __future__ import annotations
 
+import warnings
 from typing import TYPE_CHECKING
 
-from qtpy.QtCore import QSize
-from qtpy.QtGui import QIcon
+from qtpy.QtCore import QSize, Qt
+from qtpy.QtGui import QIcon, QPainter, QPixmap
+from qtpy.QtWidgets import QApplication
+
+try:
+    from pyconify import svg_path
+except ModuleNotFoundError:  # pragma: no cover
+    raise ModuleNotFoundError(
+        "pyconify is required to use QIconifyIcon. "
+        "Please install it with `pip install pyconify` or use the "
+        "`pip install superqt[iconify]` extra."
+    ) from None
 
 if TYPE_CHECKING:
     from typing import Literal
@@ -11,10 +22,7 @@
     Flip = Literal["horizontal", "vertical", "horizontal,vertical"]
     Rotation = Literal["90", "180", "270", 90, 180, 270, "-90", 1, 2, 3]
 
-try:
-    from pyconify import svg_path
-except ModuleNotFoundError:  # pragma: no cover
-    svg_path = None
+__all__ = ["QIconifyIcon"]
 
 
 class QIconifyIcon(QIcon):
@@ -72,14 +80,9 @@
         rotate: Rotation | None = None,
         dir: str | None = None,
     ):
-        if svg_path is None:  # pragma: no cover
-            raise ModuleNotFoundError(
-                "pyconify is required to use QIconifyIcon. "
-                "Please install it with `pip install pyconify` or use the "
-                "`pip install superqt[iconify]` extra."
-            )
         super().__init__()
-        self.addKey(*key, color=color, flip=flip, rotate=rotate, dir=dir)
+        if key:
+            self.addKey(*key, color=color, flip=flip, rotate=rotate, dir=dir)
 
     def addKey(
         self,
@@ -91,7 +94,7 @@
         size: QSize | None = None,
         mode: QIcon.Mode = QIcon.Mode.Normal,
         state: QIcon.State = QIcon.State.Off,
-    ) -> None:
+    ) -> QIconifyIcon:
         """Add an icon to this QIcon.
 
         This is a variant of `QIcon.addFile` that uses an iconify icon keys and
@@ -121,6 +124,33 @@
             Mode specified for the icon, passed to `QIcon.addFile`.
         state : QIcon.State, optional
             State specified for the icon, passed to `QIcon.addFile`.
+
+        Returns
+        -------
+        QIconifyIcon
+            This QIconifyIcon instance, for chaining.
         """
-        path = svg_path(*key, color=color, flip=flip, rotate=rotate, dir=dir)
-        self.addFile(str(path), size or QSize(), mode, state)
+        try:
+            path = svg_path(*key, color=color, flip=flip, rotate=rotate, 
dir=dir)
+        except OSError as e:
+            warnings.warn(
+                f"Error fetching icon: {e}.\nIcon {key} not cached. Using 
fallback.",
+                stacklevel=2,
+            )
+            self._draw_text_fallback(key)
+        else:
+            self.addFile(str(path), size or QSize(), mode, state)
+
+        return self
+
+    def _draw_text_fallback(self, key: tuple[str, ...]) -> None:
+        if style := QApplication.style():
+            pixmap = 
style.standardPixmap(style.StandardPixmap.SP_MessageBoxQuestion)
+        else:
+            pixmap = QPixmap(18, 18)
+            pixmap.fill(Qt.GlobalColor.transparent)
+            painter = QPainter(pixmap)
+            painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, "?")
+            painter.end()
+
+        self.addPixmap(pixmap)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/superqt-0.6.7/src/superqt/selection/_searchable_tree_widget.py 
new/superqt-0.7.1/src/superqt/selection/_searchable_tree_widget.py
--- old/superqt-0.6.7/src/superqt/selection/_searchable_tree_widget.py  
2020-02-02 01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/selection/_searchable_tree_widget.py  
2020-02-02 01:00:00.000000000 +0100
@@ -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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/sliders/__init__.py 
new/superqt-0.7.1/src/superqt/sliders/__init__.py
--- old/superqt-0.6.7/src/superqt/sliders/__init__.py   2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/sliders/__init__.py   2020-02-02 
01:00:00.000000000 +0100
@@ -8,6 +8,7 @@
 from ._sliders import QDoubleRangeSlider, QDoubleSlider, QRangeSlider
 
 __all__ = [
+    "MONTEREY_SLIDER_STYLES_FIX",
     "QDoubleRangeSlider",
     "QDoubleSlider",
     "QLabeledDoubleRangeSlider",
@@ -15,5 +16,4 @@
     "QLabeledRangeSlider",
     "QLabeledSlider",
     "QRangeSlider",
-    "MONTEREY_SLIDER_STYLES_FIX",
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/superqt-0.6.7/src/superqt/sliders/_generic_range_slider.py 
new/superqt-0.7.1/src/superqt/sliders/_generic_range_slider.py
--- old/superqt-0.6.7/src/superqt/sliders/_generic_range_slider.py      
2020-02-02 01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/sliders/_generic_range_slider.py      
2020-02-02 01:00:00.000000000 +0100
@@ -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 @@
         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 @@
 
     # ###############  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 @@
     # 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
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/sliders/_labeled.py 
new/superqt-0.7.1/src/superqt/sliders/_labeled.py
--- old/superqt-0.6.7/src/superqt/sliders/_labeled.py   2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/sliders/_labeled.py   2020-02-02 
01:00:00.000000000 +0100
@@ -3,7 +3,7 @@
 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 ._sliders import QDoubleRangeSlider, QDoubleSlider, QRangeSlider
 
+if TYPE_CHECKING:
+    from collections.abc import Iterable
+
 
 class LabelPosition(IntEnum):
     NoLabel = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/utils/__init__.py 
new/superqt-0.7.1/src/superqt/utils/__init__.py
--- old/superqt-0.6.7/src/superqt/utils/__init__.py     2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/utils/__init__.py     2020-02-02 
01:00:00.000000000 +0100
@@ -1,32 +1,34 @@
 from typing import TYPE_CHECKING, Any
 
 if TYPE_CHECKING:
-    from superqt.cmap import draw_colormap  # noqa: TCH004
+    from superqt.cmap import draw_colormap  # noqa: TC004
 
 __all__ = (
     "CodeSyntaxHighlight",
+    "FunctionWorker",
+    "GeneratorWorker",
+    "QFlowLayout",
+    "QMessageHandler",
+    "QSignalDebouncer",
+    "QSignalThrottler",
+    "WorkerBase",
     "create_worker",
-    "qimage_to_array",
     "draw_colormap",
     "ensure_main_thread",
     "ensure_object_thread",
     "exceptions_as_dialog",
-    "FunctionWorker",
-    "GeneratorWorker",
     "new_worker_qthread",
     "qdebounced",
-    "QMessageHandler",
-    "QSignalDebouncer",
-    "QSignalThrottler",
+    "qimage_to_array",
     "qthrottled",
     "signals_blocked",
     "thread_worker",
-    "WorkerBase",
 )
 
 from ._code_syntax_highlight import CodeSyntaxHighlight
 from ._ensure_thread import ensure_main_thread, ensure_object_thread
 from ._errormsg_context import exceptions_as_dialog
+from ._flow_layout import QFlowLayout
 from ._img_utils import qimage_to_array
 from ._message_handler import QMessageHandler
 from ._misc import signals_blocked
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/superqt-0.6.7/src/superqt/utils/_code_syntax_highlight.py 
new/superqt-0.7.1/src/superqt/utils/_code_syntax_highlight.py
--- old/superqt-0.6.7/src/superqt/utils/_code_syntax_highlight.py       
2020-02-02 01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/utils/_code_syntax_highlight.py       
2020-02-02 01:00:00.000000000 +0100
@@ -1,75 +1,268 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, cast
+
 from pygments import highlight
 from pygments.formatter import Formatter
 from pygments.lexers import find_lexer_class, get_lexer_by_name
 from pygments.util import ClassNotFound
-from qtpy import QtGui
+from qtpy.QtGui import (
+    QColor,
+    QFont,
+    QPalette,
+    QSyntaxHighlighter,
+    QTextCharFormat,
+    QTextDocument,
+)
+
+if TYPE_CHECKING:
+    from collections.abc import Mapping, Sequence
+    from typing import Literal, TypeAlias, TypedDict, Unpack
+
+    import pygments.style
+    from pygments.style import _StyleDict
+    from pygments.token import _TokenType
+    from qtpy.QtCore import QObject
+
+    class SupportsDocumentAndPalette(QObject):
+        def document(self) -> QTextDocument | None: ...
+        def palette(self) -> QPalette: ...
+        def setPalette(self, palette: QPalette) -> None: ...
+
+    KnownStyle: TypeAlias = Literal[
+        "abap",
+        "algol",
+        "algol_nu",
+        "arduino",
+        "autumn",
+        "bw",
+        "borland",
+        "coffee",
+        "colorful",
+        "default",
+        "dracula",
+        "emacs",
+        "friendly_grayscale",
+        "friendly",
+        "fruity",
+        "github-dark",
+        "gruvbox-dark",
+        "gruvbox-light",
+        "igor",
+        "inkpot",
+        "lightbulb",
+        "lilypond",
+        "lovelace",
+        "manni",
+        "material",
+        "monokai",
+        "murphy",
+        "native",
+        "nord-darker",
+        "nord",
+        "one-dark",
+        "paraiso-dark",
+        "paraiso-light",
+        "pastie",
+        "perldoc",
+        "rainbow_dash",
+        "rrt",
+        "sas",
+        "solarized-dark",
+        "solarized-light",
+        "staroffice",
+        "stata-dark",
+        "stata-light",
+        "tango",
+        "trac",
+        "vim",
+        "vs",
+        "xcode",
+        "zenburn",
+    ]
+
+    class FormatterKwargs(TypedDict, total=False):
+        style: KnownStyle | str
+        full: bool
+        title: str
+        encoding: str
+        outencoding: str
+
+
+MONO_FAMILIES = [
+    "Menlo",
+    "Courier New",
+    "Courier",
+    "Monaco",
+    "Consolas",
+    "Andale Mono",
+    "Source Code Pro",
+    "Ubuntu Mono",
+    "monospace",
+]
+
 
 # inspired by  https://github.com/Vector35/snippets/blob/master/QCodeEditor.py
 # (MIT license) and
 # https://pygments.org/docs/formatterdevelopment/#html-3-2-formatter
+def get_text_char_format(style: _StyleDict) -> QTextCharFormat:
+    """Return a QTextCharFormat object based on the given Pygments 
`_StyleDict`.
 
-
-def get_text_char_format(style):
-    text_char_format = QtGui.QTextCharFormat()
-    if hasattr(text_char_format, "setFontFamilies"):
-        text_char_format.setFontFamilies(["monospace"])
-    else:
-        text_char_format.setFontFamily("monospace")
-    if style.get("color"):
-        text_char_format.setForeground(QtGui.QColor(f"#{style['color']}"))
-
-    if style.get("bgcolor"):
-        text_char_format.setBackground(QtGui.QColor(style["bgcolor"]))
-
+    style will likely have these keys:
+    - color: str | None
+    - bold: bool
+    - italic: bool
+    - underline: bool
+    - bgcolor: str | None
+    - border: str | None
+    - roman: bool | None
+    - sans: bool | None
+    - mono: bool | None
+    - ansicolor: str | None
+    - bgansicolor: str | None
+    """
+    text_char_format = QTextCharFormat()
+    if style.get("mono"):
+        text_char_format.setFontFamilies(MONO_FAMILIES)
+    if color := style.get("color"):
+        text_char_format.setForeground(QColor(f"#{color}"))
+    if bgcolor := style.get("bgcolor"):
+        text_char_format.setBackground(QColor(f"#{bgcolor}"))
     if style.get("bold"):
-        text_char_format.setFontWeight(QtGui.QFont.Bold)
+        text_char_format.setFontWeight(QFont.Weight.Bold)
     if style.get("italic"):
         text_char_format.setFontItalic(True)
     if style.get("underline"):
         text_char_format.setFontUnderline(True)
-
-    # TODO find if it is possible to support border style.
-
+    # if style.get("border"):
+    # ...
     return text_char_format
 
 
 class QFormatter(Formatter):
-    def __init__(self, **kwargs):
+    def __init__(self, **kwargs: Unpack[FormatterKwargs]) -> None:
         super().__init__(**kwargs)
-        self.data = []
-        self._style = {name: get_text_char_format(style) for name, style in 
self.style}
-
-    def format(self, tokensource, outfile):
+        self.data: list[QTextCharFormat] = []
+        style = cast("pygments.style.StyleMeta", self.style)
+        self._style: Mapping[_TokenType, QTextCharFormat]
+        self._style = {token: get_text_char_format(style) for token, style in 
style}
+
+    def format(
+        self, tokensource: Sequence[tuple[_TokenType, str]], outfile: Any
+    ) -> None:
         """Format the given token stream.
 
-        `outfile` is argument from parent class, but
-        in Qt we do not produce string output, but QTextCharFormat, so it 
needs to be
-        collected using `self.data`.
+        When Qt calls the highlightBlock method on a `CodeSyntaxHighlight` 
object,
+        `highlight(text, self.lexer, self.formatter)`, which trigger pygments 
to call
+        this method.
+
+        Normally, this method puts output into `outfile`, but in Qt we do not 
produce
+        string output; instead we collect QTextCharFormat objects in 
`self.data`, which
+        can be used to apply formatting in the `highlightBlock` method that 
triggered
+        this method.
         """
         self.data = []
-
+        null = QTextCharFormat()
         for token, value in tokensource:
-            self.data.extend([self._style[token]] * len(value))
-
+            # using get method to workaround not defined style for plain token
+            # https://github.com/pygments/pygments/issues/2149
+            self.data.extend([self._style.get(token, null)] * len(value))
+
+
+class CodeSyntaxHighlight(QSyntaxHighlighter):
+    """A syntax highlighter for code using Pygments.
+
+    Parameters
+    ----------
+    parent : QTextDocument | QObject | None
+        The parent object. Usually a QTextDocument.  To use this class with a
+        QTextArea, pass in `text_area.document()`.
+    lang : str
+        The language of the code to highlight. This should be a string that
+        Pygments recognizes, e.g. 'python', 'pytb', 'cpp', 'java', etc.
+    theme : KnownStyle | str
+        The name of the Pygments style to use. For a complete list of available
+        styles, use `pygments.styles.get_all_styles()`.
+
+    Examples
+    --------
+    ```python
+    from qtpy.QtWidgets import QTextEdit
+    from superqt.utils import CodeSyntaxHighlight
+
+    text_area = QTextEdit()
+    highlighter = CodeSyntaxHighlight(text_area.document(), "python", 
"monokai")
+
+    # then manually apply the background color to the text area.
+    palette = text_area.palette()
+    bgrd_color = QColor(self._highlight.background_color)
+    palette.setColor(QPalette.ColorRole.Base, bgrd_color)
+    text_area.setPalette(palette)
+    ```
+    """
+
+    def __init__(
+        self,
+        parent: SupportsDocumentAndPalette | QTextDocument | QObject | None,
+        lang: str,
+        theme: KnownStyle | str = "default",
+    ) -> None:
+        self._doc_parent: SupportsDocumentAndPalette | None = None
+        if (
+            parent
+            and not isinstance(parent, QTextDocument)
+            and hasattr(parent, "document")
+            and callable(parent.document)
+            and isinstance(doc := parent.document(), QTextDocument)
+        ):
+            if hasattr(parent, "palette") and hasattr(parent, "setPalette"):
+                self._doc_parent = cast("SupportsDocumentAndPalette", parent)
+            parent = doc
 
-class CodeSyntaxHighlight(QtGui.QSyntaxHighlighter):
-    def __init__(self, parent, lang, theme):
         super().__init__(parent)
+        self.setLanguage(lang)
+        self.setTheme(theme)
+
+    def setTheme(self, theme: KnownStyle | str) -> None:
+        """Set the theme for the syntax highlighting.
+
+        This should be a string that Pygments recognizes, e.g. 'monokai', 
'solarized'.
+        Use `pygments.styles.get_all_styles()` to see a list of available 
styles.
+        """
         self.formatter = QFormatter(style=theme)
+        if self._doc_parent is not None:
+            palette = self._doc_parent.palette()
+            bgrd = QColor(self.background_color)
+            palette.setColor(QPalette.ColorRole.Base, bgrd)
+            self._doc_parent.setPalette(palette)
+
+        self.rehighlight()
+
+    def setLanguage(self, lang: str) -> None:
+        """Set the language for the syntax highlighting.
+
+        This should be a string that Pygments recognizes, e.g. 'python', 
'pytb', 'cpp',
+        'java', etc.
+        """
         try:
             self.lexer = get_lexer_by_name(lang)
-        except ClassNotFound:
-            self.lexer = find_lexer_class(lang)()
+        except ClassNotFound as e:
+            if cls := find_lexer_class(lang):
+                self.lexer = cls()
+            else:
+                raise ValueError(f"Could not find lexer for language 
{lang!r}.") from e
 
     @property
-    def background_color(self):
-        return self.formatter.style.background_color
+    def background_color(self) -> str:
+        style = cast("pygments.style.StyleMeta", self.formatter.style)
+        return style.background_color
 
-    def highlightBlock(self, text):
+    def highlightBlock(self, text: str | None) -> None:
         # dirty, dirty hack
-        # The core problem is that pygemnts by default use string streams,
+        # The core problem is that pygments by default use string streams,
         # that will not handle QTextCharFormat, so we need use `data` property 
to
         # work around this.
-        highlight(text, self.lexer, self.formatter)
-        for i in range(len(text)):
-            self.setFormat(i, 1, self.formatter.data[i])
+        if text:
+            highlight(text, self.lexer, self.formatter)
+            for i in range(len(text)):
+                self.setFormat(i, 1, self.formatter.data[i])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/utils/_flow_layout.py 
new/superqt-0.7.1/src/superqt/utils/_flow_layout.py
--- old/superqt-0.6.7/src/superqt/utils/_flow_layout.py 1970-01-01 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/utils/_flow_layout.py 2020-02-02 
01:00:00.000000000 +0100
@@ -0,0 +1,183 @@
+from __future__ import annotations
+
+from qtpy.QtCore import QPoint, QRect, QSize, Qt
+from qtpy.QtWidgets import QLayout, QLayoutItem, QSizePolicy, QStyle, QWidget
+
+
+class QFlowLayout(QLayout):
+    """Layout that handles different window sizes.
+
+    The widget placement changes depending on the width of the application 
window.
+
+    Code translated from C++ at:
+    
<https://code.qt.io/cgit/qt/qtbase.git/tree/examples/widgets/layouts/flowlayout>
+
+    described at: 
<https://doc.qt.io/qt-6/qtwidgets-layouts-flowlayout-example.html>
+
+    See also: <https://doc.qt.io/qt-6/layout.html>
+
+    Parameters
+    ----------
+    parent : QWidget, optional
+        The parent widget, by default None
+    """
+
+    def __init__(self, parent: QWidget | None = None) -> None:
+        super().__init__(parent)
+
+        self._item_list: list[QLayoutItem] = []
+        self._h_space = -1
+        self._v_space = -1
+
+    def __del__(self) -> None:
+        while item := self.takeAt(0):
+            del item
+
+    def addItem(self, item: QLayoutItem | None) -> None:
+        """Add an item to the layout."""
+        if item:
+            self._item_list.append(item)
+
+    def setHorizontalSpacing(self, space: int | None) -> None:
+        """Set the horizontal spacing.
+
+        If None or -1, the spacing is set to the default value based on the 
style
+        of the parent widget.
+        """
+        self._h_space = -1 if space is None else space
+
+    def horizontalSpacing(self) -> int:
+        """Return the horizontal spacing."""
+        if self._h_space >= 0:
+            return self._h_space
+        return 
self._smartSpacing(QStyle.PixelMetric.PM_LayoutHorizontalSpacing)
+
+    def setVerticalSpacing(self, space: int | None) -> None:
+        """Set the vertical spacing.
+
+        If None or -1, the spacing is set to the default value based on the 
style
+        of the parent widget.
+        """
+        self._v_space = -1 if space is None else space
+
+    def verticalSpacing(self) -> int:
+        """Return the vertical spacing."""
+        if self._v_space >= 0:
+            return self._v_space
+        return self._smartSpacing(QStyle.PixelMetric.PM_LayoutVerticalSpacing)
+
+    def expandingDirections(self) -> Qt.Orientation:
+        """Return the expanding directions.
+
+        These are the Qt::Orientations in which the layout can make use of 
more space
+        than its sizeHint().
+        """
+        return Qt.Orientation.Horizontal
+
+    def hasHeightForWidth(self) -> bool:
+        """Return whether the layout handles height for width."""
+        return True
+
+    def heightForWidth(self, width: int) -> int:
+        """Return the height for a given width.
+
+        `heightForWidth()` passes the width on to _doLayout() which in turn 
uses the
+        width as an argument for the layout rect, i.e., the bounds in which 
the items
+        are laid out. This rect does not include the layout margin().
+        """
+        return self._doLayout(QRect(0, 0, width, 0), True)
+
+    def count(self) -> int:
+        """Return the number of items in the layout."""
+        return len(self._item_list)
+
+    def itemAt(self, index: int) -> QLayoutItem | None:
+        """Return the item at the given index, or None if the index is out of 
range."""
+        try:
+            return self._item_list[index]
+        except IndexError:
+            return None
+
+    def minimumSize(self) -> QSize:
+        """Return the minimum size of the layout."""
+        size = QSize()
+        for item in self._item_list:
+            size = size.expandedTo(item.minimumSize())
+
+        margins = self.contentsMargins()
+        size += QSize(
+            margins.left() + margins.right(), margins.top() + margins.bottom()
+        )
+        return size
+
+    def setGeometry(self, rect: QRect) -> None:
+        """Set the geometry of the layout.
+
+        This triggers a re-layout of the items.
+        """
+        super().setGeometry(rect)
+        self._doLayout(rect)
+
+    def sizeHint(self) -> QSize:
+        """Return the size hint of the layout."""
+        return self.minimumSize()
+
+    def takeAt(self, index: int) -> QLayoutItem | None:
+        """Remove and return the item at the given index.  Or return None."""
+        if 0 <= index < len(self._item_list):
+            return self._item_list.pop(index)
+        return None
+
+    def _doLayout(self, rect: QRect, test_only: bool = False) -> int:
+        """Arrange the items in the layout.
+
+        If test_only is True, the items are not actually laid out, but the 
height
+        that the layout would have with the given width is returned.
+        """
+        left, top, right, bottom = self.getContentsMargins()
+        effective_rect = rect.adjusted(left, top, -right, -bottom)  # type: 
ignore
+        x = effective_rect.x()
+        y = effective_rect.y()
+        line_height = 0
+
+        for item in self._item_list:
+            if (wid := item.widget()) and (style := wid.style()):
+                space_x = self.horizontalSpacing()
+                space_y = self.verticalSpacing()
+                if space_x == -1:
+                    space_x = style.layoutSpacing(
+                        QSizePolicy.ControlType.PushButton,
+                        QSizePolicy.ControlType.PushButton,
+                        Qt.Orientation.Horizontal,
+                    )
+                if space_y == -1:
+                    space_y = style.layoutSpacing(
+                        QSizePolicy.ControlType.PushButton,
+                        QSizePolicy.ControlType.PushButton,
+                        Qt.Orientation.Vertical,
+                    )
+
+                # next_x is the x-coordinate of the right edge of the item
+                next_x = x + item.sizeHint().width() + space_x
+                # if the item is not the first one in a line, add the spacing
+                # to the left of it
+                if next_x - space_x > effective_rect.right() and line_height > 
0:
+                    x = effective_rect.x()
+                    y = y + line_height + space_y
+                    next_x = x + item.sizeHint().width() + space_x
+                    line_height = 0
+
+                # if this is not a test run, move the item to its proper place
+                if not test_only:
+                    item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
+
+                x = next_x
+                line_height = max(line_height, item.sizeHint().height())
+
+        return y + line_height - rect.y() + bottom
+
+    def _smartSpacing(self, pm: QStyle.PixelMetric) -> int:
+        """Return the smart spacing based on the style of the parent widget."""
+        if isinstance(parent := self.parent(), QWidget) and (style := 
parent.style()):
+            return style.pixelMetric(pm, None, parent)
+        return -1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/utils/_misc.py 
new/superqt-0.7.1/src/superqt/utils/_misc.py
--- old/superqt-0.6.7/src/superqt/utils/_misc.py        2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/utils/_misc.py        2020-02-02 
01:00:00.000000000 +0100
@@ -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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/src/superqt/utils/_qthreading.py 
new/superqt-0.7.1/src/superqt/utils/_qthreading.py
--- old/superqt-0.6.7/src/superqt/utils/_qthreading.py  2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/src/superqt/utils/_qthreading.py  2020-02-02 
01:00:00.000000000 +0100
@@ -9,9 +9,7 @@
     Any,
     Callable,
     ClassVar,
-    Generator,
     Generic,
-    Sequence,
     TypeVar,
     overload,
 )
@@ -19,6 +17,8 @@
 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]):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/tests/test_eliding_label.py 
new/superqt-0.7.1/tests/test_eliding_label.py
--- old/superqt-0.6.7/tests/test_eliding_label.py       2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/tests/test_eliding_label.py       2020-02-02 
01:00:00.000000000 +0100
@@ -69,3 +69,14 @@
     assert isinstance(wrap, list)
     assert all(isinstance(x, str) for x in wrap)
     assert 9 <= len(wrap) <= 13
+
+
+def test_minimum_size_hint():
+    # The hint should always just be the space needed for "..."
+    wdg = QElidingLabel()
+    size_hint = wdg.minimumSizeHint()
+    # Regardless of what text is contained
+    wdg.setText(TEXT)
+    new_hint = wdg.minimumSizeHint()
+    assert size_hint.width() == new_hint.width()
+    assert size_hint.height() == new_hint.height()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/tests/test_ensure_thread.py 
new/superqt-0.7.1/tests/test_ensure_thread.py
--- old/superqt-0.6.7/tests/test_ensure_thread.py       2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/tests/test_ensure_thread.py       2020-02-02 
01:00:00.000000000 +0100
@@ -162,7 +162,7 @@
     signature = inspect.signature(ob.check_object_thread_return_future)
     assert len(signature.parameters) == 1
     assert next(iter(signature.parameters.values())).name == "a"
-    assert next(iter(signature.parameters.values())).annotation == int
+    assert next(iter(signature.parameters.values())).annotation is int
     assert ob.check_main_thread_return.__name__ == "check_main_thread_return"
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/tests/test_flow_layout.py 
new/superqt-0.7.1/tests/test_flow_layout.py
--- old/superqt-0.6.7/tests/test_flow_layout.py 1970-01-01 01:00:00.000000000 
+0100
+++ new/superqt-0.7.1/tests/test_flow_layout.py 2020-02-02 01:00:00.000000000 
+0100
@@ -0,0 +1,27 @@
+from typing import Any
+
+from qtpy.QtWidgets import QPushButton, QWidget
+
+from superqt import QFlowLayout
+
+
+def test_flow_layout(qtbot: Any) -> None:
+    wdg = QWidget()
+    qtbot.addWidget(wdg)
+
+    layout = QFlowLayout(wdg)
+    layout.addWidget(QPushButton("Short"))
+    layout.addWidget(QPushButton("Longer"))
+    layout.addWidget(QPushButton("Different text"))
+    layout.addWidget(QPushButton("More text"))
+    layout.addWidget(QPushButton("Even longer button text"))
+
+    wdg.setWindowTitle("Flow Layout")
+    wdg.show()
+
+    assert layout.expandingDirections()
+    assert layout.heightForWidth(200) > layout.heightForWidth(400)
+    assert layout.count() == 5
+    assert layout.itemAt(0).widget().text() == "Short"
+    layout.takeAt(0)
+    assert layout.count() == 4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/tests/test_searchable_tree.py 
new/superqt-0.7.1/tests/test_searchable_tree.py
--- old/superqt-0.6.7/tests/test_searchable_tree.py     2020-02-02 
01:00:00.000000000 +0100
+++ new/superqt-0.7.1/tests/test_searchable_tree.py     2020-02-02 
01:00:00.000000000 +0100
@@ -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 @@
     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()]
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/superqt-0.6.7/tests/test_throttler.py 
new/superqt-0.7.1/tests/test_throttler.py
--- old/superqt-0.6.7/tests/test_throttler.py   2020-02-02 01:00:00.000000000 
+0100
+++ new/superqt-0.7.1/tests/test_throttler.py   2020-02-02 01:00:00.000000000 
+0100
@@ -125,7 +125,7 @@
 
 def test_class_with_slots(qtbot):
     class A:
-        __slots__ = ("count", "__weakref__")
+        __slots__ = ("__weakref__", "count")
 
         def __init__(self):
             self.count = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/superqt-0.6.7/tests/zz_test_sliders/test_labeled_slider.py 
new/superqt-0.7.1/tests/zz_test_sliders/test_labeled_slider.py
--- old/superqt-0.6.7/tests/zz_test_sliders/test_labeled_slider.py      
2020-02-02 01:00:00.000000000 +0100
+++ new/superqt-0.7.1/tests/zz_test_sliders/test_labeled_slider.py      
2020-02-02 01:00:00.000000000 +0100
@@ -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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/superqt-0.6.7/tests/zz_test_sliders/test_range_slider.py 
new/superqt-0.7.1/tests/zz_test_sliders/test_range_slider.py
--- old/superqt-0.6.7/tests/zz_test_sliders/test_range_slider.py        
2020-02-02 01:00:00.000000000 +0100
+++ new/superqt-0.7.1/tests/zz_test_sliders/test_range_slider.py        
2020-02-02 01:00:00.000000000 +0100
@@ -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

Reply via email to