1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
import argparse
import sys
import textwrap
from importlib import import_module
from ase import __version__
class CLIError(Exception):
"""Error for CLI commands.
A subcommand may raise this. The message will be forwarded to
the error() method of the argument parser."""
# Important: Following any change to command-line parameters, use
# python3 -m ase.cli.completion to update autocompletion.
commands = [
('info', 'ase.cli.info'),
# ('show', 'ase.cli.show'),
('test', 'ase.test'),
('gui', 'ase.gui.ag'),
('db', 'ase.db.cli'),
('run', 'ase.cli.run'),
('band-structure', 'ase.cli.band_structure'),
('build', 'ase.cli.build'),
('dimensionality', 'ase.cli.dimensionality'),
('eos', 'ase.eos'),
('ulm', 'ase.io.ulm'),
('find', 'ase.cli.find'),
('nebplot', 'ase.cli.nebplot'),
('nomad-upload', 'ase.cli.nomad'),
('nomad-get', 'ase.cli.nomadget'),
('convert', 'ase.cli.convert'),
('reciprocal', 'ase.cli.reciprocal'),
('completion', 'ase.cli.completion'),
('diff', 'ase.cli.diff'),
('exec', 'ase.cli.exec')
]
def main(prog='ase', description='ASE command line tool.',
version=__version__, commands=commands, hook=None, args=None):
parser = argparse.ArgumentParser(prog=prog,
description=description,
formatter_class=Formatter)
parser.add_argument('--version', action='version',
version='%(prog)s-{}'.format(version))
parser.add_argument('-T', '--traceback', action='store_true')
subparsers = parser.add_subparsers(title='Sub-commands',
dest='command')
subparser = subparsers.add_parser('help',
description='Help',
help='Help for sub-command.')
subparser.add_argument('helpcommand',
nargs='?',
metavar='sub-command',
help='Provide help for sub-command.')
functions = {}
parsers = {}
for command, module_name in commands:
cmd = import_module(module_name).CLICommand
docstring = cmd.__doc__
if docstring is None:
# Backwards compatibility with GPAW
short = cmd.short_description
long = getattr(cmd, 'description', short)
else:
parts = docstring.split('\n', 1)
if len(parts) == 1:
short = docstring
long = docstring
else:
short, body = parts
long = short + '\n' + textwrap.dedent(body)
subparser = subparsers.add_parser(
command,
formatter_class=Formatter,
help=short,
description=long)
cmd.add_arguments(subparser)
functions[command] = cmd.run
parsers[command] = subparser
if hook:
args = hook(parser, args)
else:
args = parser.parse_args(args)
if args.command == 'help':
if args.helpcommand is None:
parser.print_help()
else:
parsers[args.helpcommand].print_help()
elif args.command is None:
parser.print_usage()
else:
f = functions[args.command]
try:
if f.__code__.co_argcount == 1:
f(args)
else:
f(args, parsers[args.command])
except KeyboardInterrupt:
pass
except CLIError as x:
parser.error(x)
except Exception as x:
if args.traceback:
raise
else:
l1 = '{}: {}\n'.format(x.__class__.__name__, x)
l2 = ('To get a full traceback, use: {} -T {} ...'
.format(prog, args.command))
parser.error(l1 + l2)
class Formatter(argparse.HelpFormatter):
"""Improved help formatter."""
def _fill_text(self, text, width, indent):
assert indent == ''
out = ''
blocks = text.split('\n\n')
for block in blocks:
if block[0] == '*':
# List items:
for item in block[2:].split('\n* '):
out += textwrap.fill(item,
width=width - 2,
initial_indent='* ',
subsequent_indent=' ') + '\n'
elif block[0] == ' ':
# Indented literal block:
out += block + '\n'
else:
# Block of text:
out += textwrap.fill(block, width=width) + '\n'
out += '\n'
return out[:-1]
def old():
cmd = sys.argv[0].split('-')[-1]
print('Please use "ase {cmd}" instead of "ase-{cmd}"'.format(cmd=cmd))
sys.argv[:1] = ['ase', cmd]
main()
|