Package ovault
Version: 0.0.4
Python module for managing Obsidian vaults.
Installation
Install ovault
using pip
pip install ovault
Examples
Below is a list of simple examples to get you started!
- Example 1: Print an overview of the vault
- Example 2: Print all files with a specific tag
- Example 3: Find all headings in the "Start Here" note in the Obsidian Sandbox vault
- Example 4: Check that all external links in the vault are valid
- Example 5: Generates
dot
code for a connection graph of of the vault - Example 6: Generate an Obsidian Vault
- Example 7: Rename a note in an Obsidian vault
Example 1: Print an overview of the vault
# Example 1: Print an overview of the vault
# Usage: `python3 examples/1_overview.py <vault>`
import ovault
import sys
if len(sys.argv) != 2:
print(f"Usage: python3 {sys.argv[0]} <vault>")
exit(1)
vault = ovault.Vault(sys.argv[1])
print()
print("path :", vault.path)
print("notes :", len(vault.notes()))
print("attachments :", len(vault.attachments()))
print("tags :", vault.tags())
Example 2: Print all files with a specific tag
# Example 2: Print all files with a specific tag
import ovault
import sys
if len(sys.argv) != 3:
print(f"Usage: python3 {sys.argv[0]} <vault> <tag>")
exit(1)
vault = ovault.Vault(sys.argv[1])
tag = sys.argv[2]
print(f"\nSearching for notes with tag: {tag}")
notes = vault.get_notes_by_tag(tag)
if len(notes) == 0:
print(f"No notes found with tag '{tag}'.")
else:
for note in notes:
print(f" {note.name} ({note.length} characters)")
Example 3: Find all headings in the "Start Here" note in the Obsidian Sandbox vault
# Example 3: Find all headings in the "Start Here" note in the Obsidian Sandbox vault
import ovault
# Open the sandbox vault
vault = ovault.Vault("./test-vaults/Obsidian Sandbox/")
# Find a note by name
note = vault.get_note_by_name("Start Here")
# Get all tokens in the note
tokens = note.tokens()
# Iterate through tokens and print headings
for token in tokens:
if isinstance(token, token.Header):
print(f"Found heading: {token.heading} at level {token.level}")
Example 4: Check that all external links in the vault are valid
# Example 4: Check that all external links in the vault are valid
# Usage: python 4_check_external_links.py <vault_path>
# NOTE: This example requires the `requests` package to be installed.
import ovault
import sys
import requests
if len(sys.argv) != 2:
print("Usage: python 4_check_external_links.py <vault_path>")
sys.exit(1)
vault = ovault.Vault(sys.argv[1])
# Terninal Colors
GREEN = "\033[92m"
YELLOW = "\033[93m"
RED = "\033[91m"
RESET = "\033[0m"
broken_links = []
for note in vault.notes():
for token in note.tokens():
if not isinstance(token, token.ExternalLink): continue
url = token.link.url
print(f"[note: {note.name}] Checking '{url}'...", end=' ', flush=True)
if not url.startswith("http"):
print(f"{YELLOW}SKIPPED{RESET}")
continue
try:
resp = requests.get(url, timeout=5)
except requests.RequestException as e:
print(f"{RED}INVALID URL{RESET} ({e})")
continue
if resp.status_code == 200:
print(f"{GREEN}OK{RESET}")
else:
broken_links.append((note.name, url, resp.status_code))
print(f"{RED}FAILED{RESET} ({resp.status_code})")
if not broken_links:
print("All external links are valid!")
exit(0)
print(f"\nBroken links found:")
for note_name, url, status in broken_links:
print(f"- {note_name}: {url} (status code: {status})")
Example 5: Generates dot
code for a connection graph of of the vault
# Example 5: Generates `dot` code for a connection graph of of the vault
# Usage: `python3 examples/5_graph_image.py <vault> | neato -Tpng > graph.png`
import ovault
import sys
if len(sys.argv) != 2:
print(f"Usage: python3 {sys.argv[0]} <vault>")
exit(1)
vault = ovault.Vault(sys.argv[1])
notes = vault.notes()
print("digraph {")
print(" overlap=false;")
print(' node [shape=box, style=filled, fillcolor="#0099FF25"];')
print(' edge [color="#00000090"];')
for note in notes:
print(f' "{note.normalized_name()}" [label="{note.name}"]')
for note in notes:
for link in note.links:
link = link.replace("\\", "")
print(f' "{note.normalized_name()}" -> "{link}"')
print("}")
Example 6: Generate an Obsidian Vault
# Example 6: Generate an Obsidian Vault
# This script generates an obsidian vault containing manual entries
# for common GNU/Linux utilities
# NOTE: This will only work on GNU/Linux systems with the `man` command available.
# Usage: python 6_create_vault.py <vault_name>
import ovault
import subprocess
import sys
# List of common GNU/Linux utilities to include in the vault
UTILS = [
"ls", "pwd", "mkdir", "rm", "cp", "mv", "touch", "grep", "cat",
"more", "less", "chmod", "ps", "top", "free", "df", "du", "uname",
"sudo", "su", "passwd", "id", "ping", "ssh", "wget", "curl", "kill",
"tar", "gzip", "zip", "mount", "nano", "echo", "clear", "man",
"exit", "reboot", "date"
]
# Sort utilities for consistent order
UTILS.sort()
# Get the manual page for a utility using the `man` command
def manual(util: str) -> str:
res = subprocess.run(["man", util], capture_output=True, text=True);
if res.returncode != 0:
print(f"Command `{util}` does not have a manual entry")
exit(1)
return res.stdout
# Extract the description from the manual page.
# The description is usually the first line after the "NAME" section.
def extract_desc(man: str) -> str:
lines = man.split("\n")
desc = ""
for i, line in enumerate(lines):
if not line.startswith("NAME"): continue
desc_line = lines[i + 1].strip()
if " - " in desc_line:
desc = desc_line.split(" - ")[-1]
break
else:
desc = desc_line
break
if not desc:
print("Could not extract description from manual page")
exit(1)
return desc.strip().capitalize() + "."
# Generate the index page for the vault
def index_page(descs: dict[str, str]) -> str:
index_content = "# GNU/Linux Utilities Vault\n\n"
index_content += "This vault contains manual entries for common GNU/Linux utilities.\n\n"
index_content += "## Utilities\n\n"
# Fill in the index with utility names and their descriptions
for util in UTILS:
index_content += f"- **[[utils/{util}|{util}]]**: {descs[util]}\n"
return index_content
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"Usage: python {sys.argv[0]} <vault_name>")
exit(1)
# Open the vault, creating it if it doesn't exist
vault = ovault.Vault(sys.argv[1], create=True)
print("Getting manual pages for utilities...")
pages = {}
for util in UTILS:
print(f" Getting '{util}'")
pages[util] = manual(util)
print("Extracting descriptions from manual pages...")
descs = {util: extract_desc(pages[util]) for util in UTILS}
print("Creating notes...")
for util in UTILS:
path = f"utils/{util}.md"
content = f"{descs[util]}\n\n```text\n{pages[util]}\n```"
vault.add_note(path, content)
vault.add_note("Overview.md", index_page(descs))
print("Re-indexing vault...")
vault.index()
print(f"\nVault created successfully at '{vault.path}' with {len(vault.notes())} notes!")
Example 7: Rename a note in an Obsidian vault
# Example 7: Rename a note in an Obsidian vault
# Usage: python 7_rename_note.py <vault_name> <old_note_path> <new_note_path>
import ovault
import sys
if len(sys.argv) != 4:
print("Usage: python 7_rename_note.py <vault_name> <old_note_name> <new_note_name>")
sys.exit(1)
vault = ovault.Vault(sys.argv[1])
old_note_name = sys.argv[2]
new_note_name = sys.argv[3]
note = vault.rename_note(old_note_name, new_note_name)
print(f"Renamed note from '{old_note_name}' to '{new_note_name}' in vault '{vault.path}'\n")
print("Patched backlinks:")
for backlink in note.backlinks:
print(f" - {vault.get_note_by_name(backlink).path}")
Functions
def normalize(name)
-
Nomalize a note name to be used in Obsidian links.
Example:
normalize("My Note") => "my-note"
def parse_yaml(source)
def text_to_tokens(text)
-
Tokenize a string to a list of tokens.
Classes
class Attachment (...)
-
An attachment in an Obsidian vault. An attachment is any file that is not a markdown file.
Instance variables
var path
-
Path to the attachment file.
class Callout (...)
-
Represents a callout block in the document.
Example:
> [!note]- Title > This is a note callout.
Instance variables
var foldable
-
Whether the callout can be folded or collapsed.
var kind
-
The kind of callout, such as "note", "tip", "warning", etc.
var text
-
The text content of the callout.
var title
-
The title of the callout.
var tokens
-
The tokenized content of the callout, which can include text, code blocks, links, etc.
class ExternalLink (...)
-
Represents an external link to an external URL.
Example:
 [show_how](https://github.com/BalderHolst)
Instance variables
var options
-
Additional options for the link, such as size or alignment.
var position
-
The section in the linked document where the link should point to.
var render
-
Whether the link should be rendered as an image or video.
var show_how
-
The text that will be displayed for the link.
var url
-
The URL of the link.
class Frontmatter
-
Represents the frontmatter of a note, which is a collection of ordered key-value pairs. This class acts like a dictionary, where keys are strings and values can be various types, including numbers, strings, booleans, arrays
The main difference from a standard dictionary is that the order of items is preserved
Methods
def clear(self, /)
-
Clears all items
def contains(self, /, key)
-
Checks if the frontmatter contains a specific key.
def copy(self, /)
-
Creates a copy of this
Frontmatter
. def del_item(self, /, key)
-
Deletes a key-value pair from the frontmatter by key.
def dict(self, /)
-
Returns a python dictionary representation of the frontmatter.
def get_item(self, /, key)
-
Retrieves the value associated with a specific key.
def is_empty(self, /)
-
Checks if the frontmatter is empty.
def items(self, /)
-
Returns a list of key-value pairs as tuples in the frontmatter.
def keys(self, /)
-
Returns a list of keys in the frontmatter.
def len(self, /)
-
Returns the number of items in the frontmatter.
def set_item(self, /, key, value)
-
Sets the value for a specific key, or adds the key-value pair if it does not exist.
def values(self, /)
-
Returns a list of values in the frontmatter.
def yaml(self, /, indent=2, list_style=Ellipsis)
-
Converts the frontmatter to a YAML string representation.
class InternalLink (...)
-
Represents an internal link to another note.
Example:
![[note_name#position|display text]]
Instance variables
var dest
-
The destination of the link, which is the name of the note.
var options
-
Additional options for the link, such as size or alignment.
var position
-
The section in the linked document where the link should point to.
var render
-
Whether the link should be rendered as an image, video or note.
var show_how
-
The text that will be displayed for the link.
class ListStyle (...)
-
Represents the style of lists in frontmatter YAML serialization.
Class variables
var Bracketed
var Dashed
class Note (...)
-
A note in an Obsidian vault.
Instance variables
var backlinks
-
Set of backlinks to this note (links from other notes)
var length
-
Length of the note contents in characters
var links
-
Set of links to other notes or attachments
var name
-
Name of the note (file name without extension)
var path
-
Relative path within vault
-
Set of tags in the note
var vault_path
-
Path to the vault
Methods
def frontmatter(self, /)
-
Get the frontmatter as a python dictionary
def full_path(self, /)
-
Get the absolute path to the note file.
def insert_after_token(self, /, token, text, offset=0)
-
Insert a string into the note after a given token.
NOTE: The token should originate from this note as this method used the internal
Span
of the note to determine the insertion position. def insert_at(self, /, pos, text)
-
Inserts a string at a position in the note.
def insert_before_token(self, /, token, text, offset=0)
-
Inserts a string into the note before a given token.
NOTE: The token should originate from this note as this method used the internal
Span
of the note to determine the insertion position. def normalized_name(self, /)
-
Get the normalized name of the node.
def read(self, /)
-
Read the contents of the note and return it as a string
def replace_between(self, /, start, end, text)
-
Replaces the text between two positions in the note with the given text.
def replace_span(self, /, span, text)
-
Replaces a
Span
in the note with the given text. This can be used to replace tokens within the note. def tokens(self, /)
-
Get contents note as a list of tokens.
class Span (start, end)
-
Represents a span in the source text.
Instance variables
var end
-
The ending index of the span in the source text.
var start
-
The starting index of the span in the source text.
class Token (...)
-
Represents a part of a note, such as text, code blocks, links, etc.
Example - Token Stream
A note might contain the following:
# Heading This is a paragraph with a [link](https://example.com).
This would be represented as a sequence of
Token
instances:- `Token::Header { level: 1, heading: "Heading" }` - `Token::Text { text: "This is a paragraph with a " }` - `Token::ExternalLink { link: ... }` - `Token::Text { text: "." }`
Example - Find Headings
To find all headings in a note, you can iterate over the tokens:
# Example 3: Find all headings in the "Start Here" note in the Obsidian Sandbox vault import ovault # Open the sandbox vault vault = ovault.Vault("./test-vaults/Obsidian Sandbox/") # Find a note by name note = vault.get_note_by_name("Start Here") # Get all tokens in the note tokens = note.tokens() # Iterate through tokens and print headings for token in tokens: if isinstance(token, token.Header): print(f"Found heading: {token.heading} at level {token.level}")
Subclasses
- builtins.Token_Callout
- builtins.Token_Code
- builtins.Token_DisplayMath
- builtins.Token_Divider
- builtins.Token_ExternalLink
- builtins.Token_Frontmatter
- builtins.Token_Header
- builtins.Token_InlineMath
- builtins.Token_InternalLink
- builtins.Token_Quote
- builtins.Token_Tag
- builtins.Token_TemplaterCommand
- builtins.Token_Text
Class variables
var Callout
var Code
var DisplayMath
var Divider
var ExternalLink
var Frontmatter
var Header
var InlineMath
var InternalLink
var Quote
var Tag
var TemplaterCommand
var Text
class Vault (path, create=False)
-
An Obsidian vault containing notes and attachments. The vault is indexed on creation and can be re-indexed with the
index
method.Instance variables
var dangling_links
-
A map of dangling links. The key is the note name and the value is a list of links that point to non-existing notes or attachments.
var ignored
-
Paths that were ignored during indexing
var path
-
Path to Obsidian vault directory
Methods
def add_note(self, /, vault_path, contents)
-
Add a note to the vault.
Call the
index
method to reindex the vault after adding notes. def attachments(self, /)
-
Get a list of all attachments in the vault. Order is not guaranteed.
def get_note_by_name(self, /, name)
-
Get note by its name.
def get_notes_by_tag(self, /, tag)
-
Get all notes that have the given tag.
def index(self, /)
-
Index the vault. This will clear the current index and re-index the vault.
This is useful if you have edited, added or removed notes or attachments from the vault.
def notes(self, /)
-
Get a list of all notes in the vault. Order is not guaranteed.
def rename_note(self, /, old_name, new_name)
-
Rename a note in the vault. This will update the note's name, path, and all backlinks to the note.
def rename_tag(self, /, old_tag, new_tag)
-
Rename a tag in the vault. This will update all notes that have the tag.
-
Get a list of all tags in the vault. Order is not guaranteed.
class VaultItem (...)
-
An item in an Obsidian vault can be either a note or an attachment.
Subclasses
- builtins.VaultItem_Attachment
- builtins.VaultItem_Note
Class variables
var Attachment
var Note