diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml index 69583f508..f8f3ad217 100644 --- a/.github/workflows/deploy_docs.yml +++ b/.github/workflows/deploy_docs.yml @@ -15,17 +15,14 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: astral-sh/setup-uv@v6 with: - python-version: "3.x" - - run: | - python -m pip install --upgrade pip - python -m pip install -e .[docs] + enable-cache: true - name: Deploy docs to GitHub Pages if: github.event_name == 'push' - run: mkdocs gh-deploy --strict --force + run: uv run --group docs mkdocs gh-deploy --strict --force - name: Test that docs build without error if: github.event_name == 'pull_request' - run: mkdocs build --strict + run: uv run --group docs mkdocs build --strict diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index c687d183f..d20e60614 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -15,79 +15,93 @@ on: jobs: test: - name: Test - uses: pyapp-kit/workflows/.github/workflows/test-pyrepo.yml@v2 - with: - os: ${{ matrix.os }} - python-version: ${{ matrix.python-version }} - qt: ${{ matrix.qt }} - pip-install-pre-release: ${{ github.event_name == 'schedule' }} - coverage-upload: artifact + name: ${{ matrix.os }} (${{ matrix.python-version }}) ${{ matrix.add-group || '' }} ${{ matrix.resolution || ''}} + runs-on: ${{ matrix.os }} + env: + UV_PRERELEASE: ${{ github.event_name == 'schedule' && 'allow' || 'if-necessary-or-explicit' }} + UV_NO_SYNC: 1 strategy: fail-fast: false matrix: python-version: ["3.10", "3.11"] os: [ubuntu-latest, macos-latest, windows-latest] - qt: [pyqt6, pyside6] + add-group: [pyqt6, pyside6] include: + - os: windows-latest + python-version: "3.11" + resolution: "lowest-direct" + add-group: pyside6 - python-version: "3.9" os: ubuntu-latest - qt: pyqt5 + add-group: pyqt5 - python-version: "3.9" os: ubuntu-latest - qt: pyside2 - - python-version: "3.9" - os: macos-13 - qt: pyside2 - - python-version: "3.9" - os: macos-13 - qt: pyqt5 - - python-version: "3.9" + add-group: pyside2 + - python-version: "3.11" os: windows-latest - qt: pyqt5 + add-group: pyqt5 - python-version: "3.10" os: ubuntu-latest - qt: pyside2 + add-group: pyside2 - python-version: "3.12" os: ubuntu-latest - qt: pyqt6 + add-group: pyqt6 - python-version: "3.12" os: ubuntu-latest - qt: pyside6 + add-group: pyside6 - python-version: "3.13" os: ubuntu-latest - qt: pyside6 + add-group: pyside6 - python-version: "3.13" os: windows-latest - qt: pyqt6 + add-group: pyqt6 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true - test-min-reqs: - name: Test min reqs - uses: pyapp-kit/workflows/.github/workflows/test-pyrepo.yml@v2 - with: - os: ubuntu-latest - python-version: ${{ matrix.python-version }} - qt: pyqt5 - pip-install-min-reqs: true - coverage-upload: artifact - strategy: - fail-fast: false - matrix: - python-version: ["3.9", "3.10", "3.11"] + - name: ๐Ÿ Set up Python ${{ matrix.python-version }} + uses: astral-sh/setup-uv@v6 + with: + python-version: ${{ matrix.python-version }} + enable-cache: true + cache-dependency-glob: "**/pyproject.toml" - test-pydantic1: - name: Test pydantic 1 - uses: pyapp-kit/workflows/.github/workflows/test-pyrepo.yml@v2 - with: - os: ubuntu-latest - python-version: 3.11 - qt: pyqt6 - pip-post-installs: '"pydantic<2"' - coverage-upload: artifact + - uses: pyvista/setup-headless-display-action@v4 + with: + qt: true + + - name: Install Dependencies + run: uv sync --no-dev --group test --group ${{ matrix.add-group || 'test' }} --resolution ${{ matrix.resolution || 'highest'}} + + - name: ๐Ÿงช Run Tests + run: uv run coverage run -p -m pytest -v + + # If something goes wrong with --pre tests, we can open an issue in the repo + - name: ๐Ÿ“ Report --pre Failures + if: failure() && github.event_name == 'schedule' + uses: JasonEtco/create-an-issue@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PLATFORM: ${{ matrix.os }} + PYTHON: ${{ matrix.python-version }} + RUN_ID: ${{ github.run_id }} + TITLE: "[test-bot] pip install --pre is failing" + with: + filename: .github/TEST_FAIL_TEMPLATE.md + update_existing: true + + - name: Upload coverage + uses: actions/upload-artifact@v4 + with: + name: covreport-${{ matrix.os }}-py${{ matrix.python-version }}-${{ matrix.add-group }}-${{ matrix.resolution }} + path: ./.coverage* + include-hidden-files: true upload_coverage: if: always() - needs: [test, test-min-reqs, test-pydantic1] + needs: [test] uses: pyapp-kit/workflows/.github/workflows/upload-coverage.yml@v2 secrets: inherit @@ -97,7 +111,6 @@ jobs: dependency-repo: ${{ matrix.package }} dependency-ref: ${{ matrix.package-version }} dependency-extras: ${{ matrix.package-extras || 'testing' }} - host-extras: "testing" qt: pyqt5 python-version: "3.10" post-install-cmd: "python -m pip install pytest-pretty lxml_html_clean" # just for napari @@ -107,7 +120,7 @@ jobs: matrix: include: - package: napari/napari - pytest-args: napari/_tests/test_magicgui.py + pytest-args: src/napari/_tests/test_magicgui.py --import-mode=importlib - package: napari/napari package-version: "v0.4.19.post1" pytest-args: napari/_tests/test_magicgui.py @@ -116,34 +129,33 @@ jobs: package-extras: test pytest-args: package/tests/test_PartSeg/test_napari_widgets.py - deploy: + build-and-inspect-package: + name: Build & inspect package. needs: test runs-on: ubuntu-latest - if: ${{ github.repository == 'pyapp-kit/magicgui' && contains(github.ref, 'tags') }} - permissions: - id-token: write - contents: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.x" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -U setuptools setuptools_scm wheel twine build - - name: Build - run: | - git tag - python -m build - twine check dist/* - - name: Create Release - uses: softprops/action-gh-release@v2 + - uses: hynek/build-and-inspect-python-package@v2 + + upload-to-pypi: + if: success() && startsWith(github.ref, 'refs/tags/') && github.event_name != 'schedule' && github.repository == 'pyapp-kit/magicgui' + name: Upload package to PyPI + needs: build-and-inspect-package + runs-on: ubuntu-latest + permissions: + id-token: write + contents: write + steps: + - name: Download built artifact to dist/ + uses: actions/download-artifact@v4 with: - files: dist/* - body_path: ${{ github.workspace }}/CHANGELOG.md - - name: Publish to PyPI + name: Packages + path: dist + - name: ๐Ÿšข Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 + - uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + files: './dist/*' \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 046c94550..2830eefb3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,24 +5,24 @@ ci: repos: - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.23 + rev: v0.24.1 hooks: - id: validate-pyproject - repo: https://github.com/crate-ci/typos - rev: v1.30.0 + rev: v1.33.1 hooks: - id: typos - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.9 + rev: v0.11.12 hooks: - - id: ruff + - id: ruff-check args: ["--fix", "--unsafe-fixes"] - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.15.0 + rev: v1.16.0 hooks: - id: mypy files: "^src/" diff --git a/pyproject.toml b/pyproject.toml index b38411229..bfb954cb8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,10 @@ -# pyproject.toml [build-system] requires = ["hatchling", "hatch-vcs"] build-backend = "hatchling.build" +[tool.hatch.version] +source = "vcs" + # https://peps.python.org/pep-0621/ [project] name = "magicgui" @@ -31,76 +33,90 @@ classifiers = [ "Topic :: Software Development :: User Interfaces", "Topic :: Software Development :: Widget Sets", "Topic :: Utilities", + "Typing :: Typed", ] dynamic = ["version"] dependencies = [ "docstring_parser>=0.7", "psygnal>=0.8.0", - "qtpy>=1.7.0", - "superqt[iconify]>=0.6.1", + "qtpy>=2.4.0", + "superqt[iconify]>=0.7.2", "typing_extensions>=4.6", ] # extras # https://peps.python.org/pep-0621/#dependencies-optional-dependencies [project.optional-dependencies] -pyqt5 = ["pyqt5>=5.12.0"] -pyqt6 = ["pyqt6"] -pyside2 = [ - "pyside2>=5.15 ; python_version>='3.9'", +pyqt5 = [ + "PyQt5>=5.15.8", + "pyqt5-qt5<=5.15.2; sys_platform == 'win32'" ] -pyside6 = ["pyside6"] +pyqt6 = ["pyqt6>=6.4.0"] +pyside2 = ["pyside2>=5.15"] +pyside6 = ["pyside6>=6.4.0"] tqdm = ["tqdm>=4.30.0"] jupyter = ["ipywidgets>=8.0.0"] -image = ["pillow>=4.0"] +image = ["pillow>=10.4"] quantity = ["pint>=0.13.0"] + +[dependency-groups] +third-party-support = [ + "annotated-types>=0.7.0", + "attrs>=25.3.0", + "ipykernel>=6.29.5", + "matplotlib>=3.9.4", + "numpy>=1.26.4", + "pandas>=2.2.3; python_version >= '3.11'", + "pandas>=2.1", + "pydantic>=1.10.18", + "toolz>=1.0.0", +] test = [ - "pytest", - "pytest-qt", - "pytest-cov", - "pytest-mypy-plugins>=3", - "numpy", - "pandas", - "tqdm>=4.30.0", - "pillow>=4.0", - "pint>=0.13.0", - "matplotlib", - "toolz", - "ipywidgets", - "ipykernel", - "pydantic", - "attrs", - "annotated_types", + "magicgui[tqdm,jupyter,image,quantity]", + { include-group = "third-party-support" }, + "pytest>=8.4.0", + "pytest-cov >=6.1", + "pytest-mypy-plugins>=3.1", ] +test-qt = [{ include-group = "test" }, "pytest-qt >=4.3.0"] +pyqt5 = ["magicgui[pyqt5]", { include-group = "test-qt" }] +pyqt6 = ["magicgui[pyqt6]", { include-group = "test-qt" }] +pyside2 = ["magicgui[pyside2]", { include-group = "test-qt" }] +pyside6 = ["magicgui[pyside6]", { include-group = "test-qt" }] dev = [ - "ruff", - "ipython", - "mypy", - "pdbpp", - "pre-commit", - "pyqt6", - "rich", + { include-group = "test" }, + "ruff>=0.8.3", + "ipython>=8.18.0", + "mypy>=1.13.0", + "pdbpp>=0.11.6; sys_platform != 'win32'", + "pre-commit-uv>=4", + "pyqt6>=6.8.0", + "rich>=13.9.4", ] docs = [ - "mkdocs", + "mkdocs >=1.5.3", "mkdocs-material ~=9.5", "mkdocstrings ==0.26.1", "mkdocstrings-python ==1.11.1", "griffe ==1.2.0", "mkdocs-gen-files ==0.5.0", "mkdocs-literate-nav ==0.6.1", - "mkdocs-spellcheck[all]", + "mkdocs-spellcheck[all] >=1.1.1", "mkdocs-gallery ==0.10.3", "qtgallery ==0.0.2", # extras for all the widgets "napari ==0.5.3", - "pyqt6", - "pint", - "matplotlib", + "pyqt6 >=6.8.0", + "pint >=0.13.0", + "matplotlib >=3.9.4", "ipywidgets >=8.0.0", - "ipykernel", + "ipykernel>=6.29.5", ] +[tool.uv.sources] +magicgui = { workspace = true } + + [project.urls] homepage = "https://github.com/pyapp-kit/magicgui" repository = "https://github.com/pyapp-kit/magicgui" @@ -108,40 +124,14 @@ changelog = "https://github.com/pyapp-kit/magicgui/blob/main/CHANGELOG.md" issues = "https://github.com/pyapp-kit/magicgui/issues" documentation = "https://pyapp-kit.github.io/magicgui/" -[tool.hatch.version] -source = "vcs" - -[tool.hatch.envs.test] -features = ["test"] -[tool.hatch.envs.test.scripts] -run = "pytest -v --color=yes --cov-config=pyproject.toml --cov --cov-report=xml --cov-report=term-missing" -[[tool.hatch.envs.test.matrix]] -backend = ["pyqt5", "pyside2", "pyqt6", "pyside6"] -[tool.hatch.envs.test.overrides] -# matrix.deps.features = [ -# { value = "min-req", if = ["min-req"] }, -# ] -matrix.backend.features = [ - { value = "pyqt5", if = [ - "pyqt5", - ] }, - { value = "pyside2", if = [ - "pyside2", - ] }, - { value = "pyqt6", if = [ - "pyqt6", - ] }, - { value = "pyside6", if = [ - "pyside6", - ] }, -] - # https://docs.astral.sh/ruff [tool.ruff] line-length = 88 target-version = "py39" src = ["src", "tests"] +fix = true +# unsafe-fixes = true [tool.ruff.lint] pydocstyle = { convention = "numpy" } @@ -153,10 +143,10 @@ select = [ "D417", # Missing argument descriptions in Docstrings "I", # isort "UP", # pyupgrade - "C4", # flake8-comprehensions + "C4", # flake8-comprehensions "B", # flake8-bugbear "A001", # flake8-builtins - "TCH", # type checking + "TC", # type checking "TID", # tidy imports "RUF", # ruff-specific rules ] @@ -177,7 +167,8 @@ docstring-code-format = true # https://docs.pytest.org/en/6.2.x/customize.html [tool.pytest.ini_options] -minversion = "6.0" +minversion = "7.0" +addopts = ["--color=yes"] testpaths = ["tests"] filterwarnings = [ "error", @@ -185,6 +176,9 @@ filterwarnings = [ "ignore::DeprecationWarning:tqdm", "ignore::DeprecationWarning:docstring_parser", "ignore:distutils Version classes are deprecated:DeprecationWarning", + "ignore:Jupyter is migrating:DeprecationWarning", + "ignore:The `ipykernel.comm.Comm` class has been deprecated", + "ignore:.*read_binary is deprecated:", ] # https://mypy.readthedocs.io/en/stable/config_file.html @@ -245,3 +239,6 @@ ignore = [ [tool.typos.files] extend-exclude = ["CHANGELOG.md"] + +[tool.typos.default] +extend-ignore-identifiers-re = [] diff --git a/src/magicgui/schema/_ui_field.py b/src/magicgui/schema/_ui_field.py index a14c5abdd..40039a18b 100644 --- a/src/magicgui/schema/_ui_field.py +++ b/src/magicgui/schema/_ui_field.py @@ -828,7 +828,7 @@ def _get_values(obj: Any) -> dict | None: # named tuple if isinstance(obj, tuple) and hasattr(obj, "_asdict"): - return cast(dict, obj._asdict()) + return cast("dict", obj._asdict()) # dataclass if dc.is_dataclass(type(obj)): @@ -837,13 +837,13 @@ def _get_values(obj: Any) -> dict | None: # attrs attr = sys.modules.get("attr") if attr is not None and attr.has(obj): - return cast(dict, attr.asdict(obj)) + return cast("dict", attr.asdict(obj)) # pydantic models if hasattr(obj, "model_dump"): - return cast(dict, obj.model_dump()) + return cast("dict", obj.model_dump()) elif hasattr(obj, "dict"): - return cast(dict, obj.dict()) + return cast("dict", obj.dict()) return None diff --git a/src/magicgui/signature.py b/src/magicgui/signature.py index 573b5ca52..d67ed6ae3 100644 --- a/src/magicgui/signature.py +++ b/src/magicgui/signature.py @@ -17,7 +17,6 @@ import inspect import typing import warnings -from collections.abc import Sequence from types import MappingProxyType from typing import TYPE_CHECKING, Annotated, Any, Callable, cast @@ -26,6 +25,8 @@ from magicgui.types import Undefined if TYPE_CHECKING: + from collections.abc import Sequence + from typing_extensions import Unpack from magicgui.application import AppRef @@ -173,7 +174,7 @@ def __init__( @property def options(self) -> dict: """Return just this options part of the annotation.""" - return cast(dict, get_args(self.annotation)[1]) + return cast("dict", get_args(self.annotation)[1]) def __repr__(self) -> str: """Return __repr__, replacing NoneType if present.""" @@ -334,7 +335,7 @@ def replace( # type: ignore[override] return_annotation = self.return_annotation return type(self)( - cast(Sequence[inspect.Parameter], parameters), + cast("Sequence[inspect.Parameter]", parameters), return_annotation=return_annotation, ) diff --git a/src/magicgui/tqdm.py b/src/magicgui/tqdm.py index 8cddf1173..55bf49c37 100644 --- a/src/magicgui/tqdm.py +++ b/src/magicgui/tqdm.py @@ -148,7 +148,7 @@ def close(self) -> None: if not self._in_visible_gui: super().close() return - self._mgui = cast(FunctionGui, self._mgui) + self._mgui = cast("FunctionGui", self._mgui) if self.disable: return diff --git a/src/magicgui/type_map/_type_map.py b/src/magicgui/type_map/_type_map.py index 8f62f0eb2..c1b68184d 100644 --- a/src/magicgui/type_map/_type_map.py +++ b/src/magicgui/type_map/_type_map.py @@ -501,7 +501,7 @@ def create_widget( if issubclass(wdg_class, widgets.Widget): widget = wdg_class(**{**kwargs, **opts, **options_}) if param_kind: - widget.param_kind = param_kind # type: ignore + widget.param_kind = param_kind return widget # pick the appropriate subclass for the given protocol @@ -513,8 +513,8 @@ def create_widget( cls = getattr(widgets.bases, f"{p}Widget") widget = cls(**{**kwargs, **(options_ or {}), "widget_type": wdg_class}) if param_kind: - widget.param_kind = param_kind # type: ignore - return widget + widget.param_kind = param_kind + return widget # type: ignore[no-any-return] raise TypeError(f"{wdg_class!r} does not implement any known widget protocols") @@ -1013,7 +1013,7 @@ def _register_widget( widget_type: WidgetRef | None = None, **options: Any, ) -> WidgetTuple | None: - _options = cast(dict, options) + _options = cast("dict", options) previous_widget = self._type_defs.get(resolved_type) @@ -1074,7 +1074,9 @@ def inner_func(func: Callable) -> widgets.FunctionGui | MagicFactory: ) # MagicFactory is unnecessary if we are immediately instantiating the # widget, so we shortcut that and just return the FunctionGui here. - return cast(widgets.FunctionGui, magic_class(func, type_map=self, **kwargs)) + return cast( + "widgets.FunctionGui", magic_class(func, type_map=self, **kwargs) + ) return inner_func if function is None else inner_func(function) @@ -1160,7 +1162,7 @@ def _import_wdg_class(class_name: str) -> WidgetClass: mod_name, name = class_name.rsplit(".", 1) mod = importlib.import_module(mod_name) - return cast(WidgetClass, getattr(mod, name)) + return cast("WidgetClass", getattr(mod, name)) def _validate_return_callback(func: Callable) -> None: @@ -1176,4 +1178,4 @@ def _generate_union_variants(type_: Any) -> Iterator[type]: type_args = get_args(type_) for i in range(2, len(type_args) + 1): for per in itertools.combinations(type_args, i): - yield cast(type, Union[per]) + yield cast("type", Union[per]) diff --git a/src/magicgui/widgets/_concrete.py b/src/magicgui/widgets/_concrete.py index d9d7b066f..d5c7240d8 100644 --- a/src/magicgui/widgets/_concrete.py +++ b/src/magicgui/widgets/_concrete.py @@ -233,8 +233,7 @@ def decrement(self, val: float | None = None) -> None: """Decrease current value by step size, or provided value.""" self.value = self.get_value() - (val if val is not None else self.step) - # overriding because at least some backends don't have a step value for ProgressBar - @property # type: ignore + @property def step(self) -> float: """Step size for widget values.""" return self._step @@ -745,7 +744,7 @@ def _append_value(self, value: _V | _Undefined = Undefined) -> None: name=f"value_{i}", options=self._child_options, ) - widget = _ListEditChildWidget(cast(BaseValueWidget, _value_widget)) + widget = _ListEditChildWidget(cast("BaseValueWidget", _value_widget)) # connect the minus-button-clicked event def _remove_me() -> None: @@ -844,7 +843,7 @@ def __setitem__(self, key: slice, value: _V | Iterable[_V]) -> None: ... def __setitem__(self, key: int | slice, value: _V | Iterable[_V]) -> None: """Update widget value.""" if isinstance(key, int): - self._obj._get_child_widget(key).value = cast(_V, value) + self._obj._get_child_widget(key).value = cast("_V", value) elif isinstance(key, slice): with self._obj.changed.blocked(): if isinstance(value, type(self._obj._get_child_widget(0).value)): @@ -926,7 +925,7 @@ def __init__( for a in _value: i = len(self) widget = cast( - BaseValueWidget, + "BaseValueWidget", create_widget( value=a, annotation=self._args_types[i], diff --git a/src/magicgui/widgets/_function_gui.py b/src/magicgui/widgets/_function_gui.py index 55279b325..12b59d26e 100644 --- a/src/magicgui/widgets/_function_gui.py +++ b/src/magicgui/widgets/_function_gui.py @@ -25,7 +25,6 @@ from magicgui._type_resolution import resolve_single_type from magicgui.signature import MagicSignature, magic_signature from magicgui.widgets import Container, MainWindow, ProgressBar, PushButton -from magicgui.widgets.bases import BaseValueWidget if TYPE_CHECKING: from collections.abc import Iterator @@ -36,6 +35,7 @@ from magicgui.application import Application, AppRef # noqa: F401 from magicgui.type_map import TypeMap from magicgui.widgets import TextEdit + from magicgui.widgets.bases import BaseValueWidget from magicgui.widgets.protocols import ContainerProtocol, MainWindowProtocol _P = ParamSpec("_P") @@ -229,7 +229,7 @@ def __init__( @self._call_button.changed.connect def _disable_button_and_call() -> None: # disable the call button until the function has finished - self._call_button = cast(PushButton, self._call_button) + self._call_button = cast("PushButton", self._call_button) self._call_button.enabled = False try: self.__call__() # type: ignore [call-arg] @@ -241,7 +241,7 @@ def _disable_button_and_call() -> None: self._result_widget: BaseValueWidget | None = None if result_widget: self._result_widget = cast( - BaseValueWidget, + "BaseValueWidget", type_map.create_widget( value=None, annotation=self._return_annotation, diff --git a/src/magicgui/widgets/_table.py b/src/magicgui/widgets/_table.py index 98c0f1c48..020a36395 100644 --- a/src/magicgui/widgets/_table.py +++ b/src/magicgui/widgets/_table.py @@ -267,8 +267,8 @@ def set_value(self, value: TableData) -> None: nc = len(data[0]) # type: ignore except (TypeError, IndexError): nc = 0 - self.column_headers = tuple(columns) or range(nc) # type:ignore - self.row_headers = tuple(index) or range(len(data)) # type: ignore + self.column_headers = tuple(columns) or range(nc) + self.row_headers = tuple(index) or range(len(data)) for row, d in enumerate(data): self._set_rowi(row, d) @@ -364,7 +364,7 @@ def shape(self) -> tuple[int, int]: @property def size(self) -> int: """Return shape of table widget (rows, cols).""" - return cast(int, operator.mul(*self.shape)) + return cast("int", operator.mul(*self.shape)) def keys(self, axis: str = "column") -> HeadersView[TblKey]: """Return a set-like object providing a view on this table's headers.""" diff --git a/src/magicgui/widgets/bases/_categorical_widget.py b/src/magicgui/widgets/bases/_categorical_widget.py index ff39d8a0e..c5263e106 100644 --- a/src/magicgui/widgets/bases/_categorical_widget.py +++ b/src/magicgui/widgets/bases/_categorical_widget.py @@ -95,7 +95,7 @@ def reset_choices(self, *_: Any) -> None: choices as when the widget was instantiated, if the callable relies on external state. """ - self.choices = self._default_choices # type: ignore + self.choices = self._default_choices @property def current_choice(self) -> str: @@ -108,7 +108,7 @@ def __len__(self) -> int: def get_choice(self, choice_name: str) -> T: """Get data for the provided ``choice_name``.""" - return cast(T, self._widget._mgui_get_choice(choice_name)) + return cast("T", self._widget._mgui_get_choice(choice_name)) def set_choice(self, choice_name: str, data: Any | None = None) -> None: """Set data for the provided ``choice_name``.""" diff --git a/src/magicgui/widgets/bases/_container_widget.py b/src/magicgui/widgets/bases/_container_widget.py index 9415b2ede..9a2ebc31f 100644 --- a/src/magicgui/widgets/bases/_container_widget.py +++ b/src/magicgui/widgets/bases/_container_widget.py @@ -265,7 +265,7 @@ def __init__( **base_widget_kwargs, ) if value is not Undefined: - self.value = cast(T, value) + self.value = cast("T", value) if self._bound_value is not Undefined and "visible" not in base_widget_kwargs: self.hide() diff --git a/src/magicgui/widgets/bases/_ranged_widget.py b/src/magicgui/widgets/bases/_ranged_widget.py index 2e419d267..de48f59c0 100644 --- a/src/magicgui/widgets/bases/_ranged_widget.py +++ b/src/magicgui/widgets/bases/_ranged_widget.py @@ -73,7 +73,7 @@ def __init__( self.step = None self._widget._mgui_set_step(1) else: - self.step = cast(float, step) + self.step = cast("float", step) self.min, self.max = self._init_range(value, min, max) if value is not None and not isinstance(value, _Undefined): diff --git a/src/magicgui/widgets/bases/_value_widget.py b/src/magicgui/widgets/bases/_value_widget.py index b143ae87e..31fa91bf3 100644 --- a/src/magicgui/widgets/bases/_value_widget.py +++ b/src/magicgui/widgets/bases/_value_widget.py @@ -58,7 +58,7 @@ def __init__( self._call_bound: bool = True super().__init__(**base_widget_kwargs) if value is not Undefined: - self.value = cast(T, value) + self.value = cast("T", value) if self._bound_value is not Undefined and "visible" not in base_widget_kwargs: self.hide() @@ -95,7 +95,7 @@ def value(self) -> T: "access `widget.value` in your bound callback, use " "`widget.get_value()`" ) from e - return cast(T, self._bound_value) + return cast("T", self._bound_value) return self.get_value() @value.setter @@ -200,7 +200,7 @@ def get_value(self) -> T: an escape hatch if trying to access the widget's value inside of a callback bound to self._bound_value. """ - return cast(T, self._widget._mgui_get_value()) + return cast("T", self._widget._mgui_get_value()) def set_value(self, value: Any) -> None: self._widget._mgui_set_value(value)