"""
The main entry point to CLI usage of GAVO code.
"""
#c Copyright 2008-2023, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL. See the
#c COPYING file in the source distribution.
# The idea here is that you expose a CLI functionality by giving, as
# strings, the module and function to call.
#
# We also give a little startup note if we're running on a tty.
# While we do this, we import api; that should take care of most
# of the real startup time.
import os
import sys
import textwrap
import traceback
# this (and its companion in api.py) works around a race condition in
# Debian stretch's python-cryptography module. Remove about 2021.
from cryptography.hazmat.bindings.openssl.binding import Binding #noflake: see above
from gavo.user import common
functions = [
("admin", ("user.admin", "main")),
("adql", ("protocols.adqlglue", "localquery")),
("config", ("base.config", "main")),
("datapack", ("protocols.datapack", "main")),
("drop", ("user.dropping", "dropRD")),
("dlrun", ("protocols.dlasync", "main")),
("dump", ("rsc.dumping", "main")),
("gencol", ("user.mkrd", "gencol")),
("gendoc", ("user.docgen", "main")),
("import", ("user.importing", "main")),
("info", ("user.info", "main")),
("limits", ("user.limits", "main")),
("mkboost", ("grammars.directgrammar", "main")),
("mkrd", ("user.mkrd", "main")),
("publish", ("registry.publication", "main")),
("purge", ("user.dropping", "dropTable")),
("raise", ("user.errhandle", "bailOut")),
("serve", ("user.serve", "main")),
("start", ("user.mkrd", "start")),
("stc", ("stc.cli", "main")),
("show", ("user.show", "main")),
("test", ("rscdef.regtest", "main")),
("taprun", ("protocols.taprunner", "main")),
("totesturl", ("rscdef.regtest", "urlToURL")),
("validate", ("user.validation", "main")),
("upgrade", ("user.upgrade", "main")),
("uwsrun", ("protocols.useruws", "main")),
# init is special cased, but we want it in here for help generation
("init", ("user.initdachs", "main")),
]
def _enablePDB():
# This can't be a callback to the --enable-pdb option since it needs
# errhandle, and we only want to import this after the command line
# is parsed
import pdb
def enterPdb(type, value, tb):
traceback.print_exception(type, value, tb)
pdb.pm()
sys.excepthook = enterPdb
from gavo.base import events
events.PDB_ENABLED = True
def _enableDebug(*args):
from gavo import base
base.DEBUG = True
def _printVersion(*args):
from gavo import base
from gavo.user import upgrade
print("Software (%s) Schema (%s/%s)"%(
base.getVersion(),
upgrade.CURRENT_SCHEMAVERSION,
upgrade.getDBSchemaVersion()))
sys.exit(0)
def _parseCLArgs():
"""parses the command line and returns instructions on how to go on.
As a side effect, sys.argv is manipulated such that the program
called thinks it was execd in the first place.
"""
from optparse import OptionParser
sels = [n for n,x in functions]
sels.sort()
parser = OptionParser(usage="%prog {<global option>} <func>"
" {<func option>} {<func argument>}\n"+
textwrap.fill("<func> is a unique prefix into {%s}"%(", ".join(sels)),
initial_indent='', subsequent_indent=' '),
description="Try %prog <func> --help for function-specific help")
parser.disable_interspersed_args()
parser.add_option("--traceback", help="print a traceback on all errors.",
action="store_true", dest="alwaysTracebacks")
parser.add_option("--hints", help="if there are hints on an error, display"
" them", action="store_true", dest="showHints")
parser.add_option("--enable-pdb", help="run pdb on all errors.",
action="store_true", dest="enablePDB")
parser.add_option("--disable-spew", help='Ignored.',
action="store_true", dest="disableSpew")
parser.add_option("--profile-to", metavar="PROFILEPATH",
help="enable profiling and write a profile to PROFILEPATH",
action="store", dest="profilePath", default=None)
parser.add_option("--suppress-log", help="Do not log exceptions and such"
" to the dachs-specific log files", action="store_true",
dest="suppressLog")
parser.add_option("--debug", help="Produce debug info as appropirate.",
action="callback", callback=_enableDebug)
parser.add_option("--version", help="Write software version to stdout"
" and exit", action="callback", callback=_printVersion)
parser.add_option("-U", "--ui", help="use UI to show what is going on;"
" try --ui=help to see available interfaces.",
dest="uiName", action="store", type="str", default="module-dependent",
metavar="UI")
opts, args = parser.parse_args()
if len(args)<1:
parser.print_help(file=sys.stderr)
sys.exit(2)
module, funcName = common.getMatchingFunction(args[0], functions, parser)
parser.destroy()
args[0] = "dachs "+args[0]
sys.argv = args
return opts, module, funcName
[docs]def main():
# we want to preserve group-writability in all our operations; hence
# this prominent place for overriding a user decision...
os.umask(0o02)
try:
if len(sys.argv)>1 and sys.argv[1]=="init":
# Special case: initial setup, no api working yet
del sys.argv[1]
from gavo.user import initdachs
sys.exit(initdachs.main())
opts, module, funcName = _parseCLArgs()
from gavo import base
from gavo import utils
from gavo.user import errhandle
from gavo.user import plainui
from gavo.user import useless
interfaces = {
"deluge": useless.DelugeUI,
"null": useless.NullUI,
"stingy": plainui.StingyPlainUI,
"semistingy": plainui.SemiStingyPlainUI,
"plain": plainui.PlainUI,
}
if not (opts.suppressLog or os.environ.get("GAVO_LOG")=="no"):
from gavo.user import logui
logui.LoggingUI(base.ui)
if opts.uiName=="module-dependent":
opts.uiName = {"registry.publication": "semistingy",
"user.serve": "null",
"user.validation": "null",
}.get(module, "plain")
if opts.uiName not in interfaces:
raise base.ReportableError("UI %s does not exist. Choose one of"
" %s"%(opts.uiName, ", ".join(interfaces)))
interfaces[opts.uiName](base.ui)
if opts.enablePDB:
_enablePDB()
funcToRun = utils.loadInternalObject(module, funcName)
if opts.profilePath:
import cProfile
cProfile.runctx("funcToRun()", globals(), locals(), opts.profilePath)
return
except Exception:
from gavo.user import errhandle
sys.exit(errhandle.raiseAndCatch())
try:
funcToRun()
except Exception:
if opts.alwaysTracebacks:
traceback.print_exc()
sys.exit(errhandle.raiseAndCatch(opts))
if __name__=="__main__":
main()