diff --git a/client/securedrop_client/app.py b/client/securedrop_client/app.py index ff42f0c1e..43c4ee570 100644 --- a/client/securedrop_client/app.py +++ b/client/securedrop_client/app.py @@ -95,7 +95,7 @@ def configure_logging(sdc_home: Path) -> None: log_file = os.path.join(sdc_home, "logs", "client.log") # set logging format - log_fmt = "%(asctime)s - %(name)s:%(lineno)d(%(funcName)s) " "%(levelname)s: %(message)s" + log_fmt = "%(asctime)s - %(name)s:%(lineno)d(%(funcName)s) %(levelname)s: %(message)s" formatter = logging.Formatter(log_fmt) # define log handlers such as for rotating log files @@ -149,8 +149,7 @@ def arg_parser() -> ArgumentParser: default=DEFAULT_SDC_HOME, type=expand_to_absolute, help=( - f"{SDC_NAME} home directory for storing files and state. " - f"(Default {DEFAULT_SDC_HOME})" + f"{SDC_NAME} home directory for storing files and state. (Default {DEFAULT_SDC_HOME})" ), ) parser.add_argument( diff --git a/client/securedrop_client/gui/auth/dialog.py b/client/securedrop_client/gui/auth/dialog.py index 290dc26ef..d7bcaf931 100644 --- a/client/securedrop_client/gui/auth/dialog.py +++ b/client/securedrop_client/gui/auth/dialog.py @@ -181,9 +181,7 @@ def validate(self) -> None: # Validate username if len(username) < self.MIN_JOURNALIST_USERNAME: self.setDisabled(False) - self.error( - _("That username won't work.\n" "It should be at least 3 characters long.") - ) + self.error(_("That username won't work.\nIt should be at least 3 characters long.")) return # Validate password @@ -202,9 +200,7 @@ def validate(self) -> None: int(tfa_token) except ValueError: self.setDisabled(False) - self.error( - _("That two-factor code won't work.\n" "It should only contain numerals.") - ) + self.error(_("That two-factor code won't work.\nIt should only contain numerals.")) return self.submit.setText(_("SIGNING IN")) @@ -218,4 +214,4 @@ def validate(self) -> None: self.controller.login(username, password, tfa_token) else: self.setDisabled(False) - self.error(_("Please enter a username, passphrase and " "two-factor code.")) + self.error(_("Please enter a username, passphrase and two-factor code.")) diff --git a/client/securedrop_client/gui/conversation/export/export_wizard_page.py b/client/securedrop_client/gui/conversation/export/export_wizard_page.py index 4140b79e3..e806d450d 100644 --- a/client/securedrop_client/gui/conversation/export/export_wizard_page.py +++ b/client/securedrop_client/gui/conversation/export/export_wizard_page.py @@ -228,9 +228,9 @@ class PreflightPage(ExportWizardPage): def __init__(self, export: Export, summary: str) -> None: self._should_autoskip_preflight = False self.summary = summary - header = _( - "Preparing to export:
" '{}' - ).format(summary) + header = _('Preparing to export:
{}').format( + summary + ) body = _( "

Understand the risks before exporting files

