add type hints (1st pass: non problematic cases)

This commit is contained in:
deeplow 2022-07-21 11:39:08 +01:00
parent 1f8e23f164
commit d579a47a84
No known key found for this signature in database
GPG key ID: 577982871529A52A
7 changed files with 92 additions and 69 deletions

View file

@ -2,6 +2,7 @@ import json
import logging import logging
import os import os
import sys import sys
from typing import Optional
import click import click
from colorama import Fore, Style from colorama import Fore, Style
@ -11,7 +12,7 @@ from .container import convert
from .global_common import GlobalCommon from .global_common import GlobalCommon
def print_header(s): def print_header(s: str) -> None:
click.echo("") click.echo("")
click.echo(Style.BRIGHT + s) click.echo(Style.BRIGHT + s)
@ -20,7 +21,9 @@ def print_header(s):
@click.option("--output-filename", help="Default is filename ending with -safe.pdf") @click.option("--output-filename", help="Default is filename ending with -safe.pdf")
@click.option("--ocr-lang", help="Language to OCR, defaults to none") @click.option("--ocr-lang", help="Language to OCR, defaults to none")
@click.argument("filename", required=True) @click.argument("filename", required=True)
def cli_main(output_filename, ocr_lang, filename): def cli_main(
output_filename: Optional[str], ocr_lang: Optional[str], filename: str
) -> None:
setup_logging() setup_logging()
global_common = GlobalCommon() global_common = GlobalCommon()
common = Common() common = Common()
@ -92,7 +95,7 @@ def cli_main(output_filename, ocr_lang, filename):
# Convert the document # Convert the document
print_header("Converting document to safe PDF") print_header("Converting document to safe PDF")
def stdout_callback(line): def stdout_callback(line: str) -> None:
try: try:
status = json.loads(line) status = json.loads(line)
s = Style.BRIGHT + Fore.CYAN + f"{status['percentage']}% " s = Style.BRIGHT + Fore.CYAN + f"{status['percentage']}% "

View file

@ -2,6 +2,7 @@ import os
import platform import platform
import stat import stat
import tempfile import tempfile
from typing import Optional
import appdirs import appdirs
@ -11,7 +12,7 @@ class Common(object):
The Common class is a singleton of shared functionality throughout an open dangerzone window The Common class is a singleton of shared functionality throughout an open dangerzone window
""" """
def __init__(self): def __init__(self) -> None:
# Name of input and out files # Name of input and out files
self.input_filename = None self.input_filename: Optional[str] = None
self.output_filename = None self.output_filename: Optional[str] = None

View file

@ -8,6 +8,7 @@ import platform
import shutil import shutil
import subprocess import subprocess
import sys import sys
from typing import Optional
import appdirs import appdirs
import colorama import colorama
@ -24,7 +25,7 @@ class GlobalCommon(object):
The GlobalCommon class is a singleton of shared functionality throughout the app The GlobalCommon class is a singleton of shared functionality throughout the app
""" """
def __init__(self): def __init__(self) -> None:
# Version # Version
try: try:
with open(self.get_resource_path("version.txt")) as f: with open(self.get_resource_path("version.txt")) as f:
@ -210,7 +211,7 @@ class GlobalCommon(object):
# Load settings # Load settings
self.settings = Settings(self) self.settings = Settings(self)
def display_banner(self): def display_banner(self) -> None:
""" """
Raw ASCII art example: Raw ASCII art example:
@ -391,7 +392,7 @@ class GlobalCommon(object):
else: else:
return shutil.which("docker") return shutil.which("docker")
def get_resource_path(self, filename): def get_resource_path(self, filename: str) -> str:
if getattr(sys, "dangerzone_dev", False): if getattr(sys, "dangerzone_dev", False):
# Look for resources directory relative to python file # Look for resources directory relative to python file
project_root = pathlib.Path(__file__).parent.parent project_root = pathlib.Path(__file__).parent.parent
@ -420,12 +421,12 @@ class GlobalCommon(object):
else: else:
return None return None
def install_container(self): def install_container(self) -> Optional[bool]:
""" """
Make sure the podman container is installed. Linux only. Make sure the podman container is installed. Linux only.
""" """
if self.is_container_installed(): if self.is_container_installed():
return return None
# Load the container into podman # Load the container into podman
log.info("Installing Dangerzone container image...") log.info("Installing Dangerzone container image...")
@ -454,7 +455,7 @@ class GlobalCommon(object):
log.info("Container image installed") log.info("Container image installed")
return True return True
def is_container_installed(self): def is_container_installed(self) -> bool:
""" """
See if the podman container is installed. Linux only. See if the podman container is installed. Linux only.
""" """

