[LNT] r309010 - admin: Rework commandline/click handling

Matthias Braun via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 25 12:42:42 PDT 2017


Author: matze
Date: Tue Jul 25 12:42:41 2017
New Revision: 309010

URL: http://llvm.org/viewvc/llvm-project?rev=309010&view=rev
Log:
admin: Rework commandline/click handling

Change code to use click in a more canonical way: Push common options to
the `lnt admin` level and pass a config object down to subcommands.

Modified:
    lnt/trunk/lnt/lnttool/admin.py

Modified: lnt/trunk/lnt/lnttool/admin.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/lnttool/admin.py?rev=309010&r1=309009&r2=309010&view=diff
==============================================================================
--- lnt/trunk/lnt/lnttool/admin.py (original)
+++ lnt/trunk/lnt/lnttool/admin.py Tue Jul 25 12:42:41 2017
@@ -1,8 +1,6 @@
 #!/usr/bin/env python
 import click
 
-_config_filename = 'lntadmin.yaml'
-
 
 def _load_dependencies():
     global yaml, sys, requests, json, os, httplib
@@ -23,55 +21,70 @@ def _fatal(msg):
     sys.exit(1)
 
 
-def _check_normalize_config(config, need_auth_token):
-    '''Verify whether config is correct and complete. Also normalizes the
-    server URL if necessary.'''
-    lnt_url = config.get('lnt_url', None)
-    if lnt_url is None:
-        _fatal('No lnt_url specified in config or commandline\n'
-               'Tip: Use `create-config` for an example configuration')
-    if lnt_url.endswith('/'):
-        lnt_url = lnt_url[:-1]
-        config['lnt_url'] = lnt_url
-    database = config.get('database', None)
-    if database is None:
-        _fatal('No database specified in config or commandline')
-    testsuite = config.get('testsuite', None)
-    if testsuite is None:
-        config['testsuite'] = 'nts'
-
-    session = requests.Session()
-    user = config.get('user', None)
-    password = config.get('password', None)
-    if user is not None and password is not None:
-        session.auth = (user, password)
+_default_config_filename = './lntadmin.yaml'
+
+
+class AdminConfig(object):
+    def __init__(self, **args):
+        self._set('verbose', args['verbose'])
+        self._try_load_config(args['config'])
+        for key, value in args.items():
+            self._set(key, value)
+        self._check_and_normalize()
+
+    def _set(self, key, value):
+        '''Set attribute `key` of object to `value`. If `value` is None
+        then only set the attribute if it doesn't exist yet.'''
+        if value is None and hasattr(self, key):
+            return
+        setattr(self, key, value)
+
+    @property
+    def dict(self):
+        return self.__dict__
+
+    def _try_load_config(self, filename):
+        try:
+            config = yaml.load(open(filename))
+            for key, value in config.items():
+                self._set(key, value)
+        except IOError as e:
+            if self.verbose or filename != _default_config_filename:
+                _error("Could not load configuration file '%s': %s\n" %
+                       (filename, e))
+
+    def _check_and_normalize(self):
+        lnt_url = self.lnt_url
+        if lnt_url is None:
+            _fatal('No lnt_url specified in config or commandline\n'
+                   'Tip: Use `create-config` for an example configuration')
+        if lnt_url.endswith('/'):
+            lnt_url = lnt_url[:-1]
+            self.lnt_url = lnt_url
+        if self.database is None:
+            self.database = 'default'
+        if self.testsuite is None:
+            self.testsuite = 'nts'
+
+        session = requests.Session()
+        user = self.dict.get('user', None)
+        password = self.dict.get('password', None)
+        if user is not None and password is not None:
+            session.auth = (user, password)
+
+        self._set('auth_token', None)
+        auth_token = self.auth_token
+        if auth_token is not None:
+            session.headers.update({'AuthToken': auth_token})
+        self.session = session
+
+
+_pass_config = click.make_pass_decorator(AdminConfig)
+
 
-    auth_token = config.get('auth_token', None)
-    if need_auth_token and auth_token is None:
+def _check_auth_token(config):
+    if config.auth_token is None:
         _fatal('No auth_token specified in config')
-    else:
-        session.headers.update({'AuthToken': auth_token})
-    config['session'] = session
-
-
-def _make_config(kwargs, need_auth_token=False):
-    '''Load configuration from yaml file, merges it with the commandline
-    options and verifies the resulting configuration.'''
-    verbose = kwargs.get('verbose', False)
-    # Load config file
-    config = {}
-    try:
-        config = yaml.load(open(_config_filename))
-    except IOError:
-        if verbose:
-            _error("Could not load configuration file '%s'\n" %
-                   _config_filename)
-    for key, value in kwargs.items():
-        if value is None:
-            continue
-        config[key] = value
-    _check_normalize_config(config, need_auth_token=need_auth_token)
-    return config
 
 
 def _check_response(response):
@@ -97,50 +110,35 @@ def _print_run_info(run, indent=''):
         sys.stdout.write('%s%s: %s\n' % (indent, key, value))
 
 