" "Malware" @@ -285,7 +285,7 @@ def should_autoskip_preflight(self) -> bool: def on_status_received(self, status: ExportStatus) -> None: self.status = status self.stop_animate_header() - header = _("Ready to export:
" '{}').format( + header = _('Ready to export:
{}').format( self.summary ) self.header.setText(header) @@ -326,7 +326,7 @@ class InsertUSBPage(ExportWizardPage): def __init__(self, export: Export, summary: str) -> None: self.no_device_hint = 0 self.summary = summary - header = _("Ready to export:
" '{}').format( + header = _('Ready to export:
{}').format( summary ) body = _( diff --git a/client/securedrop_client/gui/conversation/export/print_dialog.py b/client/securedrop_client/gui/conversation/export/print_dialog.py index 9387475d6..1b375be97 100644 --- a/client/securedrop_client/gui/conversation/export/print_dialog.py +++ b/client/securedrop_client/gui/conversation/export/print_dialog.py @@ -42,10 +42,10 @@ def __init__(self, device: Export, file_name: str, filepaths: list[str]) -> None # Dialog content self.starting_header = _( - "Preparing to print:
" '{}' + 'Preparing to print:
{}' ).format(self.file_name) self.ready_header = _( - "Ready to print:
" '{}' + 'Ready to print:
{}' ).format(self.file_name) self.insert_usb_header = _("Connect USB printer") self.error_header = _("Printing failed") diff --git a/client/tests/functional/test_export_wizard.py b/client/tests/functional/test_export_wizard.py index 8d084489c..4c51a3a1b 100644 --- a/client/tests/functional/test_export_wizard.py +++ b/client/tests/functional/test_export_wizard.py @@ -97,16 +97,16 @@ def test_export_wizard_device_locked( functional_test_logged_in_context, qtbot, mocker, mock_export_locked ) - assert isinstance( - export_wizard.currentPage(), PreflightPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), PreflightPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) assert export_wizard.current_status == ExportStatus.NO_DEVICE_DETECTED def check_insert_usb_page(): - assert isinstance( - export_wizard.currentPage(), InsertUSBPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), InsertUSBPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) # Move to "insert usb" screen qtbot.mouseClick(export_wizard.next_button, Qt.LeftButton) @@ -115,9 +115,9 @@ def check_insert_usb_page(): assert export_wizard.current_status == ExportStatus.NO_DEVICE_DETECTED def check_password_page(): - assert isinstance( - export_wizard.currentPage(), PassphraseWizardPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), PassphraseWizardPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) # Move to "unlock usb" screen - TODO this is an extra click? qtbot.mouseClick(export_wizard.next_button, Qt.LeftButton) @@ -143,9 +143,9 @@ def check_password_page(): assert export_wizard.current_status == ExportStatus.SUCCESS_EXPORT - assert isinstance( - export_wizard.currentPage(), FinalPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), FinalPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) @flaky @@ -164,9 +164,9 @@ def test_export_wizard_device_already_unlocked( functional_test_logged_in_context, qtbot, mocker, mock_export_unlocked ) - assert isinstance( - export_wizard.currentPage(), PreflightPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), PreflightPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) assert export_wizard.current_status == ExportStatus.DEVICE_WRITABLE @@ -202,9 +202,9 @@ def test_export_wizard_no_device_then_bad_passphrase( mock_export_no_usb_then_bad_passphrase, ) - assert isinstance( - export_wizard.currentPage(), PreflightPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), PreflightPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) assert export_wizard.current_status == ExportStatus.NO_DEVICE_DETECTED @@ -218,9 +218,9 @@ def is_unlock_page(): assert export_wizard.current_status == ExportStatus.NO_DEVICE_DETECTED def check_password_page(): - assert isinstance( - export_wizard.currentPage(), PassphraseWizardPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), PassphraseWizardPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) # Move to "unlock usb" screen qtbot.mouseClick(export_wizard.next_button, Qt.LeftButton) @@ -243,9 +243,9 @@ def check_password_page_with_error_details(): After an incorrect password, the 'error details' should be visible with a message about incorrect passphrase. """ - assert isinstance( - export_wizard.currentPage(), PassphraseWizardPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), PassphraseWizardPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) assert export_wizard.currentPage().error_details.isVisible() # Click Next - Passphrase error appears @@ -268,9 +268,9 @@ def check_password_page_with_error_details(): assert export_wizard.current_status == ExportStatus.SUCCESS_EXPORT - assert isinstance( - export_wizard.currentPage(), FinalPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), FinalPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) @flaky @@ -290,9 +290,9 @@ def test_export_wizard_error( functional_test_logged_in_context, qtbot, mocker, mock_export_fail_early ) - assert isinstance( - export_wizard.currentPage(), PreflightPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), PreflightPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) assert export_wizard.current_status == ExportStatus.NO_DEVICE_DETECTED @@ -306,9 +306,9 @@ def is_unlock_page(): assert export_wizard.current_status == ExportStatus.NO_DEVICE_DETECTED def check_password_page(): - assert isinstance( - export_wizard.currentPage(), PassphraseWizardPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), PassphraseWizardPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) # Move to "Enter passphrase" screen qtbot.mouseClick(export_wizard.next_button, Qt.LeftButton) @@ -327,7 +327,7 @@ def check_password_page(): qtbot.wait(TIME_CLICK_ACTION) assert export_wizard.current_status == ExportStatus.ERROR_MOUNT - assert isinstance( - export_wizard.currentPage(), ErrorPage - ), f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + assert isinstance(export_wizard.currentPage(), ErrorPage), ( + f"Actual: {export_wizard.currentPage()} ({export_wizard.currentId()})" + ) assert export_wizard.current_status == ExportStatus.ERROR_MOUNT diff --git a/client/tests/gui/base/test_misc.py b/client/tests/gui/base/test_misc.py index 452c8a708..2dbea2820 100644 --- a/client/tests/gui/base/test_misc.py +++ b/client/tests/gui/base/test_misc.py @@ -122,7 +122,7 @@ def test_SecureQLabel_init_wordwrap(mocker): Regression test to make sure we don't remove newlines. """ long_string = ( - "1234567890123456789012345678901234567890123456789012345678901234567890\n" "12345678901" + "1234567890123456789012345678901234567890123456789012345678901234567890\n12345678901" ) sl = SecureQLabel(long_string, wordwrap=False) assert sl.text() == long_string @@ -130,7 +130,7 @@ def test_SecureQLabel_init_wordwrap(mocker): def test_SecureQLabel_init_no_wordwrap(mocker): long_string = ( - "1234567890123456789012345678901234567890123456789012345678901234567890\n" "12345678901" + "1234567890123456789012345678901234567890123456789012345678901234567890\n12345678901" ) sl = SecureQLabel(long_string, wordwrap=False) assert sl.text() == long_string diff --git a/client/tests/gui/conversation/export/test_export_wizard.py b/client/tests/gui/conversation/export/test_export_wizard.py index 23bdfcc43..37dd272ff 100644 --- a/client/tests/gui/conversation/export/test_export_wizard.py +++ b/client/tests/gui/conversation/export/test_export_wizard.py @@ -69,9 +69,9 @@ def test_wizard_exports_directly_to_unlocked_device(self, qtbot): self.mock_export.export_state_changed.emit(ExportStatus.DEVICE_WRITABLE) self.wizard.next() - assert isinstance( - self.wizard.currentPage(), FinalPage - ), f"Actually, f{type(self.wizard.currentPage())}" + assert isinstance(self.wizard.currentPage(), FinalPage), ( + f"Actually, f{type(self.wizard.currentPage())}" + ) def test_wizard_rewinds_if_device_removed(self, qtbot): self.wizard.show() diff --git a/client/tests/gui/test_actions.py b/client/tests/gui/test_actions.py index e9f86809f..d4559d02d 100644 --- a/client/tests/gui/test_actions.py +++ b/client/tests/gui/test_actions.py @@ -103,9 +103,9 @@ def test_deletes_source_when_dialog_accepted(self): self.action.trigger() self._controller.delete_sources.assert_called_once() - assert ( - self._source in self._controller.delete_sources.call_args[0][0] - ), self._controller.delete_sources.call_args[0][0] + assert self._source in self._controller.delete_sources.call_args[0][0], ( + self._controller.delete_sources.call_args[0][0] + ) def test_does_not_delete_source_when_dialog_rejected(self): # Reject the confirmation dialog from a separate thread. diff --git a/client/tests/test_export.py b/client/tests/test_export.py index 8ff556558..c20273b20 100644 --- a/client/tests/test_export.py +++ b/client/tests/test_export.py @@ -78,9 +78,9 @@ def test_Device_run_printer_preflight_checks(self): self.device.run_printer_preflight_checks() mock_qproc.start.assert_called_once() - assert ( - mock_qproc.start.call_args[0] == _QREXEC_EXPORT_COMMAND - ), f"Actual: {mock_qproc.start.call_args[0]}" + assert mock_qproc.start.call_args[0] == _QREXEC_EXPORT_COMMAND, ( + f"Actual: {mock_qproc.start.call_args[0]}" + ) def test_Device_run_print_preflight_checks_with_error(self): spy = QSignalSpy(self.device.print_preflight_check_failed) diff --git a/client/tests/test_storage.py b/client/tests/test_storage.py index 1b531b527..4ea7955eb 100644 --- a/client/tests/test_storage.py +++ b/client/tests/test_storage.py @@ -2478,7 +2478,7 @@ def test_delete_local_conversation_by_uuid_nosuchuuid(homedir, mocker, session): delete_local_conversation_by_source_uuid(session, mock_uuid, data_dir) error_logger.assert_called_once_with( - f"Tried to delete source {mock_uuid}, but UUID " "was not found" + f"Tried to delete source {mock_uuid}, but UUID was not found" ) diff --git a/export/securedrop_export/main.py b/export/securedrop_export/main.py index b730890f4..785767abe 100755 --- a/export/securedrop_export/main.py +++ b/export/securedrop_export/main.py @@ -99,7 +99,7 @@ def _configure_logging(): log_file = os.path.join(DEFAULT_HOME, LOG_DIR_NAME, EXPORT_LOG_FILENAME) # set logging format - log_fmt = "%(asctime)s - %(name)s:%(lineno)d(%(funcName)s) " "%(levelname)s: %(message)s" + log_fmt = "%(asctime)s - %(name)s:%(lineno)d(%(funcName)s) %(levelname)s: %(message)s" formatter = logging.Formatter(log_fmt) handler = TimedRotatingFileHandler(log_file) diff --git a/poetry.lock b/poetry.lock index 4810a8f7d..098503743 100644 --- a/poetry.lock +++ b/poetry.lock @@ -876,29 +876,29 @@ files = [ [[package]] name = "ruff" -version = "0.8.4" +version = "0.9.0" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60"}, - {file = "ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac"}, - {file = "ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf"}, - {file = "ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720"}, - {file = "ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae"}, - {file = "ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7"}, - {file = "ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111"}, - {file = "ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8"}, - {file = "ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835"}, - {file = "ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d"}, - {file = "ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08"}, - {file = "ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8"}, + {file = "ruff-0.9.0-py3-none-linux_armv6l.whl", hash = "sha256:949b3513f931741e006cf267bf89611edff04e1f012013424022add3ce78f319"}, + {file = "ruff-0.9.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:99fbcb8c7fe94ae1e462ab2a1ef17cb20b25fb6438b9f198b1bcf5207a0a7916"}, + {file = "ruff-0.9.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0b022afd8eb0fcfce1e0adec84322abf4d6ce3cd285b3b99c4f17aae7decf749"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:336567ce92c9ca8ec62780d07b5fa11fbc881dc7bb40958f93a7d621e7ab4589"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d338336c44bda602dc8e8766836ac0441e5b0dfeac3af1bd311a97ebaf087a75"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9b3ececf523d733e90b540e7afcc0494189e8999847f8855747acd5a9a8c45f"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a11c0872a31232e473e2e0e2107f3d294dbadd2f83fb281c3eb1c22a24866924"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5fd06220c17a9cc0dc7fc6552f2ac4db74e8e8bff9c401d160ac59d00566f54"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0457e775c74bf3976243f910805242b7dcd389e1d440deccbd1194ca17a5728c"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05415599bbcb318f730ea1b46a39e4fbf71f6a63fdbfa1dda92efb55f19d7ecf"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fbf9864b009e43cfc1c8bed1a6a4c529156913105780af4141ca4342148517f5"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:37b3da222b12e2bb2ce628e02586ab4846b1ed7f31f42a5a0683b213453b2d49"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:733c0fcf2eb0c90055100b4ed1af9c9d87305b901a8feb6a0451fa53ed88199d"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8221a454bfe5ccdf8017512fd6bb60e6ec30f9ea252b8a80e5b73619f6c3cefd"}, + {file = "ruff-0.9.0-py3-none-win32.whl", hash = "sha256:d345f2178afd192c7991ddee59155c58145e12ad81310b509bd2e25c5b0247b3"}, + {file = "ruff-0.9.0-py3-none-win_amd64.whl", hash = "sha256:0cbc0905d94d21305872f7f8224e30f4bbcd532bc21b2225b2446d8fc7220d19"}, + {file = "ruff-0.9.0-py3-none-win_arm64.whl", hash = "sha256:7b1148771c6ca88f820d761350a053a5794bc58e0867739ea93eb5e41ad978cd"}, + {file = "ruff-0.9.0.tar.gz", hash = "sha256:143f68fa5560ecf10fc49878b73cee3eab98b777fcf43b0e62d43d42f5ef9d8b"}, ] [[package]] @@ -1058,4 +1058,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "53782fe8aa6aa35b40618409c9dcff411af6c71dc7a41e3a3fa08014357d6da6" +content-hash = "d8ad67f352da27b636bdc28c601a815e3efdd59e37542edaf431c6fdb8ec02ca" diff --git a/pyproject.toml b/pyproject.toml index 5dbdfbed0..df7710f38 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ license = "AGPLv3+" python = "^3.11" [tool.poetry.group.dev.dependencies] -ruff = "^0.8.4" +ruff = "^0.9.0" safety = "*" shellcheck-py = "*" zizmor = "*"