diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ba6c2de..c17d570 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -65,6 +65,7 @@ repos: hooks: - id: ruff args: ["--extend-ignore", "I001,D301,D401,PLR2004,PLR0913"] + exclude: tests/ - repo: "https://github.com/jendrikseipp/vulture" rev: "v2.7" diff --git a/pyproject.toml b/pyproject.toml index 1e0d44e..16403b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,7 @@ ] ignore-init-module-imports = true line-length = 100 + per-file-ignores = { "cli.py" = ["PLR0913"] } select = [ "A", "B", @@ -207,7 +208,7 @@ help = "Lint this package" [[tool.poe.tasks.lint.sequence]] - shell = "ruff --extend-ignore=I001,D301,D401,PLR2004,PLR0913 src/ tests/" + shell = "ruff --extend-ignore=I001,D301,D401,PLR2004,PLR0913 src/" [[tool.poe.tasks.lint.sequence]] shell = "black --check src/ tests/" diff --git a/src/obsidian_metadata/_config/config.py b/src/obsidian_metadata/_config/config.py index 26dbe85..bf6f304 100644 --- a/src/obsidian_metadata/_config/config.py +++ b/src/obsidian_metadata/_config/config.py @@ -36,7 +36,7 @@ class ConfigQuestions: @staticmethod def _validate_valid_dir(path: str) -> bool | str: - """Validates a valid directory. + """Validate a valid directory. Returns: bool | str: True if the path is valid, otherwise a string with the error message. @@ -55,7 +55,6 @@ class Config: """Representation of a configuration file.""" def __init__(self, config_path: Path = None, vault_path: Path = None) -> None: - if vault_path is None: self.config_path: Path = self._validate_config_path(Path(config_path)) self.config: dict[str, Any] = self._load_config() diff --git a/src/obsidian_metadata/_utils/alerts.py b/src/obsidian_metadata/_utils/alerts.py index 46fd596..0faaeb9 100644 --- a/src/obsidian_metadata/_utils/alerts.py +++ b/src/obsidian_metadata/_utils/alerts.py @@ -1,5 +1,6 @@ """Logging and alerts.""" import sys +from enum import Enum from pathlib import Path from textwrap import wrap @@ -9,6 +10,28 @@ from loguru import logger from rich import print +class LogLevel(Enum): + """Enum for log levels.""" + + TRACE = 5 + DEBUG = 10 + INFO = 20 + SUCCESS = 25 + WARNING = 30 + ERROR = 40 + CRITICAL = 50 + EXCEPTION = 60 + + +class VerboseLevel(Enum): + """Enum for verbose levels.""" + + WARN = 0 + INFO = 1 + DEBUG = 2 + TRACE = 3 + + def dryrun(msg: str) -> None: """Print a message if the dry run flag is set. @@ -151,7 +174,7 @@ class LoggerManager: print("No log file specified") raise typer.Exit(1) - if self.verbosity >= 3: + if self.verbosity >= VerboseLevel.TRACE.value: logger.remove() logger.add( sys.stderr, @@ -161,7 +184,7 @@ class LoggerManager: diagnose=True, ) self.log_level = 5 - elif self.verbosity == 2: + elif self.verbosity == VerboseLevel.DEBUG.value: logger.remove() logger.add( sys.stderr, @@ -171,7 +194,7 @@ class LoggerManager: diagnose=True, ) self.log_level = 10 - elif self.verbosity == 1: + elif self.verbosity == VerboseLevel.INFO.value: logger.remove() logger.add( sys.stderr, @@ -214,7 +237,7 @@ class LoggerManager: Returns: bool: True if the current log level is TRACE or lower, False otherwise. """ - if self.log_level <= 5: + if self.log_level <= LogLevel.TRACE.value: if msg: print(msg) return True @@ -229,7 +252,7 @@ class LoggerManager: Returns: bool: True if the current log level is DEBUG or lower, False otherwise. """ - if self.log_level <= 10: + if self.log_level <= LogLevel.DEBUG.value: if msg: print(msg) return True @@ -244,7 +267,7 @@ class LoggerManager: Returns: bool: True if the current log level is INFO or lower, False otherwise. """ - if self.log_level <= 20: + if self.log_level <= LogLevel.INFO.value: if msg: print(msg) return True @@ -259,7 +282,7 @@ class LoggerManager: Returns: bool: True if the current log level is default or lower, False otherwise. """ - if self.log_level <= 30: + if self.log_level <= LogLevel.WARNING.value: if msg: print(msg) return True diff --git a/src/obsidian_metadata/_utils/utilities.py b/src/obsidian_metadata/_utils/utilities.py index b010b0d..4f2cd74 100644 --- a/src/obsidian_metadata/_utils/utilities.py +++ b/src/obsidian_metadata/_utils/utilities.py @@ -63,7 +63,7 @@ def dict_contains( def dict_values_to_lists_strings(dictionary: dict, strip_null_values: bool = False) -> dict: - """Converts all values in a dictionary to lists of strings. + """Convert all values in a dictionary to lists of strings. Args: dictionary (dict): Dictionary to convert @@ -101,7 +101,7 @@ def dict_values_to_lists_strings(dictionary: dict, strip_null_values: bool = Fal def docstring_parameter(*sub: Any) -> Any: - """Decorator to replace variables within docstrings. + """Replace variables within docstrings. Args: sub (Any): Replacement variables diff --git a/src/obsidian_metadata/cli.py b/src/obsidian_metadata/cli.py index 637ae07..0f58c70 100644 --- a/src/obsidian_metadata/cli.py +++ b/src/obsidian_metadata/cli.py @@ -1,6 +1,5 @@ """obsidian-metadata CLI.""" - from pathlib import Path from typing import Optional @@ -84,7 +83,7 @@ def main( None, "--version", help="Print version and exit", callback=version_callback, is_eager=True ), ) -> None: - r"""A script to make batch updates to metadata in an Obsidian vault. No changes are made to the Vault until they are explicitly committed. + r"""Make batch updates to metadata in an Obsidian vault. No changes are made to the Vault until they are explicitly committed. [bold] [/] [bold underline]It is strongly recommended that you back up your vault prior to committing changes.[/] This script makes changes directly to the markdown files in your vault. Once the changes are committed, there is no ability to recreate the original information unless you have a backup. Follow the instructions in the script to create a backup of your vault if needed. The author of this script is not responsible for any data loss that may occur. Use at your own risk. diff --git a/src/obsidian_metadata/models/application.py b/src/obsidian_metadata/models/application.py index ea1e48e..c62ebf6 100644 --- a/src/obsidian_metadata/models/application.py +++ b/src/obsidian_metadata/models/application.py @@ -113,7 +113,6 @@ class Application: while True: match self.questions.ask_selection(choices=choices, question="Select an action"): case "apply_path_filter": - path = self.questions.ask_filter_path() if path is None or path == "": return diff --git a/src/obsidian_metadata/models/metadata.py b/src/obsidian_metadata/models/metadata.py index 92ea63b..0dec65e 100644 --- a/src/obsidian_metadata/models/metadata.py +++ b/src/obsidian_metadata/models/metadata.py @@ -213,7 +213,6 @@ class Frontmatter: """Representation of frontmatter metadata.""" def __init__(self, file_content: str): - self.dict: dict[str, list[str]] = self._grab_note_frontmatter(file_content) self.dict_original: dict[str, list[str]] = self.dict.copy() @@ -389,7 +388,6 @@ class InlineMetadata: """Representation of inline metadata in the form of `key:: value`.""" def __init__(self, file_content: str): - self.dict: dict[str, list[str]] = self._grab_inline_metadata(file_content) self.dict_original: dict[str, list[str]] = self.dict.copy() @@ -417,7 +415,7 @@ class InlineMetadata: stripped_null_values = [tuple(filter(None, x)) for x in all_results] inline_metadata: dict[str, list[str]] = {} - for (k, v) in stripped_null_values: + for k, v in stripped_null_values: if k in inline_metadata: inline_metadata[k].append(str(v)) else: @@ -515,7 +513,6 @@ class InlineTags: """Representation of inline tags.""" def __init__(self, file_content: str): - self.metadata_key = INLINE_TAG_KEY self.list: list[str] = self._grab_inline_tags(file_content) self.list_original: list[str] = self.list.copy() diff --git a/src/obsidian_metadata/models/notes.py b/src/obsidian_metadata/models/notes.py index 722ca3e..b10d2b5 100644 --- a/src/obsidian_metadata/models/notes.py +++ b/src/obsidian_metadata/models/notes.py @@ -65,7 +65,7 @@ class Note: yield "inline_metadata", self.inline_metadata def _delete_inline_metadata(self, key: str, value: str = None) -> None: - """Deletes an inline metadata key/value pair from the text of the note. This method does not remove the key/value from the metadata attribute of the note. + """Delete an inline metadata key/value pair from the text of the note. This method does not remove the key/value from the metadata attribute of the note. Args: key (str): Key to delete. @@ -74,7 +74,7 @@ class Note: all_results = PATTERNS.find_inline_metadata.findall(self.file_content) stripped_null_values = [tuple(filter(None, x)) for x in all_results] - for (_k, _v) in stripped_null_values: + for _k, _v in stripped_null_values: if re.search(key, _k): if value is None: _k = re.escape(_k) @@ -88,7 +88,7 @@ class Note: self.sub(rf"({_k}::) ?{_v}", r"\1", is_regex=True) def _rename_inline_metadata(self, key: str, value_1: str, value_2: str = None) -> None: - """Replaces the inline metadata in the note with the current inline metadata object. + """Replace the inline metadata in the note with the current inline metadata object. Args: key (str): Key to rename. @@ -99,7 +99,7 @@ class Note: all_results = PATTERNS.find_inline_metadata.findall(self.file_content) stripped_null_values = [tuple(filter(None, x)) for x in all_results] - for (_k, _v) in stripped_null_values: + for _k, _v in stripped_null_values: if re.search(key, _k): if value_2 is None: if re.search(rf"{key}[^\w\d_-]+", _k): @@ -118,7 +118,7 @@ class Note: self.sub(f"{_k}:: ?{_v}", f"{_k}:: {value_2}", is_regex=True) def add_metadata(self, area: MetadataType, key: str, value: str | list[str] = None) -> bool: - """Adds metadata to the note. + """Add metadata to the note. Args: area (MetadataType): Area to add metadata to. @@ -143,7 +143,7 @@ class Note: return False def append(self, string_to_append: str, allow_multiple: bool = False) -> None: - """Appends a string to the end of a note. + """Append a string to the end of a note. Args: string_to_append (str): String to append to the note. @@ -156,7 +156,7 @@ class Note: self.file_content += f"\n{string_to_append}" def commit_changes(self) -> None: - """Commits changes to the note to disk.""" + """Commit changes to the note to disk.""" # TODO: rewrite frontmatter if it has changed pass @@ -198,7 +198,7 @@ class Note: return False def delete_inline_tag(self, tag: str) -> bool: - """Deletes an inline tag from the `inline_tags` attribute AND removes the tag from the text of the note if it exists. + """Delete an inline tag from the `inline_tags` attribute AND removes the tag from the text of the note if it exists. Args: tag (str): Tag to delete. @@ -220,7 +220,7 @@ class Note: return False def delete_metadata(self, key: str, value: str = None) -> bool: - """Deletes a key or key-value pair from the note's metadata. Regex is supported. + """Delete a key or key-value pair from the note's metadata. Regex is supported. If no value is provided, will delete an entire key. @@ -253,7 +253,7 @@ class Note: return False def has_changes(self) -> bool: - """Checks if the note has been updated. + """Check if the note has been updated. Returns: bool: Whether the note has been updated. @@ -273,11 +273,11 @@ class Note: return False def print_note(self) -> None: - """Prints the note to the console.""" + """Print the note to the console.""" print(self.file_content) def print_diff(self) -> None: - """Prints a diff of the note's original state and it's new state.""" + """Print a diff of the note's original state and it's new state.""" a = self.original_file_content.splitlines() b = self.file_content.splitlines() @@ -294,7 +294,7 @@ class Note: Console().print(table) def replace_frontmatter(self, sort_keys: bool = False) -> None: - """Replaces the frontmatter in the note with the current frontmatter object.""" + """Replace the frontmatter in the note with the current frontmatter object.""" try: current_frontmatter = PATTERNS.frontmatt_block_with_separators.search( self.file_content @@ -316,7 +316,7 @@ class Note: self.sub(current_frontmatter, new_frontmatter, is_regex=True) def rename_inline_tag(self, tag_1: str, tag_2: str) -> bool: - """Renames an inline tag from the note ONLY if it's not in the metadata as well. + """Rename an inline tag from the note ONLY if it's not in the metadata as well. Args: tag_1 (str): Tag to rename. @@ -336,7 +336,7 @@ class Note: return False def rename_metadata(self, key: str, value_1: str, value_2: str = None) -> bool: - """Renames a key or key-value pair in the note's metadata. + """Rename a key or key-value pair in the note's metadata. If no value is provided, will rename an entire key. @@ -383,7 +383,7 @@ class Note: self.file_content = re.sub(pattern, replacement, self.file_content, re.MULTILINE) def write(self, path: Path = None) -> None: - """Writes the note's content to disk. + """Write the note's content to disk. Args: path (Path): Path to write the note to. Defaults to the note's path. diff --git a/src/obsidian_metadata/models/questions.py b/src/obsidian_metadata/models/questions.py index 76a949f..d43edd5 100644 --- a/src/obsidian_metadata/models/questions.py +++ b/src/obsidian_metadata/models/questions.py @@ -50,7 +50,7 @@ class Questions: @staticmethod def _validate_valid_dir(path: str) -> bool | str: - """Validates a valid directory. + """Validate a valid directory. Returns: bool | str: True if the path is valid, otherwise a string with the error message. @@ -86,7 +86,7 @@ class Questions: self.key = key def _validate_existing_inline_tag(self, text: str) -> bool | str: - """Validates an existing inline tag. + """Validate an existing inline tag. Returns: bool | str: True if the tag is valid, otherwise a string with the error message. @@ -100,7 +100,7 @@ class Questions: return True def _validate_key_exists(self, text: str) -> bool | str: - """Validates a valid key. + """Validate a valid key. Returns: bool | str: True if the key is valid, otherwise a string with the error message. @@ -114,7 +114,7 @@ class Questions: return True def _validate_key_exists_regex(self, text: str) -> bool | str: - """Validates a valid key. + """Validate a valid key. Returns: bool | str: True if the key is valid, otherwise a string with the error message. @@ -200,7 +200,7 @@ class Questions: return True def _validate_valid_vault_regex(self, text: str) -> bool | str: - """Validates a valid regex. + """Validate a valid regex. Returns: bool | str: True if the regex is valid, otherwise a string with the error message. @@ -262,7 +262,7 @@ class Questions: return True def ask_application_main(self) -> str: # pragma: no cover - """Selectable list for the main application interface. + """List for the main application interface. Args: style (questionary.Style): The style to use for the question.