-def _common_options(func):
-    func = click.option("--lnt-url", help="URL of LNT server")(func)
-    func = click.option("--database", help="database to use")(func)
-    func = click.option("--testsuite", help="testsuite to use")(func)
-    func = click.option("--verbose", "-v", is_flag=True,
-                        help="verbose output")(func)
-    return func
-
-
 @click.command("list-machines")
- at _common_options
-def action_list_machines(**kwargs):
+ at _pass_config
+def action_list_machines(config):
     """List machines and their id numbers."""
-    config = _make_config(kwargs)
-
     url = ('{lnt_url}/api/db_{database}/v4/{testsuite}/machines'
-           .format(**config))
-    session = config['session']
-    response = session.get(url)
+           .format(**config.dict))
+    response = config.session.get(url)
     _check_response(response)
     data = json.loads(response.text)
     for machine in data['machines']:
         id = machine.get('id', None)
         name = machine.get('name', None)
         sys.stdout.write("%s:%s\n" % (name, id))
-        if config['verbose']:
+        if config.verbose:
             _print_machine_info(machine, indent='\t')
 
 
 @click.command("get-machine")
+ at _pass_config
 @click.argument("machine")
- at _common_options
-def action_get_machine(**kwargs):
+def action_get_machine(config, machine):
     """Download machine information and run list."""
-    config = _make_config(kwargs)
-
-    filename = 'machine_%s.json' % config['machine']
+    filename = 'machine_%s.json' % machine
     if os.path.exists(filename):
         _fatal("'%s' already exists" % filename)
 
     url = ('{lnt_url}/api/db_{database}/v4/{testsuite}/machines/{machine}'
-           .format(**config))
-    session = config['session']
-    response = session.get(url)
+           .format(machine=machine, **config.dict))
+    response = config.session.get(url)
     _check_response(response)
     data = json.loads(response.text)
     assert len(data['machines']) == 1
@@ -158,16 +156,15 @@ def action_get_machine(**kwargs):
 
 
 @click.command("rm-machine")
+ at _pass_config
 @click.argument("machine")
- at _common_options
-def action_rm_machine(**kwargs):
+def action_rm_machine(config, machine):
     """Remove machine and related data."""
-    config = _make_config(kwargs, need_auth_token=True)
+    _check_auth_token(config)
 
     url = ('{lnt_url}/api/db_{database}/v4/{testsuite}/machines/{machine}'
-           .format(**config))
-    session = config['session']
-    response = session.delete(url, stream=True)
+           .format(machine=machine, **config.dict))
+    response = config.session.delete(url, stream=True)
     _check_response(response)
     for line in response.iter_lines():
         sys.stdout.write(line + '\n')
@@ -175,52 +172,54 @@ def action_rm_machine(**kwargs):
 
 
 @click.command("rename-machine")
+ at _pass_config
 @click.argument("machine")
 @click.argument("new-name")
- at _common_options
-def action_rename_machine(**kwargs):
+def action_rename_machine(config, machine, new_name):
     """Rename machine."""
-    config = _make_config(kwargs, need_auth_token=True)
+    _check_auth_token(config)
 
     url = ('{lnt_url}/api/db_{database}/v4/{testsuite}/machines/{machine}'
-           .format(**config))
-    session = config['session']
-    response = session.post(url, data=(('action', 'rename'),
-                                       ('name', config['new_name'])))
+           .format(machine=machine, **config.dict))
+    post_data = {
+        'action': 'rename',
+        'name': new_name,
+    }
+    response = config.session.post(url, data=post_data)
     _check_response(response)
 
 
 @click.command("merge-machine-into")
+ at _pass_config
 @click.argument("machine")
 @click.argument("into")
- at _common_options
-def action_merge_machine_into(**kwargs):
+def action_merge_machine_into(config, machine, into):
     """Merge machine into another machine."""
-    config = _make_config(kwargs, need_auth_token=True)
+    _check_auth_token(config)
 
     url = ('{lnt_url}/api/db_{database}/v4/{testsuite}/machines/{machine}'
-           .format(**config))
+           .format(machine=machine, **config.dict))
     session = config['session']
-    response = session.post(url, data=(('action', 'merge'),
-                                       ('into', config['into'])))
+    post_data = {
+        'action': 'merge',
+        'into': into
+    }
+    response = config.session.post(url, data=post_data)
     _check_response(response)
 
 
 @click.command("list-runs")
+ at _pass_config
 @click.argument("machine")
- at _common_options
-def action_list_runs(**kwargs):
+def action_list_runs(config, machine):
     """List runs of a machine."""
-    config = _make_config(kwargs)
-
     url = ('{lnt_url}/api/db_{database}/v4/{testsuite}/machines/{machine}'
-           .format(**config))
-    session = config['session']
-    response = session.get(url)
+           .format(machine=machine, **config.dict))
+    response = config.session.get(url)
     _check_response(response)
     data = json.loads(response.text)
     runs = data['runs']
-    if config['verbose']:
+    if config.verbose:
         sys.stdout.write("order run-id\n")
         sys.stdout.write("------------\n")
     for run in runs:
@@ -229,28 +228,24 @@ def action_list_runs(**kwargs):
         for field in order_by:
             orders.append("%s=%s" % (field, run[field]))
         sys.stdout.write("%s %s\n" % (";".join(orders), run['id']))
