Skip to content

Commit

Permalink
Merge pull request bartfeenstra#881 from bartfeenstra/pytest-all-the-way
Browse files Browse the repository at this point in the history
Replace nose2 with pytest
  • Loading branch information
bartfeenstra authored May 31, 2022
2 parents 6484b34 + c156b78 commit d6cfa32
Show file tree
Hide file tree
Showing 93 changed files with 2,435 additions and 2,321 deletions.
1 change: 0 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ source = betty
omit =
betty/_package/*
betty/tests/*
betty/pytests/*

[report]
exclude_lines =
Expand Down
2 changes: 0 additions & 2 deletions betty/_package/pyinstaller/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ def _collect_submodules() -> List[str]:
def _filter_submodule(submodule: str) -> bool:
if submodule.startswith('betty.tests'):
return False
if submodule.startswith('betty.pytests'):
return False
if submodule.startswith('betty._package'):
return False
return True
Expand Down
32 changes: 29 additions & 3 deletions betty/anonymizer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from typing import Set, Type, TYPE_CHECKING


if TYPE_CHECKING:
from betty.builtins import _

Expand All @@ -16,7 +15,21 @@ class AnonymousSource(Source):
_ID = 'betty-anonymous-source'

def __init__(self):
super().__init__(self._ID, _('Private'))
super().__init__(self._ID)

@property # type: ignore
def name(self) -> str: # type: ignore
return _('Private')

@name.setter
def name(self, _) -> None:
# This is a no-op as the name is 'hardcoded'.
pass

@name.deleter
def name(self) -> None:
# This is a no-op as the name is 'hardcoded'.
pass

def replace(self, other: Source, ancestry: Ancestry) -> None:
if isinstance(other, AnonymousSource):
Expand All @@ -36,7 +49,20 @@ class AnonymousCitation(Citation):

def __init__(self, source: Source):
super().__init__(self._ID, source)
self.location = _("A citation is available, but has not been published in order to protect people's privacy")

@property # type: ignore
def location(self) -> str: # type: ignore
return _("A citation is available, but has not been published in order to protect people's privacy")

@location.setter
def location(self, _) -> None:
# This is a no-op as the location is 'hardcoded'.
pass

@location.deleter
def location(self) -> None:
# This is a no-op as the location is 'hardcoded'.
pass

def replace(self, other: Citation, ancestry: Ancestry) -> None:
if isinstance(other, AnonymousCitation):
Expand Down
12 changes: 5 additions & 7 deletions betty/app/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,18 @@

import asyncio
from collections import defaultdict
from importlib.metadata import entry_points
from pathlib import Path
from typing import Type, Set, Optional, Any, List, Dict, Sequence, TypeVar, Union, Iterable, TYPE_CHECKING, Generic

from betty.config import ConfigurationT, Configurable

from reactives.factory.type import ReactiveInstance

from betty import fs
from betty.config import ConfigurationT, Configurable
from betty.requirement import Requirer, AllRequirements

if TYPE_CHECKING:
from betty.app import App
try:
from importlib.metadata import entry_points
except ImportError:
from importlib_metadata import entry_points

from betty.dispatch import Dispatcher, TargetedDispatcher
from betty.importlib import import_any
Expand Down Expand Up @@ -52,6 +48,7 @@ class Extension(Requirer):
"""

def __init__(self, app: App, *args, **kwargs):
assert type(self) != Extension
super().__init__(*args, **kwargs)
self._app = app

Expand Down Expand Up @@ -93,6 +90,7 @@ def cache_directory_path(self) -> Path:

class ConfigurableExtension(Extension, Generic[ConfigurationT], Configurable[ConfigurationT]):
def __init__(self, *args, **kwargs):
assert type(self) != ConfigurableExtension
if 'configuration' not in kwargs or kwargs['configuration'] is None:
kwargs['configuration'] = self.default_configuration()
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -176,7 +174,7 @@ async def _dispatch(*args, **kwargs) -> List[Any]:
return _dispatch


def build_extension_type_graph(extension_types: Set[Type[Extension]]) -> Dict:
def build_extension_type_graph(extension_types: Iterable[Type[Extension]]) -> Dict:
extension_types_graph = defaultdict(set)
# Add dependencies to the extension graph.
for extension_type in extension_types:
Expand Down
1 change: 0 additions & 1 deletion betty/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from betty import fs


if TYPE_CHECKING:
from betty.builtins import _

Expand Down
4 changes: 2 additions & 2 deletions betty/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
from click import get_current_context, Context, Option

from betty import about, cache, demo, generate, load, serve
from betty.app import App
from betty.asyncio import sync
from betty.config import ConfigurationError
from betty.error import UserFacingError
from betty.asyncio import sync
from betty.gui import BettyApplication
from betty.gui.app import WelcomeWindow
from betty.gui.project import ProjectWindow
from betty.logging import CliHandler
from betty.app import App


class CommandProvider:
Expand Down
6 changes: 3 additions & 3 deletions betty/deriver/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
from __future__ import annotations

import logging
from typing import List, Tuple, Set, Type, Iterable, Optional, TYPE_CHECKING, cast

from betty.app.extension import Extension
from betty.model.ancestry import Person, Presence, Event, Subject, EventType, Ancestry
from betty.gui import GuiBuilder
from betty.locale import DateRange, Date, Datey
from betty.load import PostLoader
from betty.locale import DateRange, Date, Datey
from betty.model.ancestry import Person, Presence, Event, Subject, EventType, Ancestry
from betty.model.event_type import DerivableEventType, CreatableDerivableEventType
from betty.privatizer import Privatizer


if TYPE_CHECKING:
from betty.builtins import _

Expand Down
5 changes: 2 additions & 3 deletions betty/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
from os.path import getmtime
from pathlib import Path
from shutil import copy2
from typing import AsyncIterable, Optional, Tuple, AsyncContextManager, Sequence
from typing import AsyncIterable, Optional, Tuple, AsyncContextManager, Sequence, IO

import aiofiles

from betty import _ROOT_DIRECTORY_PATH
from betty.os import PathLike


ROOT_DIRECTORY_PATH = _ROOT_DIRECTORY_PATH


Expand Down Expand Up @@ -72,7 +71,7 @@ def prepend(self, path: PathLike, fs_encoding: Optional[str] = None) -> None:
def clear(self) -> None:
self._paths.clear()

def open(self, *file_paths: PathLike) -> AsyncContextManager[object]:
def open(self, *file_paths: PathLike) -> AsyncContextManager[IO]:
return self._Open(self, file_paths)

async def copy2(self, source_path: PathLike, destination_path: PathLike) -> Path:
Expand Down
2 changes: 1 addition & 1 deletion betty/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
from babel import Locale
from jinja2 import TemplateNotFound

from betty.app import App
from betty.jinja2 import Environment
from betty.json import JSONEncoder
from betty.locale import bcp_47_to_rfc_1766
from betty.model.ancestry import File, Person, Place, Event, Citation, Source, Note
from betty.openapi import build_specification
from betty.app import App

try:
from resource import getrlimit, RLIMIT_NOFILE
Expand Down
14 changes: 8 additions & 6 deletions betty/gramps/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from typing import Optional, TYPE_CHECKING
from __future__ import annotations

from typing import TYPE_CHECKING

from betty.app.extension import ConfigurableExtension

if TYPE_CHECKING:
from betty.builtins import _

from PyQt6.QtWidgets import QWidget
from betty.gramps.gui import _GrampsGuiWidget

from betty.gramps.config import GrampsConfiguration
from betty.gramps.gui import _GrampsGuiWidget
from betty.gramps.loader import load_file
from betty.gui import GuiBuilder
from betty.load import Loader
Expand All @@ -31,5 +31,7 @@ def label(cls) -> str:
def gui_description(cls) -> str:
return _('Load <a href="https://gramps-project.org/">Gramps</a> family trees.')

def gui_build(self) -> Optional[QWidget]:
return _GrampsGuiWidget(self._app, self.configuration)
def gui_build(self) -> _GrampsGuiWidget:
from betty.gramps.gui import _GrampsGuiWidget

return _GrampsGuiWidget(self._app)
60 changes: 40 additions & 20 deletions betty/gramps/gui.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import annotations

from typing import List

from PyQt6.QtCore import Qt
Expand All @@ -8,25 +9,28 @@

from betty.app import App
from betty.config import Path, ConfigurationError
from betty.gramps.config import FamilyTreeConfiguration, GrampsConfiguration
from betty.gramps import Gramps
from betty.gramps.config import FamilyTreeConfiguration
from betty.gui import BettyWindow, mark_valid, mark_invalid
from betty.gui.error import catch_exceptions
from betty.gui.locale import LocalizedWidget
from betty.gui.text import Text


@reactive
class _GrampsGuiWidget(LocalizedWidget):
def __init__(self, app: App, configuration: GrampsConfiguration, *args, **kwargs):
class _FamilyTrees(LocalizedWidget):
def __init__(self, app: App, *args, **kwargs):
super().__init__(*args, **kwargs)
self._app = app
self._configuration = configuration

self._layout = QVBoxLayout()
self.setLayout(self._layout)

self._family_trees_widget = None
self._family_trees_widget: QWidget = None # type: ignore
self._family_trees_layout: QGridLayout = None # type: ignore
self._family_trees_remove_buttons: List[QPushButton] = None # type: ignore

self._build_family_trees()

self._add_family_tree_button = QPushButton()
self._add_family_tree_button.released.connect(self._add_family_tree)
self._layout.addWidget(self._add_family_tree_button, 1)
Expand All @@ -37,36 +41,52 @@ def _build_family_trees(self) -> None:
self._layout.removeWidget(self._family_trees_widget)
self._family_trees_widget.setParent(None)
del self._family_trees_widget
del self._family_trees_layout
del self._family_trees_remove_buttons

self._family_trees_widget = QWidget()
family_trees_layout = QGridLayout()
self._family_trees_widget.setLayout(family_trees_layout)
self._family_trees_widget._remove_buttons = []
for i, family_tree in enumerate(self._configuration.family_trees):
self._family_trees_layout = QGridLayout()
self._family_trees_remove_buttons = []
self._family_trees_widget.setLayout(self._family_trees_layout)
self._layout.addWidget(self._family_trees_widget)

for i, family_tree in enumerate(self._app.extensions[Gramps].configuration.family_trees):
def _remove_family_tree() -> None:
del self._configuration.family_trees[i]
family_trees_layout.addWidget(Text(str(family_tree.file_path)), i, 0)
self._family_trees_widget._remove_buttons.insert(i, QPushButton())
self._family_trees_widget._remove_buttons[i].released.connect(_remove_family_tree)
family_trees_layout.addWidget(self._family_trees_widget._remove_buttons[i], i, 1)
del self._app.extensions[Gramps].configuration.family_trees[i]
self._family_trees_layout.addWidget(Text(str(family_tree.file_path)), i, 0)
self._family_trees_remove_buttons.insert(i, QPushButton())
self._family_trees_remove_buttons[i].released.connect(_remove_family_tree)
self._family_trees_layout.addWidget(self._family_trees_remove_buttons[i], i, 1)
self._layout.insertWidget(0, self._family_trees_widget, alignment=Qt.AlignmentFlag.AlignTop)

def _do_set_translatables(self) -> None:
self._add_family_tree_button.setText(_('Add a family tree'))
for button in self._family_trees_widget._remove_buttons:
for button in self._family_trees_remove_buttons:
button.setText(_('Remove'))

def _add_family_tree(self):
window = _AddFamilyTreeWindow(self._app, self._configuration.family_trees, self)
window = _AddFamilyTreeWindow(self._app, self)
window.show()


@reactive
class _GrampsGuiWidget(LocalizedWidget):
def __init__(self, app: App, *args, **kwargs):
super().__init__(*args, **kwargs)
self._app = app
self._layout = QVBoxLayout()
self.setLayout(self._layout)

self._family_trees = _FamilyTrees(self._app)
self._layout.addWidget(self._family_trees)


class _AddFamilyTreeWindow(BettyWindow):
width = 500
height = 100

def __init__(self, app: App, family_trees: List[FamilyTreeConfiguration], *args, **kwargs):
def __init__(self, app: App, *args, **kwargs):
super().__init__(app, *args, **kwargs)
self._family_trees = family_trees
self._family_tree = None

self._layout = QFormLayout()
Expand Down Expand Up @@ -115,7 +135,7 @@ def find_family_tree_file_path() -> None:

@catch_exceptions
def save_and_close_family_tree() -> None:
self._family_trees.append(self._family_tree)
self._app.extensions[Gramps].configuration.family_trees.append(self._family_tree)
self.close()
self._widget._save_and_close = QPushButton()
self._widget._save_and_close.setDisabled(True)
Expand Down
2 changes: 1 addition & 1 deletion betty/gui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
from betty.asyncio import sync
from betty.gui import BettyWindow, get_configuration_file_filter
from betty.gui.error import catch_exceptions
from betty.gui.locale import TranslationsLocaleCollector
from betty.gui.serve import ServeDemoWindow
from betty.gui.text import Text
from betty.gui.locale import TranslationsLocaleCollector
from betty.importlib import import_any
from betty.project import ProjectConfiguration

Expand Down
4 changes: 2 additions & 2 deletions betty/gui/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
from betty.asyncio import sync
from betty.config import ConfigurationError
from betty.gui import get_configuration_file_filter, BettyWindow, GuiBuilder, mark_invalid, mark_valid
from betty.gui.app import BettyMainWindow
from betty.gui.error import catch_exceptions
from betty.gui.locale import LocalizedWidget
from betty.gui.text import Text, Caption
from betty.gui.app import BettyMainWindow
from betty.gui.locale import TranslationsLocaleCollector
from betty.gui.logging import LogRecordViewerHandler, LogRecordViewer
from betty.gui.model import EntityReferenceCollector, EntityReferencesCollector
from betty.gui.serve import ServeAppWindow
from betty.gui.text import Text, Caption
from betty.importlib import import_any
from betty.locale import rfc_1766_to_bcp_47, bcp_47_to_rfc_1766
from betty.project import ProjectExtensionConfiguration, LocaleConfiguration, LocalesConfiguration
Expand Down
1 change: 0 additions & 1 deletion betty/gui/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ def _start(self) -> None:
self._thread.server_started.connect(self._server_started)
self._thread.start()

@sync
def close(self) -> bool:
self._stop()
return super().close()
Expand Down
2 changes: 1 addition & 1 deletion betty/jinja2.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class _Citer:
def __init__(self):
self._citations = []

def __iter__(self) -> Iterable[Citation]:
def __iter__(self) -> Iterator[Citation]:
return enumerate(self._citations, 1)

def __len__(self) -> int:
Expand Down
Loading

0 comments on commit d6cfa32

Please sign in to comment.