mirror of https://github.com/patacrep/patacrep.git
Engine for LaTeX songbooks
http://www.patacrep.com
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
4.1 KiB
131 lines
4.1 KiB
"""File system utilities."""
|
|
|
|
from contextlib import contextmanager
|
|
import importlib
|
|
import logging
|
|
import os
|
|
import posixpath
|
|
import re
|
|
|
|
LOGGER = logging.getLogger(__name__)
|
|
|
|
def recursive_find(root_directory, extensions):
|
|
"""Recursively find files with the given extensions, from a root_directory.
|
|
|
|
Return a list of files matching those conditions.
|
|
|
|
Arguments:
|
|
- `extensions`: list of accepted extensions.
|
|
- `root_directory`: root directory of the search.
|
|
"""
|
|
if not os.path.isdir(root_directory):
|
|
return []
|
|
|
|
matches = []
|
|
pattern = re.compile(r'.*\.({})$'.format('|'.join(extensions)))
|
|
with chdir(root_directory):
|
|
for root, __ignored, filenames in os.walk(os.curdir):
|
|
for filename in filenames:
|
|
if pattern.match(filename):
|
|
matches.append(os.path.join(root, filename))
|
|
return matches
|
|
|
|
def relpath(path, start=None):
|
|
"""Return relative filepath to path if a subpath of start."""
|
|
if start is None:
|
|
start = os.curdir
|
|
if os.path.abspath(path).startswith(os.path.abspath(start)):
|
|
return os.path.relpath(path, start)
|
|
else:
|
|
return os.path.abspath(path)
|
|
|
|
|
|
def path2posix(string):
|
|
""""Convert path from local format to posix format."""
|
|
if not string or string == "/":
|
|
return string
|
|
if os.path.splitdrive(string)[1] == "\\":
|
|
# Assuming DRIVE:\
|
|
return string[0:-1]
|
|
(head, tail) = os.path.split(string)
|
|
return posixpath.join(
|
|
path2posix(head),
|
|
tail,
|
|
)
|
|
|
|
@contextmanager
|
|
def chdir(path):
|
|
"""Locally change dir
|
|
|
|
Can be used as:
|
|
|
|
with chdir("some/directory"):
|
|
do_stuff()
|
|
"""
|
|
olddir = os.getcwd()
|
|
if path:
|
|
os.chdir(path)
|
|
yield
|
|
os.chdir(olddir)
|
|
else:
|
|
yield
|
|
|
|
def load_plugins(datadirs, root_modules, keyword):
|
|
"""Load all plugins, and return a dictionary of those plugins.
|
|
|
|
A plugin is a .py file, submodule of `subdir`, located in one of the
|
|
directories of `datadirs`. It contains a dictionary `keyword`. The return
|
|
value is the union of the dictionaries of the loaded plugins.
|
|
|
|
Arguments:
|
|
- datadirs: List of directories in which plugins are to be searched.
|
|
- root_modules: the submodule in which plugins are to be searched, as a
|
|
list of modules (e.g. ["some", "deep", "module"] for
|
|
"some.deep.module").
|
|
- keyword: attribute containing plugin information.
|
|
|
|
Return value: a dictionary where:
|
|
- keys are the keywords ;
|
|
- values are functions triggered when this keyword is met.
|
|
"""
|
|
plugins = {}
|
|
directory_list = (
|
|
[
|
|
os.path.join(datadir, "python", *root_modules)
|
|
for datadir in datadirs
|
|
]
|
|
+ [os.path.join(
|
|
os.path.dirname(__file__),
|
|
*root_modules
|
|
)]
|
|
)
|
|
for directory in directory_list:
|
|
if not os.path.exists(directory):
|
|
LOGGER.debug(
|
|
"Ignoring non-existent directory '%s'.",
|
|
directory
|
|
)
|
|
continue
|
|
for (dirpath, __ignored, filenames) in os.walk(directory):
|
|
modules = ["patacrep"] + root_modules
|
|
if os.path.relpath(dirpath, directory) != ".":
|
|
modules.extend(os.path.relpath(dirpath, directory).split("/"))
|
|
for name in filenames:
|
|
if name == "__init__.py":
|
|
modulename = []
|
|
elif name.endswith(".py"):
|
|
modulename = [name[:-len('.py')]]
|
|
else:
|
|
continue
|
|
plugin = importlib.import_module(".".join(modules + modulename))
|
|
if hasattr(plugin, keyword):
|
|
for (key, value) in getattr(plugin, keyword).items():
|
|
if key in plugins:
|
|
LOGGER.warning(
|
|
"File %s: Keyword '%s' is already used. Ignored.",
|
|
relpath(os.path.join(dirpath, name)),
|
|
key,
|
|
)
|
|
continue
|
|
plugins[key] = value
|
|
return plugins
|
|
|