Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/tagstudio/core/library/alchemy/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
)
from tagstudio.core.library.alchemy.visitors import SQLBoolExpressionBuilder
from tagstudio.core.library.json.library import Library as JsonLibrary
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.translations import Translations

if TYPE_CHECKING:
Expand Down Expand Up @@ -925,10 +926,9 @@ def search_library(
:return: number of entries matching the query and one page of results.
"""
assert isinstance(search, BrowsingState)
assert self.engine
assert self.library_dir

with Session(self.engine, expire_on_commit=False) as session:
with Session(unwrap(self.engine), expire_on_commit=False) as session:
statement = select(Entry.id, func.count().over())

if search.ast:
Expand Down Expand Up @@ -1705,18 +1705,18 @@ def set_prefs(self, key: str | LibraryPrefs, value: Any) -> None:
# set given item in Preferences table
with Session(self.engine) as session:
# load existing preference and update value
pref: Preferences | None

stuff = session.scalars(select(Preferences))
logger.info([x.key for x in list(stuff)])

if isinstance(key, LibraryPrefs):
pref = session.scalar(select(Preferences).where(Preferences.key == key.name))
else:
pref = session.scalar(select(Preferences).where(Preferences.key == key))
pref: Preferences = unwrap(
session.scalar(
select(Preferences).where(
Preferences.key == (key.name if isinstance(key, LibraryPrefs) else key)
)
)
)

logger.info("loading pref", pref=pref, key=key, value=value)
assert pref is not None
pref.value = value
session.add(pref)
session.commit()
Expand Down
7 changes: 3 additions & 4 deletions src/tagstudio/core/library/alchemy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ def alias_ids(self) -> list[int]:

def __init__(
self,
name: str,
id: int | None = None,
name: str | None = None,
shorthand: str | None = None,
aliases: set[TagAlias] | None = None,
parent_tags: set["Tag"] | None = None,
Expand All @@ -146,8 +146,7 @@ def __init__(
self.shorthand = shorthand
self.disambiguation_id = disambiguation_id
self.is_category = is_category
assert not self.id
self.id = id
self.id = id # pyright: ignore[reportAttributeAccessIssue]
super().__init__()

def __str__(self) -> str:
Expand Down Expand Up @@ -232,7 +231,7 @@ def __init__(
) -> None:
self.path = path
self.folder = folder
self.id = id
self.id = id # pyright: ignore[reportAttributeAccessIssue]
self.filename = path.name
self.suffix = path.suffix.lstrip(".").lower()

Expand Down
16 changes: 8 additions & 8 deletions src/tagstudio/core/utils/missing_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Entry
from tagstudio.core.library.ignore import PATH_GLOB_FLAGS, Ignore
from tagstudio.core.utils.types import unwrap

logger = structlog.get_logger()

Expand All @@ -26,12 +27,11 @@ def missing_file_entries_count(self) -> int:

def refresh_missing_files(self) -> Iterator[int]:
"""Track the number of entries that point to an invalid filepath."""
assert self.library.library_dir
logger.info("[refresh_missing_files] Refreshing missing files...")

self.missing_file_entries = []
for i, entry in enumerate(self.library.all_entries()):
full_path = self.library.library_dir / entry.path
full_path = unwrap(self.library.library_dir) / entry.path
if not full_path.exists() or not full_path.is_file():
self.missing_file_entries.append(entry)
yield i
Expand All @@ -41,19 +41,19 @@ def match_missing_file_entry(self, match_entry: Entry) -> list[Path]:

Works if files were just moved to different subfolders and don't have duplicate names.
"""
assert self.library.library_dir
library_dir = unwrap(self.library.library_dir)
matches: list[Path] = []

ignore_patterns = Ignore.get_patterns(self.library.library_dir)
for path in pathlib.Path(str(self.library.library_dir)).glob(
ignore_patterns = Ignore.get_patterns(library_dir)
for path in pathlib.Path(str(library_dir)).glob(
f"***/{match_entry.path.name}",
flags=PATH_GLOB_FLAGS,
exclude=ignore_patterns,
):
if path.is_dir():
continue
if path.name == match_entry.path.name:
new_path = Path(path).relative_to(self.library.library_dir)
new_path = Path(path).relative_to(library_dir)
matches.append(new_path)

logger.info("[MissingRegistry] Matches", matches=matches)
Expand All @@ -73,8 +73,8 @@ def fix_unlinked_entries(self) -> Iterator[int]:
)
if not self.library.update_entry_path(entry.id, item_matches[0]):
try:
match = self.library.get_entry_full_by_path(item_matches[0])
entry_full = self.library.get_entry_full(entry.id)
match = unwrap(self.library.get_entry_full_by_path(item_matches[0]))
entry_full = unwrap(self.library.get_entry_full(entry.id))
self.library.merge_entries(entry_full, match)
except AttributeError:
continue
Expand Down
14 changes: 14 additions & 0 deletions src/tagstudio/core/utils/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio

from typing import TypeVar

T = TypeVar("T")


def unwrap(optional: T | None, default: T | None = None) -> T:
if optional is not None:
return optional
if default is not None:
return default
raise ValueError("Expected a value, but got None and no default was provided.")
8 changes: 4 additions & 4 deletions src/tagstudio/qt/controller/components/tag_box_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from tagstudio.core.enums import TagClickActionOption
from tagstudio.core.library.alchemy.enums import BrowsingState
from tagstudio.core.library.alchemy.models import Tag
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.modals.build_tag import BuildTagPanel
from tagstudio.qt.view.components.tag_box_view import TagBoxWidgetView
from tagstudio.qt.widgets.panel import PanelModal
Expand Down Expand Up @@ -47,10 +48,9 @@ def _on_click(self, tag: Tag) -> None: # type: ignore[misc]
# due to needing to implement a visitor that turns an AST to a string
# So if that exists when you read this, change the following accordingly.
current = self.__driver.browsing_history.current
suffix = BrowsingState.from_tag_id(
tag.id, self.__driver.browsing_history.current
).query
assert suffix is not None
suffix = unwrap(
BrowsingState.from_tag_id(tag.id, self.__driver.browsing_history.current).query
)
self.__driver.update_browsing_state(
current.with_search_query(
f"{current.query} {suffix}" if current.query else suffix
Expand Down
4 changes: 2 additions & 2 deletions src/tagstudio/qt/helpers/file_opener.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from PySide6.QtGui import QMouseEvent
from PySide6.QtWidgets import QLabel, QWidget

from tagstudio.core.utils.types import unwrap
from tagstudio.qt.helpers.silent_popen import silent_Popen

logger = structlog.get_logger(__name__)
Expand Down Expand Up @@ -148,8 +149,7 @@ def mousePressEvent(self, ev: QMouseEvent) -> None:
ev (QMouseEvent): The mouse press event.
"""
if ev.button() == Qt.MouseButton.LeftButton:
assert self.filepath is not None, "File path is not set"
opener = FileOpenerHelper(self.filepath)
opener = FileOpenerHelper(unwrap(self.filepath))
opener.open_explorer()
elif ev.button() == Qt.MouseButton.RightButton:
# Show context menu
Expand Down
7 changes: 3 additions & 4 deletions src/tagstudio/qt/modals/folders_to_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Tag
from tagstudio.core.palette import ColorType, get_tag_color
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.flowlayout import FlowLayout
from tagstudio.qt.translations import Translations

Expand Down Expand Up @@ -93,8 +94,7 @@ def reverse_tag(library: Library, tag: Tag, items: list[Tag] | None) -> list[Tag
parent_tag = None # to avoid subtag unbound error
for parent_tag_id in tag.parent_ids:
parent_tag = library.get_tag(parent_tag_id)
assert parent_tag is not None
return reverse_tag(library, parent_tag, items)
return reverse_tag(library, unwrap(parent_tag), items)


# =========== UI ===========
Expand Down Expand Up @@ -274,8 +274,7 @@ def __init__(self, data: BranchData, parent_tag: Tag | None = None):

self.label = QLabel()
self.tag_layout.addWidget(self.label)
assert data.tag is not None and parent_tag is not None
self.tag_widget = ModifiedTagWidget(data.tag, parent_tag)
self.tag_widget = ModifiedTagWidget(unwrap(data.tag), unwrap(parent_tag))
self.tag_widget.bg_button.clicked.connect(lambda: self.hide_show())
self.tag_layout.addWidget(self.tag_widget)

Expand Down
5 changes: 1 addition & 4 deletions src/tagstudio/qt/modals/tag_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,8 @@ def update_tags(self, query: str | None = None):
logger.info("[TagSearchPanel] Updating Tags")

# Remove the "Create & Add" button if one exists
create_button: QPushButton | None = None
if self.create_button_in_layout and self.scroll_layout.count():
create_button = self.scroll_layout.takeAt(self.scroll_layout.count() - 1).widget() # type: ignore
assert create_button is not None
create_button.deleteLater()
self.scroll_layout.takeAt(self.scroll_layout.count() - 1).widget().deleteLater()
self.create_button_in_layout = False

# Get results for the search query
Expand Down
7 changes: 4 additions & 3 deletions src/tagstudio/qt/ts_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
from tagstudio.core.query_lang.util import ParsingError
from tagstudio.core.ts_core import TagStudioCore
from tagstudio.core.utils.refresh_dir import RefreshDirTracker
from tagstudio.core.utils.types import unwrap
from tagstudio.core.utils.web import strip_web_protocol
from tagstudio.qt.cache_manager import CacheManager
from tagstudio.qt.helpers.custom_runnable import CustomRunnable
Expand Down Expand Up @@ -979,7 +980,6 @@ def delete_file_confirmation(self, count: int, filename: Path | None = None) ->

def add_new_files_callback(self):
"""Run when user initiates adding new files to the Library."""
assert self.lib.library_dir
tracker = RefreshDirTracker(self.lib)

pw = ProgressWidget(
Expand All @@ -991,7 +991,9 @@ def add_new_files_callback(self):
pw.update_label(Translations["library.refresh.scanning_preparing"])
pw.show()

iterator = FunctionIterator(lambda lib=self.lib.library_dir: tracker.refresh_dir(lib))
iterator = FunctionIterator(
lambda lib=unwrap(self.lib.library_dir): tracker.refresh_dir(lib) # noqa: B008
)
iterator.value.connect(
lambda x: (
pw.update_progress(x + 1),
Expand Down Expand Up @@ -1534,7 +1536,6 @@ def update_browsing_state(self, state: BrowsingState | None = None) -> None:
if not self.lib.library_dir:
logger.info("Library not loaded")
return
assert self.lib.engine

if state:
self.browsing_history.push(state)
Expand Down
7 changes: 3 additions & 4 deletions src/tagstudio/qt/view/widgets/preview_panel_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Entry
from tagstudio.core.palette import ColorType, UiColor, get_ui_color
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.controller.widgets.preview.preview_thumb_controller import PreviewThumb
from tagstudio.qt.translations import Translations
from tagstudio.qt.widgets.preview.field_containers import FieldContainers
Expand Down Expand Up @@ -153,11 +154,9 @@ def set_selection(self, selected: list[int], update_preview: bool = True):
# One Item Selected
elif len(selected) == 1:
entry_id = selected[0]
entry: Entry | None = self.lib.get_entry(entry_id)
assert entry is not None
entry: Entry = unwrap(self.lib.get_entry(entry_id))

assert self.lib.library_dir is not None
filepath: Path = self.lib.library_dir / entry.path
filepath: Path = unwrap(self.lib.library_dir) / entry.path

if update_preview:
stats: FileAttributeData = self.__thumb.display_file(filepath)
Expand Down
7 changes: 3 additions & 4 deletions src/tagstudio/qt/widgets/collage_icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.media_types import MediaCategories
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.helpers.file_tester import is_readable_video

logger = structlog.get_logger(__name__)
Expand All @@ -35,10 +36,8 @@ def render(
data_only_mode: bool,
keep_aspect: bool,
):
entry = self.lib.get_entry(entry_id)
lib_dir = self.lib.library_dir
assert lib_dir is not None and entry is not None
filepath = lib_dir / entry.path
entry = unwrap(self.lib.get_entry(entry_id))
filepath = unwrap(self.lib.library_dir) / entry.path
color: str = ""

try:
Expand Down
4 changes: 2 additions & 2 deletions src/tagstudio/qt/widgets/item_thumb.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from tagstudio.core.library.alchemy.enums import ItemType
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.media_types import MediaCategories, MediaType
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.flowlayout import FlowWidget
from tagstudio.qt.helpers.file_opener import FileOpenerHelper
from tagstudio.qt.platform_strings import open_file_str, trash_term
Expand Down Expand Up @@ -538,8 +539,7 @@ def mouseMoveEvent(self, event: QMouseEvent) -> None: # type: ignore[misc]
if not entry:
continue

assert self.lib.library_dir is not None
url = QUrl.fromLocalFile(Path(self.lib.library_dir) / entry.path)
url = QUrl.fromLocalFile(Path(unwrap(self.lib.library_dir)) / entry.path)
paths.append(url)

mimedata.setUrls(paths)
Expand Down
10 changes: 4 additions & 6 deletions src/tagstudio/qt/widgets/preview/field_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,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.components.tag_box_controller import TagBoxWidget
from tagstudio.qt.translations import Translations
from tagstudio.qt.widgets.datetime_picker import DatetimePicker
Expand Down Expand Up @@ -110,8 +111,7 @@ def update_from_entry(self, entry_id: int, update_badges: bool = True):
"""Update tags and fields from a single Entry source."""
logger.warning("[FieldContainers] Updating Selection", entry_id=entry_id)

entry = self.lib.get_entry_full(entry_id)
assert entry is not None
entry = unwrap(self.lib.get_entry_full(entry_id))
self.cached_entries = [entry]
self.update_granular(entry.tags, entry.fields, update_badges)

Expand Down Expand Up @@ -177,9 +177,8 @@ def add_to_cluster(tag_id: int, p_ids: list[int] | None = None):
"Character" -> "Johnny Bravo",
"TV" -> Johnny Bravo"
"""
tag_obj = self.lib.get_tag(tag_id) # Get full object
tag_obj = unwrap(self.lib.get_tag(tag_id)) # Get full object
if p_ids is None:
assert tag_obj is not None
p_ids = tag_obj.parent_ids

for p_id in p_ids:
Expand All @@ -188,8 +187,7 @@ def add_to_cluster(tag_id: int, p_ids: list[int] | None = None):
# If the p_tag has p_tags of its own, recursively link those to the original Tag.
if tag_id not in cluster_map[p_id]:
cluster_map[p_id].add(tag_id)
p_tag = self.lib.get_tag(p_id) # Get full object
assert p_tag is not None
p_tag = unwrap(self.lib.get_tag(p_id)) # Get full object
if p_tag.parent_ids:
add_to_cluster(
tag_id,
Expand Down
8 changes: 3 additions & 5 deletions src/tagstudio/qt/widgets/preview/file_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from tagstudio.core.library.ignore import Ignore
from tagstudio.core.media_types import MediaCategories
from tagstudio.core.palette import ColorType, UiColor, get_ui_color
from tagstudio.core.utils.types import unwrap
from tagstudio.qt.helpers.file_opener import FileOpenerHelper, FileOpenerLabel
from tagstudio.qt.translations import Translations

Expand Down Expand Up @@ -156,13 +157,11 @@ def update_stats(self, filepath: Path | None = None, stats: FileAttributeData |
self.dimensions_label.setHidden(True)
else:
ext = filepath.suffix.lower()
self.library_path = self.library.library_dir
display_path = filepath
if self.driver.settings.show_filepath == ShowFilepathOption.SHOW_FULL_PATHS:
display_path = filepath
elif self.driver.settings.show_filepath == ShowFilepathOption.SHOW_RELATIVE_PATHS:
assert self.library_path is not None
display_path = Path(filepath).relative_to(self.library_path)
display_path = Path(filepath).relative_to(unwrap(self.library.library_dir))
elif self.driver.settings.show_filepath == ShowFilepathOption.SHOW_FILENAMES_ONLY:
display_path = Path(filepath.name)

Expand Down Expand Up @@ -215,12 +214,11 @@ def add_newline(stats_label_text: str) -> str:

if ext_display:
stats_label_text += ext_display
assert self.library.library_dir
red = get_ui_color(ColorType.PRIMARY, UiColor.RED)
orange = get_ui_color(ColorType.PRIMARY, UiColor.ORANGE)

if Ignore.compiled_patterns and not Ignore.compiled_patterns.match(
filepath.relative_to(self.library.library_dir)
filepath.relative_to(unwrap(self.library.library_dir))
):
stats_label_text = (
f"{stats_label_text}"
Expand Down
Loading