mirror of https://github.com/patacrep/patacrep.git
Louis
9 years ago
11 changed files with 266 additions and 55 deletions
@ -0,0 +1,54 @@ |
|||
"""Raw songbook utilities""" |
|||
|
|||
import logging |
|||
import os |
|||
import sys |
|||
import yaml |
|||
|
|||
from patacrep import encoding |
|||
import patacrep |
|||
|
|||
LOGGER = logging.getLogger() |
|||
|
|||
def open_songbook(filename): |
|||
"""Open songbook, and return a raw songbook object. |
|||
|
|||
:param str filename: Filename of the yaml songbook. |
|||
:rvalue: dict |
|||
:return: Songbook, as a dictionary. |
|||
""" |
|||
if os.path.exists(filename + ".sb") and not os.path.exists(filename): |
|||
filename += ".sb" |
|||
|
|||
try: |
|||
with patacrep.encoding.open_read(filename) as songbook_file: |
|||
songbook = yaml.load(songbook_file) |
|||
if 'encoding' in songbook: |
|||
with encoding.open_read( |
|||
filename, |
|||
encoding=songbook['encoding'] |
|||
) as songbook_file: |
|||
songbook = yaml.load(songbook_file) |
|||
except Exception as error: # pylint: disable=broad-except |
|||
raise patacrep.errors.YAMLError(str(error)) |
|||
|
|||
songbook['_basename'] = os.path.basename(filename)[:-3] |
|||
|
|||
# Gathering datadirs |
|||
datadirs = [] |
|||
if 'datadir' in songbook: |
|||
if isinstance(songbook['datadir'], str): |
|||
songbook['datadir'] = [songbook['datadir']] |
|||
datadirs += [ |
|||
os.path.join( |
|||
os.path.dirname(os.path.abspath(filename)), |
|||
path |
|||
) |
|||
for path in songbook['datadir'] |
|||
] |
|||
# Default value |
|||
datadirs.append(os.path.dirname(os.path.abspath(filename))) |
|||
|
|||
songbook['datadir'] = datadirs |
|||
|
|||
return songbook |
@ -0,0 +1,116 @@ |
|||
#!/bin/env python3 |
|||
|
|||
"""Command line client to :mod:`tools`""" |
|||
|
|||
import argparse |
|||
import logging |
|||
import operator |
|||
import os |
|||
import pkgutil |
|||
import re |
|||
import sys |
|||
|
|||
import patacrep |
|||
|
|||
# Logging configuration |
|||
logging.basicConfig(level=logging.INFO) |
|||
LOGGER = logging.getLogger() |
|||
|
|||
def _execlp(program, args): |
|||
"""Call :func:`os.execlp`, adding `program` as the first argument to itself.""" |
|||
return os.execlp(program, program, *args) |
|||
|
|||
def _iter_subcommands(): |
|||
"""Iterate over subcommands. |
|||
|
|||
The objects returned are tuples of: |
|||
- the name of the command; |
|||
- its description; |
|||
- the function to call to execute the subcommand. |
|||
""" |
|||
subcommands = [] |
|||
|
|||
# Get python subcommands |
|||
path = [os.path.join(item, "patacrep", "tools") for item in sys.path] |
|||
prefix = "patacrep.tools." |
|||
module_re = re.compile(r'{}(?P<subcommand>[^\.]*)\.__main__'.format(prefix)) |
|||
for module_loader, name, _ in pkgutil.walk_packages(path, prefix): |
|||
match = module_re.match(name) |
|||
if match: |
|||
module = module_loader.find_module(match.string).load_module() |
|||
if hasattr(module, "SUBCOMMAND_DESCRIPTION"): |
|||
subcommands.append(match.groupdict()['subcommand']) |
|||
yield ( |
|||
match.groupdict()['subcommand'], |
|||
getattr(module, "SUBCOMMAND_DESCRIPTION"), |
|||
module.main, |
|||
) |
|||
|
|||
class ArgumentParser(argparse.ArgumentParser): |
|||
"""Proxy class to circumvent an :mod:`argparse` bug. |
|||
|
|||
Contrarily to what documented, the `argparse.REMAINDER |
|||
<https://docs.python.org/3/library/argparse.html#nargs>`_ `nargs` setting |
|||
does not include the remainder arguments if the first one begins with `-`. |
|||
|
|||
This bug is reperted as `17050 <https://bugs.python.org/issue17050>`_. This |
|||
class can be deleted once this bug has been fixed. |
|||
""" |
|||
|
|||
def parse_args(self, args=None, namespace=None): |
|||
if args is None: |
|||
args = sys.argv[1:] |
|||
subcommands = [command[0] for command in set(_iter_subcommands())] |
|||
if len(args) > 0: |
|||
if args[0] in subcommands: |
|||
args = [args[0], "--"] + args[1:] |
|||
|
|||
value = super().parse_args(args, namespace) |
|||
|
|||
if hasattr(value, 'remainder'): |
|||
value.remainder = value.remainder[1:] |
|||
return value |
|||
|
|||
|
|||
def commandline_parser(): |
|||
"""Return a command line parser.""" |
|||
|
|||
parser = ArgumentParser( |
|||
prog="patatools", |
|||
description=( |
|||
"Miscellaneous tools for patacrep." |
|||
), |
|||
formatter_class=argparse.RawTextHelpFormatter, |
|||
) |
|||
|
|||
parser.add_argument( |
|||
'--version', |
|||
help='Show version', |
|||
action='version', |
|||
version='%(prog)s ' + patacrep.__version__ |
|||
) |
|||
|
|||
subparsers = parser.add_subparsers( |
|||
title="Subcommands", |
|||
description="List of available subcommands.", |
|||
) |
|||
|
|||
for command, message, function in sorted(_iter_subcommands(), key=operator.itemgetter(0)): |
|||
sub1 = subparsers.add_parser(command, help=message, add_help=False) |
|||
sub1.add_argument('remainder', nargs=argparse.REMAINDER) |
|||
sub1.set_defaults(function=function) |
|||
|
|||
return parser |
|||
|
|||
def main(): |
|||
"""Main function""" |
|||
|
|||
parser = commandline_parser() |
|||
args = parser.parse_args() |
|||
if hasattr(args, "function"): |
|||
args.function(args.remainder) |
|||
else: |
|||
parser.error("Missing command.") |
|||
|
|||
if __name__ == "__main__": |
|||
main() |
@ -0,0 +1,73 @@ |
|||
"""`patatools cache` command: cache manipulation.""" |
|||
|
|||
import argparse |
|||
import logging |
|||
import os |
|||
import shutil |
|||
import sys |
|||
import textwrap |
|||
|
|||
from patacrep import errors |
|||
from patacrep import songbook |
|||
|
|||
LOGGER = logging.getLogger("patatools.cache") |
|||
SUBCOMMAND_DESCRIPTION = "Perform operations on cache." |
|||
|
|||
def filename(name): |
|||
"""Check that argument is an existing, readable file name. |
|||
|
|||
Return the argument for convenience. |
|||
""" |
|||
if os.path.isfile(name) and os.access(name, os.R_OK): |
|||
return name |
|||
raise argparse.ArgumentTypeError("Cannot read file '{}'.".format(name)) |
|||
|
|||
def commandline_parser(): |
|||
"""Return a command line parser.""" |
|||
|
|||
parser = argparse.ArgumentParser( |
|||
prog="patatools cache", |
|||
description=SUBCOMMAND_DESCRIPTION, |
|||
formatter_class=argparse.RawTextHelpFormatter, |
|||
) |
|||
|
|||
subparsers = parser.add_subparsers( |
|||
description="", |
|||
dest="command", |
|||
) |
|||
subparsers.required = True |
|||
|
|||
clear = subparsers.add_parser( |
|||
"clear", |
|||
description="Delete cache.", |
|||
help="Delete cache.", |
|||
) |
|||
clear.add_argument( |
|||
'songbook', |
|||
metavar="SONGBOOK", |
|||
help=textwrap.dedent("""Songbook file to used to look for cache path."""), |
|||
type=filename, |
|||
) |
|||
clear.set_defaults(command=do_clear) |
|||
|
|||
return parser |
|||
|
|||
def do_clear(namespace): |
|||
"""Execute the `patatools cache clear` command.""" |
|||
for datadir in songbook.open_songbook(namespace.songbook)['datadir']: |
|||
cachedir = os.path.join(datadir, ".cache") |
|||
LOGGER.info("Deleting cache directory '{}'...".format(cachedir)) |
|||
if os.path.isdir(cachedir): |
|||
shutil.rmtree(cachedir) |
|||
|
|||
def main(args=None): |
|||
"""Main function: run from command line.""" |
|||
options = commandline_parser().parse_args(args) |
|||
try: |
|||
options.command(options) |
|||
except errors.SongbookError as error: |
|||
LOGGER.error(str(error)) |
|||
sys.exit(1) |
|||
|
|||
if __name__ == "__main__": |
|||
main() |
@ -0,0 +1,6 @@ |
|||
#! /bin/sh |
|||
|
|||
# Do not edit this file. This file is just a helper file for development test. |
|||
# It is not part of the distributed software. |
|||
|
|||
python -m patacrep.tools $@ |
Loading…
Reference in new issue