-        if config['verbose']:
+        if config.verbose:
             _print_run_info(run, indent='\t')
 
 
 @click.command("get-run")
+ at _pass_config
 @click.argument("runs", nargs=-1, required=True)
- at _common_options
-def action_get_run(**kwargs):
+def action_get_run(config, runs):
     """Download runs and save as report files."""
-    config = _make_config(kwargs)
-
-    runs = config['runs']
     for run in runs:
         filename = 'run_%s.json' % run
         if os.path.exists(filename):
             _fatal("'%s' already exists" % filename)
 
-    session = config['session']
     for run in runs:
         url = ('{lnt_url}/api/db_{database}/v4/{testsuite}/runs/{run}'
-               .format(run=run, **config))
-        response = session.get(url)
+               .format(run=run, **config.dict))
+        response = config.session.get(url)
         _check_response(response)
 
         data = json.loads(response.text)
@@ -261,51 +256,47 @@ def action_get_run(**kwargs):
 
 
 @click.command("rm-run")
+ at _pass_config
 @click.argument("runs", nargs=-1, required=True)
- at _common_options
-def action_rm_run(**kwargs):
+def action_rm_run(config, runs):
     """Remove runs and related data."""
-    config = _make_config(kwargs, need_auth_token=True)
+    _check_auth_token(config)
 
-    session = config['session']
-    runs = config['runs']
     for run in runs:
         url = ('{lnt_url}/api/db_{database}/v4/{testsuite}/runs/{run}'
-               .format(run=run, **config))
-        response = session.delete(url)
+               .format(run=run, **config.dict))
+        response = config.session.delete(url)
         _check_response(response)
 
 
 @click.command("post-run")
+ at _pass_config
 @click.argument("datafiles", nargs=-1, type=click.Path(exists=True),
                 required=True)
- at _common_options
 @click.option("--update-machine", is_flag=True, help="Update machine fields")
 @click.option("--merge", default="replace", show_default=True,
               type=click.Choice(['reject', 'replace', 'merge']),
               help="Merge strategy when run already exists")
-def action_post_run(**kwargs):
+def action_post_run(config, datafiles, update_machine, merge):
     """Submit report files to server."""
-    config = _make_config(kwargs, need_auth_token=True)
+    _check_auth_token(config)
 
-    session = config['session']
-    datafiles = config['datafiles']
     for datafile in datafiles:
         with open(datafile, "r") as datafile:
             data = datafile.read()
 
         url = ('{lnt_url}/api/db_{database}/v4/{testsuite}/runs'
-               .format(**config))
+               .format(**config.dict))
         url_params = {
-            'update_machine': 1 if config['update_machine'] else 0,
-            'merge': config['merge'],
+            'update_machine': 1 if update_machine else 0,
+            'merge': merge,
         }
-        response = session.post(url, params=url_params, data=data,
-                                allow_redirects=False)
+        response = config.session.post(url, params=url_params, data=data,
+                                       allow_redirects=False)
         _check_response(response)
         if response.status_code == 301:
             sys.stdout.write(response.headers.get('Location') + '\n')
-        if config['verbose']:
+        if config.verbose:
             try:
                 response_data = json.loads(response.text)
                 json.dump(response_data, sys.stderr, response_data, indent=2)
@@ -317,9 +308,9 @@ def action_post_run(**kwargs):
 @click.command('create-config')
 def action_create_config():
     """Create example configuration."""
-    if os.path.exists(_config_filename):
-        _fatal("'%s' already exists" % _config_filename)
-    with open(_config_filename, "w") as out:
+    if os.path.exists(_default_config_filename):
+        _fatal("'%s' already exists" % _default_config_filename)
+    with open(_default_config_filename, "w") as out:
         out.write('''\
 lnt_url: "http://localhost:8000"
 database: default
@@ -328,7 +319,7 @@ testsuite: nts
 # password: 'http_password'
 # auth_token: 'secret'
 ''')
-    sys.stderr.write("Created '%s'\n" % _config_filename)
+    sys.stderr.write("Created '%s'\n" % _default_config_filename)
 
 
 class AdminCLI(click.MultiCommand):
@@ -358,5 +349,16 @@ class AdminCLI(click.MultiCommand):
 
 
 @click.group("admin", cls=AdminCLI, no_args_is_help=True)
-def group_admin():
+ at click.option("--config", "-C", help="Config File", type=click.Path(),
+              default=_default_config_filename, show_default=True)
+ at click.option("--lnt-url", help="URL of LNT server", metavar="URL")
+ at click.option("--database", help="database to use", metavar="DBNAME")
+ at click.option("--testsuite", help="testsuite to use", metavar="SUITE")
+ at click.option("--verbose", "-v", is_flag=True, help="verbose output")
+ at click.pass_context
+def group_admin(ctx, **kwargs):
     """LNT server admin client."""
+    command = ctx.invoked_subcommand
+    if command is None or command == "create-config" or '--help' in sys.argv:
+        return
+    ctx.obj = AdminConfig(**kwargs)




More information about the llvm-commits mailing list