From 05e2a8a268d840a34bf8939511cb437aa7d414c0 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 6 Dec 2023 11:36:46 -0500 Subject: [PATCH 1/8] chore: changelog v0.8.1 --- CHANGELOG.md | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e862f96c5..0cb444e74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## [v0.8.1](https://github.com/pyapp-kit/magicgui/tree/v0.8.1) (2023-12-06) + +[Full Changelog](https://github.com/pyapp-kit/magicgui/compare/v0.8.0...v0.8.1) + +**Implemented enhancements:** + +- feat: add `Table.delete_row` method [\#610](https://github.com/pyapp-kit/magicgui/pull/610) ([tlambert03](https://github.com/tlambert03)) +- feat: add toolbar widget [\#597](https://github.com/pyapp-kit/magicgui/pull/597) ([tlambert03](https://github.com/tlambert03)) + +**Fixed bugs:** + +- Ensure QImage is ARGB32 before converting to numpy [\#618](https://github.com/pyapp-kit/magicgui/pull/618) ([aganders3](https://github.com/aganders3)) +- fix: allow future annotations in ipywidgets backend [\#609](https://github.com/pyapp-kit/magicgui/pull/609) ([tlambert03](https://github.com/tlambert03)) +- Make kwargs of container-like widgets consistent [\#606](https://github.com/pyapp-kit/magicgui/pull/606) ([hanjinliu](https://github.com/hanjinliu)) + +**Documentation:** + +- docs: unpin pyside6 when building docs [\#614](https://github.com/pyapp-kit/magicgui/pull/614) ([tlambert03](https://github.com/tlambert03)) + +**Merged pull requests:** + +- ci\(dependabot\): bump tlambert03/setup-qt-libs from 1.5 to 1.6 [\#615](https://github.com/pyapp-kit/magicgui/pull/615) ([dependabot[bot]](https://github.com/apps/dependabot)) +- chore!: remove older deprecations [\#607](https://github.com/pyapp-kit/magicgui/pull/607) ([tlambert03](https://github.com/tlambert03)) + ## [v0.8.0](https://github.com/pyapp-kit/magicgui/tree/v0.8.0) (2023-10-20) [Full Changelog](https://github.com/pyapp-kit/magicgui/compare/v0.7.3...v0.8.0) @@ -32,6 +56,7 @@ **Merged pull requests:** +- chore: changelog v0.8.0 [\#605](https://github.com/pyapp-kit/magicgui/pull/605) ([tlambert03](https://github.com/tlambert03)) - style: use `Unpack` for better kwargs typing [\#599](https://github.com/pyapp-kit/magicgui/pull/599) ([tlambert03](https://github.com/tlambert03)) - chore: remove setup.py [\#595](https://github.com/pyapp-kit/magicgui/pull/595) ([tlambert03](https://github.com/tlambert03)) - ci\(dependabot\): bump actions/checkout from 3 to 4 [\#578](https://github.com/pyapp-kit/magicgui/pull/578) ([dependabot[bot]](https://github.com/apps/dependabot)) @@ -548,7 +573,7 @@ ## [v0.2.9](https://github.com/pyapp-kit/magicgui/tree/v0.2.9) (2021-04-05) -[Full Changelog](https://github.com/pyapp-kit/magicgui/compare/v0.2.8...v0.2.9) +[Full Changelog](https://github.com/pyapp-kit/magicgui/compare/v0.2.8rc0...v0.2.9) **Implemented enhancements:** @@ -574,13 +599,13 @@ - \[pre-commit.ci\] pre-commit autoupdate [\#212](https://github.com/pyapp-kit/magicgui/pull/212) ([pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci)) -## [v0.2.8](https://github.com/pyapp-kit/magicgui/tree/v0.2.8) (2021-03-24) +## [v0.2.8rc0](https://github.com/pyapp-kit/magicgui/tree/v0.2.8rc0) (2021-03-24) -[Full Changelog](https://github.com/pyapp-kit/magicgui/compare/v0.2.8rc0...v0.2.8) +[Full Changelog](https://github.com/pyapp-kit/magicgui/compare/v0.2.8...v0.2.8rc0) -## [v0.2.8rc0](https://github.com/pyapp-kit/magicgui/tree/v0.2.8rc0) (2021-03-24) +## [v0.2.8](https://github.com/pyapp-kit/magicgui/tree/v0.2.8) (2021-03-24) -[Full Changelog](https://github.com/pyapp-kit/magicgui/compare/v0.2.7...v0.2.8rc0) +[Full Changelog](https://github.com/pyapp-kit/magicgui/compare/v0.2.7...v0.2.8) **Implemented enhancements:** From 5758566e9484205398c2e250a271e8fa01847ea8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:46:02 -0500 Subject: [PATCH 2/8] ci(dependabot): bump actions/setup-python from 4 to 5 (#620) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deploy_docs.yml | 2 +- .github/workflows/test_and_deploy.yml | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml index c172c3ee9..69583f508 100644 --- a/.github/workflows/deploy_docs.yml +++ b/.github/workflows/deploy_docs.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: "3.x" - run: | diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 8766e8727..f96b35a26 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -42,7 +42,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - uses: tlambert03/setup-qt-libs@v1.6 @@ -65,7 +65,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: "3.11" @@ -93,7 +93,7 @@ jobs: repository: napari/napari path: napari-from-github - uses: tlambert03/setup-qt-libs@v1.6 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install @@ -118,7 +118,7 @@ jobs: repository: hanjinliu/magic-class path: magic-class - uses: tlambert03/setup-qt-libs@v1.6 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install @@ -146,7 +146,7 @@ jobs: repository: stardist/stardist-napari path: stardist-napari - uses: tlambert03/setup-qt-libs@v1.6 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install @@ -174,7 +174,7 @@ jobs: repository: 4DNucleome/PartSeg path: PartSeg - uses: tlambert03/setup-qt-libs@v1.6 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install @@ -197,7 +197,7 @@ jobs: with: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.x" - name: Install dependencies From 96d2d99bfe8c16cf38d8dce450fe3a81a11d0d70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:46:10 -0500 Subject: [PATCH 3/8] ci(dependabot): bump aganders3/headless-gui from 1 to 2 (#619) Bumps [aganders3/headless-gui](https://github.com/aganders3/headless-gui) from 1 to 2. - [Release notes](https://github.com/aganders3/headless-gui/releases) - [Commits](https://github.com/aganders3/headless-gui/compare/v1...v2) --- updated-dependencies: - dependency-name: aganders3/headless-gui dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test_and_deploy.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index f96b35a26..5424bfe73 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -50,7 +50,7 @@ jobs: run: python -m pip install --upgrade hatch - name: Test - uses: aganders3/headless-gui@v1 + uses: aganders3/headless-gui@v2 with: run: hatch -v run +backend=${{ matrix.backend }} test:run @@ -77,7 +77,7 @@ jobs: python -m pip install pytest 'pydantic<2' attrs pytest-cov pyqt6 - name: Test - uses: aganders3/headless-gui@v1 + uses: aganders3/headless-gui@v2 with: run: pytest tests/test_ui_field.py -v --color=yes --cov=magicgui --cov-report=xml @@ -103,7 +103,7 @@ jobs: python -m pip install -e ./napari-from-github[pyqt5] - name: Test napari magicgui - uses: aganders3/headless-gui@v1 + uses: aganders3/headless-gui@v2 with: working-directory: napari-from-github run: pytest -W ignore napari/_tests/test_magicgui.py -v --color=yes @@ -128,7 +128,7 @@ jobs: python -m pip install ./magic-class[testing] - name: Test magicclass - uses: aganders3/headless-gui@v1 + uses: aganders3/headless-gui@v2 # magicclass is still in development, don't fail the whole build # this makes this much less useful... but it's better than nothing? continue-on-error: true @@ -156,7 +156,7 @@ jobs: python -m pip install ./stardist-napari[test] - name: Run stardist tests - uses: aganders3/headless-gui@v1 + uses: aganders3/headless-gui@v2 with: working-directory: stardist-napari run: python -m pytest -v --color=yes -W ignore stardist_napari @@ -183,7 +183,7 @@ jobs: python -m pip install ./PartSeg[test,pyqt5] - name: Run PartSeg tests - uses: aganders3/headless-gui@v1 + uses: aganders3/headless-gui@v2 with: working-directory: PartSeg run: python -m pytest -v --color=yes -W ignore package/tests/test_PartSeg/test_napari_widgets.py From bdda89f1e873a0ee5bd7977ae696dde35917ce4d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 10:10:42 -0500 Subject: [PATCH 4/8] ci(pre-commit.ci): autoupdate (#622) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.6 → v0.1.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.6...v0.1.9) - [github.com/psf/black: 23.11.0 → 23.12.1](https://github.com/psf/black/compare/23.11.0...23.12.1) - [github.com/pre-commit/mirrors-mypy: v1.7.1 → v1.8.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.7.1...v1.8.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4fe691bde..7bd52dad5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,13 +14,13 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.6 + rev: v0.1.9 hooks: - id: ruff args: ["--fix"] - repo: https://github.com/psf/black - rev: 23.11.0 + rev: 23.12.1 hooks: - id: black @@ -30,7 +30,7 @@ repos: - id: validate-pyproject - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.7.1 + rev: v1.8.0 hooks: - id: mypy files: "^src/" From 5e30d286cef4e778e7eeafc110daa18ad1b16373 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:13:49 -0500 Subject: [PATCH 5/8] ci(dependabot): bump tlambert03/setup-qt-libs from 1.6 to 1.7 (#625) Bumps [tlambert03/setup-qt-libs](https://github.com/tlambert03/setup-qt-libs) from 1.6 to 1.7. - [Release notes](https://github.com/tlambert03/setup-qt-libs/releases) - [Commits](https://github.com/tlambert03/setup-qt-libs/compare/v1.6...v1.7) --- updated-dependencies: - dependency-name: tlambert03/setup-qt-libs dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test_and_deploy.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 5424bfe73..c308a41a0 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -45,7 +45,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: tlambert03/setup-qt-libs@v1.6 + - uses: tlambert03/setup-qt-libs@v1.7 - name: Install dependencies run: python -m pip install --upgrade hatch @@ -69,7 +69,7 @@ jobs: with: python-version: "3.11" - - uses: tlambert03/setup-qt-libs@v1.6 + - uses: tlambert03/setup-qt-libs@v1.7 - name: Install dependencies run: | @@ -92,7 +92,7 @@ jobs: with: repository: napari/napari path: napari-from-github - - uses: tlambert03/setup-qt-libs@v1.6 + - uses: tlambert03/setup-qt-libs@v1.7 - uses: actions/setup-python@v5 with: python-version: "3.10" @@ -117,7 +117,7 @@ jobs: with: repository: hanjinliu/magic-class path: magic-class - - uses: tlambert03/setup-qt-libs@v1.6 + - uses: tlambert03/setup-qt-libs@v1.7 - uses: actions/setup-python@v5 with: python-version: "3.10" @@ -145,7 +145,7 @@ jobs: with: repository: stardist/stardist-napari path: stardist-napari - - uses: tlambert03/setup-qt-libs@v1.6 + - uses: tlambert03/setup-qt-libs@v1.7 - uses: actions/setup-python@v5 with: python-version: "3.10" @@ -173,7 +173,7 @@ jobs: with: repository: 4DNucleome/PartSeg path: PartSeg - - uses: tlambert03/setup-qt-libs@v1.6 + - uses: tlambert03/setup-qt-libs@v1.7 - uses: actions/setup-python@v5 with: python-version: "3.10" From 94c8a3bf3f0b46f41c0eb19c61e387d0ff9802bc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:15:40 -0500 Subject: [PATCH 6/8] ci(pre-commit.ci): autoupdate (#626) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci(pre-commit.ci): autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.9 → v0.2.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.9...v0.2.0) - [github.com/psf/black: 23.12.1 → 24.1.1](https://github.com/psf/black/compare/23.12.1...24.1.1) - [github.com/abravalheri/validate-pyproject: v0.15 → v0.16](https://github.com/abravalheri/validate-pyproject/compare/v0.15...v0.16) * style(pre-commit.ci): auto fixes [...] --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 ++-- docs/examples/applications/callable.py | 1 + docs/examples/applications/chaining.py | 1 + docs/examples/applications/hotdog.py | 1 + docs/examples/applications/pint_quantity.py | 1 + docs/examples/basic_example.py | 1 + docs/examples/basic_widgets_demo.py | 1 + docs/examples/demo_widgets/change_label.py | 1 + docs/examples/demo_widgets/choices.py | 1 + docs/examples/demo_widgets/file_dialog.py | 1 + docs/examples/demo_widgets/log_slider.py | 1 + docs/examples/demo_widgets/login.py | 1 + docs/examples/demo_widgets/optional.py | 1 + docs/examples/demo_widgets/range_slider.py | 1 + docs/examples/demo_widgets/selection.py | 1 + docs/examples/demo_widgets/table.py | 1 + docs/examples/matplotlib/waveform.py | 1 + docs/examples/napari/napari_combine_qt.py | 1 + docs/examples/napari/napari_forward_refs.py | 1 + docs/examples/progress_bars/progress.py | 1 + .../progress_bars/progress_indeterminate.py | 1 + .../examples/progress_bars/progress_manual.py | 1 + docs/examples/under_the_hood/class_method.py | 1 + .../examples/under_the_hood/self_reference.py | 1 + docs/scripts/_hooks.py | 1 + src/magicgui/__init__.py | 1 + src/magicgui/_util.py | 8 ++--- src/magicgui/application.py | 1 + src/magicgui/backends/__init__.py | 1 + src/magicgui/experimental.py | 1 + src/magicgui/schema/_guiclass.py | 16 ++++------ src/magicgui/signature.py | 1 + src/magicgui/tqdm.py | 1 + src/magicgui/type_map/__init__.py | 1 + src/magicgui/type_map/_magicgui.py | 24 +++++---------- src/magicgui/type_map/_type_map.py | 7 ++--- src/magicgui/types.py | 1 + src/magicgui/widgets/_concrete.py | 25 ++++++---------- src/magicgui/widgets/_function_gui.py | 1 + src/magicgui/widgets/_table.py | 3 +- src/magicgui/widgets/bases/__init__.py | 1 + .../widgets/bases/_container_widget.py | 6 ++-- src/magicgui/widgets/protocols.py | 3 +- tests/test_container.py | 3 +- tests/test_magicgui.py | 30 +++++++------------ tests/test_persistence.py | 3 +- tests/test_return_widgets.py | 6 ++-- tests/test_table.py | 1 + tests/test_tqdm.py | 1 + tests/test_types.py | 3 +- tests/test_ui_field.py | 3 +- tests/test_widgets.py | 6 ++-- 52 files changed, 92 insertions(+), 96 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7bd52dad5..948d9accc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,18 +14,18 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.9 + rev: v0.2.0 hooks: - id: ruff args: ["--fix"] - repo: https://github.com/psf/black - rev: 23.12.1 + rev: 24.1.1 hooks: - id: black - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.15 + rev: v0.16 hooks: - id: validate-pyproject diff --git a/docs/examples/applications/callable.py b/docs/examples/applications/callable.py index a5d3c4b9c..0aa756a81 100644 --- a/docs/examples/applications/callable.py +++ b/docs/examples/applications/callable.py @@ -2,6 +2,7 @@ This example demonstrates handling callable functions with magicgui. """ + from magicgui import magicgui diff --git a/docs/examples/applications/chaining.py b/docs/examples/applications/chaining.py index 0147a0f35..10d270828 100644 --- a/docs/examples/applications/chaining.py +++ b/docs/examples/applications/chaining.py @@ -2,6 +2,7 @@ This example demonstrates chaining multiple functions together. """ + from magicgui import magicgui, widgets diff --git a/docs/examples/applications/hotdog.py b/docs/examples/applications/hotdog.py index f5329a9d6..bad1a7671 100644 --- a/docs/examples/applications/hotdog.py +++ b/docs/examples/applications/hotdog.py @@ -2,6 +2,7 @@ Demo app to upload an image and classify if it's an hotdog or not. """ + import pathlib from enum import Enum diff --git a/docs/examples/applications/pint_quantity.py b/docs/examples/applications/pint_quantity.py index 9fa811355..b4ef0f1f0 100644 --- a/docs/examples/applications/pint_quantity.py +++ b/docs/examples/applications/pint_quantity.py @@ -6,6 +6,7 @@ from and to different units. https://pint.readthedocs.io/en/stable/ """ + from pint import Quantity from magicgui import magicgui diff --git a/docs/examples/basic_example.py b/docs/examples/basic_example.py index 6e06da627..890e1cd41 100644 --- a/docs/examples/basic_example.py +++ b/docs/examples/basic_example.py @@ -2,6 +2,7 @@ A basic example using magicgui. """ + from magicgui import magicgui diff --git a/docs/examples/basic_widgets_demo.py b/docs/examples/basic_widgets_demo.py index b3ae376cf..c051c9565 100644 --- a/docs/examples/basic_widgets_demo.py +++ b/docs/examples/basic_widgets_demo.py @@ -5,6 +5,7 @@ This code demonstrates a few of the widget types that magicgui can create based on the parameter types in your function. """ + import datetime from enum import Enum from pathlib import Path diff --git a/docs/examples/demo_widgets/change_label.py b/docs/examples/demo_widgets/change_label.py index 4e3b660dc..ca94598fb 100644 --- a/docs/examples/demo_widgets/change_label.py +++ b/docs/examples/demo_widgets/change_label.py @@ -2,6 +2,7 @@ An example showing how to create custom text labels for your widgets. """ + from magicgui import magicgui diff --git a/docs/examples/demo_widgets/choices.py b/docs/examples/demo_widgets/choices.py index 74b3857f3..ec309725f 100644 --- a/docs/examples/demo_widgets/choices.py +++ b/docs/examples/demo_widgets/choices.py @@ -2,6 +2,7 @@ Choices for dropdowns can be provided in a few different ways. """ + from enum import Enum from magicgui import magicgui, widgets diff --git a/docs/examples/demo_widgets/file_dialog.py b/docs/examples/demo_widgets/file_dialog.py index ff7230ebd..11093ad79 100644 --- a/docs/examples/demo_widgets/file_dialog.py +++ b/docs/examples/demo_widgets/file_dialog.py @@ -2,6 +2,7 @@ A file dialog widget example. """ + from pathlib import Path from typing import Sequence diff --git a/docs/examples/demo_widgets/log_slider.py b/docs/examples/demo_widgets/log_slider.py index bd3385fdf..cd0eb5b7e 100644 --- a/docs/examples/demo_widgets/log_slider.py +++ b/docs/examples/demo_widgets/log_slider.py @@ -2,6 +2,7 @@ A logarithmic scale range slider widget. """ + from magicgui import magicgui diff --git a/docs/examples/demo_widgets/login.py b/docs/examples/demo_widgets/login.py index 3cefafb42..4bb253079 100644 --- a/docs/examples/demo_widgets/login.py +++ b/docs/examples/demo_widgets/login.py @@ -2,6 +2,7 @@ A password login field widget. """ + from magicgui import magicgui diff --git a/docs/examples/demo_widgets/optional.py b/docs/examples/demo_widgets/optional.py index 8cae102c9..6fa88fd94 100644 --- a/docs/examples/demo_widgets/optional.py +++ b/docs/examples/demo_widgets/optional.py @@ -2,6 +2,7 @@ Optional user input using a dropdown selection widget. """ + from typing import Optional from magicgui import magicgui diff --git a/docs/examples/demo_widgets/range_slider.py b/docs/examples/demo_widgets/range_slider.py index 9d25d72e2..cf3c8a464 100644 --- a/docs/examples/demo_widgets/range_slider.py +++ b/docs/examples/demo_widgets/range_slider.py @@ -2,6 +2,7 @@ A double ended range slider widget. """ + from typing import Tuple from magicgui import magicgui diff --git a/docs/examples/demo_widgets/selection.py b/docs/examples/demo_widgets/selection.py index 8f92052c7..c956e33a9 100644 --- a/docs/examples/demo_widgets/selection.py +++ b/docs/examples/demo_widgets/selection.py @@ -2,6 +2,7 @@ A selection widget allowing multiple selections by the user. """ + from magicgui import magicgui diff --git a/docs/examples/demo_widgets/table.py b/docs/examples/demo_widgets/table.py index 4af4b870c..64c80f867 100644 --- a/docs/examples/demo_widgets/table.py +++ b/docs/examples/demo_widgets/table.py @@ -2,6 +2,7 @@ Demonstrating a few ways to input tables. """ + import numpy as np from magicgui.widgets import Table diff --git a/docs/examples/matplotlib/waveform.py b/docs/examples/matplotlib/waveform.py index 146096232..4ea94c0a7 100644 --- a/docs/examples/matplotlib/waveform.py +++ b/docs/examples/matplotlib/waveform.py @@ -2,6 +2,7 @@ Simple waveform generator widget, with plotting. """ + from dataclasses import dataclass, field from enum import Enum from functools import partial diff --git a/docs/examples/napari/napari_combine_qt.py b/docs/examples/napari/napari_combine_qt.py index 9a4351e27..317db73f4 100644 --- a/docs/examples/napari/napari_combine_qt.py +++ b/docs/examples/napari/napari_combine_qt.py @@ -10,6 +10,7 @@ This example shows how to use just that widget in the context of a larger custom QWidget. """ + import napari from qtpy.QtWidgets import QVBoxLayout, QWidget diff --git a/docs/examples/napari/napari_forward_refs.py b/docs/examples/napari/napari_forward_refs.py index 1700c0d3c..1ad2995a2 100644 --- a/docs/examples/napari/napari_forward_refs.py +++ b/docs/examples/napari/napari_forward_refs.py @@ -8,6 +8,7 @@ a *string* representation of the type (rather than the type itself). This is called a "forward reference": https://www.python.org/dev/peps/pep-0484/#forward-references """ + # Note: if you'd like to avoid circular imports, or just want to avoid having your # linter yell at you for an undefined type annotation, you can place the import # inside of an `if typing.TYPE_CHECKING` conditional. This is not evaluated at runtime, diff --git a/docs/examples/progress_bars/progress.py b/docs/examples/progress_bars/progress.py index 6f064b5e5..4e440605b 100644 --- a/docs/examples/progress_bars/progress.py +++ b/docs/examples/progress_bars/progress.py @@ -2,6 +2,7 @@ A simple progress bar demo with magicgui. """ + from time import sleep from magicgui import magicgui diff --git a/docs/examples/progress_bars/progress_indeterminate.py b/docs/examples/progress_bars/progress_indeterminate.py index e95aae28f..43051c46a 100644 --- a/docs/examples/progress_bars/progress_indeterminate.py +++ b/docs/examples/progress_bars/progress_indeterminate.py @@ -4,6 +4,7 @@ of unknown time. """ + import time from superqt.utils import thread_worker diff --git a/docs/examples/progress_bars/progress_manual.py b/docs/examples/progress_bars/progress_manual.py index ccc907862..6848067da 100644 --- a/docs/examples/progress_bars/progress_manual.py +++ b/docs/examples/progress_bars/progress_manual.py @@ -3,6 +3,7 @@ Example of a progress bar being updated manually. """ + from magicgui import magicgui from magicgui.widgets import ProgressBar diff --git a/docs/examples/under_the_hood/class_method.py b/docs/examples/under_the_hood/class_method.py index 7ca336755..f58a93919 100644 --- a/docs/examples/under_the_hood/class_method.py +++ b/docs/examples/under_the_hood/class_method.py @@ -6,6 +6,7 @@ in which the instance will always be provided as the first argument (i.e. "self") when the FunctionGui or method is called. """ + from magicgui import event_loop, magicgui from magicgui.widgets import Container diff --git a/docs/examples/under_the_hood/self_reference.py b/docs/examples/under_the_hood/self_reference.py index 58f2a68f6..0940856c0 100644 --- a/docs/examples/under_the_hood/self_reference.py +++ b/docs/examples/under_the_hood/self_reference.py @@ -2,6 +2,7 @@ Widgets created with magicgui can reference themselves, and use the widget API. """ + from magicgui import magicgui diff --git a/docs/scripts/_hooks.py b/docs/scripts/_hooks.py index d7922e5f7..887a37311 100644 --- a/docs/scripts/_hooks.py +++ b/docs/scripts/_hooks.py @@ -1,4 +1,5 @@ """https://www.mkdocs.org/dev-guide/plugins/#events .""" + from __future__ import annotations import importlib.abc diff --git a/src/magicgui/__init__.py b/src/magicgui/__init__.py index 677970e93..2f6f1a269 100644 --- a/src/magicgui/__init__.py +++ b/src/magicgui/__init__.py @@ -1,4 +1,5 @@ """magicgui is a utility for generating a GUI from a python function.""" + from importlib.metadata import PackageNotFoundError, version try: diff --git a/src/magicgui/_util.py b/src/magicgui/_util.py index 014aabdcd..85c8dde93 100644 --- a/src/magicgui/_util.py +++ b/src/magicgui/_util.py @@ -21,13 +21,13 @@ @overload -def debounce(function: Callable[P, T]) -> Callable[P, T | None]: - ... +def debounce(function: Callable[P, T]) -> Callable[P, T | None]: ... @overload -def debounce(*, wait: float = 0.2) -> Callable[[Callable[P, T]], Callable[P, T | None]]: - ... +def debounce( + *, wait: float = 0.2 +) -> Callable[[Callable[P, T]], Callable[P, T | None]]: ... def debounce(function: Callable[P, T] | None = None, wait: float = 0.2) -> Callable: diff --git a/src/magicgui/application.py b/src/magicgui/application.py index 9df1e3e91..2792ad824 100644 --- a/src/magicgui/application.py +++ b/src/magicgui/application.py @@ -1,4 +1,5 @@ """Magicgui application object, wrapping a backend application.""" + from __future__ import annotations import signal diff --git a/src/magicgui/backends/__init__.py b/src/magicgui/backends/__init__.py index 214876cad..9470e2924 100644 --- a/src/magicgui/backends/__init__.py +++ b/src/magicgui/backends/__init__.py @@ -1,4 +1,5 @@ """Backend modules implementing applications and widgets.""" + from __future__ import annotations BACKENDS: dict[str, tuple[str, str]] = { diff --git a/src/magicgui/experimental.py b/src/magicgui/experimental.py index 586e38b59..48840b67d 100644 --- a/src/magicgui/experimental.py +++ b/src/magicgui/experimental.py @@ -3,6 +3,7 @@ Note: these are not guaranteed to be stable. Names and parameters may change or be removed in future releases. Use at your own risk. """ + from .schema._guiclass import button, guiclass, is_guiclass __all__ = ["guiclass", "button", "is_guiclass"] diff --git a/src/magicgui/schema/_guiclass.py b/src/magicgui/schema/_guiclass.py index ae522f8a4..567b878a3 100644 --- a/src/magicgui/schema/_guiclass.py +++ b/src/magicgui/schema/_guiclass.py @@ -6,6 +6,7 @@ 2. Adds a `gui` property to the class that will return a `magicgui` widget, bound to the values of the dataclass instance. """ + from __future__ import annotations import contextlib @@ -63,8 +64,7 @@ def __dataclass_transform__( @__dataclass_transform__(field_specifiers=(Field, field)) @overload -def guiclass(cls: T) -> T: - ... +def guiclass(cls: T) -> T: ... @__dataclass_transform__(field_specifiers=(Field, field)) @@ -75,8 +75,7 @@ def guiclass( events_namespace: str = "events", follow_changes: bool = True, **dataclass_kwargs: Any, -) -> Callable[[T], T]: - ... +) -> Callable[[T], T]: ... def guiclass( @@ -168,13 +167,11 @@ def is_guiclass(obj: object) -> TypeGuard[GuiClassProtocol]: @overload -def button(func: F) -> F: - ... +def button(func: F) -> F: ... @overload -def button(**kwargs: Any) -> Callable[[F], F]: - ... +def button(**kwargs: Any) -> Callable[[F], F]: ... def button(func: F | None = None, **button_kwargs: Any) -> F | Callable[[F], F]: @@ -339,5 +336,4 @@ class GuiClass(metaclass=GuiClassMeta): # the mypy dataclass magic doesn't work without the literal decorator # it WILL work with pyright due to the __dataclass_transform__ above # here we just avoid a false error in mypy - def __init__(self, *args: Any, **kwargs: Any) -> None: - ... + def __init__(self, *args: Any, **kwargs: Any) -> None: ... diff --git a/src/magicgui/signature.py b/src/magicgui/signature.py index 3b61c9a25..835dd0df5 100644 --- a/src/magicgui/signature.py +++ b/src/magicgui/signature.py @@ -11,6 +11,7 @@ (it returns a ``MagicSignature``, which is a subclass of ``inspect.Signature``) """ + from __future__ import annotations import inspect diff --git a/src/magicgui/tqdm.py b/src/magicgui/tqdm.py index 065c41b8f..231ece857 100644 --- a/src/magicgui/tqdm.py +++ b/src/magicgui/tqdm.py @@ -1,4 +1,5 @@ """A wrapper around the tqdm.tqdm iterator that adds a ProgressBar to a magicgui.""" + from __future__ import annotations import contextlib diff --git a/src/magicgui/type_map/__init__.py b/src/magicgui/type_map/__init__.py index df8c1cf8b..3e32bc615 100644 --- a/src/magicgui/type_map/__init__.py +++ b/src/magicgui/type_map/__init__.py @@ -1,4 +1,5 @@ """Functions that map python types to widgets.""" + from ._type_map import get_widget_class, register_type, type2callback, type_registered __all__ = [ diff --git a/src/magicgui/type_map/_magicgui.py b/src/magicgui/type_map/_magicgui.py index 1cec52d04..dec1a00ca 100644 --- a/src/magicgui/type_map/_magicgui.py +++ b/src/magicgui/type_map/_magicgui.py @@ -45,8 +45,7 @@ def magicgui( persist: bool = False, raise_on_unknown: bool = False, **param_options: dict, -) -> FunctionGui[_P, _R]: - ... +) -> FunctionGui[_P, _R]: ... @overload @@ -65,8 +64,7 @@ def magicgui( persist: bool = False, raise_on_unknown: bool = False, **param_options: dict, -) -> Callable[[Callable[_P, _R]], FunctionGui[_P, _R]]: - ... +) -> Callable[[Callable[_P, _R]], FunctionGui[_P, _R]]: ... @overload @@ -85,8 +83,7 @@ def magicgui( persist: bool = False, raise_on_unknown: bool = False, **param_options: dict, -) -> MainFunctionGui[_P, _R]: - ... +) -> MainFunctionGui[_P, _R]: ... @overload @@ -105,8 +102,7 @@ def magicgui( persist: bool = False, raise_on_unknown: bool = False, **param_options: dict, -) -> Callable[[Callable[_P, _R]], MainFunctionGui[_P, _R]]: - ... +) -> Callable[[Callable[_P, _R]], MainFunctionGui[_P, _R]]: ... def magicgui( @@ -226,8 +222,7 @@ def magic_factory( widget_init: Callable[[FunctionGui], None] | None = None, raise_on_unknown: bool = False, **param_options: dict, -) -> MagicFactory[FunctionGui[_P, _R]]: - ... +) -> MagicFactory[FunctionGui[_P, _R]]: ... @overload @@ -247,8 +242,7 @@ def magic_factory( widget_init: Callable[[FunctionGui], None] | None = None, raise_on_unknown: bool = False, **param_options: dict, -) -> Callable[[Callable[_P, _R]], MagicFactory[FunctionGui[_P, _R]]]: - ... +) -> Callable[[Callable[_P, _R]], MagicFactory[FunctionGui[_P, _R]]]: ... @overload @@ -268,8 +262,7 @@ def magic_factory( widget_init: Callable[[FunctionGui], None] | None = None, raise_on_unknown: bool = False, **param_options: dict, -) -> MagicFactory[MainFunctionGui[_P, _R]]: - ... +) -> MagicFactory[MainFunctionGui[_P, _R]]: ... @overload @@ -289,8 +282,7 @@ def magic_factory( widget_init: Callable[[FunctionGui], None] | None = None, raise_on_unknown: bool = False, **param_options: dict, -) -> Callable[[Callable[_P, _R]], MagicFactory[MainFunctionGui[_P, _R]]]: - ... +) -> Callable[[Callable[_P, _R]], MagicFactory[MainFunctionGui[_P, _R]]]: ... def magic_factory( diff --git a/src/magicgui/type_map/_type_map.py b/src/magicgui/type_map/_type_map.py index f15021a9e..308a13660 100644 --- a/src/magicgui/type_map/_type_map.py +++ b/src/magicgui/type_map/_type_map.py @@ -1,4 +1,5 @@ """Functions in this module are responsible for mapping type annotations to widgets.""" + from __future__ import annotations import datetime @@ -433,8 +434,7 @@ def register_type( widget_type: WidgetRef | None = None, return_callback: ReturnCallback | None = None, **options: Any, -) -> _T: - ... +) -> _T: ... @overload @@ -444,8 +444,7 @@ def register_type( widget_type: WidgetRef | None = None, return_callback: ReturnCallback | None = None, **options: Any, -) -> Callable[[_T], _T]: - ... +) -> Callable[[_T], _T]: ... def register_type( diff --git a/src/magicgui/types.py b/src/magicgui/types.py index fdcc7b818..d00b7fb56 100644 --- a/src/magicgui/types.py +++ b/src/magicgui/types.py @@ -1,4 +1,5 @@ """Types used internally in magicgui.""" + from __future__ import annotations from enum import Enum, EnumMeta diff --git a/src/magicgui/widgets/_concrete.py b/src/magicgui/widgets/_concrete.py index 9b60a4447..d89d95787 100644 --- a/src/magicgui/widgets/_concrete.py +++ b/src/magicgui/widgets/_concrete.py @@ -3,6 +3,7 @@ All of these widgets should provide the `widget_type` argument to their super().__init__ calls. """ + from __future__ import annotations import contextlib @@ -68,8 +69,7 @@ @overload -def backend_widget(cls: WidgetTypeVar) -> WidgetTypeVar: - ... +def backend_widget(cls: WidgetTypeVar) -> WidgetTypeVar: ... @overload @@ -78,8 +78,7 @@ def backend_widget( *, widget_name: str | None = ..., transform: Callable[[type], type] | None = ..., -) -> Callable[[WidgetTypeVar], WidgetTypeVar]: - ... +) -> Callable[[WidgetTypeVar], WidgetTypeVar]: ... def backend_widget( @@ -785,12 +784,10 @@ def __eq__(self, other: object) -> bool: return list(self) == other @overload - def __getitem__(self, i: int) -> _V: - ... + def __getitem__(self, i: int) -> _V: ... @overload - def __getitem__(self, key: slice) -> list[_V]: - ... + def __getitem__(self, key: slice) -> list[_V]: ... def __getitem__(self, key: int | slice) -> _V | list[_V]: """Slice as a list.""" @@ -804,12 +801,10 @@ def __getitem__(self, key: int | slice) -> _V | list[_V]: ) @overload - def __setitem__(self, key: int, value: _V) -> None: - ... + def __setitem__(self, key: int, value: _V) -> None: ... @overload - def __setitem__(self, key: slice, value: _V | Iterable[_V]) -> None: - ... + def __setitem__(self, key: slice, value: _V | Iterable[_V]) -> None: ... def __setitem__(self, key: int | slice, value: _V | Iterable[_V]) -> None: """Update widget value.""" @@ -830,12 +825,10 @@ def __setitem__(self, key: int | slice, value: _V | Iterable[_V]) -> None: ) @overload - def __delitem__(self, key: int) -> None: - ... + def __delitem__(self, key: int) -> None: ... @overload - def __delitem__(self, key: slice) -> None: - ... + def __delitem__(self, key: slice) -> None: ... def __delitem__(self, key: int | slice) -> None: """Delete widget at the key(s).""" diff --git a/src/magicgui/widgets/_function_gui.py b/src/magicgui/widgets/_function_gui.py index 2cc229a81..9f3eb837f 100644 --- a/src/magicgui/widgets/_function_gui.py +++ b/src/magicgui/widgets/_function_gui.py @@ -2,6 +2,7 @@ The core `magicgui` decorator returns an instance of a FunctionGui widget. """ + from __future__ import annotations import inspect diff --git a/src/magicgui/widgets/_table.py b/src/magicgui/widgets/_table.py index 3cf5eed43..71d7f95f7 100644 --- a/src/magicgui/widgets/_table.py +++ b/src/magicgui/widgets/_table.py @@ -15,7 +15,6 @@ Literal, Mapping, MutableMapping, - NoReturn, Sequence, TypeVar, Union, @@ -835,7 +834,7 @@ def _from_records(data: list[dict[TblKey, Any]]) -> tuple[list[list], list, list def _validate_table_data( data: Collection, index: Sequence | None, column: Sequence | None -) -> None | NoReturn: +) -> None: """Make sure data matches shape of index and column.""" nr = len(data) if not nr: diff --git a/src/magicgui/widgets/bases/__init__.py b/src/magicgui/widgets/bases/__init__.py index 176a12e9f..506eda56a 100644 --- a/src/magicgui/widgets/bases/__init__.py +++ b/src/magicgui/widgets/bases/__init__.py @@ -42,6 +42,7 @@ def __init__( "annotation"). """ + from ._button_widget import ButtonWidget from ._categorical_widget import CategoricalWidget from ._container_widget import ContainerWidget, DialogWidget, MainWindowWidget diff --git a/src/magicgui/widgets/bases/_container_widget.py b/src/magicgui/widgets/bases/_container_widget.py index a1fc17ee9..302ed2f2b 100644 --- a/src/magicgui/widgets/bases/_container_widget.py +++ b/src/magicgui/widgets/bases/_container_widget.py @@ -141,12 +141,10 @@ def __setattr__(self, name: str, value: Any) -> None: object.__setattr__(self, name, value) @overload - def __getitem__(self, key: int | str) -> WidgetVar: - ... + def __getitem__(self, key: int | str) -> WidgetVar: ... @overload - def __getitem__(self, key: slice) -> MutableSequence[WidgetVar]: - ... + def __getitem__(self, key: slice) -> MutableSequence[WidgetVar]: ... def __getitem__( self, key: int | str | slice diff --git a/src/magicgui/widgets/protocols.py b/src/magicgui/widgets/protocols.py index 952dc8fdd..348d5be5d 100644 --- a/src/magicgui/widgets/protocols.py +++ b/src/magicgui/widgets/protocols.py @@ -7,6 +7,7 @@ For an example backend implementation, see ``magicgui.backends._qtpy.widgets`` """ + from __future__ import annotations from abc import ABC, abstractmethod @@ -27,7 +28,7 @@ from magicgui.widgets.bases import Widget -def assert_protocol(widget_class: type, protocol: type) -> None | NoReturn: +def assert_protocol(widget_class: type, protocol: type) -> None: """Ensure that widget_class implements protocol, or raise helpful error.""" if not isinstance(widget_class, protocol): _raise_protocol_error(widget_class, protocol) diff --git a/tests/test_container.py b/tests/test_container.py index 15a2b205f..15a85880f 100644 --- a/tests/test_container.py +++ b/tests/test_container.py @@ -199,8 +199,7 @@ def __init__(self) -> None: btn.changed.connect(self._on_clicked) super().__init__(widgets=[btn]) - def _on_clicked(self): - ... + def _on_clicked(self): ... assert isinstance(C(), widgets.Container) diff --git a/tests/test_magicgui.py b/tests/test_magicgui.py index c20d00316..3fac94e7a 100644 --- a/tests/test_magicgui.py +++ b/tests/test_magicgui.py @@ -83,8 +83,7 @@ def func(a: int = 1): # also without type annotation @magicgui(a={"widget_type": "LogSlider"}) - def g(a): - ... + def g(a): ... assert isinstance(g.a, widgets.LogSlider) @@ -173,8 +172,7 @@ class Medium(Enum): Air = 1.0003 @magicgui - def func(arg: Medium = Medium.Water): - ... + def func(arg: Medium = Medium.Water): ... assert func.arg.value == Medium.Water assert isinstance(func.arg, widgets.ComboBox) @@ -186,8 +184,7 @@ def test_dropdown_list_from_choices(): CHOICES = ["Oil", "Water", "Air"] @magicgui(arg={"choices": CHOICES}) - def func(arg="Water"): - ... + def func(arg="Water"): ... assert func.arg.value == "Water" assert isinstance(func.arg, widgets.ComboBox) @@ -196,8 +193,7 @@ def func(arg="Water"): with pytest.raises(ValueError): # the default value must be in the list @magicgui(arg={"choices": ["Oil", "Water", "Air"]}) - def func(arg="Silicone"): - ... + def func(arg="Silicone"): ... def test_dropdown_list_from_callable(): @@ -208,8 +204,7 @@ def get_choices(gui): return CHOICES @magicgui(arg={"choices": get_choices}) - def func(arg="Water"): - ... + def func(arg="Water"): ... assert func.arg.value == "Water" assert isinstance(func.arg, widgets.ComboBox) @@ -729,8 +724,7 @@ def test_empty_function(): """Test that a function with no params works.""" @magicgui(call_button=True) - def f(): - ... + def f(): ... f.show() @@ -767,8 +761,7 @@ def func(arg=None): def test_update_and_dict(): @magicgui - def test(a: int = 1, y: str = "a"): - ... + def test(a: int = 1, y: str = "a"): ... assert test.asdict() == {"a": 1, "y": "a"} @@ -784,8 +777,7 @@ def test(a: int = 1, y: str = "a"): def test_update_on_call(): @magicgui - def test(a: int = 1, y: str = "a"): - ... + def test(a: int = 1, y: str = "a"): ... assert test.call_count == 0 test(a=10, y="b", update_widget=True) @@ -839,16 +831,14 @@ def some_func2(x: int, y: str) -> str: def test_scrollable(): @magicgui(scrollable=True) - def test_scrollable(a: int = 1, y: str = "a"): - ... + def test_scrollable(a: int = 1, y: str = "a"): ... assert test_scrollable.native is not test_scrollable.root_native_widget assert not isinstance(test_scrollable.native, QScrollArea) assert isinstance(test_scrollable.root_native_widget, QScrollArea) @magicgui(scrollable=False) - def test_nonscrollable(a: int = 1, y: str = "a"): - ... + def test_nonscrollable(a: int = 1, y: str = "a"): ... assert test_nonscrollable.native is test_nonscrollable.root_native_widget assert not isinstance(test_nonscrollable.native, QScrollArea) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index f22f0e98e..2f4aa564f 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -30,8 +30,7 @@ def test_user_cache_dir(): def test_persistence(tmp_path): """Test that we can persist values across instances.""" - def _my_func(x: int, y="hello"): - ... + def _my_func(x: int, y="hello"): ... with patch("magicgui._util.user_cache_dir", lambda: tmp_path): fg = FunctionGui(_my_func, persist=True) diff --git a/tests/test_return_widgets.py b/tests/test_return_widgets.py index 6daaa5032..776c5f87f 100644 --- a/tests/test_return_widgets.py +++ b/tests/test_return_widgets.py @@ -94,12 +94,10 @@ def test_return_widget_for_type(data, expected_type, equality_check): def test_table_return_annotation(): @magicgui.magicgui(result_widget=True) - def f() -> "magicgui.widgets.Table": - ... + def f() -> "magicgui.widgets.Table": ... @magicgui.magicgui(result_widget=True) - def f2() -> widgets.Table: - ... + def f2() -> widgets.Table: ... assert isinstance(f._result_widget, widgets.Table) assert isinstance(f2._result_widget, widgets.Table) diff --git a/tests/test_table.py b/tests/test_table.py index 0dd9831be..6a9ef2cb0 100644 --- a/tests/test_table.py +++ b/tests/test_table.py @@ -1,4 +1,5 @@ """Tests for the Table widget.""" + import os import sys diff --git a/tests/test_tqdm.py b/tests/test_tqdm.py index a362338b7..28f7d8e7b 100644 --- a/tests/test_tqdm.py +++ b/tests/test_tqdm.py @@ -1,4 +1,5 @@ """Tests for the tqdm wrapper.""" + from time import sleep import pytest diff --git a/tests/test_types.py b/tests/test_types.py index b2d24cb05..01ab55321 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -117,8 +117,7 @@ def widget2(fn: Union[bytes, pathlib.Path, str]): def test_optional_type(): @magicgui(x={"choices": ["a", "b"]}) - def widget(x: Optional[str] = None): - ... + def widget(x: Optional[str] = None): ... assert isinstance(widget.x, widgets.ComboBox) assert widget.x.value is None diff --git a/tests/test_ui_field.py b/tests/test_ui_field.py index 45cb1e662..6e701d758 100644 --- a/tests/test_ui_field.py +++ b/tests/test_ui_field.py @@ -116,8 +116,7 @@ def foo( a: Optional[int], b: Annotated[str, UiField(description="the b")], c: Annotated[float, UiField(widget="FloatSlider")] = 0.0, - ): - ... + ): ... # makes to sense to instantiate a function _assert_uifields(foo, instantiate=False) diff --git a/tests/test_widgets.py b/tests/test_widgets.py index 99df22cf1..f87e4d4cb 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -1045,16 +1045,14 @@ def test_literal(): Lit = Literal[None, "a", 1, True, b"bytes"] @magicgui - def f(x: Lit): - ... + def f(x: Lit): ... cbox = f.x assert type(cbox) is widgets.ComboBox assert cbox.choices == get_args(Lit) @magicgui - def f(x: Set[Lit]): - ... + def f(x: Set[Lit]): ... sel = f.x assert type(sel) is widgets.Select From 3e10552f1108c5f21b2db668a64fd0b2830a605d Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Tue, 13 Feb 2024 20:41:07 -0500 Subject: [PATCH 7/8] fix: fix parent_changed signal, and rename to native_parent_changed (#628) * fix: fix parent_changed * add warn * style(pre-commit.ci): auto fixes [...] * lint * style(pre-commit.ci): auto fixes [...] * remove x * fixing tests --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/magicgui/backends/_qtpy/widgets.py | 4 +-- src/magicgui/widgets/_concrete.py | 4 +-- .../widgets/bases/_categorical_widget.py | 2 +- .../widgets/bases/_container_widget.py | 2 +- src/magicgui/widgets/bases/_widget.py | 20 +++++++++----- tests/test_magicgui.py | 27 ++++++++++++++----- 6 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/magicgui/backends/_qtpy/widgets.py b/src/magicgui/backends/_qtpy/widgets.py index b99c60501..7decaef0d 100644 --- a/src/magicgui/backends/_qtpy/widgets.py +++ b/src/magicgui/backends/_qtpy/widgets.py @@ -48,13 +48,13 @@ def _signals_blocked(obj: QtW.QWidget) -> Iterator[None]: class EventFilter(QObject): - parentChanged = Signal() + parentChanged = Signal(QObject) valueChanged = Signal(object) paletteChanged = Signal() def eventFilter(self, obj: QObject, event: QEvent) -> bool: if event.type() == QEvent.Type.ParentChange: - self.parentChanged.emit() + self.parentChanged.emit(obj.parent()) if event.type() == QEvent.Type.PaletteChange: self.paletteChanged.emit() return False diff --git a/src/magicgui/widgets/_concrete.py b/src/magicgui/widgets/_concrete.py index d89d95787..1eb3e0f06 100644 --- a/src/magicgui/widgets/_concrete.py +++ b/src/magicgui/widgets/_concrete.py @@ -976,11 +976,11 @@ def __init__( # type: ignore [misc] # overlap between argument names _visible = False if widget._explicitly_hidden else None self._label_widget = Label(value=label or widget.label, tooltip=widget.tooltip) super().__init__(**kwargs, visible=_visible) - self.parent_changed.disconnect() # don't need _LabeledWidget to trigger stuff + self.native_parent_changed.disconnect() # don't need _LabeledWidget to trigger self.labels = False # important to avoid infinite recursion during insert! self._inner_widget.label_changed.connect(self._on_label_change) for w in [self._label_widget, widget]: - with w.parent_changed.blocked(): + with w.native_parent_changed.blocked(): self.append(w) self.margins = (0, 0, 0, 0) diff --git a/src/magicgui/widgets/bases/_categorical_widget.py b/src/magicgui/widgets/bases/_categorical_widget.py index a91eff746..0ba1484db 100644 --- a/src/magicgui/widgets/bases/_categorical_widget.py +++ b/src/magicgui/widgets/bases/_categorical_widget.py @@ -61,7 +61,7 @@ def __init__( def _post_init(self) -> None: super()._post_init() self.reset_choices() - self.parent_changed.connect(self.reset_choices) + self.native_parent_changed.connect(self.reset_choices) @property def value(self) -> T: diff --git a/src/magicgui/widgets/bases/_container_widget.py b/src/magicgui/widgets/bases/_container_widget.py index 302ed2f2b..192a6b65f 100644 --- a/src/magicgui/widgets/bases/_container_widget.py +++ b/src/magicgui/widgets/bases/_container_widget.py @@ -117,7 +117,7 @@ def __init__( ) super().__init__(**base_widget_kwargs) self.extend(widgets) - self.parent_changed.connect(self.reset_choices) + self.native_parent_changed.connect(self.reset_choices) self._initialized = True self._unify_label_widths() diff --git a/src/magicgui/widgets/bases/_widget.py b/src/magicgui/widgets/bases/_widget.py index 791e7d7ad..57c5607ce 100644 --- a/src/magicgui/widgets/bases/_widget.py +++ b/src/magicgui/widgets/bases/_widget.py @@ -5,7 +5,7 @@ from contextlib import contextmanager from typing import TYPE_CHECKING, Any, Iterator -from psygnal import Signal +from psygnal import Signal, SignalInstance from magicgui._type_resolution import resolve_single_type from magicgui.application import Application, use_app @@ -77,7 +77,7 @@ class Widget: # if this widget becomes owned by a labeled widget _labeled_widget_ref: ReferenceType[_LabeledWidget] | None = None - parent_changed = Signal( + native_parent_changed = Signal( object, description="Emitted with the backend widget when the widget parent changes.", ) @@ -144,7 +144,7 @@ def __init__( self.enabled = enabled self.annotation: Any = annotation self.gui_only = gui_only - self._widget._mgui_bind_parent_change_callback(self._emit_parent) + self._widget._mgui_bind_parent_change_callback(self.native_parent_changed.emit) # put the magicgui widget on the native object...may cause error on some backend self.native._magic_widget = self @@ -154,6 +154,17 @@ def __init__( if visible is not None: self.visible = visible + @property + def parent_changed(self) -> SignalInstance: + """Signal emitted when the parent of the widget changes.""" + warnings.warn( + "The 'parent_changed' signal has been renamed to 'native_parent_changed'. " + "Its use will be removed or repurposed in a future version.", + FutureWarning, + stacklevel=2, + ) + return self.native_parent_changed + @property def annotation(self) -> Any: """Return type annotation for the parameter represented by the widget. @@ -408,9 +419,6 @@ def _repr_png_(self) -> bytes | None: return file_obj.read() return None - def _emit_parent(self, *_: Any) -> None: - self.parent_changed.emit(self.parent) - def _ipython_display_(self, *args: Any, **kwargs: Any) -> Any: if hasattr(self.native, "_ipython_display_"): return self.native._ipython_display_(*args, **kwargs) diff --git a/tests/test_magicgui.py b/tests/test_magicgui.py index 3fac94e7a..d862308b9 100644 --- a/tests/test_magicgui.py +++ b/tests/test_magicgui.py @@ -9,7 +9,7 @@ import pytest from qtpy.QtCore import Qt -from qtpy.QtWidgets import QScrollArea +from qtpy.QtWidgets import QScrollArea, QWidget from magicgui import magicgui, register_type, type_map, widgets from magicgui.signature import MagicSignature, magic_signature @@ -484,11 +484,26 @@ def func2(a=1) -> Sub: _RETURN_CALLBACKS.pop(Base) -# @pytest.mark.skip(reason="need to rethink how to test this") -# def test_parent_changed(qtbot, magic_func): -# """Test that setting MagicGui parent emits a signal.""" -# with qtbot.waitSignal(magic_func.parent_changed, timeout=1000): -# magic_func.native.setParent(None) +def test_parent_changed(qtbot, magic_func: widgets.FunctionGui) -> None: + """Test that setting a backend parent emits a signal.""" + wdg = QWidget() + qtbot.addWidget(wdg) + + mock = Mock() + magic_func.native_parent_changed.connect(mock) + with qtbot.waitSignal(magic_func.native_parent_changed, timeout=1000): + magic_func.native.setParent(wdg) + + # the backend parent is emitted + mock.assert_called_once_with(wdg) + + with qtbot.waitSignal(magic_func.native_parent_changed, timeout=1000): + magic_func.native.setParent(None) + + mock.assert_called_with(None) + + with pytest.warns(FutureWarning, match="'parent_changed' signal has been renamed"): + magic_func.parent_changed # noqa: B018 def test_function_binding(): From eaaa3ed0c52f5f230085214713320755dc509951 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Thu, 15 Feb 2024 23:04:39 -0500 Subject: [PATCH 8/8] feat: support psygnal v0.10 (#629) --- src/magicgui/schema/_guiclass.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/magicgui/schema/_guiclass.py b/src/magicgui/schema/_guiclass.py index 567b878a3..7788ab408 100644 --- a/src/magicgui/schema/_guiclass.py +++ b/src/magicgui/schema/_guiclass.py @@ -12,7 +12,7 @@ import contextlib import warnings from dataclasses import Field, dataclass, field, is_dataclass -from typing import TYPE_CHECKING, Any, Callable, ClassVar, TypeVar, overload +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Mapping, TypeVar, overload from psygnal import SignalGroup, SignalInstance, evented from psygnal import __version__ as psygnal_version @@ -253,7 +253,13 @@ def bind_gui_to_instance( If True, changes to the instance will be reflected in the gui, by default True """ events = getattr(instance, "events", None) if two_way else None - signals = events.signals if isinstance(events, SignalGroup) else {} + signals: Mapping[str, SignalInstance] = {} + if isinstance(events, SignalGroup): + # psygnal >=0.10 + if hasattr(events, "__iter__") and hasattr(events, "__getitem__"): + signals = {k: events[k] for k in events} # type: ignore + else: # psygnal <0.10 + signals = events.signals # in cases of classes with `__slots__`, widget.changed.connect_setattr(instance...) # will show a RuntimeWarning because `instance` will not be weak referenceable. @@ -277,7 +283,10 @@ def bind_gui_to_instance( if hasattr(instance, name): try: changed.connect_setattr( - instance, name, **_IGNORE_REF_ERR # type: ignore + instance, + name, + maxargs=1, + **_IGNORE_REF_ERR, # type: ignore ) except TypeError: warnings.warn(