Skip to content

Commit

Permalink
fixed type annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
hnesk committed Nov 2, 2020
1 parent 23963d7 commit e6f583e
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 56 deletions.
2 changes: 1 addition & 1 deletion ocrd_browser/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def on_quit(self, _action: Gio.SimpleAction, _param: str = None) -> None:
open_windows: int = 0
window: MainWindow
for window in self.get_windows():
if isinstance(window, MainWindow) and window.close_confirm():
if isinstance(window, MainWindow) and window.close_confirm(): # type: ignore[unreachable]
window.destroy()
else:
open_windows += 1
Expand Down
38 changes: 19 additions & 19 deletions ocrd_browser/model/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@
EventCallBack = Optional[Callable[[str, Any], None]]


def check_editable(func: Callable):
def check_editable(func: Callable[..., Any]) -> Callable[..., Any]:
@wraps(func)
def guard(self, *args, **kwargs):
def guard(self: 'Document', *args: List[Any], **kwargs: Dict[Any, Any]) -> Any:
if self._editable is not True:
raise PermissionError('Document is not editable, can not call {}'.format(func.__qualname__))
return_value = func(self, *args, **kwargs)
Expand All @@ -45,7 +45,7 @@ def guard(self, *args, **kwargs):


class Document:
temporary_workspaces = []
temporary_workspaces: List[str] = []

def __init__(self, workspace: Workspace, emitter: EventCallBack = None, editable: bool = False,
original_url: str = None):
Expand Down Expand Up @@ -79,16 +79,16 @@ def load(cls, mets_url: Union[Path, str] = None, emitter: EventCallBack = None)
return doc

@classmethod
def clone(cls, mets_url: Union[Path, str], emitter: EventCallBack = None, editable=True) -> 'Document':
def clone(cls, mets_url: Union[Path, str], emitter: EventCallBack = None, editable: bool = True) -> 'Document':
"""
Clones a project (mets.xml and all used files) to a temporary directory for editing
"""
doc = cls(cls._clone_workspace(mets_url), emitter=emitter, editable=editable, original_url=mets_url)
doc = cls(cls._clone_workspace(mets_url), emitter=emitter, editable=editable, original_url=str(mets_url))
doc._empty = False
return doc

@classmethod
def _clone_workspace(cls, mets_url):
def _clone_workspace(cls, mets_url: Union[Path, str]) -> Workspace:
"""
Clones a workspace (mets.xml and all used files) to a temporary directory for editing
"""
Expand All @@ -102,7 +102,7 @@ def _clone_workspace(cls, mets_url):
return workspace

