import shutil
import tempfile
from unittest import mock
from fastcore.test import *
from nbdev.showdoc import show_doc
from trouver.helper import path_name_no_ext, _test_directory
markdown.obsidian.vault
This module generally manages Obsidian.md
vaults and their files and folders.
The VaultNote
class in this module is one of the most essentially classes used in trouver
. Generally, one uses Obsidian.md
to read and write “notes”, which are .md
files. The VaultNote
class manages such notes in an Obsidian.md
vault.
See Also markdown.obsidian.personal.vault
.
Some examples for this module come from nbs/_tests/vault_1
.
Errors
NoteNotUniqueError
NoteNotUniqueError (*args, **kwargs)
A NoteNotUniqueError
is raised when a VaultNote
is specified by name and not by relative path in the vault, but the vault is found to have multiple notes of the name.
Attributes
note_name
- str- The name of the note which is not unique in the vault.
notes
- list[str]- The paths of the notes whose names are
note_name
.
- The paths of the notes whose names are
NoteNotUniqueError.from_note_names
NoteNotUniqueError.from_note_names (note_name:str, notes:list[str])
Construct a NoteNotUniqueError
object from note names
NoteDoesNotExistError
NoteDoesNotExistError (*args, **kwargs)
A NoteDoesNotExistError
is raised when a VaultNote
is specified by either name and or by relative path in the vault, but the vault is found to have no notes of the name.
NoteDoesNotExistError.from_note_name
NoteDoesNotExistError.from_note_name (note_name:str)
Construct a NoteDoesNotExistError
object from note name
NotePathIsNotIdentifiedError
NotePathIsNotIdentifiedError (*args, **kwargs)
A NotePathIsNotIdentifiedError
is raised when the rel_path
attribute of a VaultNote
object is expected to be identified (i.e. a path and not None
) but this expectation is not fulfilled.
NotePathIsNotIdentifiedError.from_note
NotePathIsNotIdentifiedError.from_note (note)
Construct a NotePatahIsNotIdentifiedErrro
object from a VaultNote
.
Convert path to Obsidian ID
path_to_obs_id
path_to_obs_id (rel_path:os.PathLike)
Convert a relative path of an Obsidian note to the Obsidian identifying str.
This identification is for a vault-internal Wikilink.
Note that this function does not have a vault as a parameter.
Type | Details | |
---|---|---|
rel_path | PathLike | A path representation the path of an Obsidian note relative to its vault. This does not have to be an existing path. |
Returns | str | The obsidian url of the hypothetical note within its vault. Note that this does not end with the file extension .md . |
Obsidian formats its file paths with ‘/’; the path_to_obs_id
function converts a relative path of an Obsidian note to the Obsidian-recognized path.
test_eq(path_to_obs_id(r'some_folder\some_subfolder\some_subsubfolder\some_file.md'),
'some_folder/some_subfolder/some_subsubfolder/some_file')
Obsidian notes might contain spaces in their paths.
test_eq(path_to_obs_id('some folder\some subfolder\some file.md')),
Path('some folder/some subfolder/some file')
Example vault
Get all notes
all_paths_to_notes_in_vault
all_paths_to_notes_in_vault (vault:os.PathLike, as_dict:bool=False)
Return the paths, relative to the Obsidian vault, of notes in the Obsidian vault.
This may not actually return all of the paths to the notes, see the parameter as_dict
.
Parameters
vault
-PathLike
- The path to the Obsidian vault directory
as_dict
-bool
- If
True
, then returns a dict. IfFalse
, then returns a list. Defaults toFalse
. If there are multiple notes with the same name in the vault, andas_dict
is set toTrue
, then the dictionary will contain only one of the (relative) paths to those notes among its values. Ifas_dict
is set toFalse
, then the list will contain all the paths to the notes even when notes with non-unique names exist.
- If
Returns
- Union[list[str], dict[str, str]]
- Each str represents the path relative to
vault
. Ifas_dict
is True, then returns a dict whose keys are str, which are (unique) names of the notes in the vault, and the values are the paths.
- Each str represents the path relative to
The all_paths_to_notes_in_vault
function returns all of the paths to notes in the Obsidian vault; only .md
files are recognized as notes.
By default, the function returns a list whose items are strings of paths to notes relative to the vault path.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
# os.startfile(os.getcwd())
= all_paths_to_notes_in_vault(temp_vault)
notes len(notes), 7)
test_eq(print(notes)
# test_shuffled(, list)
['README.md', '_index.md', 'algebra\\ring.md', 'algebra\\reference_1\\ring.md', 'analysis\\exponential_function.md', 'category_theory\\category.md', 'topology\\category.md']
Passing as_dict=True
returns a dictionary whose keys are note names and whose values are lists of paths to notes of the name relative to the vault.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory() # os.startfile(os.getcwd())
= all_paths_to_notes_in_vault(temp_vault, as_dict=True)
notes len(notes), 5)
test_eq(len(notes['ring']), 2)
test_eq(print(notes)
{'README': ['README.md'], '_index': ['_index.md'], 'ring': ['algebra\\ring.md', 'algebra\\reference_1\\ring.md'], 'exponential_function': ['analysis\\exponential_function.md'], 'category': ['category_theory\\category.md', 'topology\\category.md']}
Searching notes by name
all_note_paths_by_name
all_note_paths_by_name (name:str, vault:os.PathLike, subdirectory:Optional[os.PathLike]=None)
Return the relative paths to all notes in the Obsidian vault with the specified name in the specified subdirectory.
This function does not assume that the specified subdirectory in the vault has at most one note of the specified name.
Type | Default | Details | |
---|---|---|---|
name | str | Name of the note(s) to find | |
vault | PathLike | The path to the Obsidian vault directory | |
subdirectory | typing.Optional[os.PathLike] | None | The path to a subdirectory in the Obsidian vault, relative to vault . If None , then denotes the root of the vault. |
Returns | list | Each item is a path to a note of the given name, relative to vault . |
Basic usage:
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
= all_note_paths_by_name('ring', temp_vault)
notes print('Searched for notes of name `ring`:')
print(notes, '\n')
assert len(notes) == 2
= all_note_paths_by_name('exponential_function', temp_vault)
notes print('Searched for notes of name `exponential_function`:')
print(notes, '\n')
assert len(notes) == 1
= all_note_paths_by_name('non-existent-note-name', temp_vault)
empty_list print('Searching for a non-existent note name yields an empty list:')
print(empty_list)
assert len(empty_list) == 0
Searched for notes of name `ring`:
[Path('algebra/ring.md'), Path('algebra/reference_1/ring.md')]
Searched for notes of name `exponential_function`:
[Path('analysis/exponential_function.md')]
Searching for a non-existent note name yields an empty list:
[]
We can specify a subdirectory inside the vault to restrict the search to.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
= all_note_paths_by_name('category', temp_vault)
all_notes_named_curve_in_vault print('All notes named `category`:\n', all_notes_named_curve_in_vault, '\n')
assert len(all_notes_named_curve_in_vault) == 2
for note_path in all_notes_named_curve_in_vault:
'category')
test_eq(path_name_no_ext(note_path),
print('All notes named `category` in the subdirectory `topology`')
= all_note_paths_by_name(
notes_named_topology_in_subdirectory 'category', temp_vault, 'topology')
print(notes_named_topology_in_subdirectory)
assert len(notes_named_topology_in_subdirectory) == 1
assert path_name_no_ext(notes_named_topology_in_subdirectory[0]) == 'category'
All notes named `category`:
[Path('category_theory/category.md'), Path('topology/category.md')]
All notes named `category` in the subdirectory `topology`
[Path('topology/category.md')]
note_path_by_name
note_path_by_name (name:str, vault:os.PathLike, subdirectory:Optional[os.PathLike]=None, hints:Optional[list[os.PathLike]]=None)
Return the path, relative to a subdirectory in the vault, of the note of the specified name.
Raises
- NoteNotUniqueError
- If the note of the specified name is not unique in the subdirectory.
- NoteDoesNotExistError
- If the note of the specified name does not exist in the subdirectory.
See Also
- The constructor of the
VaultNote
class- passing an argument to the
name
parameter of this constructor method essentially does the same thing as this function, except the constructor method uses a cache.
- passing an argument to the
Type | Default | Details | |
---|---|---|---|
name | str | The path to the Obsidian vault directory. | |
vault | PathLike | The path to a subdirectory in the Obsidian vault. If None , then denotes the root of the vault. |
|
subdirectory | typing.Optional[os.PathLike] | None | The path to a subdirectory in the Obsidian vault. If None , then denotes the root of the vault. |
hints | typing.Optional[list[os.PathLike]] | None | Hints of which directories, relative to subdirectory that the note may likely be in. This is for speedup. The directories will be searched in the order listed. |
Returns | Path | The note of the specified name in the specified subdirectory of the vault. |
Assuming that there exists a unique note of a specified name in a vault, we can identify it.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
= note_path_by_name('exponential_function', temp_vault)
note_path print(note_path)
'exponential_function') test_eq(path_name_no_ext(note_path),
analysis\exponential_function.md
If there is more than one note in the vault of the specified name, then a NoteNotUniqueError
is raised.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
print(f'The vault has more than one note named `category`:\n',
'category', temp_vault))
all_note_paths_by_name(with (ExceptionExpected(ex=NoteNotUniqueError, regex='not unique')):
'category', temp_vault) note_path_by_name(
The vault has more than one note named `category`:
[Path('category_theory/category.md'), Path('topology/category.md')]
Passing an argument to the parameter subdirectory
restricts the search to the subdirectory. A NoteNotUniqueError
can be avoided if a subdirectory is specified and if the note of the specified name is unique in the subdirectory.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
= note_path_by_name('category', temp_vault, subdirectory='topology')
note print(note)
assert 'topology' in str(note) and path_name_no_ext(note) == 'category'
topology\category.md
If there is no note in the vault of the specified name, then a NoteDoesNotExistError
is raised.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
with (ExceptionExpected(ex=NoteDoesNotExistError, regex='does not exist')):
'this_note_does_not_exit', temp_vault) note_path_by_name(
The hints
parameter
TODO: finish example
note_name_unique
note_name_unique (name:str, vault:os.PathLike)
Return True
if a note of the specified name exists and is unique in the Obsidian vault.
Type | Details | |
---|---|---|
name | str | Name of the note. |
vault | PathLike | Path to the vault. |
Returns | bool |
Example use:
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
= note_name_unique('ring', temp_vault)
vault_has_unique_note_named_ring assert not vault_has_unique_note_named_ring
= note_name_unique('exponential_function', temp_vault)
vault_has_unique_note_named_exponential_function assert vault_has_unique_note_named_exponential_function
Getting note name from its path
note_name_from_path
note_name_from_path (note_path:str)
Return the name of a note from its path.
Type | Details | |
---|---|---|
note_path | str | The path of the note. The note does not need to exist. |
Returns | str | The name of the note. |
assert note_name_from_path('algebra/ring.md') == 'ring'
VaultNote class
Just as how paths in Python can be dealt either via strings of paths or via pathlib.Path
objects, It is useful to have a class to encapsulate together the name of a note, and its path/Obsidian vault identifier.
VaultNote
VaultNote (vault:os.PathLike, rel_path:os.PathLike=None, name:str=None, subdirectory:Optional[os.PathLike]='', hints:list[os.PathLike]=[])
Represents a note in an Obsidian vault, without regards to the contents.
The note does not have to exist, except in circumstances stating otherwise.
TODO go through the methods of this class to see which methods assume that the note exists and which do not.
TODO finish the sentence below. A VaultNote
can be specified by either the rel_path
or the name
argument in its constructor. If name
is specified, then the
TODO implement subdirectory hint
Attributes
- vault - Path
- The (relative or absolute) path of the Obsidian vault that the note is located in.
- name - str
- The name of the note in the vault.
- rel_path - str
- The note’s path relative to the vault. If
- cache - dict[str, dict[str, list[str]]], class attribute
- The keys are string, which are paths to vaults. The corresponding values are dict whose keys are string, which are names in the vault of the (unique) note of that name, and whose values are list of string, which are paths to the note relative to the vault. The cache is not automatically updated when notes are moved.
Parameters
- vault - PathLike
- rel_path - PathLike
- name - str
- The name of the note in the vault. Defaults to the empty str.
- If
None
, then therel_path
parameter should be used to determineself.name
instead. - If not
None
, then the note must uniquely exist in the vault.
- If
- The name of the note in the vault. Defaults to the empty str.
subdirectory
- Union[PathLike, None]hints
- list[PathLike]
Raises
- ValueError
- if
rel_path
andname
are bothNone
.
- if
Type | Default | Details | |
---|---|---|---|
vault | PathLike | The (relative or absolute) path of the Obsidian vault that the note is located in. | |
rel_path | PathLike | None | The note’s path relative to the vault. If None , then the name parameter is used to determine the note instead. Defaults to None . |
name | str | None | The name of the note. If None , then the rel_path parameter is used to determine the note instead. Defaults to None |
subdirectory | typing.Optional[os.PathLike] | The relative path to a subdirectory in the Obsidian vault. If None , then denotes the root of the vault. Defaults to the empty str. |
|
hints | list | [] | Paths, relative to subdirectory , to directories where the note file may be found. This is for speedup. Defaults to the empty list, in which case the vault note is searched in all of subdirectory . |
Functions/Methods of the VaultNote
class
VaultNote.rel_path_identified
VaultNote.rel_path_identified ()
Return True
if self.rel_path
is identified, i.e. is a path that is not None
.
VaultNote.obsidian_identifier
VaultNote.obsidian_identifier ()
Return the Obsidian identifier of the VaultNote
object.
This is the note’s unqiue Obsidian id in the vault. This is like a path str with forward slashes /
(as opposed to backwards \
slashes) and without a file extension (.md
).
VaultNote.identify_rel_path
VaultNote.identify_rel_path (update_cache=False)
Sets self.rel_path
to a path, if not already done so.
If self.rel_path
is not already set as a path, then the cache is searched to find a note whose name is self.name
(which is necessarily specified).
Type | Default | Details | |
---|---|---|---|
update_cache | bool | False | If True , if the cache is searched, and if a note of the specified name is not found in the cache, then the cache is updated and searched again. Defaults to False . |
Returns | None |
VaultNote.exists
VaultNote.exists (update_cache=False)
Returns True
if self.rel_path
is identified and if the note exists in the vault.
Type | Default | Details | |
---|---|---|---|
update_cache | bool | False | If True , then update the cache and try to identify self.rel_path before verifying whether the note exists in the vault. |
Returns | bool |
VaultNote.path
VaultNote.path (relative=False)
Returns the path to the note.
Assumes that self.rel_path
has been identified.
Raises - NotePathIsNotIdentifiedError - If the relative path of self
is not identified.
Type | Default | Details | |
---|---|---|---|
relative | bool | False | If True , then return the path relative to the vault. |
Returns | typing.Optional[pathlib.Path] | Path to the note if self.rel_path is deterined. None otherwise. |
VaultNote.directory
VaultNote.directory (relative=False)
Return the directory that the note is in.
Type | Default | Details | |
---|---|---|---|
relative | bool | False | If True , then return the path of the directory relative to the vault. |
Returns | Path | The path of the directory that the note is in. |
VaultNote.create
VaultNote.create ()
Create the note if it does not exist.
The directory of the file needs to be created separately beforehand.
If the file exists, then a FileExistsError is raised and the file modification time is not changed.
Raises
- FileExistsError
- If the file already exists.
- FileNotFoundError
- If the directory of the file does not already exist.
VaultNote.delete
VaultNote.delete ()
Delete the note if it exists.
This updates the cache if necessary
VaultNote.move_to
VaultNote.move_to (rel_path:os.PathLike)
Move/rename the note to the specified location in the vault, assuming that it exists.
Type | Details | |
---|---|---|
rel_path | PathLike | The path in which to rename the path to self as, relative to self.vault . |
Returns | None |
VaultNote.move_to_folder
VaultNote.move_to_folder (rel_dir:os.PathLike)
Move the note to the specified folder in the vault, assuming that if exists.
Type | Details | |
---|---|---|
rel_dir | PathLike | The path of the directory in which to move self to, relative to self.vault . |
Returns | None |
VaultNote.text
VaultNote.text ()
Returns the text contained in the note.
Raises
- NoteDoesNotExistError
- If
self
does not point to an existing note.
- If
VaultNote.update_cache
VaultNote.update_cache (vault:os.PathLike)
Class method to update cache for vault
by inspecting all files in subdirectories of vault
.
Type | Details | |
---|---|---|
vault | PathLike | The vault. |
Returns | None |
VaultNote.clear_cache
VaultNote.clear_cache ()
Class method to clear out the entire cache for all vaults.
VaultNote.unique_name
VaultNote.unique_name (name:str, vault:os.PathLike)
A class method to return a name for a note that is unique in the vault based on a specified name.
Type | Details | |
---|---|---|
name | str | The base name for the note. |
vault | PathLike | The vault |
Returns | str | A str obtained by appending _{some number} to the end of name . |
Constructing VaultNote instances
By name
The most convenient way to construct an existing VaultNote
is to specify the vault in which it exists and the note’s name, assuming that the note of the specified name exists and is unique in the specified vault.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
= VaultNote(temp_vault, name='exponential_function')
vault_note assert vault_note.exists()
print(vault_note.name)
print(f'`vault_note` is located, relative to `vault`, at {vault_note.rel_path}.')
exponential_function
`vault_note` is located, relative to `vault`, at analysis\exponential_function.md.
# TODO: Delete the below example
# If an argument is passed to the `name` parameter and if the note's name is not unique in the vault, then a `NoteNotUniqueError` is raised.
# If an argument is passed to the `name` parameter and if the note's name does not exist in the vault, then a `NoteDoesNotExistError` is raised.
# with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as vault:
# make_example_vault(vault)
# with ExceptionExpected(ex=NoteNotUniqueError):
# vault_note = VaultNote(vault, name='ring')
# with ExceptionExpected(ex=NoteDoesNotExistError):
# vault_note = VaultNote(vault, name='does_not_exist')
By relative path
Alternatively, a VaultNote
object can be created by passing an argument to the rel_path
parameter. In this case, the note of the specified path does not need to exist.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
= VaultNote(temp_vault, rel_path='non_existent_folder/non_existent_note.md')
vault_note assert not vault_note.exists()
# Note that there is not a unique note of name `ring`.
= VaultNote(temp_vault, rel_path='algebra/ring.md')
vault_note assert vault_note.exists()
The rel_path
parameter takes precedence over the name
parameter.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
= VaultNote(temp_vault, rel_path='non_existent_folder/non_existent_note.md', name='ring')
vault_note assert vault_note.name == 'non_existent_note'
If the arguments for both the name
and the rel_path
parameters are None
, then a ValueError
is raised:
= _test_directory() / 'test_vault_1'
test_vault with ExceptionExpected(ValueError):
= VaultNote(test_vault, rel_path=None, name=None) vault_note
Cache of the VaultNote
class
The VaultNote
class keeps a cache of the notes (files with extension .md
) in the vault. In pracctice, this cache is updated when a note of the specified name is not found when constructing a VaultNote
instance via the name
parameter.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
VaultNote.update_cache(temp_vault)
assert VaultNote._check_name_exists_and_unique_in_vault_cache(temp_vault, 'exponential_function') is None
with ExceptionExpected(ex=NoteDoesNotExistError):
'this_note_does_not_exist')
VaultNote._check_name_exists_and_unique_in_vault_cache(temp_vault, # TODO: test this works correctly when a note does not exist by being removed.
with ExceptionExpected(ex=NoteNotUniqueError):
'ring') VaultNote._check_name_exists_and_unique_in_vault_cache(temp_vault,
VaultNote.clear_cache()with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
assert VaultNote._check_if_cache_needs_to_update(temp_vault, name='ring')
VaultNote.update_cache(temp_vault)assert VaultNote._check_if_cache_needs_to_update(temp_vault, name='does_not_exist')
Identifying the VaultNote
object
When the name
parameter (as opposed to the rel_path
parameter) is specified in the constructor of a VaultNote
object, the constructor looks into the cache of the VaultNote
class to identify a note with the specified name in the specified vault. If the cache contains no such note, then the cache is updated and searched again. If the cache still contains no such note, then the rel_path
attribute of the VaultNote
object is left unidentified (i.e. is set to None
).
Assuming that a note of the specified name is created later, the relative path of the VaultNote
object can be identified using the identify_rel_path(update_cache=True)
method. Moreover, the rel_path_identified
method returns True
if the relative path is identified.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
= VaultNote(temp_vault, name='does_not_exist_at_first')
vn assert not vn.rel_path_identified()
# Create a note at the root of the vault.
open(temp_vault / 'does_not_exist_at_first.md', 'w').close()
=True)
vn.identify_rel_path(update_cacheassert vn.rel_path_identified()
assert vn.exists()
'does_not_exist_at_first.md') test_eq(vn.rel_path,
Getting information about the note
Here are some convenient ways to get information about the VaultNote
:
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
= VaultNote(temp_vault, name='exponential_function')
vault_note print(f'Obsidian vault identifier:\t{vault_note.obsidian_identifier()}')
print(f'relative path:\t{vault_note.rel_path}')
print(f'Part of the absolute path of the note:\t{str(vault_note.path(relative=False))[:7]}')
print(f'note name:\t{vault_note.name}')
print(f'directory that the note is in relative to the vault:\t{vault_note.directory(relative=True)}')
Obsidian vault identifier: analysis/exponential_function
relative path: analysis\exponential_function.md
Part of the absolute path of the note: c:\User
note name: exponential_function
directory that the note is in relative to the vault: analysis
The VaultNote.path
method raises a NotePathIsNotIdentifiedError
if the VaultNote
object’s relative path is not idetnfied:
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
= VaultNote(temp_vault, name='does_not_exist')
vn with ExceptionExpected(NotePathIsNotIdentifiedError):
vn.path()
Reading the contents of the note
The VaultNote.text()
function reads the contents of the file that the VaultNote
object represents, assuming that this file exists. If the file does not exist, then a NoteDoesNotExistError
is raised.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory() = VaultNote(temp_vault, name='exponential_function')
vault_note print(vault_note.text())
assert len(vault_note.text()) > 10
= VaultNote(temp_vault, rel_path='does_not_exist.md')
vault_note with ExceptionExpected(ex=NoteDoesNotExistError):
vault_note.text()
The **exponential function** is the function sending a complex number $z$ to **$$e^{z} = \sum_{n=0}^\infty \frac{z^n}{n!}$$**. It converges for all $z \in \mathbb{C}$.
Creating/deleting/moving the note
If a VaultNote
object represents a non-existent file, then the file can be created with empty content. The cache is updated with this single new entry, but the rest of the cache remains the same.
If the file exists, then a FileExistsError
is raised.
If the specified directory for the file does not exist, then a FileNotFoundError
is raised.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory() = VaultNote(temp_vault, rel_path='new_file.md')
vault_note
vault_note.create()assert vault_note.exists()
assert vault_note.rel_path in VaultNote.cache[str(temp_vault)]['new_file']
with ExceptionExpected(ex=FileExistsError):
vault_note.create()
= VaultNote(temp_vault, rel_path='none_existent_folder/new_file.md')
vault_note with ExceptionExpected(ex=FileNotFoundError):
vault_note.create()
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory() = VaultNote(temp_vault, name='exponential_function')
vault_note
vault_note.delete()assert not vault_note.exists()
assert vault_note.rel_path not in VaultNote.cache[str(temp_vault)]['exponential_function']
If a VaultNote
object represents an existing file, then the file can be renamed or moved.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory()
VaultNote.clear_cache()= VaultNote(temp_vault, name='exponential_function')
vault_note '')
vault_note.move_to_folder(assert vault_note.rel_path == 'exponential_function.md'
assert 'exponential_function.md' in VaultNote.cache[str(temp_vault)]['exponential_function']
Getting a unique note name
It is troublesome to create a note with a non-unique name. The unique_name
method of the VaultNote class takes a tentative name for a note to be created and adds a number to the name so that the note name will be unique in the vault.
with tempfile.TemporaryDirectory(prefix='temp_dir', dir=os.getcwd()) as temp_dir:
= Path(temp_dir) / 'test_vault_1'
temp_vault / 'test_vault_1', temp_vault)
shutil.copytree(_test_directory() # There is a note named category in the test vault.
= VaultNote.unique_name('category', temp_vault)
sample_name assert not VaultNote(temp_vault, name=sample_name).exists()
= VaultNote.unique_name('non_existent_note_name', temp_vault)
sample_name assert not VaultNote(temp_vault, name=sample_name).exists()
assert sample_name == 'non_existent_note_name'
## Copying files in an `Obsidian.md` vault to and from a subvault
# # TODO: use these methods during vault construction for a reference
# def copy_vault_file_into_subvault(
# vault: PathLike, # The Path to the vault from which to copy the files.
# subvaults: Union[PathLike, list[PathLike]], # The Paths to the subvaults to which to copy the files.
# files: Union[PathLike, list[PathLike]], # The Path to the files, relative to `vault` to copy.
# replace: bool = True, # If `True`, replace existing files in `subvaults` if necessary. Defaults to `True`
# backup: bool = True, # If `True` and if `replace=True`, create a backup for any replaced files in a subvault in a folder named `.back` in the root directory of the subvault.
# ) -> None:
# """Copy the specified files in `vault` into subvaults.
# The files are copied within the subvaults to the same relative paths as
# they are found in `vault`.
# Here, "files" include directories. If a directory is copied, then all
# files and subdirectories of that directory are also copied.
# **Parameters**
# - vault - PathLike
# - The path to the Obsidian vault from which to copy files from.
# - subvaults - PathLike or list[PathLike]
# - The paths to the subvaults to which to copy the files.
# - files - PathLike or list[PathLike]
# - The files to copy.
# **Raises**
# - FileExistsError
# - If `replace` is `False` and some subvault already has a file at
# the path in which a file-copy is attempted. In this case, no
# files are copied.
# - FileNotFoundError
# - If a path specified in `files` does not exist in `vault`. In this
# case, no files are copied.
# """
# vault = Path(vault)
# if isinstance(subvaults, PathLike):
# subvaults = [subvaults]
# if isinstance(files, PathLike):
# files = [files]
# # TODO: Implement this as a private function
# for file in files:
# if not os.path.exists(vault / file):
# raise FileNotFoundError(
# f"Attempted to copy files/folders from a vault into subvaults"
# f", but there is at least one non-existent files"
# f". No files have been copied."
# f"vault: {vault}"
# f"file: {file}")
# if not replace:
# for subvault, file in itertools.product(subvaults, files):
# if os.path.exists(subvault / file):
# raise FileExistsError(
# f"Attempted to copy files/folders from a vault into subvaults"
# f", but at least one subvault already has a file that is"
# f" supposed to be copied from the vault"
# f". No files have been copied."
# f"subvault: {subvault}"
# f"file: {file}")
# # TODO: copy and backup files
# return