refactor: unwrap instead of assert not None (#1068)

* refactor: unwrap instead of assert not None

* fix: address review feedback
This commit is contained in:
Jann Stute
2025-08-28 22:27:49 +02:00
committed by GitHub
parent 12e074b71d
commit e551359845
23 changed files with 148 additions and 197 deletions

View File

@@ -18,6 +18,7 @@ sys.path.insert(0, str(CWD.parent))
from tagstudio.core.constants import THUMB_CACHE_NAME, TS_FOLDER_NAME
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Entry, Tag
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.ts_qt import QtDriver
@@ -32,22 +33,22 @@ def file_mediatypes_library():
status = lib.open_library(Path(""), ":memory:")
assert status.success
assert lib.folder
folder = unwrap(lib.folder)
entry1 = Entry(
folder=lib.folder,
folder=folder,
path=Path("foo.png"),
fields=lib.default_fields,
)
entry2 = Entry(
folder=lib.folder,
folder=folder,
path=Path("bar.png"),
fields=lib.default_fields,
)
entry3 = Entry(
folder=lib.folder,
folder=folder,
path=Path("baz.apng"),
fields=lib.default_fields,
)
@@ -83,7 +84,7 @@ def library(request, library_dir: Path): # pyright: ignore
lib = Library()
status = lib.open_library(library_path, ":memory:")
assert status.success
assert lib.folder
folder = unwrap(lib.folder)
tag = Tag(
name="foo",
@@ -112,7 +113,7 @@ def library(request, library_dir: Path): # pyright: ignore
# default item with deterministic name
entry = Entry(
id=1,
folder=lib.folder,
folder=folder,
path=Path("foo.txt"),
fields=lib.default_fields,
)
@@ -120,7 +121,7 @@ def library(request, library_dir: Path): # pyright: ignore
entry2 = Entry(
id=2,
folder=lib.folder,
folder=folder,
path=Path("one/two/bar.md"),
fields=lib.default_fields,
)

View File

@@ -7,22 +7,23 @@ from pathlib import Path
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Entry
from tagstudio.core.utils.dupe_files import DupeRegistry
from tagstudio.core.utils.types import unwrap
CWD = Path(__file__).parent
def test_refresh_dupe_files(library: Library):
library.library_dir = Path("/tmp/")
assert library.folder
folder = unwrap(library.folder)
entry = Entry(
folder=library.folder,
folder=folder,
path=Path("bar/foo.txt"),
fields=library.default_fields,
)
entry2 = Entry(
folder=library.folder,
folder=folder,
path=Path("foo/foo.txt"),
fields=library.default_fields,
)

View File

@@ -10,6 +10,7 @@ import pytest
from tagstudio.core.library.alchemy.enums import BrowsingState
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.utils.missing_files import MissingRegistry
from tagstudio.core.utils.types import unwrap
CWD = Path(__file__).parent
@@ -20,8 +21,7 @@ def test_refresh_missing_files(library: Library):
registry = MissingRegistry(library=library)
# touch the file `one/two/bar.md` but in wrong location to simulate a moved file
assert library.library_dir
(library.library_dir / "bar.md").touch()
(unwrap(library.library_dir) / "bar.md").touch()
# no files actually exist, so it should return all entries
assert list(registry.refresh_missing_files()) == [0, 1]

View File

@@ -10,6 +10,7 @@ import pytest
from tagstudio.core.enums import LibraryPrefs
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.utils.refresh_dir import RefreshDirTracker
from tagstudio.core.utils.types import unwrap
CWD = Path(__file__).parent
@@ -17,14 +18,14 @@ CWD = Path(__file__).parent
@pytest.mark.parametrize("exclude_mode", [True, False])
@pytest.mark.parametrize("library", [TemporaryDirectory()], indirect=True)
def test_refresh_new_files(library: Library, exclude_mode: bool):
assert library.library_dir
library_dir = unwrap(library.library_dir)
# Given
library.set_prefs(LibraryPrefs.IS_EXCLUDE_LIST, exclude_mode)
library.set_prefs(LibraryPrefs.EXTENSION_LIST, [".md"])
registry = RefreshDirTracker(library=library)
library.included_files.clear()
(library.library_dir / "FOO.MD").touch()
(library_dir / "FOO.MD").touch()
# Test if the single file was added
list(registry.refresh_dir(library.library_dir, force_internal_tools=True))
list(registry.refresh_dir(library_dir, force_internal_tools=True))
assert registry.files_not_in_library == [Path("FOO.MD")]

View File

@@ -9,6 +9,7 @@ from pytestqt.qtbot import QtBot
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Tag, TagAlias
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.modals.build_tag import BuildTagPanel, CustomTableItem
from tagstudio.qt.translations import Translations
@@ -16,10 +17,8 @@ from tagstudio.qt.translations import Translations
def test_build_tag_panel_add_sub_tag_callback(
qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag]
):
parent = library.add_tag(generate_tag("xxx", id=123))
child = library.add_tag(generate_tag("xx", id=124))
assert child
assert parent
parent = unwrap(library.add_tag(generate_tag("xxx", id=123)))
child = unwrap(library.add_tag(generate_tag("xx", id=124)))
panel: BuildTagPanel = BuildTagPanel(library, child)
qtbot.addWidget(panel)
@@ -32,16 +31,12 @@ def test_build_tag_panel_add_sub_tag_callback(
def test_build_tag_panel_remove_subtag_callback(
qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag]
):
parent = library.add_tag(generate_tag("xxx", id=123))
child = library.add_tag(generate_tag("xx", id=124))
assert child
assert parent
parent = unwrap(library.add_tag(generate_tag("xxx", id=123)))
child = unwrap(library.add_tag(generate_tag("xx", id=124)))
library.update_tag(child, {parent.id}, [], [])
child = library.get_tag(child.id)
assert child
child = unwrap(library.get_tag(child.id))
panel: BuildTagPanel = BuildTagPanel(library, child)
qtbot.addWidget(panel)
@@ -59,8 +54,7 @@ os.environ["QT_QPA_PLATFORM"] = "offscreen"
def test_build_tag_panel_add_alias_callback(
qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag]
):
tag = library.add_tag(generate_tag("xxx", id=123))
assert tag
tag = unwrap(library.add_tag(generate_tag("xxx", id=123)))
panel: BuildTagPanel = BuildTagPanel(library, tag)
qtbot.addWidget(panel)
@@ -73,13 +67,11 @@ def test_build_tag_panel_add_alias_callback(
def test_build_tag_panel_remove_alias_callback(
qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag]
):
tag: Tag | None = library.add_tag(generate_tag("xxx", id=123))
assert tag
tag: Tag = unwrap(library.add_tag(generate_tag("xxx", id=123)))
library.update_tag(tag, [], {"alias", "alias_2"}, {123, 124})
tag = library.get_tag(tag.id)
assert tag
tag = unwrap(library.get_tag(tag.id))
assert "alias" in tag.alias_strings
assert "alias_2" in tag.alias_strings
@@ -87,8 +79,7 @@ def test_build_tag_panel_remove_alias_callback(
panel: BuildTagPanel = BuildTagPanel(library, tag)
qtbot.addWidget(panel)
alias: TagAlias | None = library.get_alias(tag.id, tag.alias_ids[0])
assert alias
alias: TagAlias = unwrap(library.get_alias(tag.id, tag.alias_ids[0]))
panel.remove_alias_callback(alias.name, alias.id)
@@ -100,10 +91,8 @@ def test_build_tag_panel_remove_alias_callback(
def test_build_tag_panel_set_parent_tags(
qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag]
):
parent = library.add_tag(generate_tag("parent", id=123))
child = library.add_tag(generate_tag("child", id=124))
assert parent
assert child
parent = unwrap(library.add_tag(generate_tag("parent", id=123)))
child = unwrap(library.add_tag(generate_tag("child", id=124)))
library.add_parent_tag(parent.id, child.id)
@@ -119,13 +108,11 @@ def test_build_tag_panel_set_parent_tags(
def test_build_tag_panel_add_aliases(
qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag]
):
tag: Tag | None = library.add_tag(generate_tag("xxx", id=123))
assert tag
tag: Tag = unwrap(library.add_tag(generate_tag("xxx", id=123)))
library.update_tag(tag, [], {"alias", "alias_2"}, {123, 124})
tag = library.get_tag(tag.id)
assert tag
tag = unwrap(library.get_tag(tag.id))
assert "alias" in tag.alias_strings
assert "alias_2" in tag.alias_strings
@@ -159,13 +146,11 @@ def test_build_tag_panel_add_aliases(
def test_build_tag_panel_set_aliases(
qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag]
):
tag: Tag | None = library.add_tag(generate_tag("xxx", id=123))
assert tag
tag: Tag = unwrap(library.add_tag(generate_tag("xxx", id=123)))
library.update_tag(tag, [], {"alias"}, {123})
tag = library.get_tag(tag.id)
assert tag
tag = unwrap(library.get_tag(tag.id))
assert len(tag.alias_ids) == 1
@@ -178,14 +163,12 @@ def test_build_tag_panel_set_aliases(
def test_build_tag_panel_set_tag(qtbot: QtBot, library: Library, generate_tag: Callable[..., Tag]):
tag = library.add_tag(generate_tag("xxx", id=123))
assert tag
tag = unwrap(library.add_tag(generate_tag("xxx", id=123)))
panel: BuildTagPanel = BuildTagPanel(library, tag)
qtbot.addWidget(panel)
assert panel.tag
assert panel.tag.name == "xxx"
assert unwrap(panel.tag).name == "xxx"
def test_build_tag_panel_build_tag(qtbot: QtBot, library: Library):
@@ -194,5 +177,4 @@ def test_build_tag_panel_build_tag(qtbot: QtBot, library: Library):
tag: Tag = panel.build_tag()
assert tag
assert tag.name == Translations["tag.new"]

View File

@@ -5,6 +5,7 @@
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Entry, Tag
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.controller.widgets.preview_panel_controller import PreviewPanel
from tagstudio.qt.ts_qt import QtDriver
@@ -139,8 +140,7 @@ def test_meta_tag_category(qt_driver: QtDriver, library: Library, entry_full: En
match i:
case 0:
# Check if the container is the Meta Tags category
tag: Tag | None = library.get_tag(2)
assert tag
tag: Tag = unwrap(library.get_tag(2))
assert container.title == f"<h4>{tag.name}</h4>"
case 1:
# Check if the container is the Tags category
@@ -156,8 +156,7 @@ def test_custom_tag_category(qt_driver: QtDriver, library: Library, entry_full:
panel = PreviewPanel(library, qt_driver)
# Set tag 1000 (foo) as a category
tag: Tag | None = library.get_tag(1000)
assert tag
tag: Tag = unwrap(library.get_tag(1000))
tag.is_category = True
library.update_tag(
tag,
@@ -176,8 +175,7 @@ def test_custom_tag_category(qt_driver: QtDriver, library: Library, entry_full:
match i:
case 0:
# Check if the container is the Meta Tags category
tag_2: Tag | None = library.get_tag(2)
assert tag_2
tag_2: Tag = unwrap(library.get_tag(2))
assert container.title == f"<h4>{tag_2.name}</h4>"
case 1:
# Check if the container is the custom "foo" category

View File

@@ -18,6 +18,7 @@ from pytestqt.qtbot import QtBot
from tagstudio.core.enums import ShowFilepathOption
from tagstudio.core.library.alchemy.library import Library, LibraryStatus
from tagstudio.core.library.alchemy.models import Entry
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.controller.widgets.preview_panel_controller import PreviewPanel
from tagstudio.qt.modals.settings_panel import SettingsPanel
from tagstudio.qt.ts_qt import QtDriver
@@ -76,8 +77,7 @@ def test_file_path_display(
entry = library.get_entry(2)
assert isinstance(entry, Entry)
filename = entry.path
assert library.library_dir is not None
panel._file_attributes_widget.update_stats(filepath=library.library_dir / filename) # pyright: ignore[reportPrivateUsage]
panel._file_attributes_widget.update_stats(filepath=unwrap(library.library_dir) / filename) # pyright: ignore[reportPrivateUsage]
# Generate the expected file string.
# This is copied directly from the file_attributes.py file

View File

@@ -4,6 +4,7 @@
from tagstudio.core.library.alchemy.enums import BrowsingState, ItemType
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.ts_qt import QtDriver
from tagstudio.qt.widgets.item_thumb import ItemThumb
@@ -23,23 +24,20 @@ def test_browsing_state_update(qt_driver: QtDriver):
state = BrowsingState.from_tag_name("foo")
qt_driver.update_browsing_state(state)
assert len(qt_driver.frame_content) == 1
entry = qt_driver.lib.get_entry_full(qt_driver.frame_content[0])
assert entry
entry = unwrap(qt_driver.lib.get_entry_full(qt_driver.frame_content[0]))
assert list(entry.tags)[0].name == "foo"
# When state is not changed, previous one is still applied
qt_driver.update_browsing_state()
assert len(qt_driver.frame_content) == 1
entry = qt_driver.lib.get_entry_full(qt_driver.frame_content[0])
assert entry
entry = unwrap(qt_driver.lib.get_entry_full(qt_driver.frame_content[0]))
assert list(entry.tags)[0].name == "foo"
# When state property is changed, previous one is overwritten
state = BrowsingState.from_path("*bar.md")
qt_driver.update_browsing_state(state)
assert len(qt_driver.frame_content) == 1
entry = qt_driver.lib.get_entry_full(qt_driver.frame_content[0])
assert entry
entry = unwrap(qt_driver.lib.get_entry_full(qt_driver.frame_content[0]))
assert list(entry.tags)[0].name == "bar"

View File

@@ -18,81 +18,70 @@ from tagstudio.core.library.alchemy.fields import (
)
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Entry, Tag
from tagstudio.core.utils.types import unwrap
logger = structlog.get_logger()
def test_library_add_alias(library: Library, generate_tag: Callable[..., Tag]):
tag = library.add_tag(generate_tag("xxx", id=123))
assert tag
tag = unwrap(library.add_tag(generate_tag("xxx", id=123)))
parent_ids: set[int] = set()
alias_ids: set[int] = set()
alias_names: set[str] = set()
alias_names.add("test_alias")
library.update_tag(tag, parent_ids, alias_names, alias_ids)
tag = library.get_tag(tag.id)
assert tag is not None
tag = unwrap(library.get_tag(tag.id))
alias_ids = set(tag.alias_ids)
assert len(alias_ids) == 1
def test_library_get_alias(library: Library, generate_tag: Callable[..., Tag]):
tag = library.add_tag(generate_tag("xxx", id=123))
assert tag
tag = unwrap(library.add_tag(generate_tag("xxx", id=123)))
parent_ids: set[int] = set()
alias_ids: list[int] = []
alias_names: set[str] = set()
alias_names.add("test_alias")
library.update_tag(tag, parent_ids, alias_names, alias_ids)
tag = library.get_tag(tag.id)
assert tag is not None
tag = unwrap(library.get_tag(tag.id))
alias_ids = tag.alias_ids
alias = library.get_alias(tag.id, alias_ids[0])
assert alias is not None
alias = unwrap(library.get_alias(tag.id, alias_ids[0]))
assert alias.name == "test_alias"
def test_library_update_alias(library: Library, generate_tag: Callable[..., Tag]):
tag: Tag | None = library.add_tag(generate_tag("xxx", id=123))
assert tag is not None
tag: Tag = unwrap(library.add_tag(generate_tag("xxx", id=123)))
parent_ids: set[int] = set()
alias_ids: list[int] = []
alias_names: set[str] = set()
alias_names.add("test_alias")
library.update_tag(tag, parent_ids, alias_names, alias_ids)
tag = library.get_tag(tag.id)
assert tag is not None
tag = unwrap(library.get_tag(tag.id))
alias_ids = tag.alias_ids
alias = library.get_alias(tag.id, alias_ids[0])
assert alias is not None
alias = unwrap(library.get_alias(tag.id, alias_ids[0]))
assert alias.name == "test_alias"
alias_names.remove("test_alias")
alias_names.add("alias_update")
library.update_tag(tag, parent_ids, alias_names, alias_ids)
tag = library.get_tag(tag.id)
assert tag is not None
tag = unwrap(library.get_tag(tag.id))
assert len(tag.alias_ids) == 1
alias = library.get_alias(tag.id, tag.alias_ids[0])
assert alias is not None
alias = unwrap(library.get_alias(tag.id, tag.alias_ids[0]))
assert alias.name == "alias_update"
@pytest.mark.parametrize("library", [TemporaryDirectory()], indirect=True)
def test_library_add_file(library: Library):
"""Check Entry.path handling for insert vs lookup"""
assert library.folder is not None
entry = Entry(
path=Path("bar.txt"),
folder=library.folder,
folder=unwrap(library.folder),
fields=library.default_fields,
)
@@ -103,30 +92,26 @@ def test_library_add_file(library: Library):
def test_create_tag(library: Library, generate_tag: Callable[..., Tag]):
# tag already exists
assert not library.add_tag(generate_tag("foo", id=1000))
assert library.add_tag(generate_tag("foo", id=1000)) is None
# new tag name
tag = library.add_tag(generate_tag("xxx", id=123))
assert tag
tag = unwrap(library.add_tag(generate_tag("xxx", id=123)))
assert tag.id == 123
tag_inc = library.add_tag(generate_tag("yyy"))
assert tag_inc is not None
tag_inc = unwrap(library.add_tag(generate_tag("yyy")))
assert tag_inc.id > 1000
def test_tag_self_parent(library: Library, generate_tag: Callable[..., Tag]):
# tag already exists
assert not library.add_tag(generate_tag("foo", id=1000))
assert library.add_tag(generate_tag("foo", id=1000)) is None
# new tag name
tag = library.add_tag(generate_tag("xxx", id=123))
assert tag
tag = unwrap(library.add_tag(generate_tag("xxx", id=123)))
assert tag.id == 123
library.update_tag(tag, {tag.id}, [], [])
tag = library.get_tag(tag.id)
assert tag is not None
tag = unwrap(library.get_tag(tag.id))
assert len(tag.parent_ids) == 0
@@ -153,15 +138,13 @@ def test_tag_search(library: Library):
def test_get_entry(library: Library, entry_min: Entry):
assert entry_min.id
result = library.get_entry_full(entry_min.id)
assert result
result = unwrap(library.get_entry_full(unwrap(entry_min.id)))
assert len(result.tags) == 1
def test_entries_count(library: Library):
assert library.folder is not None
entries = [Entry(path=Path(f"{x}.txt"), folder=library.folder, fields=[]) for x in range(10)]
folder = unwrap(library.folder)
entries = [Entry(path=Path(f"{x}.txt"), folder=folder, fields=[]) for x in range(10)]
new_ids = library.add_entries(entries)
assert len(new_ids) == 10
@@ -173,29 +156,21 @@ def test_entries_count(library: Library):
def test_parents_add(library: Library, generate_tag: Callable[..., Tag]):
# Given
tag: Tag | None = library.tags[0]
assert tag.id is not None
tag: Tag = library.tags[0]
parent_tag: Tag | None = generate_tag("parent_tag_01")
assert parent_tag
parent_tag = library.add_tag(parent_tag)
assert parent_tag is not None
assert parent_tag.id is not None
parent_tag: Tag = generate_tag("parent_tag_01")
parent_tag = unwrap(library.add_tag(parent_tag))
# When
assert library.add_parent_tag(tag.id, parent_tag.id)
assert library.add_parent_tag(tag.id, unwrap(parent_tag.id))
# Then
assert tag.id is not None
tag = library.get_tag(tag.id)
assert tag is not None
tag = unwrap(library.get_tag(unwrap(tag.id)))
assert tag.parent_ids
def test_remove_tag(library: Library, generate_tag: Callable[..., Tag]):
tag = library.add_tag(generate_tag("food", id=123))
assert tag
tag = unwrap(library.add_tag(generate_tag("food", id=123)))
tag_count = len(library.tags)
@@ -311,9 +286,8 @@ def test_update_entry_with_multiple_identical_fields(library: Library, entry_ful
def test_mirror_entry_fields(library: Library, entry_full: Entry):
# new entry
assert library.folder is not None
target_entry = Entry(
folder=library.folder,
folder=unwrap(library.folder),
path=Path("xxx"),
fields=[
TextField(
@@ -328,15 +302,13 @@ def test_mirror_entry_fields(library: Library, entry_full: Entry):
entry_id = library.add_entries([target_entry])[0]
# get new entry from library
new_entry = library.get_entry_full(entry_id)
assert new_entry is not None
new_entry = unwrap(library.get_entry_full(entry_id))
# mirror fields onto new entry
library.mirror_entry_fields(new_entry, entry_full)
# get new entry from library again
entry = library.get_entry_full(entry_id)
assert entry is not None
entry = unwrap(library.get_entry_full(entry_id))
# make sure fields are there after getting it from the library again
assert len(entry.fields) == 2
@@ -347,17 +319,14 @@ def test_mirror_entry_fields(library: Library, entry_full: Entry):
def test_merge_entries(library: Library):
assert library.folder is not None
folder = unwrap(library.folder)
tag_0: Tag | None = library.add_tag(Tag(id=1010, name="tag_0"))
assert tag_0 is not None
tag_1: Tag | None = library.add_tag(Tag(id=1011, name="tag_1"))
assert tag_1 is not None
tag_2: Tag | None = library.add_tag(Tag(id=1012, name="tag_2"))
assert tag_2 is not None
tag_0: Tag = unwrap(library.add_tag(Tag(id=1010, name="tag_0")))
tag_1: Tag = unwrap(library.add_tag(Tag(id=1011, name="tag_1")))
tag_2: Tag = unwrap(library.add_tag(Tag(id=1012, name="tag_2")))
a = Entry(
folder=library.folder,
folder=folder,
path=Path("a"),
fields=[
TextField(type_key=_FieldID.AUTHOR.name, value="Author McAuthorson", position=0),
@@ -365,7 +334,7 @@ def test_merge_entries(library: Library):
],
)
b = Entry(
folder=library.folder,
folder=folder,
path=Path("b"),
fields=[TextField(type_key=_FieldID.NOTES.name, value="test note", position=1)],
)
@@ -374,17 +343,14 @@ def test_merge_entries(library: Library):
library.add_tags_to_entries(ids[0], [tag_0.id, tag_2.id])
library.add_tags_to_entries(ids[1], [tag_1.id])
entry_a: Entry | None = library.get_entry_full(ids[0])
assert entry_a is not None
entry_b: Entry | None = library.get_entry_full(ids[1])
assert entry_b is not None
entry_a: Entry = unwrap(library.get_entry_full(ids[0]))
entry_b: Entry = unwrap(library.get_entry_full(ids[1]))
assert library.merge_entries(entry_a, entry_b)
assert not library.has_path_entry(Path("a"))
assert library.has_path_entry(Path("b"))
entry_b_merged = library.get_entry_full(ids[1])
assert entry_b_merged
entry_b_merged = unwrap(library.get_entry_full(ids[1]))
fields = [field.value for field in entry_b_merged.fields]
assert "Author McAuthorson" in fields