@check_editable
def save(self, backup_directory: Union[bool, Path, str] = True):
def save(self, backup_directory: Union[bool, Path, str] = True) -> None:
if not self._original_url:
raise ValueError('Need an _original_url to save')
self.save_as(self._original_url, backup_directory=backup_directory)
Expand All @@ -116,9 +116,9 @@ def save_as(self, mets_url: Union[Path, str], backup_directory: Union[bool, Path
if backup_directory:
if isinstance(backup_directory, bool):
backup_directory = self._derive_backup_directory(workspace_directory)
shutil.move(workspace_directory, backup_directory)
shutil.move(str(workspace_directory), str(backup_directory))
else:
shutil.rmtree(workspace_directory)
shutil.rmtree(str(workspace_directory))

mets_basename = mets_path.name
workspace_directory.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -228,8 +228,8 @@ def file_groups_and_mimetypes(self) -> List[Tuple[str, str]]:
return list(distinct_groups.keys())

@property
def title(self):
return self.workspace.mets.unique_identifier if self.workspace and self.workspace.mets.unique_identifier else '<unnamed>'
def title(self) -> str:
return str(self.workspace.mets.unique_identifier) if self.workspace and self.workspace.mets.unique_identifier else '<unnamed>'

def get_file_index(self) -> Dict[str, OcrdFile]:
"""
Expand Down Expand Up @@ -277,7 +277,7 @@ def get_image_paths(self, file_group: str) -> Dict[str, Path]:
image_paths[page_id] = None
return image_paths

def get_default_image_group(self, preferred_image_file_groups: Optional[List] = None) -> Optional[str]:
def get_default_image_group(self, preferred_image_file_groups: Optional[List[str]] = None) -> Optional[str]:
image_file_groups = []
for file_group, mimetype in self.file_groups_and_mimetypes:
weight = 0.0
Expand Down Expand Up @@ -431,7 +431,7 @@ def delete_images(self, page_id: str, file_group: str = 'OCR-D-IMG') -> List[Ocr
self._emit('document_changed', 'page_changed', [page_id])
return image_files

def save_mets(self):
def save_mets(self) -> None:
if not self._editable:
raise PermissionError('Can not modify Document with _editable == False')
self.workspace.save_mets()
Expand Down Expand Up @@ -462,23 +462,23 @@ def add_image(self, image: ndarray, page_id: str, file_id: str, file_group: str
return current_file

@property
def modified(self):
def modified(self) -> bool:
return self._modified

@property
def empty(self):
def empty(self) -> bool:
return self._empty

@property
def original_url(self):
def original_url(self) -> str:
return self._original_url

@property
def editable(self):
def editable(self) -> bool:
return self._editable

@editable.setter
def editable(self, editable):
def editable(self, editable: bool) -> None:
if editable:
if self._original_url:
self.workspace = self._clone_workspace(self._original_url)
Expand Down Expand Up @@ -509,7 +509,7 @@ def _derive_backup_directory(workspace_directory: Path, now: datetime = None) ->
return workspace_directory.parent / ('.bak.' + workspace_directory.name + '.' + now.strftime('%Y%m%d-%H%M%S'))

@classmethod
def delete_temporary_workspaces(cls):
def delete_temporary_workspaces(cls) -> None:
for temporary_workspace in cls.temporary_workspaces:
try:
shutil.rmtree(temporary_workspace)
Expand Down
12 changes: 6 additions & 6 deletions ocrd_browser/ui/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def update_ui(self) -> None:
for view in self.views:
view.update_ui()

def close_confirm(self):
def close_confirm(self) -> bool:
if self.document.modified:
# Do you wanna save the changes?
d = SaveChangesDialog(document=self.document, transient_for=self, modal=True)
Expand All @@ -178,7 +178,7 @@ def on_close(self, _a: Gio.SimpleAction = None, _p: None = None) -> None:
if self.close_confirm():
self.destroy()

def on_delete_event(self, _window: 'MainWindow', _event: Gdk.Event):
def on_delete_event(self, _window: 'MainWindow', _event: Gdk.Event) -> bool:
return not self.close_confirm()

def on_goto_first(self, _a: Gio.SimpleAction = None, _p: None = None) -> None:
Expand Down Expand Up @@ -211,13 +211,13 @@ def on_close_view(self, _action: Gio.SimpleAction, view_name: GLib.Variant) -> N
self.views.remove(closing_view)
del closing_view

def on_save(self, _a: Gio.SimpleAction = None, _p: None = None):
def on_save(self, _a: Gio.SimpleAction = None, _p: None = None) -> bool:
if self.document.original_url:
return self.save()
else:
return self.save_as()

def on_save_as(self, _a: Gio.SimpleAction = None, _p: None = None):
def on_save_as(self, _a: Gio.SimpleAction = None, _p: None = None) -> None:
self.save_as()

def save(self) -> bool:
Expand All @@ -232,13 +232,13 @@ def save_as(self) -> bool:
else:
save_dialog.set_current_name('mets.xml')
response = save_dialog.run()
should_save = response == Gtk.ResponseType.OK
should_save = bool(response == Gtk.ResponseType.OK)
if should_save:
self.document.save_as(save_dialog.get_uri())
save_dialog.destroy()
self.update_ui()
return should_save

def on_toggle_edit_mode(self, _a: Gio.SimpleAction = None, _p: None = None):
def on_toggle_edit_mode(self, _a: Gio.SimpleAction = None, _p: None = None) -> None:
self.document.editable = not self.document.editable
self.update_ui()
28 changes: 15 additions & 13 deletions ocrd_browser/util/config.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import os
from collections import OrderedDict
from configparser import ConfigParser, SectionProxy
from configparser import ConfigParser
import shlex
from typing import List, Optional
from typing import List, Optional, MutableMapping
from shutil import which

from gi.repository import GLib
from ocrd_utils import getLogger

ConfigDict = MutableMapping[str, str]


class _SubSettings:
@classmethod
def from_section(cls, section: SectionProxy):
def from_section(cls, _name: str, section: ConfigDict) -> '_SubSettings':
raise NotImplementedError('please override from_section')

def __repr__(self):
def __repr__(self) -> str:
return '{}({})'.format(self.__class__.__name__, repr(vars(self)))


Expand All @@ -23,7 +25,7 @@ def __init__(self, preferred_images: List[str]):
self.preferred_images = preferred_images

@classmethod
def from_section(cls, section: SectionProxy):
def from_section(cls, _name: str, section: ConfigDict) -> '_FileGroups':
preferred_images = section.get('preferredImages', 'OCR-D-IMG, OCR-D-IMG.*')
return cls(
[grp.strip() for grp in preferred_images.split(',')]
Expand All @@ -43,39 +45,39 @@ def __init__(self, name: str, commandline: str, shortcut: Optional[str] = None):
self.shortcut = shortcut

@classmethod
def from_section(cls, section: SectionProxy):
def from_section(cls, name: str, section: ConfigDict) -> '_Tool':
return cls(
section.name[len(cls.PREFIX):],
name[len(cls.PREFIX):],
section['commandline'],
section.get('shortcut', None)
)


class _Settings:
def __init__(self, config: ConfigParser):
self.file_groups = _FileGroups.from_section(config['FileGroups'] if 'FileGroups' in config else {})
self.file_groups = _FileGroups.from_section('FileGroups', config['FileGroups'] if 'FileGroups' in config else {'': ''})
self.tools = OrderedDict()
for name, section in config.items():
if name.startswith(_Tool.PREFIX):
tool = _Tool.from_section(section)
tool = _Tool.from_section(name, section)
self.tools[tool.name] = tool

def __repr__(self):
def __repr__(self) -> str:
return '{}({})'.format(self.__class__.__name__, repr(vars(self)))

@classmethod
def build_default(cls, config_dirs=None) -> '_Settings':
def build_default(cls, config_dirs: Optional[List[str]] = None) -> '_Settings':
if config_dirs is None:
config_dirs = GLib.get_system_config_dirs() + [GLib.get_user_config_dir()] + [os.getcwd()]

config_files = [dir + '/ocrd-browser.conf' for dir in config_dirs]
return cls.build_from_files(config_files)

@classmethod
def build_from_files(cls, files) -> '_Settings':
def build_from_files(cls, files: List[str]) -> '_Settings':
log = getLogger('ocrd_browser.util.config._Settings.build_from_files')
config = ConfigParser()
config.optionxform = lambda option: option
setattr(config, 'optionxform', lambda option: option)
read_files = config.read(files)
log.warning('Read config files: %s, tried %s', ', '.join(read_files), ', '.join(str(file) for file in files))
return cls(config)
Expand Down
20 changes: 10 additions & 10 deletions ocrd_browser/util/launcher.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import shlex
from subprocess import Popen
from typing import Dict, Optional
from typing import Dict, Optional, Any

from ocrd_models import OcrdFile
from ocrd_utils import getLogger
Expand All @@ -10,27 +10,27 @@


class ResolvableFileName:
def __init__(self, filename, in_doc: Document):
def __init__(self, filename: str, in_doc: Document):
self.filename = filename
self.in_doc = in_doc

@property
def absolute(self):
def absolute(self) -> str:
return shlex.quote(str(self.in_doc.path(self.filename).absolute()))

@property
def relative(self):
def relative(self) -> str:
return shlex.quote(self.filename)

def __str__(self):
def __str__(self) -> str:
return self.absolute


class QuotingProxy:
def __init__(self, object_):
def __init__(self, object_:Any):
self.object = object_

def __getattr__(self, item):
def __getattr__(self, item: str) -> str:
if hasattr(self.object, item):
return shlex.quote(getattr(self.object, item))
else:
Expand All @@ -54,7 +54,7 @@ class Launcher:
def __init__(self, tools: Optional[Dict[str, _Tool]] = None):
self.tools = tools if tools else SETTINGS.tools

def launch(self, toolname: str, doc: Document, file: OcrdFile) -> Optional[Popen]:
def launch(self, toolname: str, doc: Document, file: OcrdFile) -> Optional[Popen[bytes]]:
if toolname in self.tools:
return self.launch_tool(self.tools[toolname], doc, file)
else:
Expand All @@ -66,13 +66,13 @@ def launch(self, toolname: str, doc: Document, file: OcrdFile) -> Optional[Popen
log.error('commandline = /usr/bin/yourtool --base-dir {workspace.directory} {file.path.absolute}')
return None

def launch_tool(self, tool: _Tool, doc: Document, file: OcrdFile, **kwargs) -> Popen:
def launch_tool(self, tool: _Tool, doc: Document, file: OcrdFile, **kwargs: Any) -> Popen[bytes]:
log = getLogger('ocrd_browser.util.launcher.Launcher.launch_tool')
commandline = self._template(tool.commandline, doc, file)
log.debug('Calling tool "%s" with commandline: ', tool.name)
log.debug('%s', commandline)
process = Popen(args=commandline, shell=True, cwd=doc.directory, **kwargs)
return process

def _template(self, arg: str, doc: Document, file: OcrdFile):
def _template(self, arg: str, doc: Document, file: OcrdFile) -> str:
return arg.format(file=FileProxy(file, doc), workspace=QuotingProxy(doc.workspace))
8 changes: 4 additions & 4 deletions ocrd_browser/view/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from gi.repository import Gtk, Pango, GObject

from typing import List, Tuple, Any, Optional, Dict
from typing import List, Tuple, Any, Optional, Dict, cast

from enum import Enum
from ocrd_utils.constants import MIMETYPE_PAGE, MIME_TO_EXT
Expand Down Expand Up @@ -125,7 +125,7 @@ def changed(self, page_qty: int) -> None:

class ImageZoomSelector(Gtk.Box, Configurator):

def __init__(self, base: float = 2, step=0.1, min_=-4.0, max_=4.0) -> None:
def __init__(self, base: float = 2, step: float = 0.1, min_: float = -4.0, max_: float = 4.0) -> None:
super().__init__(visible=True, spacing=3)
self.value = None

Expand All @@ -143,8 +143,8 @@ def __init__(self, base: float = 2, step=0.1, min_=-4.0, max_=4.0) -> None:
self.pack_start(label, False, True, 0)
self.pack_start(self.scale, False, True, 0)

def get_exp(self):
return self.base ** self.value
def get_exp(self) -> float:
return cast(float, self.base ** self.value)

def set_value(self, value: float) -> None:
adj: Gtk.Adjustment = self.scale.get_adjustment()
Expand Down
2 changes: 1 addition & 1 deletion ocrd_browser/view/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def rescale(self) -> None:
thumbnail = pil_scale(img, None, int(scale_config.get_exp() * img.height))
image.set_from_pixbuf(pil_to_pixbuf(thumbnail))

def on_scroll(self, _widget: Gtk.Widget, event: Gdk.EventButton):
def on_scroll(self, _widget: Gtk.Widget, event: Gdk.EventButton) -> bool:
# Handles zoom in / zoom out on Ctrl+mouse wheel
accel_mask = Gtk.accelerator_get_default_mod_mask()
if event.state & accel_mask == Gdk.ModifierType.CONTROL_MASK:
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
ignore=E501

[mypy]
python_version = 3.7
warn_return_any = True
warn_unused_configs = True
warn_unreachable = True
Expand All @@ -24,6 +23,7 @@ ignore_missing_imports = True

[mypy-gi.*]
ignore_missing_imports = True
follow_imports = skip

[mypy-lxml.etree]
ignore_missing_imports = True
Expand Down
Loading

0 comments on commit e6f583e

Please sign in to comment.