fix: add custom exceptions (#29)

* feat: add custom exceptions to metadata creation

* refactor: utility function for finding inline metadata in content

* fix: use InlineTagError for exceptions parsing inline tags

* fix: improve error messages

* build(deps): bump dependencies

* fix: use BadParameter exception when appropriate
This commit is contained in:
Nathaniel Landau
2023-03-29 13:31:23 -04:00
committed by GitHub
parent 375dceb8c6
commit c5766af678
14 changed files with 247 additions and 80 deletions

View File

@@ -10,6 +10,7 @@ from obsidian_metadata._utils.utilities import (
dict_keys_to_lower,
dict_values_to_lists_strings,
docstring_parameter,
inline_metadata_from_string,
merge_dictionaries,
remove_markdown_sections,
rename_in_dict,
@@ -27,6 +28,7 @@ __all__ = [
"dict_values_to_lists_strings",
"docstring_parameter",
"LoggerManager",
"inline_metadata_from_string",
"merge_dictionaries",
"rename_in_dict",
"remove_markdown_sections",

View File

@@ -178,8 +178,7 @@ class LoggerManager:
self.log_level = log_level
if self.log_file == Path("/logs") and self.log_to_file: # pragma: no cover
console.print("No log file specified")
raise typer.Exit(1)
raise typer.BadParameter("No log file specified")
if self.verbosity >= VerboseLevel.TRACE.value:
logger.remove()

View File

@@ -183,6 +183,21 @@ def docstring_parameter(*sub: Any) -> Any:
return dec
def inline_metadata_from_string(string: str) -> list[tuple[Any, ...]]:
"""Search for inline metadata in a string and return a list tuples containing (key, value).
Args:
string (str): String to get metadata from
Returns:
tuple[str]: (key, value)
"""
from obsidian_metadata.models import Patterns
results = Patterns().find_inline_metadata.findall(string)
return [tuple(filter(None, x)) for x in results]
def merge_dictionaries(dict1: dict, dict2: dict) -> dict:
"""Merge two dictionaries. When the values are lists, they are merged and sorted.
@@ -322,4 +337,4 @@ def version_callback(value: bool) -> None:
"""Print version and exit."""
if value:
console.print(f"{__package__.split('.')[0]}: v{__version__}")
raise typer.Exit()
raise typer.Exit(0)

View File

@@ -132,7 +132,7 @@ def main(
config: Config = Config(config_path=config_file, vault_path=vault_path)
if len(config.vaults) == 0:
typer.echo("No vaults configured. Exiting.")
raise typer.Exit(1)
raise typer.BadParameter("No vaults configured. Exiting.")
if len(config.vaults) == 1:
application = Application(dry_run=dry_run, config=config.vaults[0])

View File

@@ -0,0 +1,17 @@
"""Custom exceptions for the obsidian_metadata package."""
class ObsidianMetadataError(Exception):
"""Base exception for the obsidian_metadata package."""
class FrontmatterError(ObsidianMetadataError):
"""Exception for errors in the frontmatter."""
class InlineMetadataError(ObsidianMetadataError):
"""Exception for errors in the inlined metadata."""
class InlineTagError(ObsidianMetadataError):
"""Exception for errors in the inline tags."""

View File

@@ -13,13 +13,20 @@ from obsidian_metadata._utils import (
delete_from_dict,
dict_contains,
dict_values_to_lists_strings,
inline_metadata_from_string,
merge_dictionaries,
remove_markdown_sections,
rename_in_dict,
)
from obsidian_metadata._utils.alerts import logger as log
from obsidian_metadata._utils.console import console
from obsidian_metadata.models import Patterns # isort: ignore
from obsidian_metadata.models.enums import MetadataType
from obsidian_metadata.models.exceptions import (
FrontmatterError,
InlineMetadataError,
InlineTagError,
)
PATTERNS = Patterns()
INLINE_TAG_KEY: str = "inline_tag"
@@ -230,7 +237,7 @@ class Frontmatter:
try:
frontmatter: dict = yaml.load(frontmatter_block)
except Exception as e: # noqa: BLE001
raise AttributeError(e) from e
raise FrontmatterError(e) from e
if frontmatter is None or frontmatter == [None]:
return {}
@@ -400,15 +407,26 @@ class InlineMetadata:
strip_inlinecode=True,
strip_frontmatter=True,
)
all_results = PATTERNS.find_inline_metadata.findall(content)
stripped_null_values = [tuple(filter(None, x)) for x in all_results]
found_inline_metadata = inline_metadata_from_string(content)
inline_metadata: dict[str, list[str]] = {}
for k, v in stripped_null_values:
if k in inline_metadata:
inline_metadata[k].append(str(v))
else:
inline_metadata[k] = [str(v)]
try:
for k, v in found_inline_metadata:
if not k:
log.trace(f"Skipping empty key associated with value: {v}")
continue
if k in inline_metadata:
inline_metadata[k].append(str(v))
else:
inline_metadata[k] = [str(v)]
except ValueError as e:
raise InlineMetadataError(
f"Error parsing inline metadata: {found_inline_metadata}"
) from e
except AttributeError as e:
raise InlineMetadataError(
f"Error parsing inline metadata: {found_inline_metadata}"
) from e
return clean_dictionary(inline_metadata)
@@ -537,15 +555,22 @@ class InlineTags:
Returns:
list[str]: Inline tags from the note.
"""
return sorted(
PATTERNS.find_inline_tags.findall(
remove_markdown_sections(
file_content,
strip_codeblocks=True,
strip_inlinecode=True,
try:
return sorted(
PATTERNS.find_inline_tags.findall(
remove_markdown_sections(
file_content,
strip_codeblocks=True,
strip_inlinecode=True,
)
)
)
)
except AttributeError as e:
raise InlineTagError("Error parsing inline tags.") from e
except TypeError as e:
raise InlineTagError("Error parsing inline tags.") from e
except ValueError as e:
raise InlineTagError("Error parsing inline tags.") from e
def add(self, new_tag: str | list[str]) -> bool:
"""Add a new inline tag.

View File

@@ -10,7 +10,7 @@ import rich.repr
import typer
from rich.table import Table
from obsidian_metadata._utils import alerts
from obsidian_metadata._utils import alerts, inline_metadata_from_string
from obsidian_metadata._utils.alerts import logger as log
from obsidian_metadata._utils.console import console
from obsidian_metadata.models import (
@@ -21,6 +21,11 @@ from obsidian_metadata.models import (
MetadataType,
Patterns,
)
from obsidian_metadata.models.exceptions import (
FrontmatterError,
InlineMetadataError,
InlineTagError,
)
PATTERNS = Patterns()
@@ -50,19 +55,24 @@ class Note:
try:
with self.note_path.open():
self.file_content: str = self.note_path.read_text()
self.original_file_content: str = self.file_content
except FileNotFoundError as e:
alerts.error(f"Note {self.note_path} not found. Exiting")
raise typer.Exit(code=1) from e
try:
self.frontmatter: Frontmatter = Frontmatter(self.file_content)
except AttributeError as e:
alerts.error(f"Note {self.note_path} has invalid frontmatter.\n{e}")
self.inline_metadata: InlineMetadata = InlineMetadata(self.file_content)
self.tags: InlineTags = InlineTags(self.file_content)
except FrontmatterError as e:
alerts.error(f"Invalid frontmatter: {self.note_path}\n{e}")
raise typer.Exit(code=1) from e
except InlineMetadataError as e:
alerts.error(f"Error parsing inline metadata: {self.note_path}.\n{e}")
raise typer.Exit(code=1) from e
except InlineTagError as e:
alerts.error(f"Error parsing inline tags: {self.note_path}\n{e}")
raise typer.Exit(code=1) from e
self.tags: InlineTags = InlineTags(self.file_content)
self.inline_metadata: InlineMetadata = InlineMetadata(self.file_content)
self.original_file_content: str = self.file_content
def __rich_repr__(self) -> rich.repr.Result: # pragma: no cover
"""Define rich representation of Vault."""
@@ -552,10 +562,9 @@ class Note:
value_2 (str, optional): New value.
"""
all_results = PATTERNS.find_inline_metadata.findall(self.file_content)
stripped_null_values = [tuple(filter(None, x)) for x in all_results]
found_inline_metadata = inline_metadata_from_string(self.file_content)
for _k, _v in stripped_null_values:
for _k, _v in found_inline_metadata:
if re.search(key, _k):
if value_2 is None:
if re.search(rf"{key}[^\\w\\d_-]+", _k):