move code linting to a stricter pep8-esque auto-formatting tool, black

This commit is contained in:
Ryan Petrello
2021-03-19 12:44:51 -04:00
parent 9b702e46fe
commit c2ef0a6500
671 changed files with 20538 additions and 21924 deletions

View File

@@ -56,14 +56,7 @@ def run(stdout=sys.stdout, stderr=sys.stderr, argv=[]):
json.dump(e.msg, sys.stdout)
print('')
elif cli.get_config('format') == 'yaml':
sys.stdout.write(to_str(
yaml.safe_dump(
e.msg,
default_flow_style=False,
encoding='utf-8',
allow_unicode=True
)
))
sys.stdout.write(to_str(yaml.safe_dump(e.msg, default_flow_style=False, encoding='utf-8', allow_unicode=True)))
elif cli.get_config('format') == 'human':
sys.stdout.write(e.__class__.__name__)
print('')

View File

@@ -8,9 +8,7 @@ import sys
from requests.exceptions import RequestException
from .custom import handle_custom_actions
from .format import (add_authentication_arguments,
add_output_formatting_arguments,
FORMATTERS, format_response)
from .format import add_authentication_arguments, add_output_formatting_arguments, FORMATTERS, format_response
from .options import ResourceOptionsParser, UNIQUENESS_RULES
from .resource import parse_resource, is_control_resource
from awxkit import api, config, utils, exceptions, WSClient # noqa
@@ -88,7 +86,9 @@ class CLI(object):
token = self.get_config('token')
if token:
self.root.connection.login(
None, None, token=token,
None,
None,
token=token,
)
else:
config.use_sessions = True
@@ -102,12 +102,14 @@ class CLI(object):
if self.get_config('insecure'):
config.assume_untrusted = True
config.credentials = utils.PseudoNamespace({
'default': {
'username': self.get_config('username'),
'password': self.get_config('password'),
config.credentials = utils.PseudoNamespace(
{
'default': {
'username': self.get_config('username'),
'password': self.get_config('password'),
}
}
})
)
_, remainder = self.parser.parse_known_args()
if remainder and remainder[0] == 'config':
@@ -133,11 +135,7 @@ class CLI(object):
try:
self.v2 = self.root.get().available_versions.v2.get()
except AttributeError:
raise RuntimeError(
'An error occurred while fetching {}/api/'.format(
self.get_config('host')
)
)
raise RuntimeError('An error occurred while fetching {}/api/'.format(self.get_config('host')))
def parse_resource(self, skip_deprecated=False):
"""Attempt to parse the <resource> (e.g., jobs) specified on the CLI
@@ -170,33 +168,15 @@ class CLI(object):
_filter = self.get_config('filter')
# human format for metrics, settings is special
if (
self.resource in ('metrics', 'settings') and
self.get_config('format') == 'human'
):
response.json = {
'count': len(response.json),
'results': [
{'key': k, 'value': v}
for k, v in response.json.items()
]
}
if self.resource in ('metrics', 'settings') and self.get_config('format') == 'human':
response.json = {'count': len(response.json), 'results': [{'key': k, 'value': v} for k, v in response.json.items()]}
_filter = 'key, value'
if (
self.get_config('format') == 'human' and
_filter == '.' and
self.resource in UNIQUENESS_RULES
):
if self.get_config('format') == 'human' and _filter == '.' and self.resource in UNIQUENESS_RULES:
_filter = ', '.join(UNIQUENESS_RULES[self.resource])
formatted = format_response(
response,
fmt=self.get_config('format'),
filter=_filter,
changed=self.original_action in (
'modify', 'create', 'associate', 'disassociate'
)
response, fmt=self.get_config('format'), filter=_filter, changed=self.original_action in ('modify', 'create', 'associate', 'disassociate')
)
if formatted:
print(utils.to_str(formatted), file=self.stdout)
@@ -219,10 +199,7 @@ class CLI(object):
_without_ triggering a SystemExit (argparse's
behavior if required arguments are missing)
"""
subparsers = self.subparsers[self.resource].add_subparsers(
dest='action',
metavar='action'
)
subparsers = self.subparsers[self.resource].add_subparsers(dest='action', metavar='action')
subparsers.required = True
# parse the action from OPTIONS
@@ -252,10 +229,7 @@ class CLI(object):
if self.resource != 'settings':
for method in ('list', 'modify', 'create'):
if method in parser.parser.choices:
parser.build_query_arguments(
method,
'GET' if method == 'list' else 'POST'
)
parser.build_query_arguments(method, 'GET' if method == 'list' else 'POST')
if from_sphinx:
parsed, extra = self.parser.parse_known_args(self.argv)
else:
@@ -263,10 +237,7 @@ class CLI(object):
if extra and self.verbose:
# If extraneous arguments were provided, warn the user
cprint('{}: unrecognized arguments: {}'.format(
self.parser.prog,
' '.join(extra)
), 'yellow', file=self.stdout)
cprint('{}: unrecognized arguments: {}'.format(self.parser.prog, ' '.join(extra)), 'yellow', file=self.stdout)
# build a dictionary of all of the _valid_ flags specified on the
# command line so we can pass them on to the underlying awxkit call
@@ -275,14 +246,7 @@ class CLI(object):
# everything else is a flag used as a query argument for the HTTP
# request we'll make (e.g., --username="Joe", --verbosity=3)
parsed = parsed.__dict__
parsed = dict(
(k, v) for k, v in parsed.items()
if (
v is not None and
k not in ('help', 'resource') and
not k.startswith('conf.')
)
)
parsed = dict((k, v) for k, v in parsed.items() if (v is not None and k not in ('help', 'resource') and not k.startswith('conf.')))
# if `id` is one of the arguments, it's a detail view
if 'id' in parsed:
@@ -290,9 +254,7 @@ class CLI(object):
# determine the awxkit method to call
action = self.original_action = parsed.pop('action')
page, action = handle_custom_actions(
self.resource, action, page
)
page, action = handle_custom_actions(self.resource, action, page)
self.method = {
'list': 'get',
'modify': 'patch',
@@ -327,13 +289,7 @@ class CLI(object):
action='store_true',
help='prints usage information for the awx tool',
)
self.parser.add_argument(
'--version',
dest='conf.version',
action='version',
help='display awx CLI version',
version=__version__
)
self.parser.add_argument('--version', dest='conf.version', action='version', help='display awx CLI version', version=__version__)
add_authentication_arguments(self.parser, env)
add_output_formatting_arguments(self.parser, env)

View File

@@ -16,7 +16,6 @@ def handle_custom_actions(resource, action, page):
class CustomActionRegistryMeta(CustomRegistryMeta):
@property
def name(self):
return ' '.join([self.resource, self.action])
@@ -45,33 +44,16 @@ class CustomAction(metaclass=CustomActionRegistryMeta):
class Launchable(object):
def add_arguments(self, parser, resource_options_parser, with_pk=True):
from .options import pk_or_name
if with_pk:
parser.choices[self.action].add_argument(
'id',
type=functools.partial(
pk_or_name, None, self.resource, page=self.page
),
help=''
)
parser.choices[self.action].add_argument(
'--monitor', action='store_true',
help='If set, prints stdout of the launched job until it finishes.'
)
parser.choices[self.action].add_argument(
'--timeout', type=int,
help='If set with --monitor or --wait, time out waiting on job completion.' # noqa
)
parser.choices[self.action].add_argument(
'--wait', action='store_true',
help='If set, waits until the launched job finishes.'
)
launch_time_options = self.page.connection.options(
self.page.endpoint + '1/{}/'.format(self.action)
)
if with_pk:
parser.choices[self.action].add_argument('id', type=functools.partial(pk_or_name, None, self.resource, page=self.page), help='')
parser.choices[self.action].add_argument('--monitor', action='store_true', help='If set, prints stdout of the launched job until it finishes.')
parser.choices[self.action].add_argument('--timeout', type=int, help='If set with --monitor or --wait, time out waiting on job completion.') # noqa
parser.choices[self.action].add_argument('--wait', action='store_true', help='If set, waits until the launched job finishes.')
launch_time_options = self.page.connection.options(self.page.endpoint + '1/{}/'.format(self.action))
if launch_time_options.ok:
launch_time_options = launch_time_options.json()['actions']['POST']
resource_options_parser.options['LAUNCH'] = launch_time_options
@@ -118,24 +100,15 @@ class ProjectCreate(CustomAction):
resource = 'projects'
def add_arguments(self, parser, resource_options_parser):
parser.choices[self.action].add_argument(
'--monitor', action='store_true',
help=('If set, prints stdout of the project update until '
'it finishes.')
)
parser.choices[self.action].add_argument(
'--wait', action='store_true',
help='If set, waits until the new project has updated.'
)
parser.choices[self.action].add_argument('--monitor', action='store_true', help=('If set, prints stdout of the project update until ' 'it finishes.'))
parser.choices[self.action].add_argument('--wait', action='store_true', help='If set, waits until the new project has updated.')
def post(self, kwargs):
should_monitor = kwargs.pop('monitor', False)
wait = kwargs.pop('wait', False)
response = self.page.post(kwargs)
if should_monitor or wait:
update = response.related.project_updates.get(
order_by='-created'
).results[0]
update = response.related.project_updates.get(order_by='-created').results[0]
monitor(
update,
self.page.connection.session,
@@ -154,9 +127,7 @@ class AdhocCommandLaunch(Launchable, CustomAction):
resource = 'ad_hoc_commands'
def add_arguments(self, parser, resource_options_parser):
Launchable.add_arguments(
self, parser, resource_options_parser, with_pk=False
)
Launchable.add_arguments(self, parser, resource_options_parser, with_pk=False)
def perform(self, **kwargs):
monitor_kwargs = {
@@ -182,22 +153,14 @@ class HasStdout(object):
def add_arguments(self, parser, resource_options_parser):
from .options import pk_or_name
parser.choices['stdout'].add_argument(
'id',
type=functools.partial(
pk_or_name, None, self.resource, page=self.page
),
help=''
)
parser.choices['stdout'].add_argument('id', type=functools.partial(pk_or_name, None, self.resource, page=self.page), help='')
def perform(self):
fmt = 'txt_download'
if color_enabled():
fmt = 'ansi_download'
return self.page.connection.get(
self.page.get().related.stdout,
query_parameters=dict(format=fmt)
).content.decode('utf-8')
return self.page.connection.get(self.page.get().related.stdout, query_parameters=dict(format=fmt)).content.decode('utf-8')
class JobStdout(HasStdout, CustomAction):
@@ -222,13 +185,8 @@ class AssociationMixin(object):
def add_arguments(self, parser, resource_options_parser):
from .options import pk_or_name
parser.choices[self.action].add_argument(
'id',
type=functools.partial(
pk_or_name, None, self.resource, page=self.page
),
help=''
)
parser.choices[self.action].add_argument('id', type=functools.partial(pk_or_name, None, self.resource, page=self.page), help='')
group = parser.choices[self.action].add_mutually_exclusive_group(required=True)
for param, endpoint in self.targets.items():
field, model_name = endpoint
@@ -237,7 +195,6 @@ class AssociationMixin(object):
help_text = 'The ID (or name) of the {} to {}'.format(model_name, self.action)
class related_page(object):
def __init__(self, connection, resource):
self.conn = connection
self.resource = {
@@ -256,20 +213,15 @@ class AssociationMixin(object):
group.add_argument(
'--{}'.format(param),
metavar='',
type=functools.partial(
pk_or_name, None, param,
page=related_page(self.page.connection, param)
),
help=help_text
type=functools.partial(pk_or_name, None, param, page=related_page(self.page.connection, param)),
help=help_text,
)
def perform(self, **kwargs):
for k, v in kwargs.items():
endpoint, _ = self.targets[k]
try:
self.page.get().related[endpoint].post(
{'id': v, self.action: True}
)
self.page.get().related[endpoint].post({'id': v, self.action: True})
except NoContent:
# we expect to enter this block because these endpoints return
# HTTP 204 on success
@@ -279,18 +231,9 @@ class AssociationMixin(object):
class NotificationAssociateMixin(AssociationMixin):
targets = {
'start_notification': [
'notification_templates_started',
'notification_template'
],
'success_notification': [
'notification_templates_success',
'notification_template'
],
'failure_notification': [
'notification_templates_error',
'notification_template'
],
'start_notification': ['notification_templates_started', 'notification_template'],
'success_notification': ['notification_templates_success', 'notification_template'],
'failure_notification': ['notification_templates_error', 'notification_template'],
}
@@ -306,12 +249,16 @@ class JobTemplateNotificationDisAssociation(NotificationAssociateMixin, CustomAc
targets = NotificationAssociateMixin.targets.copy()
JobTemplateNotificationAssociation.targets.update({
'credential': ['credentials', None],
})
JobTemplateNotificationDisAssociation.targets.update({
'credential': ['credentials', None],
})
JobTemplateNotificationAssociation.targets.update(
{
'credential': ['credentials', None],
}
)
JobTemplateNotificationDisAssociation.targets.update(
{
'credential': ['credentials', None],
}
)
class WorkflowJobTemplateNotificationAssociation(NotificationAssociateMixin, CustomAction):
@@ -326,12 +273,16 @@ class WorkflowJobTemplateNotificationDisAssociation(NotificationAssociateMixin,
targets = NotificationAssociateMixin.targets.copy()
WorkflowJobTemplateNotificationAssociation.targets.update({
'approval_notification': ['notification_templates_approvals', 'notification_template'],
})
WorkflowJobTemplateNotificationDisAssociation.targets.update({
'approval_notification': ['notification_templates_approvals', 'notification_template'],
})
WorkflowJobTemplateNotificationAssociation.targets.update(
{
'approval_notification': ['notification_templates_approvals', 'notification_template'],
}
)
WorkflowJobTemplateNotificationDisAssociation.targets.update(
{
'approval_notification': ['notification_templates_approvals', 'notification_template'],
}
)
class ProjectNotificationAssociation(NotificationAssociateMixin, CustomAction):
@@ -366,14 +317,18 @@ class OrganizationNotificationDisAssociation(NotificationAssociateMixin, CustomA
targets = NotificationAssociateMixin.targets.copy()
OrganizationNotificationAssociation.targets.update({
'approval_notification': ['notification_templates_approvals', 'notification_template'],
'galaxy_credential': ['galaxy_credentials', 'credential'],
})
OrganizationNotificationDisAssociation.targets.update({
'approval_notification': ['notification_templates_approvals', 'notification_template'],
'galaxy_credential': ['galaxy_credentials', 'credential'],
})
OrganizationNotificationAssociation.targets.update(
{
'approval_notification': ['notification_templates_approvals', 'notification_template'],
'galaxy_credential': ['galaxy_credentials', 'credential'],
}
)
OrganizationNotificationDisAssociation.targets.update(
{
'approval_notification': ['notification_templates_approvals', 'notification_template'],
'galaxy_credential': ['galaxy_credentials', 'credential'],
}
)
class SettingsList(CustomAction):
@@ -381,9 +336,7 @@ class SettingsList(CustomAction):
resource = 'settings'
def add_arguments(self, parser, resource_options_parser):
parser.choices['list'].add_argument(
'--slug', help='optional setting category/slug', default='all'
)
parser.choices['list'].add_argument('--slug', help='optional setting category/slug', default='all')
def perform(self, slug):
self.page.endpoint = self.page.endpoint + '{}/'.format(slug)
@@ -409,30 +362,18 @@ class RoleMixin(object):
if not RoleMixin.roles:
for resource, flag in self.has_roles:
options = self.page.__class__(
self.page.endpoint.replace(self.resource, resource),
self.page.connection
).options()
RoleMixin.roles[flag] = [
role.replace('_role', '')
for role in options.json.get('object_roles', [])
]
options = self.page.__class__(self.page.endpoint.replace(self.resource, resource), self.page.connection).options()
RoleMixin.roles[flag] = [role.replace('_role', '') for role in options.json.get('object_roles', [])]
possible_roles = set()
for v in RoleMixin.roles.values():
possible_roles.update(v)
resource_group = parser.choices[self.action].add_mutually_exclusive_group(
required=True
)
resource_group = parser.choices[self.action].add_mutually_exclusive_group(required=True)
parser.choices[self.action].add_argument(
'id',
type=functools.partial(
pk_or_name, None, self.resource, page=self.page
),
help='The ID (or name) of the {} to {} access to/from'.format(
self.resource, self.action
)
type=functools.partial(pk_or_name, None, self.resource, page=self.page),
help='The ID (or name) of the {} to {} access to/from'.format(self.resource, self.action),
)
for _type in RoleMixin.roles.keys():
if _type == 'team' and self.resource == 'team':
@@ -440,7 +381,6 @@ class RoleMixin(object):
continue
class related_page(object):
def __init__(self, connection, resource):
self.conn = connection
if resource == 'inventories':
@@ -453,19 +393,12 @@ class RoleMixin(object):
resource_group.add_argument(
'--{}'.format(_type),
type=functools.partial(
pk_or_name, None, _type,
page=related_page(
self.page.connection,
dict((v, k) for k, v in self.has_roles)[_type]
)
),
type=functools.partial(pk_or_name, None, _type, page=related_page(self.page.connection, dict((v, k) for k, v in self.has_roles)[_type])),
metavar='ID',
help='The ID (or name) of the target {}'.format(_type),
)
parser.choices[self.action].add_argument(
'--role', type=str, choices=possible_roles, required=True,
help='The name of the role to {}'.format(self.action)
'--role', type=str, choices=possible_roles, required=True, help='The name of the role to {}'.format(self.action)
)
def perform(self, **kwargs):
@@ -474,17 +407,10 @@ class RoleMixin(object):
role = kwargs['role']
if role not in RoleMixin.roles[flag]:
options = ', '.join(RoleMixin.roles[flag])
raise ValueError(
"invalid choice: '{}' must be one of {}".format(
role, options
)
)
raise ValueError("invalid choice: '{}' must be one of {}".format(role, options))
value = kwargs[flag]
target = '/api/v2/{}/{}'.format(resource, value)
detail = self.page.__class__(
target,
self.page.connection
).get()
detail = self.page.__class__(target, self.page.connection).get()
object_roles = detail['summary_fields']['object_roles']
actual_role = object_roles[role + '_role']
params = {'id': actual_role['id']}
@@ -530,15 +456,8 @@ class SettingsModify(CustomAction):
resource = 'settings'
def add_arguments(self, parser, resource_options_parser):
options = self.page.__class__(
self.page.endpoint + 'all/', self.page.connection
).options()
parser.choices['modify'].add_argument(
'key',
choices=sorted(options['actions']['PUT'].keys()),
metavar='key',
help=''
)
options = self.page.__class__(self.page.endpoint + 'all/', self.page.connection).options()
parser.choices['modify'].add_argument('key', choices=sorted(options['actions']['PUT'].keys()), metavar='key', help='')
parser.choices['modify'].add_argument('value', help='')
def perform(self, key, value):
@@ -563,13 +482,8 @@ class HasMonitor(object):
def add_arguments(self, parser, resource_options_parser):
from .options import pk_or_name
parser.choices[self.action].add_argument(
'id',
type=functools.partial(
pk_or_name, None, self.resource, page=self.page
),
help=''
)
parser.choices[self.action].add_argument('id', type=functools.partial(pk_or_name, None, self.resource, page=self.page), help='')
def perform(self, **kwargs):
response = self.page.get()

View File

@@ -27,9 +27,7 @@ author = 'Ansible by Red Hat'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'awxkit.cli.sphinx'
]
extensions = ['awxkit.cli.sphinx']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

View File

@@ -48,24 +48,21 @@ def add_output_formatting_arguments(parser, env):
dest='conf.format',
choices=FORMATTERS.keys(),
default=env.get('TOWER_FORMAT', 'json'),
help=(
'specify a format for the input and output'
),
help=('specify a format for the input and output'),
)
formatting.add_argument(
'--filter',
dest='conf.filter',
default='.',
metavar='TEXT',
help=(
'specify an output filter (only valid with jq or human format)'
),
help=('specify an output filter (only valid with jq or human format)'),
)
formatting.add_argument(
'--conf.color',
metavar='BOOLEAN',
help='Display colorized output. Defaults to True',
default=env.get('TOWER_COLOR', 't'), type=strtobool,
default=env.get('TOWER_COLOR', 't'),
type=strtobool,
)
formatting.add_argument(
'-v',
@@ -73,7 +70,7 @@ def add_output_formatting_arguments(parser, env):
dest='conf.verbose',
help='print debug-level logs, including requests made',
default=strtobool(env.get('TOWER_VERBOSE', 'f')),
action="store_true"
action="store_true",
)
@@ -105,11 +102,10 @@ def format_jq(output, fmt):
if fmt == '.':
return output
raise ImportError(
'To use `-f jq`, you must install the optional jq dependency.\n'
'`pip install jq`\n',
'To use `-f jq`, you must install the optional jq dependency.\n' '`pip install jq`\n',
'Note that some platforms may require additional programs to '
'build jq from source (like `libtool`).\n'
'See https://pypi.org/project/jq/ for instructions.'
'See https://pypi.org/project/jq/ for instructions.',
)
results = []
for x in jq.jq(fmt).transform(output, multiple_output=True):
@@ -127,11 +123,7 @@ def format_json(output, fmt):
def format_yaml(output, fmt):
output = json.loads(json.dumps(output))
return yaml.safe_dump(
output,
default_flow_style=False,
allow_unicode=True
)
return yaml.safe_dump(output, default_flow_style=False, allow_unicode=True)
def format_human(output, fmt):
@@ -151,10 +143,7 @@ def format_human(output, fmt):
column_names.remove(k)
table = [column_names]
table.extend([
[record.get(col, '') for col in column_names]
for record in output
])
table.extend([[record.get(col, '') for col in column_names] for record in output])
col_paddings = []
def format_num(v):
@@ -184,9 +173,4 @@ def format_human(output, fmt):
return '\n'.join(lines)
FORMATTERS = {
'json': format_json,
'yaml': format_yaml,
'jq': format_jq,
'human': format_human
}
FORMATTERS = {'json': format_json, 'yaml': format_yaml, 'jq': format_jq, 'human': format_human}

View File

@@ -21,10 +21,7 @@ UNIQUENESS_RULES = {
def pk_or_name_list(v2, model_name, value, page=None):
return [
pk_or_name(v2, model_name, v.strip(), page=page)
for v in value.split(',')
]
return [pk_or_name(v2, model_name, v.strip(), page=page) for v in value.split(',')]
def pk_or_name(v2, model_name, value, page=None):
@@ -58,17 +55,9 @@ def pk_or_name(v2, model_name, value, page=None):
return int(results.results[0].id)
if results.count > 1:
raise argparse.ArgumentTypeError(
'Multiple {0} exist with that {1}. '
'To look up an ID, run:\n'
'awx {0} list --{1} "{2}" -f human'.format(
model_name, identity, value
)
'Multiple {0} exist with that {1}. ' 'To look up an ID, run:\n' 'awx {0} list --{1} "{2}" -f human'.format(model_name, identity, value)
)
raise argparse.ArgumentTypeError(
'Could not find any {0} with that {1}.'.format(
model_name, identity
)
)
raise argparse.ArgumentTypeError('Could not find any {0} with that {1}.'.format(model_name, identity))
return value
@@ -90,9 +79,7 @@ class ResourceOptionsParser(object):
self.page = page
self.resource = resource
self.parser = parser
self.options = getattr(
self.page.options().json, 'actions', {'GET': {}}
)
self.options = getattr(self.page.options().json, 'actions', {'GET': {}})
self.get_allowed_options()
if self.resource != 'settings':
# /api/v2/settings is a special resource that doesn't have
@@ -103,9 +90,7 @@ class ResourceOptionsParser(object):
self.handle_custom_actions()
def get_allowed_options(self):
options = self.page.connection.options(
self.page.endpoint + '1/'
)
options = self.page.connection.options(self.page.endpoint + '1/')
warning = options.headers.get('Warning', '')
if '299' in warning and 'deprecated' in warning:
self.deprecated = True
@@ -121,11 +106,10 @@ class ResourceOptionsParser(object):
parser = self.parser.add_parser(method, help='')
if method == 'list':
parser.add_argument(
'--all', dest='all_pages', action='store_true',
help=(
'fetch all pages of content from the API when '
'returning results (instead of just the first page)'
)
'--all',
dest='all_pages',
action='store_true',
help=('fetch all pages of content from the API when ' 'returning results (instead of just the first page)'),
)
add_output_formatting_arguments(parser, {})
@@ -138,9 +122,7 @@ class ResourceOptionsParser(object):
for method in allowed:
parser = self.parser.add_parser(method, help='')
self.parser.choices[method].add_argument(
'id',
type=functools.partial(pk_or_name, self.v2, self.resource),
help='the ID (or unique name) of the resource'
'id', type=functools.partial(pk_or_name, self.v2, self.resource), help='the ID (or unique name) of the resource'
)
if method == 'get':
add_output_formatting_arguments(parser, {})
@@ -148,10 +130,7 @@ class ResourceOptionsParser(object):
def build_query_arguments(self, method, http_method):
required_group = None
for k, param in self.options.get(http_method, {}).items():
required = (
method == 'create' and
param.get('required', False) is True
)
required = method == 'create' and param.get('required', False) is True
help_text = param.get('help_text', '')
if method == 'list':
@@ -159,10 +138,7 @@ class ResourceOptionsParser(object):
# don't allow `awx <resource> list` to filter on `--id`
# it's weird, and that's what awx <resource> get is for
continue
help_text = 'only list {} with the specified {}'.format(
self.resource,
k
)
help_text = 'only list {} with the specified {}'.format(self.resource, k)
if method == 'list' and param.get('filterable') is False:
continue
@@ -256,9 +232,8 @@ class ResourceOptionsParser(object):
# unlike *other* actual JSON fields in the API, inventory and JT
# variables *actually* want json.dumps() strings (ugh)
# see: https://github.com/ansible/awx/issues/2371
if (
(self.resource in ('job_templates', 'workflow_job_templates') and k == 'extra_vars') or
(self.resource in ('inventory', 'groups', 'hosts') and k == 'variables')
if (self.resource in ('job_templates', 'workflow_job_templates') and k == 'extra_vars') or (
self.resource in ('inventory', 'groups', 'hosts') and k == 'variables'
):
kwargs['type'] = jsonstr
@@ -267,15 +242,9 @@ class ResourceOptionsParser(object):
required_group = self.parser.choices[method].add_argument_group('required arguments')
# put the required group first (before the optional args group)
self.parser.choices[method]._action_groups.reverse()
required_group.add_argument(
'--{}'.format(k),
**kwargs
)
required_group.add_argument('--{}'.format(k), **kwargs)
else:
self.parser.choices[method].add_argument(
'--{}'.format(k),
**kwargs
)
self.parser.choices[method].add_argument('--{}'.format(k), **kwargs)
def handle_custom_actions(self):
for _, action in CustomAction.registry.items():

View File

@@ -40,11 +40,9 @@ DEPRECATED_RESOURCES = {
'teams': 'team',
'workflow_job_templates': 'workflow',
'workflow_jobs': 'workflow_job',
'users': 'user'
'users': 'user',
}
DEPRECATED_RESOURCES_REVERSE = dict(
(v, k) for k, v in DEPRECATED_RESOURCES.items()
)
DEPRECATED_RESOURCES_REVERSE = dict((v, k) for k, v in DEPRECATED_RESOURCES.items())
class CustomCommand(metaclass=CustomRegistryMeta):
@@ -81,9 +79,7 @@ class Login(CustomCommand):
auth.add_argument('--description', help='description of the generated OAuth2.0 token', metavar='TEXT')
auth.add_argument('--conf.client_id', metavar='TEXT')
auth.add_argument('--conf.client_secret', metavar='TEXT')
auth.add_argument(
'--conf.scope', choices=['read', 'write'], default='write'
)
auth.add_argument('--conf.scope', choices=['read', 'write'], default='write')
if client.help:
self.print_help(parser)
raise SystemExit()
@@ -99,10 +95,7 @@ class Login(CustomCommand):
token = api.Api().get_oauth2_token(**kwargs)
except Exception as e:
self.print_help(parser)
cprint(
'Error retrieving an OAuth2.0 token ({}).'.format(e.__class__),
'red'
)
cprint('Error retrieving an OAuth2.0 token ({}).'.format(e.__class__), 'red')
else:
fmt = client.get_config('format')
if fmt == 'human':
@@ -186,9 +179,7 @@ def parse_resource(client, skip_deprecated=False):
# check if the user is running a custom command
for command in CustomCommand.__subclasses__():
client.subparsers[command.name] = subparsers.add_parser(
command.name, help=command.help_text
)
client.subparsers[command.name] = subparsers.add_parser(command.name, help=command.help_text)
if hasattr(client, 'v2'):
for k in client.v2.json.keys():
@@ -202,15 +193,11 @@ def parse_resource(client, skip_deprecated=False):
if k in DEPRECATED_RESOURCES:
kwargs['aliases'] = [DEPRECATED_RESOURCES[k]]
client.subparsers[k] = subparsers.add_parser(
k, help='', **kwargs
)
client.subparsers[k] = subparsers.add_parser(k, help='', **kwargs)
resource = client.parser.parse_known_args()[0].resource
if resource in DEPRECATED_RESOURCES.values():
client.argv[
client.argv.index(resource)
] = DEPRECATED_RESOURCES_REVERSE[resource]
client.argv[client.argv.index(resource)] = DEPRECATED_RESOURCES_REVERSE[resource]
resource = DEPRECATED_RESOURCES_REVERSE[resource]
if resource in CustomCommand.registry:
@@ -219,27 +206,14 @@ def parse_resource(client, skip_deprecated=False):
response = command.handle(client, parser)
if response:
_filter = client.get_config('filter')
if (
resource == 'config' and
client.get_config('format') == 'human'
):
response = {
'count': len(response),
'results': [
{'key': k, 'value': v}
for k, v in response.items()
]
}
if resource == 'config' and client.get_config('format') == 'human':
response = {'count': len(response), 'results': [{'key': k, 'value': v} for k, v in response.items()]}
_filter = 'key, value'
try:
connection = client.root.connection
except AttributeError:
connection = None
formatted = format_response(
Page.from_json(response, connection=connection),
fmt=client.get_config('format'),
filter=_filter
)
formatted = format_response(Page.from_json(response, connection=connection), fmt=client.get_config('format'), filter=_filter)
print(formatted)
raise SystemExit()
else:

View File

@@ -8,7 +8,6 @@ from .resource import is_control_resource, CustomCommand
class CustomAutoprogramDirective(AutoprogramDirective):
def run(self):
nodes = super(CustomAutoprogramDirective, self).run()
@@ -23,12 +22,7 @@ class CustomAutoprogramDirective(AutoprogramDirective):
nodes[0][0].children = [heading]
# add a descriptive top synopsis of the reference guide
nodes[0].children.insert(1, paragraph(
text=(
'This is an exhaustive guide of every available command in '
'the awx CLI tool.'
)
))
nodes[0].children.insert(1, paragraph(text=('This is an exhaustive guide of every available command in ' 'the awx CLI tool.')))
disclaimer = (
'The commands and parameters documented here can (and will) '
'vary based on a variety of factors, such as the AWX API '
@@ -51,9 +45,7 @@ def render():
# Sphinx document from.
for e in ('TOWER_HOST', 'TOWER_USERNAME', 'TOWER_PASSWORD'):
if not os.environ.get(e):
raise SystemExit(
'Please specify a valid {} for a real (running) Tower install.'.format(e) # noqa
)
raise SystemExit('Please specify a valid {} for a real (running) Tower install.'.format(e)) # noqa
cli = CLI()
cli.parse_args(['awx', '--help'])
cli.connect()

View File

@@ -9,8 +9,7 @@ from .utils import cprint, color_enabled, STATUS_COLORS
from awxkit.utils import to_str
def monitor_workflow(response, session, print_stdout=True, timeout=None,
interval=.25):
def monitor_workflow(response, session, print_stdout=True, timeout=None, interval=0.25):
get = response.url.get
payload = {
'order_by': 'finished',
@@ -18,9 +17,7 @@ def monitor_workflow(response, session, print_stdout=True, timeout=None,
}
def fetch(seen):
results = response.connection.get(
'/api/v2/unified_jobs', payload
).json()['results']
results = response.connection.get('/api/v2/unified_jobs', payload).json()['results']
# erase lines we've previously printed
if print_stdout and sys.stdout.isatty():
@@ -61,7 +58,7 @@ def monitor_workflow(response, session, print_stdout=True, timeout=None,
# all at the end
fetch(seen)
time.sleep(.25)
time.sleep(0.25)
json = get().json
if json.finished:
fetch(seen)
@@ -71,7 +68,7 @@ def monitor_workflow(response, session, print_stdout=True, timeout=None,
return get().json.status
def monitor(response, session, print_stdout=True, timeout=None, interval=.25):
def monitor(response, session, print_stdout=True, timeout=None, interval=0.25):
get = response.url.get
payload = {'order_by': 'start_line', 'no_truncate': True}
if response.type == 'job':
@@ -108,12 +105,9 @@ def monitor(response, session, print_stdout=True, timeout=None, interval=.25):
if next_line:
payload['start_line__gte'] = next_line
time.sleep(.25)
time.sleep(0.25)
json = get().json
if (
json.event_processing_finished is True or
json.status in ('error', 'canceled')
):
if json.event_processing_finished is True or json.status in ('error', 'canceled'):
fetch(next_line)
break
if print_stdout:

View File

@@ -9,8 +9,7 @@ _color = threading.local()
_color.enabled = True
__all__ = ['CustomRegistryMeta', 'HelpfulArgumentParser', 'disable_color',
'color_enabled', 'colored', 'cprint', 'STATUS_COLORS']
__all__ = ['CustomRegistryMeta', 'HelpfulArgumentParser', 'disable_color', 'color_enabled', 'colored', 'cprint', 'STATUS_COLORS']
STATUS_COLORS = {
@@ -25,17 +24,12 @@ STATUS_COLORS = {
class CustomRegistryMeta(type):
@property
def registry(cls):
return dict(
(command.name, command)
for command in cls.__subclasses__()
)
return dict((command.name, command) for command in cls.__subclasses__())
class HelpfulArgumentParser(ArgumentParser):
def error(self, message): # pragma: nocover
"""Prints a usage message incorporating the message to stderr and
exits.
@@ -67,10 +61,16 @@ COLORS = dict(
list(
zip(
[
'grey', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan',
'grey',
'red',
'green',
'yellow',
'blue',
'magenta',
'cyan',
'white',
],
list(range(30, 38))
list(range(30, 38)),
)
)
)