View file

@ -4,6 +4,7 @@ import platform
import signal import signal
import sys import sys
import uuid import uuid
from typing import Dict, NoReturn, Optional, TextIO
import click import click
from PySide2 import QtCore, QtWidgets from PySide2 import QtCore, QtWidgets
@ -21,14 +22,14 @@ class ApplicationWrapper(QtCore.QObject):
new_window = QtCore.Signal() new_window = QtCore.Signal()
application_activated = QtCore.Signal() application_activated = QtCore.Signal()
def __init__(self): def __init__(self) -> None:
super(ApplicationWrapper, self).__init__() super(ApplicationWrapper, self).__init__()
self.app = QtWidgets.QApplication() self.app = QtWidgets.QApplication()
self.app.setQuitOnLastWindowClosed(False) self.app.setQuitOnLastWindowClosed(False)
self.original_event = self.app.event self.original_event = self.app.event
def monkeypatch_event(event): def monkeypatch_event(event: QtCore.QEvent) -> bool:
# In macOS, handle the file open event # In macOS, handle the file open event
if event.type() == QtCore.QEvent.FileOpen: if event.type() == QtCore.QEvent.FileOpen:
# Skip file open events in dev mode # Skip file open events in dev mode
@ -46,7 +47,7 @@ class ApplicationWrapper(QtCore.QObject):
@click.command() @click.command()
@click.argument("filename", required=False) @click.argument("filename", required=False)
def gui_main(filename): def gui_main(filename: str) -> bool:
setup_logging() setup_logging()
if platform.system() == "Darwin": if platform.system() == "Darwin":
@ -61,16 +62,16 @@ def gui_main(filename):
from strip_ansi import strip_ansi from strip_ansi import strip_ansi
class StdoutFilter: class StdoutFilter:
def __init__(self, stream): def __init__(self, stream: TextIO) -> None:
self.stream = stream self.stream = stream
def __getattr__(self, attr_name): def __getattr__(self, attr_name): # type: ignore [no-untyped-def]
return getattr(self.stream, attr_name) return getattr(self.stream, attr_name)
def write(self, data): def write(self, data: str) -> None:
self.stream.write(strip_ansi(data)) self.stream.write(strip_ansi(data))
def flush(self): def flush(self) -> None:
self.stream.flush() self.stream.flush()
sys.stdout = StdoutFilter(sys.stdout) sys.stdout = StdoutFilter(sys.stdout)
@ -90,15 +91,15 @@ def gui_main(filename):
# Create the system tray # Create the system tray
systray = SysTray(global_common, gui_common, app, app_wrapper) systray = SysTray(global_common, gui_common, app, app_wrapper)
closed_windows = {} closed_windows: Dict[str, MainWindow] = {}
windows = {} windows: Dict[str, MainWindow] = {}
def delete_window(window_id): def delete_window(window_id: str) -> None:
closed_windows[window_id] = windows[window_id] closed_windows[window_id] = windows[window_id]
del windows[window_id] del windows[window_id]
# Open a document in a window # Open a document in a window
def select_document(filename=None): def select_document(filename: Optional[str] = None) -> bool:
if ( if (
len(windows) == 1 len(windows) == 1
and windows[list(windows.keys())[0]].common.input_filename == None and windows[list(windows.keys())[0]].common.input_filename == None
@ -112,16 +113,16 @@ def gui_main(filename):
if filename: if filename:
# Validate filename # Validate filename
filename = os.path.abspath(os.path.expanduser(filename)) file_path: str = os.path.abspath(os.path.expanduser(filename))
try: try:
open(filename, "rb") open(file_path, "rb")
except FileNotFoundError: except FileNotFoundError:
click.echo("File not found") click.echo("File not found")
return False return False
except PermissionError: except PermissionError:
click.echo("Permission denied") click.echo("Permission denied")
return False return False
window.common.input_filename = filename window.common.input_filename = file_path
window.content_widget.doc_selection_widget.document_selected.emit() window.content_widget.doc_selection_widget.document_selected.emit()
return True return True
@ -135,7 +136,7 @@ def gui_main(filename):
return True return True
# Open a new window, if all windows are closed # Open a new window, if all windows are closed
def application_activated(): def application_activated() -> None:
if len(windows) == 0: if len(windows) == 0:
select_document() select_document()

View file

@ -16,6 +16,7 @@ elif platform.system() == "Linux":
import getpass import getpass
from xdg.DesktopEntry import DesktopEntry from xdg.DesktopEntry import DesktopEntry
from ..global_common import GlobalCommon
from ..settings import Settings from ..settings import Settings
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -26,7 +27,7 @@ class GuiCommon(object):
The GuiCommon class is a singleton of shared functionality for the GUI The GuiCommon class is a singleton of shared functionality for the GUI
""" """
def __init__(self, app, global_common): def __init__(self, app, global_common: GlobalCommon) -> None:
# Qt app # Qt app
self.app = app self.app = app
@ -42,14 +43,14 @@ class GuiCommon(object):
# Are we done waiting (for Docker Desktop to be installed, or for container to install) # Are we done waiting (for Docker Desktop to be installed, or for container to install)
self.is_waiting_finished = False self.is_waiting_finished = False
def get_window_icon(self): def get_window_icon(self) -> QtGui.QIcon:
if platform.system() == "Windows": if platform.system() == "Windows":
path = self.global_common.get_resource_path("dangerzone.ico") path = self.global_common.get_resource_path("dangerzone.ico")
else: else:
path = self.global_common.get_resource_path("icon.png") path = self.global_common.get_resource_path("icon.png")
return QtGui.QIcon(path) return QtGui.QIcon(path)
def open_pdf_viewer(self, filename): def open_pdf_viewer(self, filename: str) -> None:
if platform.system() == "Darwin": if platform.system() == "Darwin":
# Open in Preview # Open in Preview
args = ["open", "-a", "Preview.app", filename] args = ["open", "-a", "Preview.app", filename]
@ -79,8 +80,8 @@ class GuiCommon(object):
log.info(Fore.YELLOW + "> " + Fore.CYAN + args_str) log.info(Fore.YELLOW + "> " + Fore.CYAN + args_str)
subprocess.Popen(args) subprocess.Popen(args)
def _find_pdf_viewers(self): def _find_pdf_viewers(self) -> dict[str, str]:
pdf_viewers = {} pdf_viewers: dict[str, str] = {}
if platform.system() == "Linux": if platform.system() == "Linux":
# Find all .desktop files # Find all .desktop files
for search_path in [ for search_path in [
@ -111,8 +112,13 @@ class GuiCommon(object):
class Alert(QtWidgets.QDialog): class Alert(QtWidgets.QDialog):
def __init__( def __init__(
self, gui_common, global_common, message, ok_text="Ok", extra_button_text=None self,
): gui_common: GuiCommon,
global_common: GlobalCommon,
message: str,
ok_text: str = "Ok",
extra_button_text: str = None,
) -> None:
super(Alert, self).__init__() super(Alert, self).__init__()
self.global_common = global_common self.global_common = global_common
self.gui_common = gui_common self.gui_common = gui_common
@ -167,14 +173,14 @@ class Alert(QtWidgets.QDialog):
layout.addLayout(buttons_layout) layout.addLayout(buttons_layout)
self.setLayout(layout) self.setLayout(layout)
def clicked_ok(self): def clicked_ok(self) -> None:
self.done(QtWidgets.QDialog.Accepted) self.done(QtWidgets.QDialog.Accepted)
def clicked_extra(self): def clicked_extra(self) -> None:
self.done(2) self.done(2)
def clicked_cancel(self): def clicked_cancel(self) -> None:
self.done(QtWidgets.QDialog.Rejected) self.done(QtWidgets.QDialog.Rejected)
def launch(self): def launch(self) -> int:
return self.exec_() return self.exec_()

View file

@ -5,12 +5,15 @@ import platform
import shutil import shutil
import subprocess import subprocess
import tempfile import tempfile
from typing import Optional
from colorama import Fore, Style from colorama import Fore, Style
from PySide2 import QtCore, QtGui, QtWidgets from PySide2 import QtCore, QtGui, QtWidgets
from ..common import Common from ..common import Common
from ..container import convert from ..container import convert
from ..global_common import GlobalCommon
from .common import GuiCommon
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -18,7 +21,9 @@ log = logging.getLogger(__name__)
class MainWindow(QtWidgets.QMainWindow): class MainWindow(QtWidgets.QMainWindow):
delete_window = QtCore.Signal(str) delete_window = QtCore.Signal(str)
def __init__(self, global_common, gui_common, window_id): def __init__(
self, global_common: GlobalCommon, gui_common: GuiCommon, window_id: str
) -> None:
super(MainWindow, self).__init__() super(MainWindow, self).__init__()
self.global_common = global_common self.global_common = global_common
self.gui_common = gui_common self.gui_common = gui_common
@ -78,12 +83,12 @@ class MainWindow(QtWidgets.QMainWindow):
self.show() self.show()
def waiting_finished(self): def waiting_finished(self) -> None:
self.gui_common.is_waiting_finished = True self.gui_common.is_waiting_finished = True
self.waiting_widget.hide() self.waiting_widget.hide()
self.content_widget.show() self.content_widget.show()
def closeEvent(self, e): def closeEvent(self, e: QtGui.QCloseEvent) -> None:
e.accept() e.accept()
self.delete_window.emit(self.window_id) self.delete_window.emit(self.window_id)
@ -94,11 +99,11 @@ class MainWindow(QtWidgets.QMainWindow):
class InstallContainerThread(QtCore.QThread): class InstallContainerThread(QtCore.QThread):
finished = QtCore.Signal() finished = QtCore.Signal()
def __init__(self, global_common): def __init__(self, global_common: GlobalCommon) -> None:
super(InstallContainerThread, self).__init__() super(InstallContainerThread, self).__init__()
self.global_common = global_common self.global_common = global_common
def run(self): def run(self) -> None:
self.global_common.install_container() self.global_common.install_container()
self.finished.emit() self.finished.emit()
@ -115,7 +120,7 @@ class WaitingWidget(QtWidgets.QWidget):
# - "install_container" # - "install_container"
finished = QtCore.Signal() finished = QtCore.Signal()
def __init__(self, global_common, gui_common): def __init__(self, global_common: GlobalCommon, gui_common: GuiCommon) -> None:
super(WaitingWidget, self).__init__() super(WaitingWidget, self).__init__()
self.global_common = global_common self.global_common = global_common
self.gui_common = gui_common self.gui_common = gui_common
@ -148,8 +153,8 @@ class WaitingWidget(QtWidgets.QWidget):
# Check the state # Check the state
self.check_state() self.check_state()
def check_state(self): def check_state(self) -> None:
state = None state: Optional[str] = None
# Can we find the container runtime binary binary # Can we find the container runtime binary binary
if platform.system() == "Linux": if platform.system() == "Linux":
@ -180,7 +185,7 @@ class WaitingWidget(QtWidgets.QWidget):
# Update the state # Update the state
self.state_change(state) self.state_change(state)
def state_change(self, state): def state_change(self, state: str) -> None:
if state == "not_installed": if state == "not_installed":
self.label.setText( self.label.setText(
"<strong>Dangerzone Requires Docker Desktop</strong><br><br><a href='https://www.docker.com/products/docker-desktop'>Download Docker Desktop</a>, install it, and open it." "<strong>Dangerzone Requires Docker Desktop</strong><br><br><a href='https://www.docker.com/products/docker-desktop'>Download Docker Desktop</a>, install it, and open it."
@ -204,7 +209,9 @@ class WaitingWidget(QtWidgets.QWidget):
class ContentWidget(QtWidgets.QWidget): class ContentWidget(QtWidgets.QWidget):
close_window = QtCore.Signal() close_window = QtCore.Signal()
def __init__(self, global_common, gui_common, common): def __init__(
self, global_common: GlobalCommon, gui_common: GuiCommon, common: Common
) -> None:
super(ContentWidget, self).__init__() super(ContentWidget, self).__init__()
self.global_common = global_common self.global_common = global_common
@ -244,22 +251,22 @@ class ContentWidget(QtWidgets.QWidget):
layout.addWidget(self.convert_widget, stretch=1) layout.addWidget(self.convert_widget, stretch=1)
self.setLayout(layout) self.setLayout(layout)
def document_selected(self): def document_selected(self) -> None:
self.doc_selection_widget.hide() self.doc_selection_widget.hide()
self.settings_widget.show() self.settings_widget.show()
def start_clicked(self): def start_clicked(self) -> None:
self.settings_widget.hide() self.settings_widget.hide()
self.convert_widget.show() self.convert_widget.show()
def _close_window(self): def _close_window(self) -> None:
self.close_window.emit() self.close_window.emit()
class DocSelectionWidget(QtWidgets.QWidget): class DocSelectionWidget(QtWidgets.QWidget):
document_selected = QtCore.Signal() document_selected = QtCore.Signal()
def __init__(self, common): def __init__(self, common: Common) -> None:
super(DocSelectionWidget, self).__init__() super(DocSelectionWidget, self).__init__()
self.common = common self.common = common
@ -286,7 +293,7 @@ class DocSelectionWidget(QtWidgets.QWidget):
layout.addStretch() layout.addStretch()
self.setLayout(layout) self.setLayout(layout)
def dangerous_doc_button_clicked(self): def dangerous_doc_button_clicked(self) -> None:
filename = QtWidgets.QFileDialog.getOpenFileName( filename = QtWidgets.QFileDialog.getOpenFileName(
self, self,
"Open document", "Open document",
@ -302,7 +309,9 @@ class SettingsWidget(QtWidgets.QWidget):
start_clicked = QtCore.Signal() start_clicked = QtCore.Signal()
close_window = QtCore.Signal() close_window = QtCore.Signal()
def __init__(self, global_common, gui_common, common): def __init__(
self, global_common: GlobalCommon, gui_common: GuiCommon, common: Common
) -> None:
super(SettingsWidget, self).__init__() super(SettingsWidget, self).__init__()
self.global_common = global_common self.global_common = global_common
self.gui_common = gui_common self.gui_common = gui_common
@ -424,7 +433,7 @@ class SettingsWidget(QtWidgets.QWidget):
if index != -1: if index != -1:
self.open_combobox.setCurrentIndex(index) self.open_combobox.setCurrentIndex(index)
def update_ui(self): def update_ui(self) -> None:
if platform.system() == "Windows": if platform.system() == "Windows":
# Because the save checkbox is always checked in Windows, the # Because the save checkbox is always checked in Windows, the
# start button can be enabled # start button can be enabled
@ -439,7 +448,7 @@ class SettingsWidget(QtWidgets.QWidget):
else: else:
self.start_button.setEnabled(False) self.start_button.setEnabled(False)
def document_selected(self): def document_selected(self) -> None:
# Update the danger doc label # Update the danger doc label
self.dangerous_doc_label.setText( self.dangerous_doc_label.setText(
f"Suspicious: {os.path.basename(self.common.input_filename)}" f"Suspicious: {os.path.basename(self.common.input_filename)}"
@ -450,7 +459,7 @@ class SettingsWidget(QtWidgets.QWidget):
self.common.output_filename = output_filename self.common.output_filename = output_filename
self.save_lineedit.setText(os.path.basename(output_filename)) self.save_lineedit.setText(os.path.basename(output_filename))
def save_browse_button_clicked(self): def save_browse_button_clicked(self) -> None:
filename = QtWidgets.QFileDialog.getSaveFileName( filename = QtWidgets.QFileDialog.getSaveFileName(
self, self,
"Save safe PDF as...", "Save safe PDF as...",
@ -461,7 +470,7 @@ class SettingsWidget(QtWidgets.QWidget):
self.common.output_filename = filename[0] self.common.output_filename = filename[0]
self.save_lineedit.setText(os.path.basename(self.common.output_filename)) self.save_lineedit.setText(os.path.basename(self.common.output_filename))
def start_button_clicked(self): def start_button_clicked(self) -> None:
if self.common.output_filename is None: if self.common.output_filename is None:
# If not saving, then save it to a temp file instead # If not saving, then save it to a temp file instead
tmp = tempfile.mkstemp(suffix=".pdf", prefix="dangerzone_") tmp = tempfile.mkstemp(suffix=".pdf", prefix="dangerzone_")
@ -493,13 +502,13 @@ class ConvertThread(QtCore.QThread):
finished = QtCore.Signal(bool) finished = QtCore.Signal(bool)
update = QtCore.Signal(bool, str, int) update = QtCore.Signal(bool, str, int)
def __init__(self, global_common, common): def __init__(self, global_common: GlobalCommon, common: Common) -> None:
super(ConvertThread, self).__init__() super(ConvertThread, self).__init__()
self.global_common = global_common self.global_common = global_common
self.common = common self.common = common
self.error = False self.error = False
def run(self): def run(self) -> None:
if self.global_common.settings.get("ocr"): if self.global_common.settings.get("ocr"):
ocr_lang = self.global_common.ocr_languages[ ocr_lang = self.global_common.ocr_languages[
self.global_common.settings.get("ocr_language") self.global_common.settings.get("ocr_language")
@ -515,7 +524,7 @@ class ConvertThread(QtCore.QThread):
): ):
self.finished.emit(self.error) self.finished.emit(self.error)
def stdout_callback(self, line): def stdout_callback(self, line: str) -> None:
try: try:
status = json.loads(line) status = json.loads(line)
except: except:
@ -541,7 +550,9 @@ class ConvertThread(QtCore.QThread):
class ConvertWidget(QtWidgets.QWidget): class ConvertWidget(QtWidgets.QWidget):
close_window = QtCore.Signal() close_window = QtCore.Signal()
def __init__(self, global_common, gui_common, common): def __init__(
self, global_common: GlobalCommon, gui_common: GuiCommon, common: Common
) -> None:
super(ConvertWidget, self).__init__() super(ConvertWidget, self).__init__()
self.global_common = global_common self.global_common = global_common
self.gui_common = gui_common self.gui_common = gui_common
@ -588,19 +599,19 @@ class ConvertWidget(QtWidgets.QWidget):
layout.addStretch() layout.addStretch()
self.setLayout(layout) self.setLayout(layout)
def document_selected(self): def document_selected(self) -> None:
# Update the danger doc label # Update the danger doc label
self.dangerous_doc_label.setText( self.dangerous_doc_label.setText(
f"Suspicious: {os.path.basename(self.common.input_filename)}" f"Suspicious: {os.path.basename(self.common.input_filename)}"
) )
def start(self): def start(self) -> None:
self.convert_t = ConvertThread(self.global_common, self.common) self.convert_t = ConvertThread(self.global_common, self.common)
self.convert_t.update.connect(self.update) self.convert_t.update.connect(self.update)
self.convert_t.finished.connect(self.all_done) self.convert_t.finished.connect(self.all_done)
self.convert_t.start() self.convert_t.start()
def update(self, error, text, percentage): def update(self, error, text, percentage) -> None:
if error: if error:
self.error = True self.error = True
self.error_image.show() self.error_image.show()
@ -609,7 +620,7 @@ class ConvertWidget(QtWidgets.QWidget):
self.label.setText(text) self.label.setText(text)
self.progress.setValue(percentage) self.progress.setValue(percentage)
def all_done(self): def all_done(self) -> None:
if self.error: if self.error:
return return

View file

@ -19,13 +19,13 @@ class Settings:
self.load() self.load()
def get(self, key): def get(self, key: str):
return self.settings[key] return self.settings[key]
def set(self, key, val): def set(self, key: str, val) -> None:
self.settings[key] = val self.settings[key] = val
def load(self): def load(self) -> None:
if os.path.isfile(self.settings_filename): if os.path.isfile(self.settings_filename):
# If the settings file exists, load it # If the settings file exists, load it
try: try: