refactor(ci): remove mypy (#1371)

* refactor(ci): remove mypy

* fix(ci): restore 'type: ignore' comment for st_birthtime property
This commit is contained in:
Travis Abendshien
2026-05-14 08:24:26 -04:00
committed by GitHub
parent 2ab8203967
commit 41f9749814
24 changed files with 84 additions and 137 deletions

View File

@@ -1,36 +0,0 @@
# SPDX-FileCopyrightText: (c) TagStudio Contributors
# SPDX-License-Identifier: GPL-3.0-only
---
name: MyPy
on: [push, pull_request]
jobs:
mypy:
name: Run MyPy
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup reviewdog
uses: reviewdog/action-setup@v1
with:
reviewdog_version: latest
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Install Python dependencies
run: |
python -m pip install --upgrade uv
uv pip install --system .[mypy]
- name: Execute MyPy
uses: tsuyoshicho/action-mypy@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
fail_on_error: true

View File

@@ -2,9 +2,9 @@
repos:
- repo: local
hooks:
- id: mypy
name: mypy
entry: mypy
- id: pyright
name: pyright
entry: pyright
language: system
types_or: [python, pyi]
require_serial: true

View File

@@ -27,7 +27,7 @@ Thank you so much for showing interest in contributing to TagStudio! Here are a
- I've read the [FAQ](https://github.com/TagStudioDev/TagStudio/blob/main/README.md#faq)
- I've checked the project's [Issues](https://github.com/TagStudioDev/TagStudio/issues) and [Pull Requests](https://github.com/TagStudioDev/TagStudio/pulls)
- **I've created a new issue for my feature/fix _before_ starting work on it**, or have at least notified others in the relevant existing issue(s) of my intention to work on it
- I've set up my development environment including Ruff, Mypy, and PyTest
- I've set up my development environment including Ruff, Pyright, and Pytest
- I've read the CONTRIBUTING.md/Contributing page on the documentation site as well as the and/or [Style Guide](style.md)
- **_I mean it, I've found or created an issue for my feature/fix!_**
@@ -75,7 +75,11 @@ When pushing your code, several automated workflows will check it against predef
### [Ruff](https://github.com/astral-sh/ruff)
A Python linter and code formatter. Ruff uses the `pyproject.toml` as its config file and runs whenever code is pushed or pulled into the project.
Ruff is a Python linter and code formatter that helps enforce a consistent formatting style across our codebase.
Ruff is installed alongside the `pip install -e ".[dev]"` command, but is also available as a VS Code [extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff), PyCharm [plugin](https://plugins.jetbrains.com/plugin/20574-ruff), and [more](https://docs.astral.sh/ruff/integrations/).
Our Ruff workflow runs whenever a pull request is opened or commits are pushed. Note that this workflow **does NOT** format code, but just serves to report any issues that have not been addressed by that point.
#### Running Locally
@@ -87,24 +91,31 @@ Inside the root repository directory:
Ruff should automatically discover the configuration options inside the [pyproject.toml](https://github.com/TagStudioDev/TagStudio/blob/main/pyproject.toml) file. For more information, see the [ruff configuration discovery docs](https://docs.astral.sh/ruff/configuration/#config-file-discovery).
Ruff is also available as a VS Code [extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff), PyCharm [plugin](https://plugins.jetbrains.com/plugin/20574-ruff), and [more](https://docs.astral.sh/ruff/integrations/).
### [Pyright](https://github.com/microsoft/pyright)
### [Mypy](https://github.com/python/mypy)
Pyright is a static type checker for Python that helps enforce type strictness and prevent easy-to-miss errors across our codebase.
Mypy is a static type checker for Python. It sure has a lot to say sometimes, but we recommend you take its advice when possible. Mypy also uses the `pyproject.toml` as its config file and runs whenever code is pushed or pulled into the project.
Pyright is installed alongside the `pip install -e ".[dev]"` command, but is also available as VS Code extensions (see the [Pyright](https://marketplace.visualstudio.com/items?itemName=ms-pyright.pyright), [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance), and [basedpyright](https://marketplace.visualstudio.com/items?itemName=detachhead.basedpyright) extensions), a PyCharm [setting](https://www.jetbrains.com/help/pycharm/lsp-tools.html#pyright), or in the form of forks such as [basedpyright](https://docs.basedpyright.com/latest/).
Our Pyright workflow runs whenever a pull request is opened or commits are pushed. Note that this **does NOT** format code or fix issues, but just serves to report any issues that have not been addressed by that point.
#### Running Locally
- **(First time only)** Run the following:
- `mkdir -p .mypy_cache`
- `mypy --install-types --non-interactive`
- You can now check code by running `mypy --config-file pyproject.toml .` in the repository root. _(Don't forget the "." at the end!)_
- Run `pyright` to check code
Mypy is also available as a VS Code [extension](https://marketplace.visualstudio.com/items?itemName=matangover.mypy), PyCharm [plugin](https://plugins.jetbrains.com/plugin/11086-mypy), and [more](https://plugins.jetbrains.com/plugin/11086-mypy).
Pyright/basedpyright should automatically discover the configuration options inside the [pyproject.toml](https://github.com/TagStudioDev/TagStudio/blob/main/pyproject.toml) file.
### PyTest
### [Pytest](https://github.com/pytest-dev/pytest)
- Run all tests by running `pytest tests/` in the repository root.
Pytest is our testing suite and runs all our Python code against the tests in the [`tests/`](https://github.com/TagStudioDev/TagStudio/tree/main/tests) directory.
Pytest is installed alongside the `pip install -e ".[dev]"` command.
Our Pytest workflow runs whenever a pull request is opened or commits are pushed.
#### Running Locally
- Run all tests by running `pytest tests/` in the repository root
## Code Style

View File

@@ -146,7 +146,7 @@ The entry point for TagStudio is `src/tagstudio/main.py`. You can target this fi
### pre-commit
There is a [pre-commit](https://pre-commit.com/) configuration that will run through some checks before code is committed. Namely, mypy and the Ruff linter and formatter will check your code, catching those nits right away.
There is a [pre-commit](https://pre-commit.com/) configuration that will run through some checks before code is committed. Namely Pyright and Ruff will check your code, catching those nits right away.
Once you have pre-commit installed, just run:

View File

@@ -42,9 +42,8 @@ dependencies = [
]
[project.optional-dependencies]
dev = ["tagstudio[mkdocs,mypy,pre-commit,pyinstaller,pytest,ruff]"]
dev = ["tagstudio[mkdocs,pyright,pre-commit,pyinstaller,pytest,ruff]"]
mkdocs = ["mkdocs-material[imaging]>=9.6.14", "mkdocs-redirects~=1.2"]
mypy = ["mypy==1.15.0", "mypy-extensions==1.*", "types-ujson~=5.10"]
pyright = ["pyright~=1.1.409"]
pre-commit = ["pre-commit~=4.2"]
pyinstaller = ["Pyinstaller~=6.13"]
@@ -63,31 +62,6 @@ tagstudio = "tagstudio.main:main"
[tool.hatch.build.targets.wheel]
packages = ["src/tagstudio"]
[tool.mypy]
mypy_path = ["src/tagstudio"]
disable_error_code = [
"annotation-unchecked",
"func-returns-value",
"import-untyped",
]
explicit_package_bases = true
ignore_missing_imports = true
implicit_optional = true
strict_optional = false
warn_unused_ignores = true
exclude = ["build", "dist"]
[[tool.mypy.overrides]]
module = "tagstudio.qt.main_window"
ignore_errors = true
[[tool.mypy.overrides]]
module = "tagstudio.qt.ui.home_ui"
ignore_errors = true
[[tool.mypy.overrides]]
module = "tagstudio.core.ts_core"
ignore_errors = true
[tool.pytest.ini_options]
#addopts = "-m 'not qt'"

View File

@@ -32,7 +32,7 @@ class BaseField(Base):
@declared_attr
def entry(self) -> Mapped[Entry]:
return relationship(foreign_keys=[self.entry_id]) # type: ignore # pyright: ignore[reportArgumentType]
return relationship(foreign_keys=[self.entry_id]) # pyright: ignore[reportArgumentType]
@property
def class_name(self) -> str:

View File

@@ -957,7 +957,7 @@ class Library:
)
statement = statement.distinct()
entries: ScalarResult[Entry] | list[Entry] = session.execute(statement).scalars()
entries = entries.unique() # type: ignore
entries = entries.unique()
entry_order_dict = {e_id: order for order, e_id in enumerate(entry_ids)}
entries = sorted(entries, key=lambda e: entry_order_dict[e.id])
@@ -1414,7 +1414,7 @@ class Library:
if tag:
tags.append(tag.id)
else:
new = session.add(Tag(name=string)) # type: ignore
new = session.add(Tag(name=string))
if new:
tags.append(new.id)
session.flush()

View File

@@ -47,21 +47,21 @@ class SQLBoolExpressionBuilder(BaseVisitor[ColumnElement[bool]]):
self.lib = lib
@override
def visit_or_list(self, node: ORList) -> ColumnElement[bool]: # type: ignore
def visit_or_list(self, node: ORList) -> ColumnElement[bool]:
tag_ids, bool_expressions = self.__separate_tags(node.elements, only_single=False)
if len(tag_ids) > 0:
bool_expressions.append(self.__entry_has_any_tags(tag_ids))
return or_(*bool_expressions)
@override
def visit_and_list(self, node: ANDList) -> ColumnElement[bool]: # type: ignore
def visit_and_list(self, node: ANDList) -> ColumnElement[bool]:
tag_ids, bool_expressions = self.__separate_tags(node.terms, only_single=True)
if len(tag_ids) > 0:
bool_expressions.append(self.__entry_has_all_tags(tag_ids))
return and_(*bool_expressions)
@override
def visit_constraint(self, node: Constraint) -> ColumnElement[bool]: # type: ignore
def visit_constraint(self, node: Constraint) -> ColumnElement[bool]:
"""Returns a Boolean Expression that is true, if the Entry satisfies the constraint."""
if len(node.properties) != 0:
raise NotImplementedError("Properties are not implemented yet") # TODO TSQLANG
@@ -113,11 +113,11 @@ class SQLBoolExpressionBuilder(BaseVisitor[ColumnElement[bool]]):
raise NotImplementedError("This type of constraint is not implemented yet")
@override
def visit_property(self, node: Property) -> ColumnElement[bool]: # type: ignore
def visit_property(self, node: Property) -> ColumnElement[bool]:
raise NotImplementedError("This should never be reached!")
@override
def visit_not(self, node: Not) -> ColumnElement[bool]: # type: ignore
def visit_not(self, node: Not) -> ColumnElement[bool]:
return ~self.visit(node.child)
def __get_tag_ids(self, tag_name: str, include_children: bool = True) -> list[int]:

View File

@@ -88,6 +88,6 @@ class FixIgnoredEntriesModal(FixIgnoredEntriesModalView):
self.driver.library_info_window.update_cleanup()
@override
def showEvent(self, event: QtGui.QShowEvent) -> None: # type: ignore
def showEvent(self, event: QtGui.QShowEvent) -> None:
self.update_ignored_count()
return super().showEvent(event)

View File

@@ -46,6 +46,6 @@ class IgnoreModal(IgnoreModalView):
Ignore.write_ignore_file(self.lib.library_dir, lines)
@override
def showEvent(self, event: QShowEvent) -> None: # type: ignore
def showEvent(self, event: QShowEvent) -> None:
self.__load_file()
return super().showEvent(event)

View File

@@ -162,6 +162,6 @@ class LibraryInfoWindow(LibraryInfoWindowView):
return size
@override
def showEvent(self, event: QtGui.QShowEvent): # type: ignore
def showEvent(self, event: QtGui.QShowEvent):
self.refresh()
return super().showEvent(event)

View File

@@ -34,7 +34,7 @@ class TagBoxWidget(TagBoxWidgetView):
self.__entries = entries
@override
def _on_click(self, tag: Tag) -> None: # type: ignore[misc]
def _on_click(self, tag: Tag) -> None:
match self.__driver.settings.tag_click_action:
case TagClickActionOption.OPEN_EDIT:
self._on_edit(tag)
@@ -58,7 +58,7 @@ class TagBoxWidget(TagBoxWidgetView):
)
@override
def _on_remove(self, tag: Tag) -> None: # type: ignore[misc]
def _on_remove(self, tag: Tag) -> None:
logger.info(
"[TagBoxWidget] remove_tag",
selected=self.__entries,
@@ -70,7 +70,7 @@ class TagBoxWidget(TagBoxWidgetView):
self.on_update.emit()
@override
def _on_edit(self, tag: Tag) -> None: # type: ignore[misc]
def _on_edit(self, tag: Tag) -> None:
build_tag_panel = BuildTagPanel(self.__driver.lib, tag=tag)
edit_modal = PanelModal(
@@ -92,7 +92,7 @@ class TagBoxWidget(TagBoxWidgetView):
edit_modal.show()
@override
def _on_search(self, tag: Tag) -> None: # type: ignore[misc]
def _on_search(self, tag: Tag) -> None:
self.__driver.main_window.search_field.setText(f"tag_id:{tag.id}")
self.__driver.update_browsing_state(
BrowsingState.from_tag_id(tag.id, self.__driver.browsing_history.current)

View File

@@ -138,7 +138,7 @@ class ColorBoxWidget(FieldWidget):
)
self.edit_modal.saved.connect(
lambda: (self.lib.update_color(*build_color_panel.build_color()), self.updated.emit()) # type: ignore
lambda: (self.lib.update_color(*build_color_panel.build_color()), self.updated.emit())
)
self.edit_modal.show()

View File

@@ -114,6 +114,7 @@ class FileAttributes(QWidget):
if filepath and filepath.is_file():
created: dt
if platform.system() == "Windows" or platform.system() == "Darwin":
# NOTE: Accessing stat().st_birthtime causes linter checks to fail on some systems.
created = dt.fromtimestamp(filepath.stat().st_birthtime) # type: ignore[attr-defined, unused-ignore]
else:
created = dt.fromtimestamp(filepath.stat().st_ctime)

View File

@@ -467,12 +467,12 @@ class ItemThumb(FlowWidget):
badge.setHidden(is_hidden)
@override
def enterEvent(self, event: QEnterEvent) -> None: # type: ignore[misc]
def enterEvent(self, event: QEnterEvent) -> None:
self.show_check_badges(show=True)
return super().enterEvent(event)
@override
def leaveEvent(self, event: QEvent) -> None: # type: ignore[misc]
def leaveEvent(self, event: QEvent) -> None:
self.show_check_badges(show=False)
return super().leaveEvent(event)
@@ -502,7 +502,7 @@ class ItemThumb(FlowWidget):
)
@override
def mouseMoveEvent(self, event: QMouseEvent) -> None: # type: ignore[misc]
def mouseMoveEvent(self, event: QMouseEvent) -> None:
if event.buttons() is not Qt.MouseButton.LeftButton:
return

View File

@@ -324,7 +324,7 @@ class MediaPlayer(QGraphicsView):
"""Manage events for the media player."""
if (
arg__2.type() == QEvent.Type.MouseButtonPress
and arg__2.button() == Qt.MouseButton.LeftButton # type: ignore
and arg__2.button() == Qt.MouseButton.LeftButton # pyright: ignore[reportAttributeAccessIssue]
):
if arg__1 == self.play_pause:
self.toggle_play()

View File

@@ -364,7 +364,7 @@ class JsonMigrationModal(QObject):
iterator = FunctionIterator(self.migration_iterator)
iterator.value.connect(
lambda x: (
pb.setLabelText(f"<h4>{x}</h4>"), # type: ignore
pb.setLabelText(f"<h4>{x}</h4>"),
self.update_sql_value_ui(show_msg_box=False)
if x == Translations["json_migration.checking_for_parity"]
else (),
@@ -377,8 +377,8 @@ class JsonMigrationModal(QObject):
r.done.connect(
lambda: (
self.update_sql_value_ui(show_msg_box=not skip_ui),
pb.setMinimum(1), # type: ignore
pb.setValue(1), # type: ignore
pb.setMinimum(1),
pb.setValue(1),
# Enable the finish button
cast(QPushButtonWrapper, self.stack[1].buttons[4]).setDisabled(False),
)
@@ -483,26 +483,26 @@ class JsonMigrationModal(QObject):
def update_json_entry_count(self, value: int):
self.old_entry_count = value
label: QLabel = self.old_content_layout.itemAtPosition(self.entries_row, 1).widget() # type:ignore
label: QLabel = self.old_content_layout.itemAtPosition(self.entries_row, 1).widget() # pyright: ignore[reportAssignmentType]
label.setText(self.color_value_default(value))
def update_json_tag_count(self, value: int):
self.old_tag_count = value
label: QLabel = self.old_content_layout.itemAtPosition(self.tags_row, 1).widget() # type:ignore
label: QLabel = self.old_content_layout.itemAtPosition(self.tags_row, 1).widget() # pyright: ignore[reportAssignmentType]
label.setText(self.color_value_default(value))
def update_sql_value(self, row: int, value: int | bool, old_value: int | bool):
label: QLabel = self.new_content_layout.itemAtPosition(row, 1).widget() # type:ignore
warning_icon: QLabel = self.new_content_layout.itemAtPosition(row, 2).widget() # type:ignore
label: QLabel = self.new_content_layout.itemAtPosition(row, 1).widget() # pyright: ignore[reportAssignmentType]
warning_icon: QLabel = self.new_content_layout.itemAtPosition(row, 2).widget() # pyright: ignore[reportAssignmentType]
label.setText(self.color_value_conditional(old_value, value))
warning_icon.setText("" if old_value == value else self.warning)
def update_parity_value(self, row: int, value: bool):
result: str = self.match_text if value else self.differ_text
old_label: QLabel = self.old_content_layout.itemAtPosition(row, 1).widget() # type:ignore
new_label: QLabel = self.new_content_layout.itemAtPosition(row, 1).widget() # type:ignore
old_warning_icon: QLabel = self.old_content_layout.itemAtPosition(row, 2).widget() # type:ignore
new_warning_icon: QLabel = self.new_content_layout.itemAtPosition(row, 2).widget() # type:ignore
old_label: QLabel = self.old_content_layout.itemAtPosition(row, 1).widget() # pyright: ignore[reportAssignmentType]
new_label: QLabel = self.new_content_layout.itemAtPosition(row, 1).widget() # pyright: ignore[reportAssignmentType]
old_warning_icon: QLabel = self.old_content_layout.itemAtPosition(row, 2).widget() # pyright: ignore[reportAssignmentType]
new_warning_icon: QLabel = self.new_content_layout.itemAtPosition(row, 2).widget() # pyright: ignore[reportAssignmentType]
old_label.setText(self.color_value_conditional(self.match_text, result))
new_label.setText(self.color_value_conditional(self.match_text, result))
old_warning_icon.setText("" if value else self.warning)

View File

@@ -218,7 +218,7 @@ class Pagination(QWidget):
if i < index:
if (i != 0) and i >= index - 4:
self.start_buffer_layout.itemAt(i - start_offset).widget().setHidden(False)
self.start_buffer_layout.itemAt(i - start_offset).widget().setText( # type: ignore
self.start_buffer_layout.itemAt(i - start_offset).widget().setText( # pyright: ignore[reportAttributeAccessIssue]
str(i + 1)
)
self._assign_click(
@@ -237,9 +237,7 @@ class Pagination(QWidget):
elif i > index:
if i != page_count - 1 and i <= index + 4:
self.end_buffer_layout.itemAt(i - end_offset).widget().setHidden(False)
self.end_buffer_layout.itemAt(i - end_offset).widget().setText( # type: ignore
str(i + 1)
)
self.end_buffer_layout.itemAt(i - end_offset).widget().setText(str(i + 1)) # pyright: ignore[reportAttributeAccessIssue]
self._assign_click(
cast(
QPushButtonWrapper,

View File

@@ -62,6 +62,6 @@ class ProgressWidget(QWidget):
r = CustomRunnable(lambda: iterator.run())
r.done.connect(
lambda: (self.hide(), self.deleteLater(), [callback() for callback in done_callbacks]) # type: ignore
lambda: (self.hide(), self.deleteLater(), [callback() for callback in done_callbacks])
)
QThreadPool.globalInstance().start(r)

View File

@@ -59,15 +59,15 @@ class TagDatabasePanel(TagSearchPanel):
return
message_box = QMessageBox(
QMessageBox.Question, # type: ignore
QMessageBox.Question, # pyright: ignore[reportAttributeAccessIssue]
Translations["tag.remove"],
Translations.format("tag.confirm_delete", tag_name=self.lib.tag_display_name(tag)),
QMessageBox.Ok | QMessageBox.Cancel, # type: ignore
QMessageBox.Ok | QMessageBox.Cancel, # pyright: ignore[reportAttributeAccessIssue]
)
result = message_box.exec()
if result != QMessageBox.Ok: # type: ignore
if result != QMessageBox.Ok: # pyright: ignore[reportAttributeAccessIssue]
return
self.lib.remove_tag(tag.id)

View File

@@ -299,7 +299,7 @@ class ThumbRenderer(QObject):
im: Image.Image = Image.new(
mode="L",
size=tuple([d * smooth_factor for d in size]), # type: ignore # pyright: ignore[reportArgumentType]
size=tuple([d * smooth_factor for d in size]), # pyright: ignore[reportArgumentType]
color="black",
)
draw = ImageDraw.Draw(im)
@@ -330,7 +330,7 @@ class ThumbRenderer(QObject):
# Highlight
im_hl: Image.Image = Image.new(
mode="RGBA",
size=tuple([d * smooth_factor for d in size]), # type: ignore # pyright: ignore[reportArgumentType]
size=tuple([d * smooth_factor for d in size]), # pyright: ignore[reportArgumentType]
color="#00000000",
)
draw = ImageDraw.Draw(im_hl)
@@ -349,7 +349,7 @@ class ThumbRenderer(QObject):
# Shadow
im_sh: Image.Image = Image.new(
mode="RGBA",
size=tuple([d * smooth_factor for d in size]), # type: ignore # pyright: ignore[reportArgumentType]
size=tuple([d * smooth_factor for d in size]), # pyright: ignore[reportArgumentType]
color="#00000000",
)
draw = ImageDraw.Draw(im_sh)
@@ -394,7 +394,7 @@ class ThumbRenderer(QObject):
# Create larger blank image based on smooth_factor
im: Image.Image = Image.new(
"RGBA",
size=tuple([d * smooth_factor for d in size]), # type: ignore # pyright: ignore[reportArgumentType]
size=tuple([d * smooth_factor for d in size]), # pyright: ignore[reportArgumentType]
color="#FF000000",
)
@@ -402,13 +402,13 @@ class ThumbRenderer(QObject):
bg: Image.Image
bg = Image.new(
"RGB",
size=tuple([d * smooth_factor for d in size]), # type: ignore # pyright: ignore[reportArgumentType]
size=tuple([d * smooth_factor for d in size]), # pyright: ignore[reportArgumentType]
color="#000000FF",
)
# Use a background image if provided
if bg_image:
bg_im = Image.Image.resize(bg_image, size=tuple([d * smooth_factor for d in size])) # type: ignore # pyright: ignore[reportArgumentType]
bg_im = Image.Image.resize(bg_image, size=tuple([d * smooth_factor for d in size])) # pyright: ignore[reportArgumentType]
bg_im = ImageEnhance.Brightness(bg_im).enhance(0.3) # Reduce the brightness
bg.paste(bg_im)
@@ -417,7 +417,7 @@ class ThumbRenderer(QObject):
bg,
(0, 0),
mask=self._get_mask(
tuple([d * smooth_factor for d in size]), # type: ignore # pyright: ignore[reportArgumentType]
tuple([d * smooth_factor for d in size]), # pyright: ignore[reportArgumentType]
(pixel_ratio * smooth_factor),
),
)
@@ -501,19 +501,19 @@ class ThumbRenderer(QObject):
# Create larger blank image based on smooth_factor
im: Image.Image = Image.new(
"RGBA",
size=tuple([d * smooth_factor for d in size]), # type: ignore # pyright: ignore[reportArgumentType]
size=tuple([d * smooth_factor for d in size]), # pyright: ignore[reportArgumentType]
color="#00000000",
)
bg: Image.Image
# Use a background image if provided
if bg_image:
bg = Image.Image.resize(bg_image, size=tuple([d * smooth_factor for d in size])) # type: ignore # pyright: ignore[reportArgumentType]
bg = Image.Image.resize(bg_image, size=tuple([d * smooth_factor for d in size])) # pyright: ignore[reportArgumentType]
# Create solid background color
else:
bg = Image.new(
"RGB",
size=tuple([d * smooth_factor for d in size]), # type: ignore # pyright: ignore[reportArgumentType]
size=tuple([d * smooth_factor for d in size]), # pyright: ignore[reportArgumentType]
color="#000000",
)
# Apply color overlay
@@ -527,7 +527,7 @@ class ThumbRenderer(QObject):
bg,
(0, 0),
mask=self._get_mask(
tuple([d * smooth_factor for d in size]), # type: ignore # pyright: ignore[reportArgumentType]
tuple([d * smooth_factor for d in size]), # pyright: ignore[reportArgumentType]
(pixel_ratio * smooth_factor),
),
)
@@ -1216,7 +1216,7 @@ class ThumbRenderer(QObject):
# Write the image to a buffer as png
buffer: QBuffer = QBuffer()
buffer.open(QBuffer.OpenModeFlag.ReadWrite)
q_image.save(device=buffer, format="PNG") # type: ignore # pyright: ignore[reportArgumentType]
q_image.save(device=buffer, format="PNG") # pyright: ignore[reportArgumentType]
# Load the image from the buffer
im = Image.new("RGB", (size, size), color="#1e1e1e")
@@ -1338,7 +1338,7 @@ class ThumbRenderer(QObject):
buffer: QBuffer = QBuffer()
buffer.open(QBuffer.OpenModeFlag.ReadWrite)
try:
q_image.save(buffer, "PNG") # type: ignore # pyright: ignore
q_image.save(buffer, "PNG") # pyright: ignore
im = Image.open(BytesIO(buffer.buffer().data()))
finally:
buffer.close()

View File

@@ -2,7 +2,6 @@
# SPDX-FileCopyrightText: (c) TagStudio Contributors
# SPDX-License-Identifier: GPL-3.0-only
# Vendored from pydub
# type: ignore
import array

View File

@@ -350,7 +350,7 @@ class QtDriver(DriverMixin, QObject):
if os.name == "nt":
appid = "cyanvoxel.tagstudio.9"
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appid) # type: ignore[attr-defined,unused-ignore]
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appid)
self.app.setApplicationName("tagstudio")
self.app.setApplicationDisplayName("TagStudio")
@@ -1100,7 +1100,7 @@ class QtDriver(DriverMixin, QObject):
pw.hide(),
pw.deleteLater(),
# refresh the library only when new items are added
files_count and self.update_browsing_state(), # type: ignore
files_count and self.update_browsing_state(),
)
)
QThreadPool.globalInstance().start(r)

View File

@@ -86,7 +86,7 @@ class ThumbButton(QPushButtonWrapper):
)
@override
def paintEvent(self, arg__1: QPaintEvent) -> None: # type: ignore
def paintEvent(self, arg__1: QPaintEvent) -> None:
super().paintEvent(arg__1)
if self.hovered or self.selected:
painter = QPainter()
@@ -127,13 +127,13 @@ class ThumbButton(QPushButtonWrapper):
painter.end()
@override
def enterEvent(self, event: QEnterEvent) -> None: # type: ignore
def enterEvent(self, event: QEnterEvent) -> None:
self.hovered = True
self.repaint()
return super().enterEvent(event)
@override
def leaveEvent(self, event: QEvent) -> None: # type: ignore
def leaveEvent(self, event: QEvent) -> None:
self.hovered = False
self.repaint()
return super().leaveEvent(event)