fix: inherit .otag.toml from parent folders and use the file's own folder config
This commit is contained in:
parent
81d9d62db3
commit
042a60713a
4 changed files with 59 additions and 50 deletions
|
|
@ -138,7 +138,7 @@ stems/ --- @archive
|
|||
|
||||
The file can be edited by hand. A line without ` --- ` stops otag with an error naming the file and line, so nothing gets overwritten until it is fixed. Note words starting with `+`, `@` or `#` are read as tags.
|
||||
|
||||
An optional `.otag.toml` in the same folder configures `scan` and `write` there:
|
||||
An optional `.otag.toml` configures `scan` and `write` for its folder and all subfolders. Each file uses the nearest `.otag.toml` above it, which replaces any further up as a whole:
|
||||
|
||||
```toml
|
||||
plugins = ["id3", "pdf"]
|
||||
|
|
|
|||
|
|
@ -59,8 +59,6 @@ class OrvaniTag:
|
|||
self.CACHEFILE = cache # search index cache ('.otag.cache.json')
|
||||
self.SCHEMA = contexts # Default context schema
|
||||
self.DB = dict() # holds the folder context data
|
||||
self.CONFIG = dict() # Currently used configuration
|
||||
self.CONF_FILES = [] # List to keep track of config files in the directory tree
|
||||
self.FILE = '' # holds the folder context file path
|
||||
self.INDEX = self.__emptyindex() # our search index
|
||||
self.__cd('')
|
||||
|
|
@ -72,42 +70,28 @@ class OrvaniTag:
|
|||
return index
|
||||
|
||||
|
||||
def __cd(self, path: str, config: bool=False):
|
||||
def __cd(self, path: str):
|
||||
"""
|
||||
Change folder context to other directory.
|
||||
Checks if a folder context file exists and loads context DB.
|
||||
|
||||
path (str): path of the new folder context
|
||||
"""
|
||||
|
||||
# Helper function to load the custom configuration from a folder context
|
||||
# If none is found, we check if there is one further up the directory tree
|
||||
def __config(path):
|
||||
if self.CONF_FILES: # do we have known configurations up the tree?
|
||||
for conf_file in self.CONF_FILES:
|
||||
if path == '.': path = './'
|
||||
# has current directory has a common tree branch with a known configuration?
|
||||
common = '.' + os.path.commonpath([conf_file, str(path)])
|
||||
if common == '.': common = './'
|
||||
if common == conf_file:
|
||||
cfile = Path(conf_file) / CONFIG_FILE
|
||||
if cfile.exists():
|
||||
self.CONFIG = tomllib.loads(cfile.read_text(encoding='utf-8'))
|
||||
continue
|
||||
else: # if there is no config up the tree, then use defaults
|
||||
self.CONFIG = dict()
|
||||
|
||||
self.FILE = Path(str(path)) / self.FILENAME
|
||||
self.DB = self.__read(self.FILE) if self.FILE.exists() else dict()
|
||||
|
||||
cfile = Path(str(path)) / CONFIG_FILE
|
||||
if cfile.exists(): # load optional local configuration
|
||||
self.CONFIG = tomllib.loads(cfile.read_text(encoding='utf-8'))
|
||||
self.CONF_FILES.append(str(path)) # and save it in our known files list
|
||||
self.CONF_FILES = [x for x in self.CONF_FILES if x != '']
|
||||
self.CONF_FILES = sorted(list(set(self.CONF_FILES)), key=lambda x: x.count('/'), reverse=True)
|
||||
elif config:
|
||||
__config(path)
|
||||
|
||||
def config(self, folder: str='.') -> dict:
|
||||
"""
|
||||
Returns the configuration of a folder: the nearest .otag.toml in it or any parent folder.
|
||||
A nearer configuration replaces the ones above it entirely.
|
||||
"""
|
||||
folder = Path(str(folder) or '.').resolve()
|
||||
for directory in [folder, *folder.parents]:
|
||||
cfile = directory / CONFIG_FILE
|
||||
if cfile.is_file():
|
||||
return tomllib.loads(cfile.read_text(encoding='utf-8'))
|
||||
return dict()
|
||||
|
||||
|
||||
def __read(self, file: Path) -> dict:
|
||||
|
|
@ -589,9 +573,7 @@ class OrvaniTag:
|
|||
if self.ENABLE_PLUGINS:
|
||||
if files:
|
||||
for file in files:
|
||||
fpath = os.path.join(root, file)
|
||||
self.__cd(root, True)
|
||||
self.read_metadata(fpath)
|
||||
self.read_metadata(os.path.join(root, file))
|
||||
|
||||
if not recursive: # check if to include subdirectories
|
||||
break
|
||||
|
|
@ -626,10 +608,12 @@ class OrvaniTag:
|
|||
Processes a file with all reader plugins to extract its metadata
|
||||
"""
|
||||
|
||||
config = self.config(Path(filename).parent)
|
||||
|
||||
# Utilities for reader plugins
|
||||
obj = {
|
||||
'file' : Path(),
|
||||
'config' : self.CONFIG,
|
||||
'config' : config,
|
||||
'schema' : self.SCHEMA,
|
||||
'context' : self.__context,
|
||||
'string' : self.__get_argv
|
||||
|
|
@ -638,15 +622,15 @@ class OrvaniTag:
|
|||
|
||||
for plugin_name, plugin in self.__plugins().items():
|
||||
# Check if plugin is (de)activated by a local folder configuration
|
||||
if 'plugins' in self.CONFIG:
|
||||
if plugin_name not in self.CONFIG['plugins']:
|
||||
if 'plugins' in config:
|
||||
if plugin_name not in config['plugins']:
|
||||
continue
|
||||
|
||||
# Load a list of file types the plugin is able to read
|
||||
suffixes = plugin.TYPES # The plugin's default file types
|
||||
if plugin_name in self.CONFIG: # check for local folder config overrides
|
||||
if 'types' in self.CONFIG[plugin_name]:
|
||||
suffixes = self.CONFIG[plugin_name]['types']
|
||||
if plugin_name in config: # check for local folder config overrides
|
||||
if 'types' in config[plugin_name]:
|
||||
suffixes = config[plugin_name]['types']
|
||||
|
||||
o.file = Path(filename)
|
||||
if o.file.suffix in suffixes: # Does the file match a permitted file type
|
||||
|
|
@ -670,9 +654,7 @@ class OrvaniTag:
|
|||
if self.ENABLE_PLUGINS:
|
||||
if files:
|
||||
for file in files:
|
||||
fpath = os.path.join(root, file)
|
||||
self.__cd(root, True)
|
||||
self.write_metadata(fpath, method)
|
||||
self.write_metadata(os.path.join(root, file), method)
|
||||
|
||||
if not recursive: # check if to include subdirectories
|
||||
break
|
||||
|
|
@ -686,10 +668,12 @@ class OrvaniTag:
|
|||
method (str): The name of a plugin able to process the file type
|
||||
"""
|
||||
|
||||
config = self.config(Path(filename).parent)
|
||||
|
||||
# Utilities for writer plugins
|
||||
obj = {
|
||||
'file' : Path(),
|
||||
'config' : self.CONFIG,
|
||||
'config' : config,
|
||||
'schema' : self.SCHEMA,
|
||||
'context' : {},
|
||||
'collect' : self.__collect_contexts,
|
||||
|
|
@ -700,17 +684,17 @@ class OrvaniTag:
|
|||
for plugin_name, plugin in self.__plugins().items():
|
||||
|
||||
# Check if plugin is (de)activated by a local folder configuration
|
||||
if 'plugins' in self.CONFIG:
|
||||
if plugin_name not in self.CONFIG['plugins']:
|
||||
if 'plugins' in config:
|
||||
if plugin_name not in config['plugins']:
|
||||
continue
|
||||
|
||||
if plugin.NAME == method: # select the reference plugin
|
||||
|
||||
# Load a list of file types the plugin is able to read
|
||||
suffixes = plugin.TYPES # Load the plugin's defaults
|
||||
if plugin_name in self.CONFIG: # Check for local folder config overrides
|
||||
if 'types' in self.CONFIG[plugin_name]:
|
||||
suffixes = self.CONFIG[plugin_name]['types']
|
||||
if plugin_name in config: # Check for local folder config overrides
|
||||
if 'types' in config[plugin_name]:
|
||||
suffixes = config[plugin_name]['types']
|
||||
for suffix in suffixes:
|
||||
if not suffix in plugin.TYPES:
|
||||
suffixes.remove(suffix)
|
||||
|
|
@ -735,7 +719,7 @@ class OrvaniTag:
|
|||
return [5, plugin_name]
|
||||
return [2, filename]
|
||||
else:
|
||||
return [4, suffixes, plugin_name, plugin_name in self.CONFIG]
|
||||
return [4, suffixes, plugin_name, plugin_name in config]
|
||||
return [6, method]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -144,7 +144,21 @@ def test_copy_tags_from_subfolder_to_folder(tree):
|
|||
|
||||
def test_config_without_tags(tree):
|
||||
(tree / orvani.CONFIG_FILE).write_text('plugins = ["pdf"]\n')
|
||||
assert OrvaniTag().CONFIG == {'plugins': ['pdf']}
|
||||
assert OrvaniTag().config('.') == {'plugins': ['pdf']}
|
||||
|
||||
|
||||
def test_subfolders_inherit_config(tree):
|
||||
(tree / orvani.CONFIG_FILE).write_text('plugins = ["pdf"]\n')
|
||||
(tree / 'sub' / 'deeper').mkdir()
|
||||
assert OrvaniTag().config('sub/deeper') == {'plugins': ['pdf']}
|
||||
|
||||
|
||||
def test_nearest_config_wins(tree):
|
||||
(tree / orvani.CONFIG_FILE).write_text('plugins = ["pdf"]\n')
|
||||
(tree / 'sub' / orvani.CONFIG_FILE).write_text('plugins = ["id3"]\n')
|
||||
o = OrvaniTag()
|
||||
assert o.config('sub') == {'plugins': ['id3']}
|
||||
assert o.config('.') == {'plugins': ['pdf']}
|
||||
|
||||
|
||||
def test_removing_last_entry_keeps_config(tree):
|
||||
|
|
|
|||
|
|
@ -120,3 +120,14 @@ def test_cmd_list_is_recursive_unless_no_recursive(cwd, monkeypatch, capsys):
|
|||
main()
|
||||
out = capsys.readouterr().out
|
||||
assert 'a.mp3' in out and 'deep.mp3' not in out
|
||||
|
||||
|
||||
def test_write_uses_config_of_the_files_folder(cwd):
|
||||
(cwd / 'sub').mkdir()
|
||||
(cwd / 'sub' / orvani.CONFIG_FILE).write_text('plugins = ["office"]\n')
|
||||
writer = PdfWriter()
|
||||
writer.add_blank_page(72, 72)
|
||||
writer.write(cwd / 'sub' / 'doc.pdf')
|
||||
|
||||
OrvaniTag().add('sub/doc.pdf', '#lofi')
|
||||
assert OrvaniTag().write_metadata('sub/doc.pdf', 'pdf') == [6, 'pdf']
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue