summaryrefslogtreecommitdiffstats
path: root/awxkit
diff options
context:
space:
mode:
Diffstat (limited to 'awxkit')
-rw-r--r--awxkit/awxkit/__init__.py8
-rw-r--r--awxkit/awxkit/api/__init__.py4
-rw-r--r--awxkit/awxkit/api/client.py6
-rw-r--r--awxkit/awxkit/api/mixins/has_copy.py1
-rw-r--r--awxkit/awxkit/api/mixins/has_create.py18
-rw-r--r--awxkit/awxkit/api/mixins/has_instance_groups.py1
-rw-r--r--awxkit/awxkit/api/mixins/has_notifications.py18
-rw-r--r--awxkit/awxkit/api/mixins/has_status.py13
-rw-r--r--awxkit/awxkit/api/mixins/has_survey.py12
-rw-r--r--awxkit/awxkit/api/mixins/has_variables.py1
-rw-r--r--awxkit/awxkit/api/pages/__init__.py2
-rw-r--r--awxkit/awxkit/api/pages/access_list.py21
-rw-r--r--awxkit/awxkit/api/pages/activity_stream.py3
-rw-r--r--awxkit/awxkit/api/pages/ad_hoc_commands.py49
-rw-r--r--awxkit/awxkit/api/pages/api.py22
-rw-r--r--awxkit/awxkit/api/pages/applications.py19
-rw-r--r--awxkit/awxkit/api/pages/base.py48
-rw-r--r--awxkit/awxkit/api/pages/config.py15
-rw-r--r--awxkit/awxkit/api/pages/credential_input_sources.py3
-rw-r--r--awxkit/awxkit/api/pages/credentials.py147
-rw-r--r--awxkit/awxkit/api/pages/execution_environments.py9
-rw-r--r--awxkit/awxkit/api/pages/instance_groups.py10
-rw-r--r--awxkit/awxkit/api/pages/instances.py3
-rw-r--r--awxkit/awxkit/api/pages/inventory.py379
-rw-r--r--awxkit/awxkit/api/pages/job_templates.py112
-rw-r--r--awxkit/awxkit/api/pages/jobs.py16
-rw-r--r--awxkit/awxkit/api/pages/labels.py40
-rw-r--r--awxkit/awxkit/api/pages/metrics.py7
-rw-r--r--awxkit/awxkit/api/pages/notification_templates.py169
-rw-r--r--awxkit/awxkit/api/pages/notifications.py11
-rw-r--r--awxkit/awxkit/api/pages/organizations.py30
-rw-r--r--awxkit/awxkit/api/pages/page.py114
-rw-r--r--awxkit/awxkit/api/pages/projects.py89
-rw-r--r--awxkit/awxkit/api/pages/roles.py12
-rw-r--r--awxkit/awxkit/api/pages/schedules.py8
-rw-r--r--awxkit/awxkit/api/pages/settings.py40
-rw-r--r--awxkit/awxkit/api/pages/subscriptions.py1
-rw-r--r--awxkit/awxkit/api/pages/survey_spec.py4
-rw-r--r--awxkit/awxkit/api/pages/system_job_templates.py5
-rw-r--r--awxkit/awxkit/api/pages/teams.py15
-rw-r--r--awxkit/awxkit/api/pages/unified_job_templates.py31
-rw-r--r--awxkit/awxkit/api/pages/unified_jobs.py24
-rw-r--r--awxkit/awxkit/api/pages/users.py39
-rw-r--r--awxkit/awxkit/api/pages/workflow_approvals.py1
-rw-r--r--awxkit/awxkit/api/pages/workflow_job_nodes.py16
-rw-r--r--awxkit/awxkit/api/pages/workflow_job_template_nodes.py88
-rw-r--r--awxkit/awxkit/api/pages/workflow_job_templates.py18
-rw-r--r--awxkit/awxkit/api/pages/workflow_jobs.py6
-rw-r--r--awxkit/awxkit/api/registry.py4
-rw-r--r--awxkit/awxkit/api/resources.py1
-rw-r--r--awxkit/awxkit/api/utils.py8
-rw-r--r--awxkit/awxkit/awx/inventory.py16
-rw-r--r--awxkit/awxkit/awx/utils.py19
-rw-r--r--awxkit/awxkit/cli/__init__.py9
-rwxr-xr-xawxkit/awxkit/cli/client.py88
-rw-r--r--awxkit/awxkit/cli/custom.py220
-rw-r--r--awxkit/awxkit/cli/docs/source/conf.py4
-rw-r--r--awxkit/awxkit/cli/format.py36
-rw-r--r--awxkit/awxkit/cli/options.py63
-rw-r--r--awxkit/awxkit/cli/resource.py46
-rw-r--r--awxkit/awxkit/cli/sphinx.py12
-rw-r--r--awxkit/awxkit/cli/stdout.py18
-rw-r--r--awxkit/awxkit/cli/utils.py20
-rw-r--r--awxkit/awxkit/exceptions.py2
-rwxr-xr-xawxkit/awxkit/scripts/basic_session.py48
-rw-r--r--awxkit/awxkit/utils/__init__.py44
-rw-r--r--awxkit/awxkit/utils/toposort.py18
-rw-r--r--awxkit/awxkit/words.py1718
-rw-r--r--awxkit/awxkit/ws.py17
-rw-r--r--awxkit/awxkit/yaml_file.py2
-rw-r--r--awxkit/setup.py13
-rw-r--r--awxkit/test/cli/test_client.py15
-rw-r--r--awxkit/test/cli/test_config.py32
-rw-r--r--awxkit/test/cli/test_format.py11
-rw-r--r--awxkit/test/cli/test_options.py156
-rw-r--r--awxkit/test/test_credentials.py37
-rw-r--r--awxkit/test/test_dependency_resolver.py106
-rw-r--r--awxkit/test/test_registry.py27
-rw-r--r--awxkit/test/test_utils.py209
-rw-r--r--awxkit/test/test_ws.py16
80 files changed, 2572 insertions, 2171 deletions
diff --git a/awxkit/awxkit/__init__.py b/awxkit/awxkit/__init__.py
index 23e0598237..c75166cf5a 100644
--- a/awxkit/awxkit/__init__.py
+++ b/awxkit/awxkit/__init__.py
@@ -1,4 +1,4 @@
-from awxkit.api import pages, client, resources # NOQA
-from awxkit.config import config # NOQA
-from awxkit import awx # NOQA
-from awxkit.ws import WSClient # NOQA
+from awxkit.api import pages, client, resources # NOQA
+from awxkit.config import config # NOQA
+from awxkit import awx # NOQA
+from awxkit.ws import WSClient # NOQA
diff --git a/awxkit/awxkit/api/__init__.py b/awxkit/awxkit/api/__init__.py
index 6f65601438..04f0f38514 100644
--- a/awxkit/awxkit/api/__init__.py
+++ b/awxkit/awxkit/api/__init__.py
@@ -1,2 +1,2 @@
-from .pages import * # NOQA
-from .client import * # NOQA
+from .pages import * # NOQA
+from .client import * # NOQA
diff --git a/awxkit/awxkit/api/client.py b/awxkit/awxkit/api/client.py
index 77c71a569b..1cea4a61c2 100644
--- a/awxkit/awxkit/api/client.py
+++ b/awxkit/awxkit/api/client.py
@@ -49,8 +49,7 @@ class Connection(object):
_next = kwargs.get('next')
if _next:
headers = self.session.headers.copy()
- self.post('/api/login/', headers=headers,
- data=dict(username=username, password=password, next=_next))
+ self.post('/api/login/', headers=headers, data=dict(username=username, password=password, next=_next))
self.session_id = self.session.cookies.get('sessionid')
self.uses_session_cookie = True
else:
@@ -79,8 +78,7 @@ class Connection(object):
use_endpoint = use_endpoint[1:]
url = '/'.join([self.server, use_endpoint])
- kwargs = dict(verify=self.verify, params=query_parameters, json=json, data=data,
- hooks=dict(response=log_elapsed))
+ kwargs = dict(verify=self.verify, params=query_parameters, json=json, data=data, hooks=dict(response=log_elapsed))
if headers is not None:
kwargs['headers'] = headers
diff --git a/awxkit/awxkit/api/mixins/has_copy.py b/awxkit/awxkit/api/mixins/has_copy.py
index d05ebd4fcd..79bc5589c0 100644
--- a/awxkit/awxkit/api/mixins/has_copy.py
+++ b/awxkit/awxkit/api/mixins/has_copy.py
@@ -3,7 +3,6 @@ from awxkit.utils import random_title
class HasCopy(object):
-
def can_copy(self):
return self.get_related('copy').can_copy
diff --git a/awxkit/awxkit/api/mixins/has_create.py b/awxkit/awxkit/api/mixins/has_create.py
index 43a2810697..1e8b24db5a 100644
--- a/awxkit/awxkit/api/mixins/has_create.py
+++ b/awxkit/awxkit/api/mixins/has_create.py
@@ -24,7 +24,7 @@ def dependency_graph(page, *provided_dependencies):
return graph
-def optional_dependency_graph(page, *provided_dependencies):
+def optional_dependency_graph(page, *provided_dependencies):
"""Creates a dependency graph for a page including all dependencies and optional_dependencies
Any optional provided_dependencies will be included as if they were dependencies,
without affecting the value of each keyed page.
@@ -104,8 +104,7 @@ def all_instantiated_dependencies(*potential_parents):
"""
scope_provided_dependencies = []
- instantiated = set([x for x in potential_parents
- if not isinstance(x, type) and not isinstance(x, tuple)])
+ instantiated = set([x for x in potential_parents if not isinstance(x, type) and not isinstance(x, tuple)])
for potential_parent in [x for x in instantiated if hasattr(x, '_dependency_store')]:
for dependency in potential_parent._dependency_store.values():
@@ -178,7 +177,6 @@ class DSAdapter(object):
# Hijack json.dumps and simplejson.dumps (used by requests)
# to allow HasCreate.create_payload() serialization without impacting payload.ds access
def filter_ds_from_payload(dumps):
-
def _filter_ds_from_payload(obj, *a, **kw):
if hasattr(obj, 'get') and isinstance(obj.get('ds'), DSAdapter):
filtered = obj.copy()
@@ -191,10 +189,12 @@ def filter_ds_from_payload(dumps):
import json # noqa
+
json.dumps = filter_ds_from_payload(json.dumps)
try:
import simplejson # noqa
+
simplejson.dumps = filter_ds_from_payload(simplejson.dumps)
except ImportError:
pass
@@ -299,8 +299,7 @@ class HasCreate(object):
# remove falsy values
provided_and_desired_dependencies = [x for x in provided_and_desired_dependencies if x]
# (HasCreate(), True) tells HasCreate._update_dependencies to link
- provided_dependencies = [(x, True) for x in provided_and_desired_dependencies
- if not isinstance(x, type) and not isinstance(x, tuple)]
+ provided_dependencies = [(x, True) for x in provided_and_desired_dependencies if not isinstance(x, type) and not isinstance(x, tuple)]
# Since dependencies are often declared at runtime, we need to use some introspection
# to determine previously created ones for proper dependency store linking.
@@ -374,12 +373,7 @@ class HasCreate(object):
to_teardown = all_instantiated_dependencies(self)
to_teardown_types = set(map(get_class_if_instance, to_teardown))
order = [
- set(
- [
- potential for potential in (
- get_class_if_instance(x) for x in group) if potential in to_teardown_types
- ]
- )
+ set([potential for potential in (get_class_if_instance(x) for x in group) if potential in to_teardown_types])
for group in page_creation_order(self, *to_teardown)
]
order.reverse()
diff --git a/awxkit/awxkit/api/mixins/has_instance_groups.py b/awxkit/awxkit/api/mixins/has_instance_groups.py
index 7d06499e21..8a61e7618e 100644
--- a/awxkit/awxkit/api/mixins/has_instance_groups.py
+++ b/awxkit/awxkit/api/mixins/has_instance_groups.py
@@ -3,7 +3,6 @@ import awxkit.exceptions as exc
class HasInstanceGroups(object):
-
def add_instance_group(self, instance_group):
with suppress(exc.NoContent):
self.related['instance_groups'].post(dict(id=instance_group.id))
diff --git a/awxkit/awxkit/api/mixins/has_notifications.py b/awxkit/awxkit/api/mixins/has_notifications.py
index aced603af2..15d387712b 100644
--- a/awxkit/awxkit/api/mixins/has_notifications.py
+++ b/awxkit/awxkit/api/mixins/has_notifications.py
@@ -2,29 +2,25 @@ from awxkit.utils import suppress
import awxkit.exceptions as exc
-notification_endpoints = ("notification_templates", "notification_templates_started", "notification_templates_error",
- "notification_templates_success")
+notification_endpoints = ("notification_templates", "notification_templates_started", "notification_templates_error", "notification_templates_success")
wfjt_notification_endpoints = notification_endpoints + ('notification_templates_approvals',)
class HasNotifications(object):
-
def add_notification_template(self, notification_template, endpoint="notification_templates_success"):
from awxkit.api.pages.workflow_job_templates import WorkflowJobTemplate
- supported_endpoints = wfjt_notification_endpoints if isinstance(self, WorkflowJobTemplate) \
- else notification_endpoints
+
+ supported_endpoints = wfjt_notification_endpoints if isinstance(self, WorkflowJobTemplate) else notification_endpoints
if endpoint not in supported_endpoints:
- raise ValueError('Unsupported notification endpoint "{0}". Please use one of {1}.'
- .format(endpoint, notification_endpoints))
+ raise ValueError('Unsupported notification endpoint "{0}". Please use one of {1}.'.format(endpoint, notification_endpoints))
with suppress(exc.NoContent):
self.related[endpoint].post(dict(id=notification_template.id))
def remove_notification_template(self, notification_template, endpoint="notification_templates_success"):
from awxkit.api.pages.workflow_job_templates import WorkflowJobTemplate
- supported_endpoints = wfjt_notification_endpoints if isinstance(self, WorkflowJobTemplate) \
- else notification_endpoints
+
+ supported_endpoints = wfjt_notification_endpoints if isinstance(self, WorkflowJobTemplate) else notification_endpoints
if endpoint not in supported_endpoints:
- raise ValueError('Unsupported notification endpoint "{0}". Please use one of {1}.'
- .format(endpoint, notification_endpoints))
+ raise ValueError('Unsupported notification endpoint "{0}". Please use one of {1}.'.format(endpoint, notification_endpoints))
with suppress(exc.NoContent):
self.related[endpoint].post(dict(id=notification_template.id, disassociate=notification_template.id))
diff --git a/awxkit/awxkit/api/mixins/has_status.py b/awxkit/awxkit/api/mixins/has_status.py
index db14874b6c..e4bd603327 100644
--- a/awxkit/awxkit/api/mixins/has_status.py
+++ b/awxkit/awxkit/api/mixins/has_status.py
@@ -40,8 +40,7 @@ class HasStatus(object):
if not getattr(self, 'event_processing_finished', True):
elapsed = datetime.utcnow() - start_time
time_left = timeout - elapsed.total_seconds()
- poll_until(lambda: getattr(self.get(), 'event_processing_finished', True),
- interval=interval, timeout=time_left, **kwargs)
+ poll_until(lambda: getattr(self.get(), 'event_processing_finished', True), interval=interval, timeout=time_left, **kwargs)
return self
def wait_until_started(self, interval=1, timeout=60):
@@ -65,9 +64,7 @@ class HasStatus(object):
msg = ''
else:
msg += '\n'
- msg += '{0}-{1} has status of {2}, which is not in {3}.'.format(
- self.type.title(), self.id, self.status, status_list
- )
+ msg += '{0}-{1} has status of {2}, which is not in {3}.'.format(self.type.title(), self.id, self.status, status_list)
if getattr(self, 'job_explanation', ''):
msg += '\njob_explanation: {}'.format(bytes_to_str(self.job_explanation))
if getattr(self, 'result_traceback', ''):
@@ -79,10 +76,8 @@ class HasStatus(object):
try:
data = json.loads(self.job_explanation.replace('Previous Task Failed: ', ''))
dep_output = self.connection.get(
- '{0}/api/v2/{1}s/{2}/stdout/'.format(
- self.endpoint.split('/api')[0], data['job_type'], data['job_id']
- ),
- query_parameters=dict(format='txt_download')
+ '{0}/api/v2/{1}s/{2}/stdout/'.format(self.endpoint.split('/api')[0], data['job_type'], data['job_id']),
+ query_parameters=dict(format='txt_download'),
).content
msg += '\nDependency output:\n{}'.format(bytes_to_str(dep_output))
except Exception as e:
diff --git a/awxkit/awxkit/api/mixins/has_survey.py b/awxkit/awxkit/api/mixins/has_survey.py
index 07729f805e..e447580595 100644
--- a/awxkit/awxkit/api/mixins/has_survey.py
+++ b/awxkit/awxkit/api/mixins/has_survey.py
@@ -3,13 +3,11 @@ from awxkit.utils import random_title
class HasSurvey(object):
def add_survey(self, spec=None, name=None, description=None, required=False, enabled=True):
- payload = dict(name=name or 'Survey - {}'.format(random_title()),
- description=description or random_title(10),
- spec=spec or [dict(required=required,
- question_name="What's the password?",
- variable="secret",
- type="password",
- default="foo")])
+ payload = dict(
+ name=name or 'Survey - {}'.format(random_title()),
+ description=description or random_title(10),
+ spec=spec or [dict(required=required, question_name="What's the password?", variable="secret", type="password", default="foo")],
+ )
if enabled != self.survey_enabled:
self.patch(survey_enabled=enabled)
return self.related.survey_spec.post(payload).get()
diff --git a/awxkit/awxkit/api/mixins/has_variables.py b/awxkit/awxkit/api/mixins/has_variables.py
index f69b9b38e9..d3cb859582 100644
--- a/awxkit/awxkit/api/mixins/has_variables.py
+++ b/awxkit/awxkit/api/mixins/has_variables.py
@@ -4,7 +4,6 @@ from awxkit.utils import PseudoNamespace
class HasVariables(object):
-
@property
def variables(self):
return PseudoNamespace(yaml.safe_load(self.json.variables))
diff --git a/awxkit/awxkit/api/pages/__init__.py b/awxkit/awxkit/api/pages/__init__.py
index 1d78d4ba5e..aa55a938e6 100644
--- a/awxkit/awxkit/api/pages/__init__.py
+++ b/awxkit/awxkit/api/pages/__init__.py
@@ -33,7 +33,7 @@ from .workflow_job_templates import * # NOQA
from .workflow_job_template_nodes import * # NOQA
from .workflow_jobs import * # NOQA
from .workflow_job_nodes import * # NOQA
-from .workflow_approvals import * # NOQA
+from .workflow_approvals import * # NOQA
from .settings import * # NOQA
from .instances import * # NOQA
from .instance_groups import * # NOQA
diff --git a/awxkit/awxkit/api/pages/access_list.py b/awxkit/awxkit/api/pages/access_list.py
index f654f84a01..f037fcfa72 100644
--- a/awxkit/awxkit/api/pages/access_list.py
+++ b/awxkit/awxkit/api/pages/access_list.py
@@ -8,11 +8,16 @@ class AccessList(page.PageList, users.User):
pass
-page.register_page([resources.organization_access_list,
- resources.user_access_list,
- resources.inventory_access_list,
- resources.group_access_list,
- resources.credential_access_list,
- resources.project_access_list,
- resources.job_template_access_list,
- resources.team_access_list], AccessList)
+page.register_page(
+ [
+ resources.organization_access_list,
+ resources.user_access_list,
+ resources.inventory_access_list,
+ resources.group_access_list,
+ resources.credential_access_list,
+ resources.project_access_list,
+ resources.job_template_access_list,
+ resources.team_access_list,
+ ],
+ AccessList,
+)
diff --git a/awxkit/awxkit/api/pages/activity_stream.py b/awxkit/awxkit/api/pages/activity_stream.py
index fda9d429e0..0be25e47c9 100644
--- a/awxkit/awxkit/api/pages/activity_stream.py
+++ b/awxkit/awxkit/api/pages/activity_stream.py
@@ -16,5 +16,4 @@ class ActivityStreams(page.PageList, ActivityStream):
pass
-page.register_page([resources.activity_stream,
- resources.object_activity_stream], ActivityStreams)
+page.register_page([resources.activity_stream, resources.object_activity_stream], ActivityStreams)
diff --git a/awxkit/awxkit/api/pages/ad_hoc_commands.py b/awxkit/awxkit/api/pages/ad_hoc_commands.py
index 374e13c127..39b11d8746 100644
--- a/awxkit/awxkit/api/pages/ad_hoc_commands.py
+++ b/awxkit/awxkit/api/pages/ad_hoc_commands.py
@@ -24,31 +24,40 @@ class AdHocCommand(HasCreate, UnifiedJob):
return self.walk(result.url)
def payload(self, inventory, credential, module_name='ping', **kwargs):
- payload = PseudoNamespace(inventory=inventory.id,
- credential=credential.id,
- module_name=module_name)
+ payload = PseudoNamespace(inventory=inventory.id, credential=credential.id, module_name=module_name)
- optional_fields = ('diff_mode', 'extra_vars', 'module_args', 'job_type', 'limit', 'forks',
- 'verbosity')
+ optional_fields = ('diff_mode', 'extra_vars', 'module_args', 'job_type', 'limit', 'forks', 'verbosity')
return update_payload(payload, optional_fields, kwargs)
- def create_payload(self, module_name='ping', module_args=np, job_type=np, limit=np, verbosity=np,
- inventory=Inventory, credential=Credential, **kwargs):
+ def create_payload(self, module_name='ping', module_args=np, job_type=np, limit=np, verbosity=np, inventory=Inventory, credential=Credential, **kwargs):
self.create_and_update_dependencies(inventory, credential)
- payload = self.payload(module_name=module_name, module_args=module_args, job_type=job_type, limit=limit,
- verbosity=verbosity, inventory=self.ds.inventory, credential=self.ds.credential,
- **kwargs)
+ payload = self.payload(
+ module_name=module_name,
+ module_args=module_args,
+ job_type=job_type,
+ limit=limit,
+ verbosity=verbosity,
+ inventory=self.ds.inventory,
+ credential=self.ds.credential,
+ **kwargs
+ )
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
- def create(self, module_name='ping', module_args=np, job_type=np, limit=np, verbosity=np,
- inventory=Inventory, credential=Credential, **kwargs):
-
- payload = self.create_payload(module_name=module_name, module_args=module_args,
- job_type=job_type, limit=limit, verbosity=verbosity,
- inventory=inventory, credential=credential, **kwargs)
+ def create(self, module_name='ping', module_args=np, job_type=np, limit=np, verbosity=np, inventory=Inventory, credential=Credential, **kwargs):
+
+ payload = self.create_payload(
+ module_name=module_name,
+ module_args=module_args,
+ job_type=job_type,
+ limit=limit,
+ verbosity=verbosity,
+ inventory=inventory,
+ credential=credential,
+ **kwargs
+ )
return self.update_identity(AdHocCommands(self.connection).post(payload))
@@ -60,7 +69,7 @@ class AdHocCommands(page.PageList, AdHocCommand):
pass
-page.register_page([resources.ad_hoc_commands,
- resources.inventory_related_ad_hoc_commands,
- resources.group_related_ad_hoc_commands,
- resources.host_related_ad_hoc_commands], AdHocCommands)
+page.register_page(
+ [resources.ad_hoc_commands, resources.inventory_related_ad_hoc_commands, resources.group_related_ad_hoc_commands, resources.host_related_ad_hoc_commands],
+ AdHocCommands,
+)
diff --git a/awxkit/awxkit/api/pages/api.py b/awxkit/awxkit/api/pages/api.py
index 4edc07857f..a333314fc1 100644
--- a/awxkit/awxkit/api/pages/api.py
+++ b/awxkit/awxkit/api/pages/api.py
@@ -90,18 +90,14 @@ class ApiV2(base.Base):
return None
# Note: doing _page[key] automatically parses json blob strings, which can be a problem.
- fields = {
- key: _page.json[key] for key in post_fields
- if key in _page.json and key not in _page.related and key != 'id'
- }
+ fields = {key: _page.json[key] for key in post_fields if key in _page.json and key not in _page.related and key != 'id'}
for key in post_fields:
if key in _page.related:
related = _page.related[key]
else:
if post_fields[key]['type'] == 'id' and _page.json.get(key) is not None:
- log.warning("Related link %r missing from %s, attempting to reconstruct endpoint.",
- key, _page.endpoint)
+ log.warning("Related link %r missing from %s, attempting to reconstruct endpoint.", key, _page.endpoint)
resource = getattr(self, key, None)
if resource is None:
log.error("Unable to infer endpoint for %r on %s.", key, _page.endpoint)
@@ -119,8 +115,7 @@ class ApiV2(base.Base):
continue
rel_natural_key = rel_endpoint.get_natural_key(self._cache)
if rel_natural_key is None:
- log.error("Unable to construct a natural key for foreign key %r of object %s.",
- key, _page.endpoint)
+ log.error("Unable to construct a natural key for foreign key %r of object %s.", key, _page.endpoint)
return None # This foreign key has unresolvable dependencies
fields[key] = rel_natural_key
@@ -154,10 +149,7 @@ class ApiV2(base.Base):
continue
if 'results' in rel_page:
- results = (
- x.get_natural_key(self._cache) if by_natural_key else self._export(x, rel_post_fields)
- for x in rel_page.results
- )
+ results = (x.get_natural_key(self._cache) if by_natural_key else self._export(x, rel_post_fields) for x in rel_page.results)
related[key] = [x for x in results if x is not None]
else:
related[key] = rel_page.json
@@ -190,8 +182,7 @@ class ApiV2(base.Base):
if isinstance(value, int) or value.isdecimal():
return endpoint.get(id=int(value))
options = self._cache.get_options(endpoint)
- identifier = next(field for field in options['search_fields']
- if field in ('name', 'username', 'hostname'))
+ identifier = next(field for field in options['search_fields'] if field in ('name', 'username', 'hostname'))
return endpoint.get(**{identifier: value})
def export_assets(self, **kwargs):
@@ -214,8 +205,7 @@ class ApiV2(base.Base):
# Import methods
def _dependent_resources(self, data):
- page_resource = {getattr(self, resource)._create().__item_class__: resource
- for resource in self.json}
+ page_resource = {getattr(self, resource)._create().__item_class__: resource for resource in self.json}
data_pages = [getattr(self, resource)._create().__item_class__ for resource in EXPORTABLE_RESOURCES]
for page_cls in itertools.chain(*has_create.page_creation_order(*data_pages)):
diff --git a/awxkit/awxkit/api/pages/applications.py b/awxkit/awxkit/api/pages/applications.py
index 843aefec58..18737cd883 100644
--- a/awxkit/awxkit/api/pages/applications.py
+++ b/awxkit/awxkit/api/pages/applications.py
@@ -12,10 +12,12 @@ class OAuth2Application(HasCreate, base.Base):
dependencies = [Organization]
def payload(self, **kwargs):
- payload = PseudoNamespace(name=kwargs.get('name') or 'OAuth2Application - {}'.format(random_title()),
- description=kwargs.get('description') or random_title(10),
- client_type=kwargs.get('client_type', 'public'),
- authorization_grant_type=kwargs.get('authorization_grant_type', 'password'))
+ payload = PseudoNamespace(
+ name=kwargs.get('name') or 'OAuth2Application - {}'.format(random_title()),
+ description=kwargs.get('description') or random_title(10),
+ client_type=kwargs.get('client_type', 'public'),
+ authorization_grant_type=kwargs.get('authorization_grant_type', 'password'),
+ )
if kwargs.get('organization'):
payload.organization = kwargs['organization'].id
@@ -35,8 +37,7 @@ class OAuth2Application(HasCreate, base.Base):
return self.update_identity(OAuth2Applications(self.connection).post(payload))
-page.register_page((resources.application,
- (resources.applications, 'post')), OAuth2Application)
+page.register_page((resources.application, (resources.applications, 'post')), OAuth2Application)
class OAuth2Applications(page.PageList, OAuth2Application):
@@ -51,8 +52,7 @@ class OAuth2AccessToken(HasCreate, base.Base):
optional_dependencies = [OAuth2Application]
def payload(self, **kwargs):
- payload = PseudoNamespace(description=kwargs.get('description') or random_title(10),
- scope=kwargs.get('scope', 'write'))
+ payload = PseudoNamespace(description=kwargs.get('description') or random_title(10), scope=kwargs.get('scope', 'write'))
if kwargs.get('oauth_2_application'):
payload.application = kwargs['oauth_2_application'].id
@@ -73,8 +73,7 @@ class OAuth2AccessToken(HasCreate, base.Base):
return self.update_identity(OAuth2AccessTokens(self.connection).post(payload))
-page.register_page((resources.token,
- (resources.tokens, 'post')), OAuth2AccessToken)
+page.register_page((resources.token, (resources.tokens, 'post')), OAuth2AccessToken)
class OAuth2AccessTokens(page.PageList, OAuth2AccessToken):
diff --git a/awxkit/awxkit/api/pages/base.py b/awxkit/awxkit/api/pages/base.py
index f3c3957c9d..7e9d210ab3 100644
--- a/awxkit/awxkit/api/pages/base.py
+++ b/awxkit/awxkit/api/pages/base.py
@@ -3,11 +3,7 @@ import logging
from requests.auth import HTTPBasicAuth
-from awxkit.api.pages import (
- Page,
- get_registered_page,
- exception_from_status_code
-)
+from awxkit.api.pages import Page, get_registered_page, exception_from_status_code
from awxkit.config import config
from awxkit.api.resources import resources
import awxkit.exceptions as exc
@@ -17,7 +13,6 @@ log = logging.getLogger(__name__)
class Base(Page):
-
def silent_delete(self):
"""Delete the object. If it's already deleted, ignore the error"""
try:
@@ -129,14 +124,14 @@ class Base(Page):
@property
def object_roles(self):
from awxkit.api.pages import Roles, Role
+
url = self.get().json.related.object_roles
for obj_role in Roles(self.connection, endpoint=url).get().json.results:
yield Role(self.connection, endpoint=obj_role.url).get()
def get_authtoken(self, username='', password=''):
default_cred = config.credentials.default
- payload = dict(username=username or default_cred.username,
- password=password or default_cred.password)
+ payload = dict(username=username or default_cred.username, password=password or default_cred.password)
auth_url = resources.authtoken
return get_registered_page(auth_url)(self.connection, endpoint=auth_url).post(payload).token
@@ -146,9 +141,7 @@ class Base(Page):
load_default_authtoken = load_authtoken
- def get_oauth2_token(self, username='', password='', client_id=None,
- description='AWX CLI',
- client_secret=None, scope='write'):
+ def get_oauth2_token(self, username='', password='', client_id=None, description='AWX CLI', client_secret=None, scope='write'):
default_cred = config.credentials.default
username = username or default_cred.username
password = password or default_cred.password
@@ -157,38 +150,21 @@ class Base(Page):
HTTPBasicAuth(client_id, client_secret)(req)
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
resp = self.connection.post(
- '/api/o/token/',
- data={
- "grant_type": "password",
- "username": username,
- "password": password,
- "scope": scope
- },
- headers=req.headers
+ '/api/o/token/', data={"grant_type": "password", "username": username, "password": password, "scope": scope}, headers=req.headers
)
elif client_id:
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
resp = self.connection.post(
'/api/o/token/',
- data={
- "grant_type": "password",
- "username": username,
- "password": password,
- "client_id": client_id,
- "scope": scope
- },
- headers=req.headers
+ data={"grant_type": "password", "username": username, "password": password, "client_id": client_id, "scope": scope},
+ headers=req.headers,
)
else:
HTTPBasicAuth(username, password)(req)
resp = self.connection.post(
'/api/v2/users/{}/personal_tokens/'.format(username),
- json={
- "description": description,
- "application": None,
- "scope": scope
- },
- headers=req.headers
+ json={"description": description, "application": None, "scope": scope},
+ headers=req.headers,
)
if resp.ok:
result = resp.json()
@@ -201,9 +177,9 @@ class Base(Page):
def load_session(self, username='', password=''):
default_cred = config.credentials.default
- self.connection.login(username=username or default_cred.username,
- password=password or default_cred.password,
- **self.connection.get_session_requirements())
+ self.connection.login(
+ username=username or default_cred.username, password=password or default_cred.password, **self.connection.get_session_requirements()
+ )
return self
def cleanup(self):
diff --git a/awxkit/awxkit/api/pages/config.py b/awxkit/awxkit/api/pages/config.py
index 56a620da7f..e4ea14765f 100644
--- a/awxkit/awxkit/api/pages/config.py
+++ b/awxkit/awxkit/api/pages/config.py
@@ -4,22 +4,17 @@ from . import page
class Config(base.Base):
-
@property
def is_aws_license(self):
- return self.license_info.get('is_aws', False) or \
- 'ami-id' in self.license_info or \
- 'instance-id' in self.license_info
+ return self.license_info.get('is_aws', False) or 'ami-id' in self.license_info or 'instance-id' in self.license_info
@property
def is_valid_license(self):
- return self.license_info.get('valid_key', False) and \
- 'instance_count' in self.license_info
+ return self.license_info.get('valid_key', False) and 'instance_count' in self.license_info
@property
def is_trial_license(self):
- return self.is_valid_license and \
- self.license_info.get('trial', False)
+ return self.is_valid_license and self.license_info.get('trial', False)
@property
def is_awx_license(self):
@@ -27,8 +22,7 @@ class Config(base.Base):
@property
def is_enterprise_license(self):
- return self.is_valid_license and \
- self.license_info.get('license_type', None) == 'enterprise'
+ return self.is_valid_license and self.license_info.get('license_type', None) == 'enterprise'
@property
def features(self):
@@ -37,7 +31,6 @@ class Config(base.Base):
class ConfigAttach(page.Page):
-
def attach(self, **kwargs):
return self.post(json=kwargs).json
diff --git a/awxkit/awxkit/api/pages/credential_input_sources.py b/awxkit/awxkit/api/pages/credential_input_sources.py
index 72112ec098..c500cfca81 100644
--- a/awxkit/awxkit/api/pages/credential_input_sources.py
+++ b/awxkit/awxkit/api/pages/credential_input_sources.py
@@ -16,5 +16,4 @@ class CredentialInputSources(page.PageList, CredentialInputSource):
pass
-page.register_page([resources.credential_input_sources,
- resources.related_input_sources], CredentialInputSources)
+page.register_page([resources.credential_input_sources, resources.related_input_sources], CredentialInputSources)
diff --git a/awxkit/awxkit/api/pages/credentials.py b/awxkit/awxkit/api/pages/credentials.py
index e5b80cf7d0..11fadd8711 100644
--- a/awxkit/awxkit/api/pages/credentials.py
+++ b/awxkit/awxkit/api/pages/credentials.py
@@ -44,7 +44,8 @@ credential_input_fields = (
'tenant',
'username',
'vault_password',
- 'vault_id')
+ 'vault_id',
+)
def generate_private_key():
@@ -52,15 +53,9 @@ def generate_private_key():
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
- key = rsa.generate_private_key(
- public_exponent=65537,
- key_size=4096,
- backend=default_backend()
- )
+ key = rsa.generate_private_key(public_exponent=65537, key_size=4096, backend=default_backend())
return key.private_bytes(
- encoding=serialization.Encoding.PEM,
- format=serialization.PrivateFormat.TraditionalOpenSSL,
- encryption_algorithm=serialization.NoEncryption()
+ encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()
).decode('utf-8')
@@ -98,11 +93,10 @@ credential_type_name_to_config_kind_map = {
'source control': 'scm',
'machine': 'ssh',
'vault': 'vault',
- 'vmware vcenter': 'vmware'}
+ 'vmware vcenter': 'vmware',
+}
-config_kind_to_credential_type_name_map = {
- kind: name
- for name, kind in credential_type_name_to_config_kind_map.items()}
+config_kind_to_credential_type_name_map = {kind: name for name, kind in credential_type_name_to_config_kind_map.items()}
def kind_and_config_cred_from_credential_type(credential_type):
@@ -115,8 +109,7 @@ def kind_and_config_cred_from_credential_type(credential_type):
config_cred = config.credentials.network
kind = 'net'
elif credential_type.kind == 'cloud':
- kind = credential_type_name_to_config_kind_map[credential_type.name.lower(
- )]
+ kind = credential_type_name_to_config_kind_map[credential_type.name.lower()]
config_kind = kind if kind != 'azure_rm' else 'azure'
config_cred = config.credentials.cloud[config_kind]
else:
@@ -127,11 +120,8 @@ def kind_and_config_cred_from_credential_type(credential_type):
return kind, PseudoNamespace()
-def get_payload_field_and_value_from_kwargs_or_config_cred(
- field, kind, kwargs, config_cred):
- if field in (
- 'project_id',
- 'project_name'): # Needed to prevent Project kwarg collision
+def get_payload_field_and_value_from_kwargs_or_config_cred(field, kind, kwargs, config_cred):
+ if field in ('project_id', 'project_name'): # Needed to prevent Project kwarg collision
config_field = 'project'
elif field == 'subscription' and 'azure' in kind:
config_field = 'subscription_id'
@@ -159,10 +149,8 @@ class CredentialType(HasCreate, base.Base):
def payload(self, kind='cloud', **kwargs):
payload = PseudoNamespace(
- name=kwargs.get('name') or 'CredentialType - {}'.format(
- random_title()),
- description=kwargs.get('description') or random_title(10),
- kind=kind)
+ name=kwargs.get('name') or 'CredentialType - {}'.format(random_title()), description=kwargs.get('description') or random_title(10), kind=kind
+ )
fields = ('inputs', 'injectors')
update_payload(payload, fields, kwargs)
return payload
@@ -174,17 +162,13 @@ class CredentialType(HasCreate, base.Base):
def create(self, kind='cloud', **kwargs):
payload = self.create_payload(kind=kind, **kwargs)
- return self.update_identity(
- CredentialTypes(
- self.connection).post(payload))
+ return self.update_identity(CredentialTypes(self.connection).post(payload))
def test(self, data):
"""Test the credential type endpoint."""
response = self.connection.post(urljoin(str(self.url), 'test/'), data)
exception = exception_from_status_code(response.status_code)
- exc_str = "%s (%s) received" % (
- http.responses[response.status_code], response.status_code
- )
+ exc_str = "%s (%s) received" % (http.responses[response.status_code], response.status_code)
if exception:
raise exception(exc_str, response.json())
elif response.status_code == http.FORBIDDEN:
@@ -192,8 +176,7 @@ class CredentialType(HasCreate, base.Base):
return response
-page.register_page([resources.credential_type,
- (resources.credential_types, 'post')], CredentialType)
+page.register_page([resources.credential_type, (resources.credential_types, 'post')], CredentialType)
class CredentialTypes(page.PageList, CredentialType):
@@ -210,27 +193,19 @@ class Credential(HasCopy, HasCreate, base.Base):
optional_dependencies = [Organization, User, Team]
NATURAL_KEY = ('organization', 'name', 'credential_type')
- def payload(
- self,
- credential_type,
- user=None,
- team=None,
- organization=None,
- inputs=None,
- **kwargs):
+ def payload(self, credential_type, user=None, team=None, organization=None, inputs=None, **kwargs):
if not any((user, team, organization)):
- raise TypeError(
- '{0.__class__.__name__} requires user, team, and/or organization instances.'.format(self))
+ raise TypeError('{0.__class__.__name__} requires user, team, and/or organization instances.'.format(self))
if inputs is None:
inputs = {}
payload = PseudoNamespace(
- name=kwargs.get('name') or 'Credential - {}'.format(
- random_title()),
+ name=kwargs.get('name') or 'Credential - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
credential_type=credential_type.id,
- inputs=inputs)
+ inputs=inputs,
+ )
if user:
payload.user = user.id
if team:
@@ -238,38 +213,26 @@ class Credential(HasCopy, HasCreate, base.Base):
if organization:
payload.organization = organization.id
- kind, config_cred = kind_and_config_cred_from_credential_type(
- credential_type)
+ kind, config_cred = kind_and_config_cred_from_credential_type(credential_type)
for field in credential_input_fields:
- field, value = get_payload_field_and_value_from_kwargs_or_config_cred(
- field, kind, inputs or kwargs, config_cred)
+ field, value = get_payload_field_and_value_from_kwargs_or_config_cred(field, kind, inputs or kwargs, config_cred)
if value != not_provided:
payload.inputs[field] = value
if kind == 'net':
- payload.inputs.authorize = inputs.get(
- 'authorize', bool(inputs.get('authorize_password')))
+ payload.inputs.authorize = inputs.get('authorize', bool(inputs.get('authorize_password')))
if kind in ('ssh', 'net') and 'ssh_key_data' not in payload.inputs:
- payload.inputs.ssh_key_data = inputs.get(
- 'ssh_key_data', generate_private_key())
+ payload.inputs.ssh_key_data = inputs.get('ssh_key_data', generate_private_key())
return payload
- def create_payload(
- self,
- credential_type=CredentialType,
- user=None,
- team=None,
- organization=Organization,
- inputs=None,
- **kwargs):
+ def create_payload(self, credential_type=CredentialType, user=None, team=None, organization=Organization, inputs=None, **kwargs):
if isinstance(credential_type, int):
# if an int was passed, it is assumed to be the pk id of a
# credential type
- credential_type = CredentialTypes(
- self.connection).get(id=credential_type).results.pop()
+ credential_type = CredentialTypes(self.connection).get(id=credential_type).results.pop()
if credential_type == CredentialType:
kind = kwargs.pop('kind', 'ssh')
@@ -282,57 +245,29 @@ class Credential(HasCopy, HasCreate, base.Base):
inputs = config.credentials.cloud['openstack']
else:
credential_type_name = config_kind_to_credential_type_name_map[kind]
- credential_type = CredentialTypes(
- self.connection).get(
- managed_by_tower=True,
- name__icontains=credential_type_name).results.pop()
+ credential_type = CredentialTypes(self.connection).get(managed_by_tower=True, name__icontains=credential_type_name).results.pop()
- credential_type, organization, user, team = filter_by_class(
- (credential_type, CredentialType), (organization, Organization), (user, User), (team, Team))
+ credential_type, organization, user, team = filter_by_class((credential_type, CredentialType), (organization, Organization), (user, User), (team, Team))
if not any((user, team, organization)):
organization = Organization
- self.create_and_update_dependencies(
- credential_type, organization, user, team)
+ self.create_and_update_dependencies(credential_type, organization, user, team)
user = self.ds.user if user else None
team = self.ds.team if team else None
organization = self.ds.organization if organization else None
- payload = self.payload(
- self.ds.credential_type,
- user=user,
- team=team,
- organization=organization,
- inputs=inputs,
- **kwargs)
+ payload = self.payload(self.ds.credential_type, user=user, team=team, organization=organization, inputs=inputs, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
- def create(
- self,
- credential_type=CredentialType,
- user=None,
- team=None,
- organization=None,
- inputs=None,
- **kwargs):
- payload = self.create_payload(
- credential_type=credential_type,
- user=user,
- team=team,
- organization=organization,
- inputs=inputs,
- **kwargs)
- return self.update_identity(
- Credentials(
- self.connection)).post(payload)
+ def create(self, credential_type=CredentialType, user=None, team=None, organization=None, inputs=None, **kwargs):
+ payload = self.create_payload(credential_type=credential_type, user=user, team=team, organization=organization, inputs=inputs, **kwargs)
+ return self.update_identity(Credentials(self.connection)).post(payload)
def test(self, data):
"""Test the credential endpoint."""
response = self.connection.post(urljoin(str(self.url), 'test/'), data)
exception = exception_from_status_code(response.status_code)
- exc_str = "%s (%s) received" % (
- http.responses[response.status_code], response.status_code
- )
+ exc_str = "%s (%s) received" % (http.responses[response.status_code], response.status_code)
if exception:
raise exception(exc_str, response.json())
elif response.status_code == http.FORBIDDEN:
@@ -343,11 +278,7 @@ class Credential(HasCopy, HasCreate, base.Base):
def expected_passwords_needed_to_start(self):
"""Return a list of expected passwords needed to start a job using this credential."""
passwords = []
- for field in (
- 'password',
- 'become_password',
- 'ssh_key_unlock',
- 'vault_password'):
+ for field in ('password', 'become_password', 'ssh_key_unlock', 'vault_password'):
if getattr(self.inputs, field, None) == 'ASK':
if field == 'password':
passwords.append('ssh_password')
@@ -356,9 +287,7 @@ class Credential(HasCopy, HasCreate, base.Base):
return passwords
-page.register_page([resources.credential,
- (resources.credentials, 'post'),
- (resources.credential_copy, 'post')], Credential)
+page.register_page([resources.credential, (resources.credentials, 'post'), (resources.credential_copy, 'post')], Credential)
class Credentials(page.PageList, Credential):
@@ -366,9 +295,7 @@ class Credentials(page.PageList, Credential):
pass
-page.register_page([resources.credentials,
- resources.related_credentials],
- Credentials)
+page.register_page([resources.credentials, resources.related_credentials], Credentials)
class CredentialCopy(base.Base):
diff --git a/awxkit/awxkit/api/pages/execution_environments.py b/awxkit/awxkit/api/pages/execution_environments.py
index 0471b1f1d3..01ee33afb5 100644
--- a/awxkit/awxkit/api/pages/execution_environments.py
+++ b/awxkit/awxkit/api/pages/execution_environments.py
@@ -46,14 +46,13 @@ class ExecutionEnvironment(HasCreate, HasCopy, base.Base):
return payload
-page.register_page([resources.execution_environment,
- (resources.execution_environments, 'post'),
- (resources.organization_execution_environments, 'post')], ExecutionEnvironment)
+page.register_page(
+ [resources.execution_environment, (resources.execution_environments, 'post'), (resources.organization_execution_environments, 'post')], ExecutionEnvironment
+)
class ExecutionEnvironments(page.PageList, ExecutionEnvironment):
pass
-page.register_page([resources.execution_environments,
- resources.organization_execution_environments], ExecutionEnvironments)
+page.register_page([resources.execution_environments, resources.organization_execution_environments], ExecutionEnvironments)
diff --git a/awxkit/awxkit/api/pages/instance_groups.py b/awxkit/awxkit/api/pages/instance_groups.py
index 31af3e82fe..28aa614300 100644
--- a/awxkit/awxkit/api/pages/instance_groups.py
+++ b/awxkit/awxkit/api/pages/instance_groups.py
@@ -7,7 +7,6 @@ from . import page
class InstanceGroup(HasCreate, base.Base):
-
def add_instance(self, instance):
with suppress(exc.NoContent):
self.related.instances.post(dict(id=instance.id))
@@ -17,8 +16,7 @@ class InstanceGroup(HasCreate, base.Base):
self.related.instances.post(dict(id=instance.id, disassociate=True))
def payload(self, **kwargs):
- payload = PseudoNamespace(name=kwargs.get('name') or
- 'Instance Group - {}'.format(random_title()))
+ payload = PseudoNamespace(name=kwargs.get('name') or 'Instance Group - {}'.format(random_title()))
fields = ('policy_instance_percentage', 'policy_instance_minimum', 'policy_instance_list', 'is_container_group')
update_payload(payload, fields, kwargs)
@@ -35,8 +33,7 @@ class InstanceGroup(HasCreate, base.Base):
return self.update_identity(InstanceGroups(self.connection).post(payload))
-page.register_page([resources.instance_group,
- (resources.instance_groups, 'post')], InstanceGroup)
+page.register_page([resources.instance_group, (resources.instance_groups, 'post')], InstanceGroup)
class InstanceGroups(page.PageList, InstanceGroup):
@@ -44,5 +41,4 @@ class InstanceGroups(page.PageList, InstanceGroup):
pass
-page.register_page([resources.instance_groups,
- resources.related_instance_groups], InstanceGroups)
+page.register_page([resources.instance_groups, resources.related_instance_groups], InstanceGroups)
diff --git a/awxkit/awxkit/api/pages/instances.py b/awxkit/awxkit/api/pages/instances.py
index 7e8a0cff6d..38695014bf 100644
--- a/awxkit/awxkit/api/pages/instances.py
+++ b/awxkit/awxkit/api/pages/instances.py
@@ -16,5 +16,4 @@ class Instances(page.PageList, Instance):
pass
-page.register_page([resources.instances,
- resources.related_instances], Instances)
+page.register_page([resources.instances, resources.related_instances], Instances)
diff --git a/awxkit/awxkit/api/pages/inventory.py b/awxkit/awxkit/api/pages/inventory.py
index 31393b9ad4..beeb36a5a3 100644
--- a/awxkit/awxkit/api/pages/inventory.py
+++ b/awxkit/awxkit/api/pages/inventory.py
@@ -2,23 +2,8 @@ import logging
import json
import re
-from awxkit.api.pages import (
- Credential,
- Organization,
- Project,
- UnifiedJob,
- UnifiedJobTemplate
-)
-from awxkit.utils import (
- filter_by_class,
- random_title,
- update_payload,
- suppress,
- not_provided,
- PseudoNamespace,
- poll_until,
- random_utf8
-)
+from awxkit.api.pages import Credential, Organization, Project, UnifiedJob, UnifiedJobTemplate
+from awxkit.utils import filter_by_class, random_title, update_payload, suppress, not_provided, PseudoNamespace, poll_until, random_utf8
from awxkit.api.mixins import DSAdapter, HasCreate, HasInstanceGroups, HasNotifications, HasVariables, HasCopy
from awxkit.api.resources import resources
import awxkit.exceptions as exc
@@ -68,56 +53,31 @@ class Inventory(HasCopy, HasCreate, HasInstanceGroups, HasVariables, base.Base):
def payload(self, organization, **kwargs):
payload = PseudoNamespace(
- name=kwargs.get('name') or 'Inventory - {}'.format(
- random_title()),
+ name=kwargs.get('name') or 'Inventory - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
- organization=organization.id)
+ organization=organization.id,
+ )
- optional_fields = (
- 'host_filter',
- 'insights_credential',
- 'kind',
- 'variables')
+ optional_fields = ('host_filter', 'insights_credential', 'kind', 'variables')
update_payload(payload, optional_fields, kwargs)
if 'variables' in payload and isinstance(payload.variables, dict):
payload.variables = json.dumps(payload.variables)
- if 'insights_credential' in payload and isinstance(
- payload.insights_credential, Credential):
+ if 'insights_credential' in payload and isinstance(payload.insights_credential, Credential):
payload.insights_credential = payload.insights_credential.id
return payload
- def create_payload(
- self,
- name='',
- description='',
- organization=Organization,
- **kwargs):
+ def create_payload(self, name='', description='', organization=Organization, **kwargs):
self.create_and_update_dependencies(organization)
- payload = self.payload(
- name=name,
- description=description,
- organization=self.ds.organization,
- **kwargs)
+ payload = self.payload(name=name, description=description, organization=self.ds.organization, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
- def create(
- self,
- name='',
- description='',
- organization=Organization,
- **kwargs):
- payload = self.create_payload(
- name=name,
- description=description,
- organization=organization,
- **kwargs)
- return self.update_identity(
- Inventories(
- self.connection).post(payload))
+ def create(self, name='', description='', organization=Organization, **kwargs):
+ payload = self.create_payload(name=name, description=description, organization=organization, **kwargs)
+ return self.update_identity(Inventories(self.connection).post(payload))
def add_host(self, host=None):
if host is None:
@@ -135,17 +95,16 @@ class Inventory(HasCopy, HasCreate, HasInstanceGroups, HasVariables, base.Base):
self.get()
except exc.NotFound:
return True
+
poll_until(_wait, interval=1, timeout=60)
def update_inventory_sources(self, wait=False):
response = self.related.update_inventory_sources.post()
- source_ids = [entry['inventory_source']
- for entry in response if entry['status'] == 'started']
+ source_ids = [entry['inventory_source'] for entry in response if entry['status'] == 'started']
inv_updates = []
for source_id in source_ids:
- inv_source = self.related.inventory_sources.get(
- id=source_id).results.pop()
+ inv_source = self.related.inventory_sources.get(id=source_id).results.pop()
inv_updates.append(inv_source.related.current_job.get())
if wait:
@@ -154,9 +113,7 @@ class Inventory(HasCopy, HasCreate, HasInstanceGroups, HasVariables, base.Base):
return inv_updates
-page.register_page([resources.inventory,
- (resources.inventories, 'post'),
- (resources.inventory_copy, 'post')], Inventory)
+page.register_page([resources.inventory, (resources.inventories, 'post'), (resources.inventory_copy, 'post')], Inventory)
class Inventories(page.PageList, Inventory):
@@ -164,8 +121,7 @@ class Inventories(page.PageList, Inventory):
pass
-page.register_page([resources.inventories,
- resources.related_inventories], Inventories)
+page.register_page([resources.inventories, resources.related_inventories], Inventories)
class InventoryScript(HasCopy, HasCreate, base.Base):
@@ -174,77 +130,48 @@ class InventoryScript(HasCopy, HasCreate, base.Base):
def payload(self, organization, **kwargs):
payload = PseudoNamespace(
- name=kwargs.get('name') or 'Inventory Script - {}'.format(
- random_title()),
+ name=kwargs.get('name') or 'Inventory Script - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
organization=organization.id,
- script=kwargs.get('script') or self._generate_script())
+ script=kwargs.get('script') or self._generate_script(),
+ )
return payload
- def create_payload(
- self,
- name='',
- description='',
- organization=Organization,
- script='',
- **kwargs):
+ def create_payload(self, name='', description='', organization=Organization, script='', **kwargs):
self.create_and_update_dependencies(organization)
- payload = self.payload(
- name=name,
- description=description,
- organization=self.ds.organization,
- script=script,
- **kwargs)
+ payload = self.payload(name=name, description=description, organization=self.ds.organization, script=script, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
- def create(
- self,
- name='',
- description='',
- organization=Organization,
- script='',
- **kwargs):
- payload = self.create_payload(
- name=name,
- description=description,
- organization=organization,
- script=script,
- **kwargs)
- return self.update_identity(
- InventoryScripts(
- self.connection).post(payload))
+ def create(self, name='', description='', organization=Organization, script='', **kwargs):
+ payload = self.create_payload(name=name, description=description, organization=organization, script=script, **kwargs)
+ return self.update_identity(InventoryScripts(self.connection).post(payload))
def _generate_script(self):
- script = '\n'.join([
- '#!/usr/bin/env python',
- '# -*- coding: utf-8 -*-',
- 'import json',
- 'inventory = dict()',
- 'inventory["{0}"] = dict()',
- 'inventory["{0}"]["hosts"] = list()',
- 'inventory["{0}"]["hosts"].append("{1}")',
- 'inventory["{0}"]["hosts"].append("{2}")',
- 'inventory["{0}"]["hosts"].append("{3}")',
- 'inventory["{0}"]["hosts"].append("{4}")',
- 'inventory["{0}"]["hosts"].append("{5}")',
- 'inventory["{0}"]["vars"] = dict(ansible_host="127.0.0.1", ansible_connection="local")',
- 'print(json.dumps(inventory))'
- ])
+ script = '\n'.join(
+ [
+ '#!/usr/bin/env python',
+ '# -*- coding: utf-8 -*-',
+ 'import json',
+ 'inventory = dict()',
+ 'inventory["{0}"] = dict()',
+ 'inventory["{0}"]["hosts"] = list()',
+ 'inventory["{0}"]["hosts"].append("{1}")',
+ 'inventory["{0}"]["hosts"].append("{2}")',
+ 'inventory["{0}"]["hosts"].append("{3}")',
+ 'inventory["{0}"]["hosts"].append("{4}")',
+ 'inventory["{0}"]["hosts"].append("{5}")',
+ 'inventory["{0}"]["vars"] = dict(ansible_host="127.0.0.1", ansible_connection="local")',
+ 'print(json.dumps(inventory))',
+ ]
+ )
group_name = re.sub(r"[\']", "", "group_{}".format(random_title(non_ascii=False)))
- host_names = [
- re.sub(
- r"[\':]",
- "",
- "host_{}".format(
- random_utf8())) for _ in range(5)]
+ host_names = [re.sub(r"[\':]", "", "host_{}".format(random_utf8())) for _ in range(5)]
return script.format(group_name, *host_names)
-page.register_page([resources.inventory_script,
- (resources.inventory_scripts, 'post'),
- (resources.inventory_script_copy, 'post')], InventoryScript)
+page.register_page([resources.inventory_script, (resources.inventory_scripts, 'post'), (resources.inventory_script_copy, 'post')], InventoryScript)
class InventoryScripts(page.PageList, InventoryScript):
@@ -272,11 +199,10 @@ class Group(HasCreate, HasVariables, base.Base):
def payload(self, inventory, credential=None, **kwargs):
payload = PseudoNamespace(
- name=kwargs.get('name') or 'Group{}'.format(
- random_title(
- non_ascii=False)),
+ name=kwargs.get('name') or 'Group{}'.format(random_title(non_ascii=False)),
description=kwargs.get('description') or random_title(10),
- inventory=inventory.id)
+ inventory=inventory.id,
+ )
if credential:
payload.credential = credential.id
@@ -288,38 +214,19 @@ class Group(HasCreate, HasVariables, base.Base):
return payload
- def create_payload(
- self,
- name='',
- description='',
- inventory=Inventory,
- credential=None,
- source_script=None,
- **kwargs):
- credential, source_script = filter_by_class(
- (credential, Credential), (source_script, InventoryScript))
- self.create_and_update_dependencies(
- inventory, credential, source_script)
+ def create_payload(self, name='', description='', inventory=Inventory, credential=None, source_script=None, **kwargs):
+ credential, source_script = filter_by_class((credential, Credential), (source_script, InventoryScript))
+ self.create_and_update_dependencies(inventory, credential, source_script)
credential = self.ds.credential if credential else None
- payload = self.payload(
- inventory=self.ds.inventory,
- credential=credential,
- name=name,
- description=description,
- **kwargs)
+ payload = self.payload(inventory=self.ds.inventory, credential=credential, name=name, description=description, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(self, name='', description='', inventory=Inventory, **kwargs):
- payload = self.create_payload(
- name=name,
- description=description,
- inventory=inventory,
- **kwargs)
+ payload = self.create_payload(name=name, description=description, inventory=inventory, **kwargs)
parent = kwargs.get('parent', None) # parent must be a Group instance
- resource = parent.related.children if parent else Groups(
- self.connection)
+ resource = parent.related.children if parent else Groups(self.connection)
return self.update_identity(resource.post(payload))
def add_host(self, host=None):
@@ -348,8 +255,7 @@ class Group(HasCreate, HasVariables, base.Base):
self.related.children.post(dict(id=group.id, disassociate=True))
-page.register_page([resources.group,
- (resources.groups, 'post')], Group)
+page.register_page([resources.group, (resources.groups, 'post')], Group)
class Groups(page.PageList, Group):
@@ -357,12 +263,17 @@ class Groups(page.PageList, Group):
pass
-page.register_page([resources.groups,
- resources.host_groups,
- resources.inventory_related_groups,
- resources.inventory_related_root_groups,
- resources.group_children,
- resources.group_potential_children], Groups)
+page.register_page(
+ [
+ resources.groups,
+ resources.host_groups,
+ resources.inventory_related_groups,
+ resources.inventory_related_root_groups,
+ resources.group_children,
+ resources.group_potential_children,
+ ],
+ Groups,
+)
class Host(HasCreate, HasVariables, base.Base):
@@ -372,11 +283,10 @@ class Host(HasCreate, HasVariables, base.Base):
def payload(self, inventory, **kwargs):
payload = PseudoNamespace(
- name=kwargs.get('name') or 'Host{}'.format(
- random_title(
- non_ascii=False)),
+ name=kwargs.get('name') or 'Host{}'.format(random_title(non_ascii=False)),
description=kwargs.get('description') or random_title(10),
- inventory=inventory.id)
+ inventory=inventory.id,
+ )
optional_fields = ('enabled', 'instance_id')
@@ -385,9 +295,7 @@ class Host(HasCreate, HasVariables, base.Base):
variables = kwargs.get('variables', not_provided)
if variables is None:
- variables = dict(
- ansible_host='127.0.0.1',
- ansible_connection='local')
+ variables = dict(ansible_host='127.0.0.1', ansible_connection='local')
if variables != not_provided:
if isinstance(variables, dict):
@@ -396,42 +304,18 @@ class Host(HasCreate, HasVariables, base.Base):
return payload
- def create_payload(
- self,
- name='',
- description='',
- variables=None,
- inventory=Inventory,
- **kwargs):
- self.create_and_update_dependencies(
- *filter_by_class((inventory, Inventory)))
- payload = self.payload(
- inventory=self.ds.inventory,
- name=name,
- description=description,
- variables=variables,
- **kwargs)
+ def create_payload(self, name='', description='', variables=None, inventory=Inventory, **kwargs):
+ self.create_and_update_dependencies(*filter_by_class((inventory, Inventory)))
+ payload = self.payload(inventory=self.ds.inventory, name=name, description=description, variables=variables, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
- def create(
- self,
- name='',
- description='',
- variables=None,
- inventory=Inventory,
- **kwargs):
- payload = self.create_payload(
- name=name,
- description=description,
- variables=variables,
- inventory=inventory,
- **kwargs)
+ def create(self, name='', description='', variables=None, inventory=Inventory, **kwargs):
+ payload = self.create_payload(name=name, description=description, variables=variables, inventory=inventory, **kwargs)
return self.update_identity(Hosts(self.connection).post(payload))
-page.register_page([resources.host,
- (resources.hosts, 'post')], Host)
+page.register_page([resources.host, (resources.hosts, 'post')], Host)
class Hosts(page.PageList, Host):
@@ -439,10 +323,7 @@ class Hosts(page.PageList, Host):
pass
-page.register_page([resources.hosts,
- resources.group_related_hosts,
- resources.inventory_related_hosts,
- resources.inventory_sources_related_hosts], Hosts)
+page.register_page([resources.hosts, resources.group_related_hosts, resources.inventory_related_hosts, resources.inventory_sources_related_hosts], Hosts)
class FactVersion(base.Base):
@@ -454,7 +335,6 @@ page.register_page(resources.host_related_fact_version, FactVersion)
class FactVersions(page.PageList, FactVersion):
-
@property
def count(self):
return len(self.results)
@@ -478,20 +358,13 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
optional_dependencies = [Credential, InventoryScript, Project]
NATURAL_KEY = ('organization', 'name', 'inventory')
- def payload(
- self,
- inventory,
- source='custom',
- credential=None,
- source_script=None,
- project=None,
- **kwargs):
+ def payload(self, inventory, source='custom', credential=None, source_script=None, project=None, **kwargs):
payload = PseudoNamespace(
- name=kwargs.get('name') or 'InventorySource - {}'.format(
- random_title()),
+ name=kwargs.get('name') or 'InventorySource - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
inventory=inventory.id,
- source=source)
+ source=source,
+ )
if credential:
payload.credential = credential.id
@@ -509,22 +382,16 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
'update_cache_timeout',
'update_on_launch',
'update_on_project_update',
- 'verbosity')
+ 'verbosity',
+ )
update_payload(payload, optional_fields, kwargs)
return payload
def create_payload(
- self,
- name='',
- description='',
- source='custom',
- inventory=Inventory,
- credential=None,
- source_script=InventoryScript,
- project=None,
- **kwargs):
+ self, name='', description='', source='custom', inventory=Inventory, credential=None, source_script=InventoryScript, project=None, **kwargs
+ ):
if source != 'custom' and source_script == InventoryScript:
source_script = None
if source == 'scm':
@@ -532,12 +399,10 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
if project is None:
project = Project
- inventory, credential, source_script, project = filter_by_class((inventory, Inventory),
- (credential, Credential),
- (source_script, InventoryScript),
- (project, Project))
- self.create_and_update_dependencies(
- inventory, credential, source_script, project)
+ inventory, credential, source_script, project = filter_by_class(
+ (inventory, Inventory), (credential, Credential), (source_script, InventoryScript), (project, Project)
+ )
+ self.create_and_update_dependencies(inventory, credential, source_script, project)
if credential:
credential = self.ds.credential
@@ -554,20 +419,12 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
project=project,
name=name,
description=description,
- **kwargs)
+ **kwargs
+ )
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
- def create(
- self,
- name='',
- description='',
- source='custom',
- inventory=Inventory,
- credential=None,
- source_script=InventoryScript,
- project=None,
- **kwargs):
+ def create(self, name='', description='', source='custom', inventory=Inventory, credential=None, source_script=InventoryScript, project=None, **kwargs):
payload = self.create_payload(
name=name,
description=description,
@@ -576,10 +433,9 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
credential=credential,
source_script=source_script,
project=project,
- **kwargs)
- return self.update_identity(
- InventorySources(
- self.connection).post(payload))
+ **kwargs
+ )
+ return self.update_identity(InventorySources(self.connection).post(payload))
def update(self):
"""Update the inventory_source using related->update endpoint"""
@@ -587,45 +443,37 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
update_pg = self.get_related('update')
# assert can_update == True
- assert update_pg.can_update, \
- "The specified inventory_source (id:%s) is not able to update (can_update:%s)" % \
- (self.id, update_pg.can_update)
+ assert update_pg.can_update, "The specified inventory_source (id:%s) is not able to update (can_update:%s)" % (self.id, update_pg.can_update)
# start the inventory_update
result = update_pg.post()
# assert JSON response
- assert 'inventory_update' in result.json, \
- "Unexpected JSON response when starting an inventory_update.\n%s" % \
- json.dumps(result.json, indent=2)
+ assert 'inventory_update' in result.json, "Unexpected JSON response when starting an inventory_update.\n%s" % json.dumps(result.json, indent=2)
# locate and return the inventory_update
- jobs_pg = self.related.inventory_updates.get(
- id=result.json['inventory_update'])
- assert jobs_pg.count == 1, \
- "An inventory_update started (id:%s) but job not found in response at %s/inventory_updates/" % \
- (result.json['inventory_update'], self.url)
+ jobs_pg = self.related.inventory_updates.get(id=result.json['inventory_update'])
+ assert jobs_pg.count == 1, "An inventory_update started (id:%s) but job not found in response at %s/inventory_updates/" % (
+ result.json['inventory_update'],
+ self.url,
+ )
return jobs_pg.results[0]
@property
def is_successful(self):
"""An inventory_source is considered successful when source != "" and super().is_successful ."""
- return self.source != "" and super(
- InventorySource, self).is_successful
+ return self.source != "" and super(InventorySource, self).is_successful
def add_credential(self, credential):
with suppress(exc.NoContent):
- self.related.credentials.post(
- dict(id=credential.id, associate=True))
+ self.related.credentials.post(dict(id=credential.id, associate=True))
def remove_credential(self, credential):
with suppress(exc.NoContent):
- self.related.credentials.post(
- dict(id=credential.id, disassociate=True))
+ self.related.credentials.post(dict(id=credential.id, disassociate=True))
-page.register_page([resources.inventory_source,
- (resources.inventory_sources, 'post')], InventorySource)
+page.register_page([resources.inventory_source, (resources.inventory_sources, 'post')], InventorySource)
class InventorySources(page.PageList, InventorySource):
@@ -633,9 +481,7 @@ class InventorySources(page.PageList, InventorySource):
pass
-page.register_page([resources.inventory_sources,
- resources.related_inventory_sources],
- InventorySources)
+page.register_page([resources.inventory_sources, resources.related_inventory_sources], InventorySources)
class InventorySourceGroups(page.PageList, Group):
@@ -643,9 +489,7 @@ class InventorySourceGroups(page.PageList, Group):
pass
-page.register_page(
- resources.inventory_sources_related_groups,
- InventorySourceGroups)
+page.register_page(resources.inventory_sources_related_groups, InventorySourceGroups)
class InventorySourceUpdate(base.Base):
@@ -653,9 +497,7 @@ class InventorySourceUpdate(base.Base):
pass
-page.register_page([resources.inventory_sources_related_update,
- resources.inventory_related_update_inventory_sources],
- InventorySourceUpdate)
+page.register_page([resources.inventory_sources_related_update, resources.inventory_related_update_inventory_sources], InventorySourceUpdate)
class InventoryUpdate(UnifiedJob):
@@ -671,10 +513,7 @@ class InventoryUpdates(page.PageList, InventoryUpdate):
pass
-page.register_page([resources.inventory_updates,
- resources.inventory_source_updates,
- resources.project_update_scm_inventory_updates],
- InventoryUpdates)
+page.register_page([resources.inventory_updates, resources.inventory_source_updates, resources.project_update_scm_inventory_updates], InventoryUpdates)
class InventoryUpdateCancel(base.Base):
diff --git a/awxkit/awxkit/api/pages/job_templates.py b/awxkit/awxkit/api/pages/job_templates.py
index 693112375a..7de4cc902e 100644
--- a/awxkit/awxkit/api/pages/job_templates.py
+++ b/awxkit/awxkit/api/pages/job_templates.py
@@ -1,13 +1,6 @@
import json
-from awxkit.utils import (
- filter_by_class,
- not_provided,
- random_title,
- suppress,
- update_payload,
- set_payload_foreign_key_args,
- PseudoNamespace)
+from awxkit.utils import filter_by_class, not_provided, random_title, suppress, update_payload, set_payload_foreign_key_args, PseudoNamespace
from awxkit.api.pages import Credential, Inventory, Project, UnifiedJobTemplate
from awxkit.api.mixins import HasCreate, HasInstanceGroups, HasNotifications, HasSurvey, HasCopy, DSAdapter
from awxkit.api.resources import resources
@@ -16,13 +9,7 @@ from . import base
from . import page
-class JobTemplate(
- HasCopy,
- HasCreate,
- HasInstanceGroups,
- HasNotifications,
- HasSurvey,
- UnifiedJobTemplate):
+class JobTemplate(HasCopy, HasCreate, HasInstanceGroups, HasNotifications, HasSurvey, UnifiedJobTemplate):
optional_dependencies = [Inventory, Credential, Project]
NATURAL_KEY = ('organization', 'name')
@@ -38,16 +25,13 @@ class JobTemplate(
# return job
if result.json['type'] == 'job':
jobs_pg = self.get_related('jobs', id=result.json['job'])
- assert jobs_pg.count == 1, \
- "job_template launched (id:%s) but job not found in response at %s/jobs/" % \
- (result.json['job'], self.url)
+ assert jobs_pg.count == 1, "job_template launched (id:%s) but job not found in response at %s/jobs/" % (result.json['job'], self.url)
return jobs_pg.results[0]
elif result.json['type'] == 'workflow_job':
- slice_workflow_jobs = self.get_related(
- 'slice_workflow_jobs', id=result.json['id'])
- assert slice_workflow_jobs.count == 1, (
- "job_template launched sliced job (id:%s) but not found in related %s/slice_workflow_jobs/" %
- (result.json['id'], self.url)
+ slice_workflow_jobs = self.get_related('slice_workflow_jobs', id=result.json['id'])
+ assert slice_workflow_jobs.count == 1, "job_template launched sliced job (id:%s) but not found in related %s/slice_workflow_jobs/" % (
+ result.json['id'],
+ self.url,
)
return slice_workflow_jobs.results[0]
else:
@@ -56,10 +40,7 @@ class JobTemplate(
def payload(self, job_type='run', playbook='ping.yml', **kwargs):
name = kwargs.get('name') or 'JobTemplate - {}'.format(random_title())
description = kwargs.get('description') or random_title(10)
- payload = PseudoNamespace(
- name=name,
- description=description,
- job_type=job_type)
+ payload = PseudoNamespace(name=name, description=description, job_type=job_type)
optional_fields = (
'ask_scm_branch_on_launch',
@@ -90,7 +71,8 @@ class JobTemplate(
'job_slice_count',
'webhook_service',
'webhook_credential',
- 'scm_branch')
+ 'scm_branch',
+ )
update_payload(payload, optional_fields, kwargs)
@@ -113,94 +95,53 @@ class JobTemplate(
with suppress(exc.NoContent):
self.related.labels.post(label)
- def create_payload(
- self,
- name='',
- description='',
- job_type='run',
- playbook='ping.yml',
- credential=Credential,
- inventory=Inventory,
- project=None,
- **kwargs):
+ def create_payload(self, name='', description='', job_type='run', playbook='ping.yml', credential=Credential, inventory=Inventory, project=None, **kwargs):
if not project:
project = Project
if not inventory and not kwargs.get('ask_inventory_on_launch', False):
inventory = Inventory
- self.create_and_update_dependencies(
- *
- filter_by_class(
- (credential,
- Credential),
- (inventory,
- Inventory),
- (project,
- Project)))
+ self.create_and_update_dependencies(*filter_by_class((credential, Credential), (inventory, Inventory), (project, Project)))
project = self.ds.project if project else None
inventory = self.ds.inventory if inventory else None
credential = self.ds.credential if credential else None
payload = self.payload(
- name=name,
- description=description,
- job_type=job_type,
- playbook=playbook,
- credential=credential,
- inventory=inventory,
- project=project,
- **kwargs)
+ name=name, description=description, job_type=job_type, playbook=playbook, credential=credential, inventory=inventory, project=project, **kwargs
+ )
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload, credential
- def create(
- self,
- name='',
- description='',
- job_type='run',
- playbook='ping.yml',
- credential=Credential,
- inventory=Inventory,
- project=None,
- **kwargs):
- payload, credential = self.create_payload(name=name, description=description, job_type=job_type,
- playbook=playbook, credential=credential, inventory=inventory,
- project=project, **kwargs)
- ret = self.update_identity(
- JobTemplates(
- self.connection).post(payload))
+ def create(self, name='', description='', job_type='run', playbook='ping.yml', credential=Credential, inventory=Inventory, project=None, **kwargs):
+ payload, credential = self.create_payload(
+ name=name, description=description, job_type=job_type, playbook=playbook, credential=credential, inventory=inventory, project=project, **kwargs
+ )
+ ret = self.update_identity(JobTemplates(self.connection).post(payload))
if credential:
with suppress(exc.NoContent):
self.related.credentials.post(dict(id=credential.id))
if 'vault_credential' in kwargs:
with suppress(exc.NoContent):
if not isinstance(kwargs['vault_credential'], int):
- raise ValueError(
- "Expected 'vault_credential' value to be an integer, the id of the desired vault credential")
- self.related.credentials.post(
- dict(id=kwargs['vault_credential']))
+ raise ValueError("Expected 'vault_credential' value to be an integer, the id of the desired vault credential")
+ self.related.credentials.post(dict(id=kwargs['vault_credential']))
return ret
def add_credential(self, credential):
with suppress(exc.NoContent):
- self.related.credentials.post(
- dict(id=credential.id, associate=True))
+ self.related.credentials.post(dict(id=credential.id, associate=True))
def remove_credential(self, credential):
with suppress(exc.NoContent):
- self.related.credentials.post(
- dict(id=credential.id, disassociate=True))
+ self.related.credentials.post(dict(id=credential.id, disassociate=True))
def remove_all_credentials(self):
for cred in self.related.credentials.get().results:
with suppress(exc.NoContent):
- self.related.credentials.post(
- dict(id=cred.id, disassociate=True))
+ self.related.credentials.post(dict(id=cred.id, disassociate=True))
-page.register_page([resources.job_template,
- (resources.job_templates, 'post'),
- (resources.job_template_copy, 'post')], JobTemplate)
+page.register_page([resources.job_template, (resources.job_templates, 'post'), (resources.job_template_copy, 'post')], JobTemplate)
class JobTemplates(page.PageList, JobTemplate):
@@ -208,8 +149,7 @@ class JobTemplates(page.PageList, JobTemplate):
pass
-page.register_page([resources.job_templates,
- resources.related_job_templates], JobTemplates)
+page.register_page([resources.job_templates, resources.related_job_templates], JobTemplates)
class JobTemplateCallback(base.Base):
diff --git a/awxkit/awxkit/api/pages/jobs.py b/awxkit/awxkit/api/pages/jobs.py
index 5c0ed24f28..358009e59b 100644
--- a/awxkit/awxkit/api/pages/jobs.py
+++ b/awxkit/awxkit/api/pages/jobs.py
@@ -5,7 +5,6 @@ from . import page
class Job(UnifiedJob):
-
def relaunch(self, payload={}):
result = self.related.relaunch.post(payload)
return self.walk(result.endpoint)
@@ -19,9 +18,7 @@ class Jobs(page.PageList, Job):
pass
-page.register_page([resources.jobs,
- resources.job_template_jobs,
- resources.system_job_template_jobs], Jobs)
+page.register_page([resources.jobs, resources.job_template_jobs, resources.system_job_template_jobs], Jobs)
class JobCancel(UnifiedJob):
@@ -37,8 +34,7 @@ class JobEvent(base.Base):
pass
-page.register_page([resources.job_event,
- resources.job_job_event], JobEvent)
+page.register_page([resources.job_event, resources.job_job_event], JobEvent)
class JobEvents(page.PageList, JobEvent):
@@ -46,10 +42,7 @@ class JobEvents(page.PageList, JobEvent):
pass
-page.register_page([resources.job_events,
- resources.job_job_events,
- resources.job_event_children,
- resources.group_related_job_events], JobEvents)
+page.register_page([resources.job_events, resources.job_job_events, resources.job_event_children, resources.group_related_job_events], JobEvents)
class JobPlay(base.Base):
@@ -97,8 +90,7 @@ class JobHostSummaries(page.PageList, JobHostSummary):
pass
-page.register_page([resources.job_host_summaries,
- resources.group_related_job_host_summaries], JobHostSummaries)
+page.register_page([resources.job_host_summaries, resources.group_related_job_host_summaries], JobHostSummaries)
class JobRelaunch(base.Base):
diff --git a/awxkit/awxkit/api/pages/labels.py b/awxkit/awxkit/api/pages/labels.py
index 34022f66d2..b6cb88b073 100644
--- a/awxkit/awxkit/api/pages/labels.py
+++ b/awxkit/awxkit/api/pages/labels.py
@@ -19,43 +19,24 @@ class Label(HasCreate, base.Base):
def payload(self, organization, **kwargs):
payload = PseudoNamespace(
- name=kwargs.get('name') or 'Label - {}'.format(
- random_title()),
+ name=kwargs.get('name') or 'Label - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
- organization=organization.id)
+ organization=organization.id,
+ )
return payload
- def create_payload(
- self,
- name='',
- description='',
- organization=Organization,
- **kwargs):
+ def create_payload(self, name='', description='', organization=Organization, **kwargs):
self.create_and_update_dependencies(organization)
- payload = self.payload(
- organization=self.ds.organization,
- name=name,
- description=description,
- **kwargs)
+ payload = self.payload(organization=self.ds.organization, name=name, description=description, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
- def create(
- self,
- name='',
- description='',
- organization=Organization,
- **kwargs):
- payload = self.create_payload(
- name=name,
- description=description,
- organization=organization,
- **kwargs)
+ def create(self, name='', description='', organization=Organization, **kwargs):
+ payload = self.create_payload(name=name, description=description, organization=organization, **kwargs)
return self.update_identity(Labels(self.connection).post(payload))
-page.register_page([resources.label,
- (resources.labels, 'post')], Label)
+page.register_page([resources.label, (resources.labels, 'post')], Label)
class Labels(page.PageList, Label):
@@ -63,7 +44,4 @@ class Labels(page.PageList, Label):
pass
-page.register_page([resources.labels,
- resources.job_labels,
- resources.job_template_labels,
- resources.workflow_job_template_labels], Labels)
+page.register_page([resources.labels, resources.job_labels, resources.job_template_labels, resources.workflow_job_template_labels], Labels)
diff --git a/awxkit/awxkit/api/pages/metrics.py b/awxkit/awxkit/api/pages/metrics.py
index 2e3cafaafd..88a57b7139 100644
--- a/awxkit/awxkit/api/pages/metrics.py
+++ b/awxkit/awxkit/api/pages/metrics.py
@@ -4,12 +4,9 @@ from . import page
class Metrics(base.Base):
-
def get(self, **query_parameters):
- request = self.connection.get(self.endpoint, query_parameters,
- headers={'Accept': 'application/json'})
+ request = self.connection.get(self.endpoint, query_parameters, headers={'Accept': 'application/json'})
return self.page_identity(request)
-page.register_page([resources.metrics,
- (resources.metrics, 'get')], Metrics)
+page.register_page([resources.metrics, (resources.metrics, 'get')], Metrics)
diff --git a/awxkit/awxkit/api/pages/notification_templates.py b/awxkit/awxkit/api/pages/notification_templates.py
index ff192d4433..0688cc1342 100644
--- a/awxkit/awxkit/api/pages/notification_templates.py
+++ b/awxkit/awxkit/api/pages/notification_templates.py
@@ -9,16 +9,7 @@ from . import page
job_results = ('any', 'error', 'success')
-notification_types = (
- 'email',
- 'irc',
- 'pagerduty',
- 'slack',
- 'twilio',
- 'webhook',
- 'mattermost',
- 'grafana',
- 'rocketchat')
+notification_types = ('email', 'irc', 'pagerduty', 'slack', 'twilio', 'webhook', 'mattermost', 'grafana', 'rocketchat')
class NotificationTemplate(HasCopy, HasCreate, base.Base):
@@ -28,18 +19,17 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
def test(self):
"""Create test notification"""
- assert 'test' in self.related, \
- "No such related attribute 'test'"
+ assert 'test' in self.related, "No such related attribute 'test'"
# trigger test notification
notification_id = self.related.test.post().notification
# return notification page
- notifications_pg = self.get_related(
- 'notifications', id=notification_id).wait_until_count(1)
- assert notifications_pg.count == 1, \
- "test notification triggered (id:%s) but notification not found in response at %s/notifications/" % \
- (notification_id, self.url)
+ notifications_pg = self.get_related('notifications', id=notification_id).wait_until_count(1)
+ assert notifications_pg.count == 1, "test notification triggered (id:%s) but notification not found in response at %s/notifications/" % (
+ notification_id,
+ self.url,
+ )
return notifications_pg.results[0]
def silent_delete(self):
@@ -53,41 +43,25 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
def payload(self, organization, notification_type='slack', messages=not_provided, **kwargs):
payload = PseudoNamespace(
- name=kwargs.get('name') or 'NotificationTemplate ({0}) - {1}' .format(
- notification_type,
- random_title()),
+ name=kwargs.get('name') or 'NotificationTemplate ({0}) - {1}'.format(notification_type, random_title()),
description=kwargs.get('description') or random_title(10),
organization=organization.id,
- notification_type=notification_type)
+ notification_type=notification_type,
+ )
if messages != not_provided:
payload['messages'] = messages
- notification_configuration = kwargs.get(
- 'notification_configuration', {})
+ notification_configuration = kwargs.get('notification_configuration', {})
payload.notification_configuration = notification_configuration
if payload.notification_configuration == {}:
services = config.credentials.notification_services
if notification_type == 'email':
- fields = (
- 'host',
- 'username',
- 'password',
- 'port',
- 'use_ssl',
- 'use_tls',
- 'sender',
- 'recipients')
+ fields = ('host', 'username', 'password', 'port', 'use_ssl', 'use_tls', 'sender', 'recipients')
cred = services.email
elif notification_type == 'irc':
- fields = (
- 'server',
- 'port',
- 'use_ssl',
- 'password',
- 'nickname',
- 'targets')
+ fields = ('server', 'port', 'use_ssl', 'password', 'nickname', 'targets')
cred = services.irc
elif notification_type == 'pagerduty':
fields = ('client_name', 'service_key', 'subdomain', 'token')
@@ -96,34 +70,22 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
fields = ('channels', 'token')
cred = services.slack
elif notification_type == 'twilio':
- fields = (
- 'account_sid',
- 'account_token',
- 'from_number',
- 'to_numbers')
+ fields = ('account_sid', 'account_token', 'from_number', 'to_numbers')
cred = services.twilio
elif notification_type == 'webhook':
fields = ('url', 'headers')
cred = services.webhook
elif notification_type == 'mattermost':
- fields = (
- 'mattermost_url',
- 'mattermost_username',
- 'mattermost_channel',
- 'mattermost_icon_url',
- 'mattermost_no_verify_ssl')
+ fields = ('mattermost_url', 'mattermost_username', 'mattermost_channel', 'mattermost_icon_url', 'mattermost_no_verify_ssl')
cred = services.mattermost
elif notification_type == 'grafana':
- fields = ('grafana_url',
- 'grafana_key')
+ fields = ('grafana_url', 'grafana_key')
cred = services.grafana
elif notification_type == 'rocketchat':
- fields = ('rocketchat_url',
- 'rocketchat_no_verify_ssl')
+ fields = ('rocketchat_url', 'rocketchat_no_verify_ssl')
cred = services.rocketchat
else:
- raise ValueError(
- 'Unknown notification_type {0}'.format(notification_type))
+ raise ValueError('Unknown notification_type {0}'.format(notification_type))
for field in fields:
if field == 'bot_token':
@@ -136,47 +98,21 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
return payload
- def create_payload(
- self,
- name='',
- description='',
- notification_type='slack',
- organization=Organization,
- messages=not_provided,
- **kwargs):
+ def create_payload(self, name='', description='', notification_type='slack', organization=Organization, messages=not_provided, **kwargs):
if notification_type not in notification_types:
- raise ValueError(
- 'Unsupported notification type "{0}". Please use one of {1}.' .format(
- notification_type, notification_types))
+ raise ValueError('Unsupported notification type "{0}". Please use one of {1}.'.format(notification_type, notification_types))
self.create_and_update_dependencies(organization)
payload = self.payload(
- organization=self.ds.organization,
- notification_type=notification_type,
- name=name,
- description=description,
- messages=messages,
- **kwargs)
+ organization=self.ds.organization, notification_type=notification_type, name=name, description=description, messages=messages, **kwargs
+ )
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
- def create(
- self,
- name='',
- description='',
- notification_type='slack',
- organization=Organization,
- messages=not_provided,
- **kwargs):
+ def create(self, name='', description='', notification_type='slack', organization=Organization, messages=not_provided, **kwargs):
payload = self.create_payload(
- name=name,
- description=description,
- notification_type=notification_type,
- organization=organization,
- messages=messages,
- **kwargs)
- return self.update_identity(
- NotificationTemplates(
- self.connection).post(payload))
+ name=name, description=description, notification_type=notification_type, organization=organization, messages=messages, **kwargs
+ )
+ return self.update_identity(NotificationTemplates(self.connection).post(payload))
def associate(self, resource, job_result='any'):
"""Associates a NotificationTemplate with the provided resource"""
@@ -188,15 +124,11 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
def _associate(self, resource, job_result='any', disassociate=False):
if job_result not in job_results:
- raise ValueError(
- 'Unsupported job_result type "{0}". Please use one of {1}.' .format(
- job_result, job_results))
+ raise ValueError('Unsupported job_result type "{0}". Please use one of {1}.'.format(job_result, job_results))
result_attr = 'notification_templates_{0}'.format(job_result)
if result_attr not in resource.related:
- raise ValueError(
- 'Unsupported resource "{0}". Does not have a related {1} field.' .format(
- resource, result_attr))
+ raise ValueError('Unsupported resource "{0}". Does not have a related {1} field.'.format(resource, result_attr))
payload = dict(id=self.id)
if disassociate:
@@ -206,14 +138,19 @@ class NotificationTemplate(HasCopy, HasCreate, base.Base):
getattr(resource.related, result_attr).post(payload)
-page.register_page([resources.notification_template,
- (resources.notification_templates, 'post'),
- (resources.notification_template_copy, 'post'),
- resources.notification_template_any,
- resources.notification_template_started,
- resources.notification_template_error,
- resources.notification_template_success,
- resources.notification_template_approval], NotificationTemplate)
+page.register_page(
+ [
+ resources.notification_template,
+ (resources.notification_templates, 'post'),
+ (resources.notification_template_copy, 'post'),
+ resources.notification_template_any,
+ resources.notification_template_started,
+ resources.notification_template_error,
+ resources.notification_template_success,
+ resources.notification_template_approval,
+ ],
+ NotificationTemplate,
+)
class NotificationTemplates(page.PageList, NotificationTemplate):
@@ -221,14 +158,18 @@ class NotificationTemplates(page.PageList, NotificationTemplate):
pass
-page.register_page([resources.notification_templates,
- resources.related_notification_templates,
- resources.notification_templates_any,
- resources.notification_templates_started,
- resources.notification_templates_error,
- resources.notification_templates_success,
- resources.notification_templates_approvals],
- NotificationTemplates)
+page.register_page(
+ [
+ resources.notification_templates,
+ resources.related_notification_templates,
+ resources.notification_templates_any,
+ resources.notification_templates_started,
+ resources.notification_templates_error,
+ resources.notification_templates_success,
+ resources.notification_templates_approvals,
+ ],
+ NotificationTemplates,
+)
class NotificationTemplateCopy(base.Base):
@@ -244,6 +185,4 @@ class NotificationTemplateTest(base.Base):
pass
-page.register_page(
- resources.notification_template_test,
- NotificationTemplateTest)
+page.register_page(resources.notification_template_test, NotificationTemplateTest)
diff --git a/awxkit/awxkit/api/pages/notifications.py b/awxkit/awxkit/api/pages/notifications.py
index 0342f3565f..f4ee63313e 100644
--- a/awxkit/awxkit/api/pages/notifications.py
+++ b/awxkit/awxkit/api/pages/notifications.py
@@ -6,10 +6,8 @@ from . import page
class Notification(HasStatus, base.Base):
-
def __str__(self):
- items = ['id', 'notification_type', 'status', 'error', 'notifications_sent',
- 'subject', 'recipients']
+ items = ['id', 'notification_type', 'status', 'error', 'notifications_sent', 'subject', 'recipients']
info = []
for item in [x for x in items if hasattr(self, x)]:
info.append('{0}:{1}'.format(item, getattr(self, item)))
@@ -40,13 +38,10 @@ page.register_page(resources.notification, Notification)
class Notifications(page.PageList, Notification):
-
def wait_until_count(self, count, interval=10, timeout=60, **kw):
"""Poll notifications page until it is populated with `count` number of notifications."""
- poll_until(lambda: getattr(self.get(), 'count') == count,
- interval=interval, timeout=timeout, **kw)
+ poll_until(lambda: getattr(self.get(), 'count') == count, interval=interval, timeout=timeout, **kw)
return self
-page.register_page([resources.notifications,
- resources.related_notifications], Notifications)
+page.register_page([resources.notifications, resources.related_notifications], Notifications)
diff --git a/awxkit/awxkit/api/pages/organizations.py b/awxkit/awxkit/api/pages/organizations.py
index b03c9e6a3a..4ef9b8a26e 100644
--- a/awxkit/awxkit/api/pages/organizations.py
+++ b/awxkit/awxkit/api/pages/organizations.py
@@ -26,22 +26,27 @@ class Organization(HasCreate, HasInstanceGroups, HasNotifications, base.Base):
if isinstance(credential, page.Page):
credential = credential.json
with suppress(exc.NoContent):
- self.related.galaxy_credentials.post({
- "id": credential.id,
- })
+ self.related.galaxy_credentials.post(
+ {
+ "id": credential.id,
+ }
+ )
def remove_galaxy_credential(self, credential):
if isinstance(credential, page.Page):
credential = credential.json
with suppress(exc.NoContent):
- self.related.galaxy_credentials.post({
- "id": credential.id,
- "disassociate": True,
- })
+ self.related.galaxy_credentials.post(
+ {
+ "id": credential.id,
+ "disassociate": True,
+ }
+ )
def payload(self, **kwargs):
- payload = PseudoNamespace(name=kwargs.get('name') or 'Organization - {}'.format(random_title()),
- description=kwargs.get('description') or random_title(10))
+ payload = PseudoNamespace(
+ name=kwargs.get('name') or 'Organization - {}'.format(random_title()), description=kwargs.get('description') or random_title(10)
+ )
payload = set_payload_foreign_key_args(payload, ('default_environment',), kwargs)
@@ -57,8 +62,7 @@ class Organization(HasCreate, HasInstanceGroups, HasNotifications, base.Base):
return self.update_identity(Organizations(self.connection).post(payload))
-page.register_page([resources.organization,
- (resources.organizations, 'post')], Organization)
+page.register_page([resources.organization, (resources.organizations, 'post')], Organization)
class Organizations(page.PageList, Organization):
@@ -66,6 +70,4 @@ class Organizations(page.PageList, Organization):
pass
-page.register_page([resources.organizations,
- resources.user_organizations,
- resources.project_organizations], Organizations)
+page.register_page([resources.organizations, resources.user_organizations, resources.project_organizations], Organizations)
diff --git a/awxkit/awxkit/api/pages/page.py b/awxkit/awxkit/api/pages/page.py
index 3ee1c38490..82872fe6f1 100644
--- a/awxkit/awxkit/api/pages/page.py
+++ b/awxkit/awxkit/api/pages/page.py
@@ -6,15 +6,7 @@ import re
from requests import Response
import http.client as http
-from awxkit.utils import (
- PseudoNamespace,
- is_relative_endpoint,
- are_same_endpoint,
- super_dir_set,
- suppress,
- is_list_or_tuple,
- to_str
-)
+from awxkit.utils import PseudoNamespace, is_relative_endpoint, are_same_endpoint, super_dir_set, suppress, is_list_or_tuple, to_str
from awxkit.api import utils
from awxkit.api.client import Connection
from awxkit.api.registry import URLRegistry
@@ -41,17 +33,11 @@ def is_license_invalid(response):
def is_license_exceeded(response):
- if re.match(
- r".*license range of.*instances has been exceeded.*",
- response.text):
+ if re.match(r".*license range of.*instances has been exceeded.*", response.text):
return True
- if re.match(
- r".*License count of.*instances has been reached.*",
- response.text):
+ if re.match(r".*License count of.*instances has been reached.*", response.text):
return True
- if re.match(
- r".*License count of.*instances has been exceeded.*",
- response.text):
+ if re.match(r".*License count of.*instances has been exceeded.*", response.text):
return True
if re.match(r".*License has expired.*", response.text):
return True
@@ -67,6 +53,7 @@ def is_duplicate_error(response):
def register_page(urls, page_cls):
if not _page_registry.default:
from awxkit.api.pages import Base
+
_page_registry.setdefault(Base)
if not is_list_or_tuple(urls):
@@ -108,32 +95,23 @@ class Page(object):
if 'endpoint' in kw:
self.endpoint = kw['endpoint']
- self.connection = connection or Connection(
- config.base_url, kw.get(
- 'verify', not config.assume_untrusted))
+ self.connection = connection or Connection(config.base_url, kw.get('verify', not config.assume_untrusted))
self.r = kw.get('r', None)
- self.json = kw.get(
- 'json', objectify_response_json(
- self.r) if self.r else {})
+ self.json = kw.get('json', objectify_response_json(self.r) if self.r else {})
self.last_elapsed = kw.get('last_elapsed', None)
def __getattr__(self, name):
if 'json' in self.__dict__ and name in self.json:
value = self.json[name]
- if not isinstance(
- value,
- TentativePage) and is_relative_endpoint(value):
+ if not isinstance(value, TentativePage) and is_relative_endpoint(value):
value = TentativePage(value, self.connection)
elif isinstance(value, dict):
for key, item in value.items():
- if not isinstance(
- item, TentativePage) and is_relative_endpoint(item):
+ if not isinstance(item, TentativePage) and is_relative_endpoint(item):
value[key] = TentativePage(item, self.connection)
return value
- raise AttributeError(
- "{!r} object has no attribute {!r}".format(
- self.__class__.__name__, name))
+ raise AttributeError("{!r} object has no attribute {!r}".format(self.__class__.__name__, name))
def __setattr__(self, name, value):
if 'json' in self.__dict__ and name in self.json:
@@ -200,20 +178,15 @@ class Page(object):
text = response.text
if len(text) > 1024:
text = text[:1024] + '... <<< Truncated >>> ...'
- log.debug(
- "Unable to parse JSON response ({0.status_code}): {1} - '{2}'".format(response, e, text))
+ log.debug("Unable to parse JSON response ({0.status_code}): {1} - '{2}'".format(response, e, text))
- exc_str = "%s (%s) received" % (
- http.responses[response.status_code], response.status_code)
+ exc_str = "%s (%s) received" % (http.responses[response.status_code], response.status_code)
exception = exception_from_status_code(response.status_code)
if exception:
raise exception(exc_str, data)
- if response.status_code in (
- http.OK,
- http.CREATED,
- http.ACCEPTED):
+ if response.status_code in (http.OK, http.CREATED, http.ACCEPTED):
# Not all JSON responses include a URL. Grab it from the request
# object, if needed.
@@ -232,13 +205,7 @@ class Page(object):
return self
registered_type = get_registered_page(request_path, request_method)
- return registered_type(
- self.connection,
- endpoint=endpoint,
- json=data,
- last_elapsed=response.elapsed,
- r=response,
- ds=ds)
+ return registered_type(self.connection, endpoint=endpoint, json=data, last_elapsed=response.elapsed, r=response, ds=ds)
elif response.status_code == http.FORBIDDEN:
if is_license_invalid(response):
@@ -341,14 +308,16 @@ class Page(object):
return natural_key
-_exception_map = {http.NO_CONTENT: exc.NoContent,
- http.NOT_FOUND: exc.NotFound,
- http.INTERNAL_SERVER_ERROR: exc.InternalServerError,
- http.BAD_GATEWAY: exc.BadGateway,
- http.METHOD_NOT_ALLOWED: exc.MethodNotAllowed,
- http.UNAUTHORIZED: exc.Unauthorized,
- http.PAYMENT_REQUIRED: exc.PaymentRequired,
- http.CONFLICT: exc.Conflict}
+_exception_map = {
+ http.NO_CONTENT: exc.NoContent,
+ http.NOT_FOUND: exc.NotFound,
+ http.INTERNAL_SERVER_ERROR: exc.InternalServerError,
+ http.BAD_GATEWAY: exc.BadGateway,
+ http.METHOD_NOT_ALLOWED: exc.MethodNotAllowed,
+ http.UNAUTHORIZED: exc.Unauthorized,
+ http.PAYMENT_REQUIRED: exc.PaymentRequired,
+ http.CONFLICT: exc.Conflict,
+}
def exception_from_status_code(status_code):
@@ -380,12 +349,7 @@ class PageList(object):
registered_type = self.__item_class__
else:
registered_type = get_registered_page(endpoint)
- items.append(
- registered_type(
- self.connection,
- endpoint=endpoint,
- json=item,
- r=self.r))
+ items.append(registered_type(self.connection, endpoint=endpoint, json=item, r=self.r))
return items
def go_to_next(self):
@@ -407,7 +371,6 @@ class PageList(object):
class TentativePage(str):
-
def __new__(cls, endpoint, connection):
return super(TentativePage, cls).__new__(cls, to_str(endpoint))
@@ -416,10 +379,7 @@ class TentativePage(str):
self.connection = connection
def _create(self):
- return get_registered_page(
- self.endpoint)(
- self.connection,
- endpoint=self.endpoint)
+ return get_registered_page(self.endpoint)(self.connection, endpoint=self.endpoint)
def get(self, **params):
return self._create().get(**params)
@@ -436,21 +396,15 @@ class TentativePage(str):
page = None
# look up users by username not name
if 'users' in self:
- assert query_parameters.get(
- 'username'), 'For this resource, you must call this method with a "username" to look up the object by'
+ assert query_parameters.get('username'), 'For this resource, you must call this method with a "username" to look up the object by'
page = self.get(username=query_parameters['username'])
else:
- assert query_parameters.get(
- 'name'), 'For this resource, you must call this method with a "name" to look up the object by'
+ assert query_parameters.get('name'), 'For this resource, you must call this method with a "name" to look up the object by'
if query_parameters.get('organization'):
if isinstance(query_parameters.get('organization'), int):
- page = self.get(
- name=query_parameters['name'],
- organization=query_parameters.get('organization'))
+ page = self.get(name=query_parameters['name'], organization=query_parameters.get('organization'))
else:
- page = self.get(
- name=query_parameters['name'],
- organization=query_parameters.get('organization').id)
+ page = self.get(name=query_parameters['name'], organization=query_parameters.get('organization').id)
else:
page = self.get(name=query_parameters['name'])
if page and page.results:
@@ -476,13 +430,9 @@ class TentativePage(str):
if query_parameters.get('name'):
if query_parameters.get('organization'):
if isinstance(query_parameters.get('organization'), int):
- page = self.get(
- name=query_parameters['name'],
- organization=query_parameters.get('organization'))
+ page = self.get(name=query_parameters['name'], organization=query_parameters.get('organization'))
else:
- page = self.get(
- name=query_parameters['name'],
- organization=query_parameters.get('organization').id)
+ page = self.get(name=query_parameters['name'], organization=query_parameters.get('organization').id)
else:
page = self.get(name=query_parameters['name'])
diff --git a/awxkit/awxkit/api/pages/projects.py b/awxkit/awxkit/api/pages/projects.py
index 047d471c8f..b00109f61b 100644
--- a/awxkit/awxkit/api/pages/projects.py
+++ b/awxkit/awxkit/api/pages/projects.py
@@ -18,13 +18,11 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
def payload(self, organization, scm_type='git', **kwargs):
payload = PseudoNamespace(
- name=kwargs.get('name') or 'Project - {}'.format(
- random_title()),
+ name=kwargs.get('name') or 'Project - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
scm_type=scm_type,
- scm_url=kwargs.get('scm_url') or config.project_urls.get(
- scm_type,
- ''))
+ scm_url=kwargs.get('scm_url') or config.project_urls.get(scm_type, ''),
+ )
if organization is not None:
payload.organization = organization.id
@@ -40,43 +38,25 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
'scm_update_cache_timeout',
'scm_update_on_launch',
'scm_refspec',
- 'allow_override')
+ 'allow_override',
+ )
update_payload(payload, fields, kwargs)
payload = set_payload_foreign_key_args(payload, ('execution_environment', 'default_environment'), kwargs)
return payload
- def create_payload(
- self,
- name='',
- description='',
- scm_type='git',
- scm_url='',
- scm_branch='',
- organization=Organization,
- credential=None,
- **kwargs):
+ def create_payload(self, name='', description='', scm_type='git', scm_url='', scm_branch='', organization=Organization, credential=None, **kwargs):
if credential:
if isinstance(credential, Credential):
- if credential.ds.credential_type.namespace not in (
- 'scm', 'insights'):
+ if credential.ds.credential_type.namespace not in ('scm', 'insights'):
credential = None # ignore incompatible credential from HasCreate dependency injection
elif credential in (Credential,):
- credential = (
- Credential, dict(
- credential_type=(
- True, dict(
- kind='scm'))))
+ credential = (Credential, dict(credential_type=(True, dict(kind='scm'))))
elif credential is True:
- credential = (
- Credential, dict(
- credential_type=(
- True, dict(
- kind='scm'))))
+ credential = (Credential, dict(credential_type=(True, dict(kind='scm'))))
- self.create_and_update_dependencies(
- *filter_by_class((credential, Credential), (organization, Organization)))
+ self.create_and_update_dependencies(*filter_by_class((credential, Credential), (organization, Organization)))
credential = self.ds.credential if credential else None
organization = self.ds.organization if organization else None
@@ -89,20 +69,12 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
scm_url=scm_url,
scm_branch=scm_branch,
credential=credential,
- **kwargs)
+ **kwargs
+ )
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
- def create(
- self,
- name='',
- description='',
- scm_type='git',
- scm_url='',
- scm_branch='',
- organization=Organization,
- credential=None,
- **kwargs):
+ def create(self, name='', description='', scm_type='git', scm_url='', scm_branch='', organization=Organization, credential=None, **kwargs):
payload = self.create_payload(
name=name,
description=description,
@@ -111,7 +83,8 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
scm_branch=scm_branch,
organization=organization,
credential=credential,
- **kwargs)
+ **kwargs
+ )
self.update_identity(Projects(self.connection).post(payload))
if kwargs.get('wait', True):
@@ -127,25 +100,20 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
update_pg = self.get_related('update')
# assert can_update == True
- assert update_pg.can_update, \
- "The specified project (id:%s) is not able to update (can_update:%s)" % \
- (self.id, update_pg.can_update)
+ assert update_pg.can_update, "The specified project (id:%s) is not able to update (can_update:%s)" % (self.id, update_pg.can_update)
# start the update
result = update_pg.post()
# assert JSON response
- assert 'project_update' in result.json, \
- "Unexpected JSON response when starting an project_update.\n%s" % \
- json.dumps(result.json, indent=2)
+ assert 'project_update' in result.json, "Unexpected JSON response when starting an project_update.\n%s" % json.dumps(result.json, indent=2)
# locate and return the specific update
- jobs_pg = self.get_related(
- 'project_updates',
- id=result.json['project_update'])
- assert jobs_pg.count == 1, \
- "An project_update started (id:%s) but job not found in response at %s/inventory_updates/" % \
- (result.json['project_update'], self.url)
+ jobs_pg = self.get_related('project_updates', id=result.json['project_update'])
+ assert jobs_pg.count == 1, "An project_update started (id:%s) but job not found in response at %s/inventory_updates/" % (
+ result.json['project_update'],
+ self.url,
+ )
return jobs_pg.results[0]
@property
@@ -154,13 +122,10 @@ class Project(HasCopy, HasCreate, HasNotifications, UnifiedJobTemplate):
0) scm_type != ""
1) unified_job_template.is_successful
"""
- return self.scm_type != "" and \
- super(Project, self).is_successful
+ return self.scm_type != "" and super(Project, self).is_successful
-page.register_page([resources.project,
- (resources.projects, 'post'),
- (resources.project_copy, 'post')], Project)
+page.register_page([resources.project, (resources.projects, 'post'), (resources.project_copy, 'post')], Project)
class Projects(page.PageList, Project):
@@ -168,8 +133,7 @@ class Projects(page.PageList, Project):
pass
-page.register_page([resources.projects,
- resources.related_projects], Projects)
+page.register_page([resources.projects, resources.related_projects], Projects)
class ProjectUpdate(UnifiedJob):
@@ -185,8 +149,7 @@ class ProjectUpdates(page.PageList, ProjectUpdate):
pass
-page.register_page([resources.project_updates,
- resources.project_project_updates], ProjectUpdates)
+page.register_page([resources.project_updates, resources.project_project_updates], ProjectUpdates)
class ProjectUpdateLaunch(base.Base):
diff --git a/awxkit/awxkit/api/pages/roles.py b/awxkit/awxkit/api/pages/roles.py
index 2b317dece1..07f0790b60 100644
--- a/awxkit/awxkit/api/pages/roles.py
+++ b/awxkit/awxkit/api/pages/roles.py
@@ -18,15 +18,11 @@ class Role(base.Base):
cache = page.PageCache()
natural_key = super(Role, self).get_natural_key(cache=cache)
- related_objs = [
- related for name, related in self.related.items()
- if name not in ('users', 'teams')
- ]
+ related_objs = [related for name, related in self.related.items() if name not in ('users', 'teams')]
if related_objs:
related_endpoint = cache.get_page(related_objs[0])
if related_endpoint is None:
- log.error("Unable to obtain content_object %s for role %s",
- related_objs[0], self.endpoint)
+ log.error("Unable to obtain content_object %s for role %s", related_objs[0], self.endpoint)
return None
natural_key['content_object'] = related_endpoint.get_natural_key(cache=cache)
@@ -41,6 +37,4 @@ class Roles(page.PageList, Role):
pass
-page.register_page([resources.roles,
- resources.related_roles,
- resources.related_object_roles], Roles)
+page.register_page([resources.roles, resources.related_roles, resources.related_object_roles], Roles)
diff --git a/awxkit/awxkit/api/pages/schedules.py b/awxkit/awxkit/api/pages/schedules.py
index 8603b2ad5b..d1520a0be4 100644
--- a/awxkit/awxkit/api/pages/schedules.py
+++ b/awxkit/awxkit/api/pages/schedules.py
@@ -11,12 +11,10 @@ class Schedule(UnifiedJob):
NATURAL_KEY = ('unified_job_template', 'name')
-page.register_page([resources.schedule,
- resources.related_schedule], Schedule)
+page.register_page([resources.schedule, resources.related_schedule], Schedule)
class Schedules(page.PageList, Schedule):
-
def get_zoneinfo(self):
return SchedulesZoneInfo(self.connection).get()
@@ -33,8 +31,7 @@ class Schedules(page.PageList, Schedule):
self.related.credentials.post(dict(id=cred.id, disassociate=True))
-page.register_page([resources.schedules,
- resources.related_schedules], Schedules)
+page.register_page([resources.schedules, resources.related_schedules], Schedules)
class SchedulesPreview(base.Base):
@@ -46,7 +43,6 @@ page.register_page(((resources.schedules_preview, 'post'),), SchedulesPreview)
class SchedulesZoneInfo(base.Base):
-
def __getitem__(self, idx):
return self.json[idx]
diff --git a/awxkit/awxkit/api/pages/settings.py b/awxkit/awxkit/api/pages/settings.py
index fbf133dc02..59168d40c5 100644
--- a/awxkit/awxkit/api/pages/settings.py
+++ b/awxkit/awxkit/api/pages/settings.py
@@ -8,27 +8,31 @@ class Setting(base.Base):
pass
-page.register_page([resources.setting,
- resources.settings_all,
- resources.settings_authentication,
- resources.settings_changed,
- resources.settings_github,
- resources.settings_github_org,
- resources.settings_github_team,
- resources.settings_google_oauth2,
- resources.settings_jobs,
- resources.settings_ldap,
- resources.settings_radius,
- resources.settings_saml,
- resources.settings_system,
- resources.settings_tacacsplus,
- resources.settings_ui,
- resources.settings_user,
- resources.settings_user_defaults], Setting)
+page.register_page(
+ [
+ resources.setting,
+ resources.settings_all,
+ resources.settings_authentication,
+ resources.settings_changed,
+ resources.settings_github,
+ resources.settings_github_org,
+ resources.settings_github_team,
+ resources.settings_google_oauth2,
+ resources.settings_jobs,
+ resources.settings_ldap,
+ resources.settings_radius,
+ resources.settings_saml,
+ resources.settings_system,
+ resources.settings_tacacsplus,
+ resources.settings_ui,
+ resources.settings_user,
+ resources.settings_user_defaults,
+ ],
+ Setting,
+)
class Settings(page.PageList, Setting):
-
def get_endpoint(self, endpoint):
"""Helper method used to navigate to a specific settings endpoint.
(Pdb) settings_pg.get_endpoint('all')
diff --git a/awxkit/awxkit/api/pages/subscriptions.py b/awxkit/awxkit/api/pages/subscriptions.py
index 749776c000..8be55ee6fd 100644
--- a/awxkit/awxkit/api/pages/subscriptions.py
+++ b/awxkit/awxkit/api/pages/subscriptions.py
@@ -3,7 +3,6 @@ from . import page
class Subscriptions(page.Page):
-
def get_possible_licenses(self, **kwargs):
return self.post(json=kwargs).json
diff --git a/awxkit/awxkit/api/pages/survey_spec.py b/awxkit/awxkit/api/pages/survey_spec.py
index 28a870cf84..e9ea99bbff 100644
--- a/awxkit/awxkit/api/pages/survey_spec.py
+++ b/awxkit/awxkit/api/pages/survey_spec.py
@@ -5,7 +5,6 @@ from awxkit.api.resources import resources
class SurveySpec(base.Base):
-
def get_variable_default(self, var):
for item in self.spec:
if item.get('variable') == var:
@@ -26,5 +25,4 @@ class SurveySpec(base.Base):
return required_vars
-page.register_page([resources.job_template_survey_spec,
- resources.workflow_job_template_survey_spec], SurveySpec)
+page.register_page([resources.job_template_survey_spec, resources.workflow_job_template_survey_spec], SurveySpec)
diff --git a/awxkit/awxkit/api/pages/system_job_templates.py b/awxkit/awxkit/api/pages/system_job_templates.py
index 0c5f13d7cd..cc0d28857a 100644
--- a/awxkit/awxkit/api/pages/system_job_templates.py
+++ b/awxkit/awxkit/api/pages/system_job_templates.py
@@ -5,16 +5,13 @@ from . import page
class SystemJobTemplate(UnifiedJobTemplate, HasNotifications):
-
def launch(self, payload={}):
"""Launch the system_job_template using related->launch endpoint."""
result = self.related.launch.post(payload)
# return job
jobs_pg = self.get_related('jobs', id=result.json['system_job'])
- assert jobs_pg.count == 1, \
- "system_job_template launched (id:%s) but unable to find matching " \
- "job at %s/jobs/" % (result.json['job'], self.url)
+ assert jobs_pg.count == 1, "system_job_template launched (id:%s) but unable to find matching " "job at %s/jobs/" % (result.json['job'], self.url)
return jobs_pg.results[0]
diff --git a/awxkit/awxkit/api/pages/teams.py b/awxkit/awxkit/api/pages/teams.py
index cb5577b5b2..96fafb3341 100644
--- a/awxkit/awxkit/api/pages/teams.py
+++ b/awxkit/awxkit/api/pages/teams.py
@@ -20,9 +20,11 @@ class Team(HasCreate, base.Base):
self.related.users.post(user)
def payload(self, organization, **kwargs):
- payload = PseudoNamespace(name=kwargs.get('name') or 'Team - {}'.format(random_title()),
- description=kwargs.get('description') or random_title(10),
- organization=organization.id)
+ payload = PseudoNamespace(
+ name=kwargs.get('name') or 'Team - {}'.format(random_title()),
+ description=kwargs.get('description') or random_title(10),
+ organization=organization.id,
+ )
return payload
def create_payload(self, name='', description='', organization=Organization, **kwargs):
@@ -36,8 +38,7 @@ class Team(HasCreate, base.Base):
return self.update_identity(Teams(self.connection).post(payload))
-page.register_page([resources.team,
- (resources.teams, 'post')], Team)
+page.register_page([resources.team, (resources.teams, 'post')], Team)
class Teams(page.PageList, Team):
@@ -45,6 +46,4 @@ class Teams(page.PageList, Team):
pass
-page.register_page([resources.teams,
- resources.credential_owner_teams,
- resources.related_teams], Teams)
+page.register_page([resources.teams, resources.credential_owner_teams, resources.related_teams], Teams)
diff --git a/awxkit/awxkit/api/pages/unified_job_templates.py b/awxkit/awxkit/api/pages/unified_job_templates.py
index 286ca18ed7..22a7a70106 100644
--- a/awxkit/awxkit/api/pages/unified_job_templates.py
+++ b/awxkit/awxkit/api/pages/unified_job_templates.py
@@ -26,38 +26,19 @@ class UnifiedJobTemplate(HasStatus, base.Base):
# formatting issue where result_stdout contained '%s'. This later caused
# a python traceback when attempting to display output from this
# method.
- items = [
- 'id',
- 'name',
- 'status',
- 'source',
- 'last_update_failed',
- 'last_updated',
- 'result_traceback',
- 'job_explanation',
- 'job_args']
+ items = ['id', 'name', 'status', 'source', 'last_update_failed', 'last_updated', 'result_traceback', 'job_explanation', 'job_args']
info = []
for item in [x for x in items if hasattr(self, x)]:
info.append('{0}:{1}'.format(item, getattr(self, item)))
output = '<{0.__class__.__name__} {1}>'.format(self, ', '.join(info))
return output.replace('%', '%%')
- def add_schedule(
- self,
- name='',
- description='',
- enabled=True,
- rrule=None,
- **kwargs):
+ def add_schedule(self, name='', description='', enabled=True, rrule=None, **kwargs):
if rrule is None:
rrule = "DTSTART:30180101T000000Z RRULE:FREQ=YEARLY;INTERVAL=1"
payload = dict(
- name=name or "{0} Schedule {1}".format(
- self.name,
- random_title()),
- description=description or random_title(10),
- enabled=enabled,
- rrule=str(rrule))
+ name=name or "{0} Schedule {1}".format(self.name, random_title()), description=description or random_title(10), enabled=enabled, rrule=str(rrule)
+ )
update_payload(payload, self.optional_schedule_fields, kwargs)
@@ -70,9 +51,7 @@ class UnifiedJobTemplate(HasStatus, base.Base):
2) not last_update_failed
3) last_updated
"""
- return super(
- UnifiedJobTemplate,
- self).is_successful and not self.last_update_failed and self.last_updated is not None
+ return super(UnifiedJobTemplate, self).is_successful and not self.last_update_failed and self.last_updated is not None
page.register_page(resources.unified_job_template, UnifiedJobTemplate)
diff --git a/awxkit/awxkit/api/pages/unified_jobs.py b/awxkit/awxkit/api/pages/unified_jobs.py
index 20c6175ed3..09dea1ebbb 100644
--- a/awxkit/awxkit/api/pages/unified_jobs.py
+++ b/awxkit/awxkit/api/pages/unified_jobs.py
@@ -21,8 +21,7 @@ class UnifiedJob(HasStatus, base.Base):
# NOTE: I use .replace('%', '%%') to workaround an odd string
# formatting issue where result_stdout contained '%s'. This later caused
# a python traceback when attempting to display output from this method.
- items = ['id', 'name', 'status', 'failed', 'result_stdout', 'result_traceback',
- 'job_explanation', 'job_args']
+ items = ['id', 'name', 'status', 'failed', 'result_stdout', 'result_traceback', 'job_explanation', 'job_args']
info = []
for item in [x for x in items if hasattr(self, x)]:
info.append('{0}:{1}'.format(item, getattr(self, item)))
@@ -32,9 +31,7 @@ class UnifiedJob(HasStatus, base.Base):
@property
def result_stdout(self):
if 'result_stdout' not in self.json and 'stdout' in self.related:
- return self.connection.get(
- self.related.stdout, query_parameters=dict(format='txt_download')
- ).content.decode()
+ return self.connection.get(self.related.stdout, query_parameters=dict(format='txt_download')).content.decode()
return self.json.result_stdout.decode()
def assert_text_in_stdout(self, expected_text, replace_spaces=None, replace_newlines=' '):
@@ -55,9 +52,7 @@ class UnifiedJob(HasStatus, base.Base):
stdout = stdout.replace(' ', replace_spaces)
if expected_text not in stdout:
pretty_stdout = pformat(stdout)
- raise AssertionError(
- 'Expected "{}", but it was not found in stdout. Full stdout:\n {}'.format(expected_text, pretty_stdout)
- )
+ raise AssertionError('Expected "{}", but it was not found in stdout. Full stdout:\n {}'.format(expected_text, pretty_stdout))
@property
def is_successful(self):
@@ -103,7 +98,7 @@ class UnifiedJob(HasStatus, base.Base):
# Race condition where job finishes between can_cancel
# check and post.
if not any("not allowed" in field for field in e.msg.values()):
- raise(e)
+ raise (e)
return self.get()
@property
@@ -114,6 +109,7 @@ class UnifiedJob(HasStatus, base.Base):
```assert dict(extra_var=extra_var_val) in unified_job.job_args```
If you need to ensure the job_args are of awx-provided format use raw unified_job.json.job_args.
"""
+
def attempt_yaml_load(arg):
try:
return yaml.safe_load(arg)
@@ -151,10 +147,7 @@ class UnifiedJob(HasStatus, base.Base):
if host_loc.startswith(expected_prefix):
return host_loc
raise RuntimeError(
- 'Could not find a controller private_data_dir for this job. '
- 'Searched for volume mount to {} inside of args {}'.format(
- expected_prefix, job_args
- )
+ 'Could not find a controller private_data_dir for this job. ' 'Searched for volume mount to {} inside of args {}'.format(expected_prefix, job_args)
)
@@ -163,7 +156,4 @@ class UnifiedJobs(page.PageList, UnifiedJob):
pass
-page.register_page([resources.unified_jobs,
- resources.instance_related_jobs,
- resources.instance_group_related_jobs,
- resources.schedules_jobs], UnifiedJobs)
+page.register_page([resources.unified_jobs, resources.instance_related_jobs, resources.instance_group_related_jobs, resources.schedules_jobs], UnifiedJobs)
diff --git a/awxkit/awxkit/api/pages/users.py b/awxkit/awxkit/api/pages/users.py
index 22ab78dd11..f8a4d9cc17 100644
--- a/awxkit/awxkit/api/pages/users.py
+++ b/awxkit/awxkit/api/pages/users.py
@@ -13,26 +13,13 @@ class User(HasCreate, base.Base):
def payload(self, **kwargs):
payload = PseudoNamespace(
- username=kwargs.get('username') or 'User-{}'.format(
- random_title(
- non_ascii=False)),
+ username=kwargs.get('username') or 'User-{}'.format(random_title(non_ascii=False)),
password=kwargs.get('password') or config.credentials.default.password,
- is_superuser=kwargs.get(
- 'is_superuser',
- False),
- is_system_auditor=kwargs.get(
- 'is_system_auditor',
- False),
- first_name=kwargs.get(
- 'first_name',
- random_title()),
- last_name=kwargs.get(
- 'last_name',
- random_title()),
- email=kwargs.get(
- 'email',
- '{}@example.com'.format(random_title(5, non_ascii=False))
- )
+ is_superuser=kwargs.get('is_superuser', False),
+ is_system_auditor=kwargs.get('is_system_auditor', False),
+ first_name=kwargs.get('first_name', random_title()),
+ last_name=kwargs.get('last_name', random_title()),
+ email=kwargs.get('email', '{}@example.com'.format(random_title(5, non_ascii=False))),
)
return payload
@@ -42,8 +29,7 @@ class User(HasCreate, base.Base):
return payload
def create(self, username='', password='', organization=None, **kwargs):
- payload = self.create_payload(
- username=username, password=password, **kwargs)
+ payload = self.create_payload(username=username, password=password, **kwargs)
self.password = payload.password
self.update_identity(Users(self.connection).post(payload))
@@ -54,8 +40,7 @@ class User(HasCreate, base.Base):
return self
-page.register_page([resources.user,
- (resources.users, 'post')], User)
+page.register_page([resources.user, (resources.users, 'post')], User)
class Users(page.PageList, User):
@@ -63,11 +48,9 @@ class Users(page.PageList, User):
pass
-page.register_page([resources.users,
- resources.organization_admins,
- resources.related_users,
- resources.credential_owner_users,
- resources.user_admin_organizations], Users)
+page.register_page(
+ [resources.users, resources.organization_admins, resources.related_users, resources.credential_owner_users, resources.user_admin_organizations], Users
+)
class Me(Users):
diff --git a/awxkit/awxkit/api/pages/workflow_approvals.py b/awxkit/awxkit/api/pages/workflow_approvals.py
index d4ededcdec..bd26aae7e3 100644
--- a/awxkit/awxkit/api/pages/workflow_approvals.py
+++ b/awxkit/awxkit/api/pages/workflow_approvals.py
@@ -5,7 +5,6 @@ from awxkit import exceptions
class WorkflowApproval(UnifiedJob):
-
def approve(self):
try:
self.related.approve.post()
diff --git a/awxkit/awxkit/api/pages/workflow_job_nodes.py b/awxkit/awxkit/api/pages/workflow_job_nodes.py
index 320618323a..4eabaebe16 100644
--- a/awxkit/awxkit/api/pages/workflow_job_nodes.py
+++ b/awxkit/awxkit/api/pages/workflow_job_nodes.py
@@ -5,7 +5,6 @@ from . import page
class WorkflowJobNode(base.Base):
-
def wait_for_job(self, interval=5, timeout=60, **kw):
"""Waits until node's job exists"""
adjusted_timeout = timeout - seconds_since_date_string(self.created)
@@ -30,8 +29,13 @@ class WorkflowJobNodes(page.PageList, WorkflowJobNode):
pass
-page.register_page([resources.workflow_job_nodes,
- resources.workflow_job_workflow_nodes,
- resources.workflow_job_node_always_nodes,
- resources.workflow_job_node_failure_nodes,
- resources.workflow_job_node_success_nodes], WorkflowJobNodes)
+page.register_page(
+ [
+ resources.workflow_job_nodes,
+ resources.workflow_job_workflow_nodes,
+ resources.workflow_job_node_always_nodes,
+ resources.workflow_job_node_failure_nodes,
+ resources.workflow_job_node_success_nodes,
+ ],
+ WorkflowJobNodes,
+)
diff --git a/awxkit/awxkit/api/pages/workflow_job_template_nodes.py b/awxkit/awxkit/api/pages/workflow_job_template_nodes.py
index 5494d6063b..8a68476030 100644
--- a/awxkit/awxkit/api/pages/workflow_job_template_nodes.py
+++ b/awxkit/awxkit/api/pages/workflow_job_template_nodes.py
@@ -15,12 +15,9 @@ class WorkflowJobTemplateNode(HasCreate, base.Base):
def payload(self, workflow_job_template, unified_job_template, **kwargs):
if not unified_job_template:
# May pass "None" to explicitly create an approval node
- payload = PseudoNamespace(
- workflow_job_template=workflow_job_template.id)
+ payload = PseudoNamespace(workflow_job_template=workflow_job_template.id)
else:
- payload = PseudoNamespace(
- workflow_job_template=workflow_job_template.id,
- unified_job_template=unified_job_template.id)
+ payload = PseudoNamespace(workflow_job_template=workflow_job_template.id, unified_job_template=unified_job_template.id)
optional_fields = (
'diff_mode',
@@ -33,7 +30,8 @@ class WorkflowJobTemplateNode(HasCreate, base.Base):
'verbosity',
'extra_data',
'identifier',
- 'all_parents_must_converge')
+ 'all_parents_must_converge',
+ )
update_payload(payload, optional_fields, kwargs)
@@ -42,45 +40,23 @@ class WorkflowJobTemplateNode(HasCreate, base.Base):
return payload
- def create_payload(
- self,
- workflow_job_template=WorkflowJobTemplate,
- unified_job_template=JobTemplate,
- **kwargs):
+ def create_payload(self, workflow_job_template=WorkflowJobTemplate, unified_job_template=JobTemplate, **kwargs):
if not unified_job_template:
self.create_and_update_dependencies(workflow_job_template)
- payload = self.payload(
- workflow_job_template=self.ds.workflow_job_template,
- unified_job_template=None,
- **kwargs)
+ payload = self.payload(workflow_job_template=self.ds.workflow_job_template, unified_job_template=None, **kwargs)
else:
- self.create_and_update_dependencies(
- workflow_job_template, unified_job_template)
- payload = self.payload(
- workflow_job_template=self.ds.workflow_job_template,
- unified_job_template=self.ds.unified_job_template,
- **kwargs)
+ self.create_and_update_dependencies(workflow_job_template, unified_job_template)
+ payload = self.payload(workflow_job_template=self.ds.workflow_job_template, unified_job_template=self.ds.unified_job_template, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
- def create(
- self,
- workflow_job_template=WorkflowJobTemplate,
- unified_job_template=JobTemplate,
- **kwargs):
- payload = self.create_payload(
- workflow_job_template=workflow_job_template,
- unified_job_template=unified_job_template,
- **kwargs)
- return self.update_identity(
- WorkflowJobTemplateNodes(
- self.connection).post(payload))
+ def create(self, workflow_job_template=WorkflowJobTemplate, unified_job_template=JobTemplate, **kwargs):
+ payload = self.create_payload(workflow_job_template=workflow_job_template, unified_job_template=unified_job_template, **kwargs)
+ return self.update_identity(WorkflowJobTemplateNodes(self.connection).post(payload))
def _add_node(self, endpoint, unified_job_template, **kwargs):
- node = endpoint.post(
- dict(unified_job_template=unified_job_template.id, **kwargs))
- node.create_and_update_dependencies(
- self.ds.workflow_job_template, unified_job_template)
+ node = endpoint.post(dict(unified_job_template=unified_job_template.id, **kwargs))
+ node.create_and_update_dependencies(self.ds.workflow_job_template, unified_job_template)
return node
def add_always_node(self, unified_job_template, **kwargs):
@@ -94,24 +70,18 @@ class WorkflowJobTemplateNode(HasCreate, base.Base):
def add_credential(self, credential):
with suppress(exc.NoContent):
- self.related.credentials.post(
- dict(id=credential.id, associate=True))
+ self.related.credentials.post(dict(id=credential.id, associate=True))
def remove_credential(self, credential):
with suppress(exc.NoContent):
- self.related.credentials.post(
- dict(id=credential.id, disassociate=True))
+ self.related.credentials.post(dict(id=credential.id, disassociate=True))
def remove_all_credentials(self):
for cred in self.related.credentials.get().results:
with suppress(exc.NoContent):
- self.related.credentials.post(
- dict(id=cred.id, disassociate=True))
+ self.related.credentials.post(dict(id=cred.id, disassociate=True))
- def make_approval_node(
- self,
- **kwargs
- ):
+ def make_approval_node(self, **kwargs):
if 'name' not in kwargs:
kwargs['name'] = 'approval node {}'.format(random_title())
self.related.create_approval_template.post(kwargs)
@@ -122,10 +92,10 @@ class WorkflowJobTemplateNode(HasCreate, base.Base):
return candidates.results.pop()
-page.register_page([resources.workflow_job_template_node,
- (resources.workflow_job_template_nodes, 'post'),
- (resources.workflow_job_template_workflow_nodes, 'post')],
- WorkflowJobTemplateNode)
+page.register_page(
+ [resources.workflow_job_template_node, (resources.workflow_job_template_nodes, 'post'), (resources.workflow_job_template_workflow_nodes, 'post')],
+ WorkflowJobTemplateNode,
+)
class WorkflowJobTemplateNodes(page.PageList, WorkflowJobTemplateNode):
@@ -133,9 +103,13 @@ class WorkflowJobTemplateNodes(page.PageList, WorkflowJobTemplateNode):
pass
-page.register_page([resources.workflow_job_template_nodes,
- resources.workflow_job_template_workflow_nodes,
- resources.workflow_job_template_node_always_nodes,
- resources.workflow_job_template_node_failure_nodes,
- resources.workflow_job_template_node_success_nodes],
- WorkflowJobTemplateNodes)
+page.register_page(
+ [
+ resources.workflow_job_template_nodes,
+ resources.workflow_job_template_workflow_nodes,
+ resources.workflow_job_template_node_always_nodes,
+ resources.workflow_job_template_node_failure_nodes,
+ resources.workflow_job_template_node_success_nodes,
+ ],
+ WorkflowJobTemplateNodes,
+)
diff --git a/awxkit/awxkit/api/pages/workflow_job_templates.py b/awxkit/awxkit/api/pages/workflow_job_templates.py
index 17f3b56342..b8254f34d9 100644
--- a/awxkit/awxkit/api/pages/workflow_job_templates.py
+++ b/awxkit/awxkit/api/pages/workflow_job_templates.py
@@ -26,15 +26,14 @@ class WorkflowJobTemplate(HasCopy, HasCreate, HasNotifications, HasSurvey, Unifi
# return job
jobs_pg = self.related.workflow_jobs.get(id=result.workflow_job)
if jobs_pg.count != 1:
- msg = "workflow_job_template launched (id:{}) but job not found in response at {}/workflow_jobs/".format(
- result.json['workflow_job'], self.url
- )
+ msg = "workflow_job_template launched (id:{}) but job not found in response at {}/workflow_jobs/".format(result.json['workflow_job'], self.url)
raise exc.UnexpectedAWXState(msg)
return jobs_pg.results[0]
def payload(self, **kwargs):
- payload = PseudoNamespace(name=kwargs.get('name') or 'WorkflowJobTemplate - {}'.format(random_title()),
- description=kwargs.get('description') or random_title(10))
+ payload = PseudoNamespace(
+ name=kwargs.get('name') or 'WorkflowJobTemplate - {}'.format(random_title()), description=kwargs.get('description') or random_title(10)
+ )
optional_fields = (
"allow_simultaneous",
@@ -91,9 +90,9 @@ class WorkflowJobTemplate(HasCopy, HasCreate, HasNotifications, HasSurvey, Unifi
self.related.labels.post(label)
-page.register_page([resources.workflow_job_template,
- (resources.workflow_job_templates, 'post'),
- (resources.workflow_job_template_copy, 'post')], WorkflowJobTemplate)
+page.register_page(
+ [resources.workflow_job_template, (resources.workflow_job_templates, 'post'), (resources.workflow_job_template_copy, 'post')], WorkflowJobTemplate
+)
class WorkflowJobTemplates(page.PageList, WorkflowJobTemplate):
@@ -101,8 +100,7 @@ class WorkflowJobTemplates(page.PageList, WorkflowJobTemplate):
pass
-page.register_page([resources.workflow_job_templates,
- resources.related_workflow_job_templates], WorkflowJobTemplates)
+page.register_page([resources.workflow_job_templates, resources.related_workflow_job_templates], WorkflowJobTemplates)
class WorkflowJobTemplateLaunch(base.Base):
diff --git a/awxkit/awxkit/api/pages/workflow_jobs.py b/awxkit/awxkit/api/pages/workflow_jobs.py
index 36afc94460..200eb0ef30 100644
--- a/awxkit/awxkit/api/pages/workflow_jobs.py
+++ b/awxkit/awxkit/api/pages/workflow_jobs.py
@@ -4,7 +4,6 @@ from . import page
class WorkflowJob(UnifiedJob):
-
def __str__(self):
# TODO: Update after endpoint's fields are finished filling out
return super(UnifiedJob, self).__str__()
@@ -56,7 +55,4 @@ class WorkflowJobs(page.PageList, WorkflowJob):
pass
-page.register_page([resources.workflow_jobs,
- resources.workflow_job_template_jobs,
- resources.job_template_slice_workflow_jobs],
- WorkflowJobs)
+page.register_page([resources.workflow_jobs, resources.workflow_job_template_jobs, resources.job_template_slice_workflow_jobs], WorkflowJobs)
diff --git a/awxkit/awxkit/api/registry.py b/awxkit/awxkit/api/registry.py
index 67d6bb23b8..c7cea080b9 100644
--- a/awxkit/awxkit/api/registry.py
+++ b/awxkit/awxkit/api/registry.py
@@ -8,7 +8,6 @@ log = logging.getLogger(__name__)
class URLRegistry(object):
-
def __init__(self):
self.store = defaultdict(dict)
self.default = {}
@@ -81,8 +80,7 @@ class URLRegistry(object):
if method_pattern.pattern == not_provided:
exc_msg = '"{0.pattern}" already has methodless registration.'.format(url_pattern)
else:
- exc_msg = ('"{0.pattern}" already has registered method "{1.pattern}"'
- .format(url_pattern, method_pattern))
+ exc_msg = '"{0.pattern}" already has registered method "{1.pattern}"'.format(url_pattern, method_pattern)
raise TypeError(exc_msg)
self.store[url_pattern][method_pattern] = resource
diff --git a/awxkit/awxkit/api/resources.py b/awxkit/awxkit/api/resources.py
index 997ada1e70..573c96598f 100644
--- a/awxkit/awxkit/api/resources.py
+++ b/awxkit/awxkit/api/resources.py
@@ -1,4 +1,3 @@
-
class Resources(object):
_activity = r'activity_stream/\d+/'
diff --git a/awxkit/awxkit/api/utils.py b/awxkit/awxkit/api/utils.py
index a3e6739b26..119d68aa41 100644
--- a/awxkit/awxkit/api/utils.py
+++ b/awxkit/awxkit/api/utils.py
@@ -15,12 +15,11 @@ def freeze(key):
def parse_description(desc):
options = {}
- for line in desc[desc.index('POST'):].splitlines():
+ for line in desc[desc.index('POST') :].splitlines():
match = descRE.match(line)
if not match:
continue
- options[match.group(1)] = {'type': match.group(2),
- 'required': match.group(3) == 'required'}
+ options[match.group(1)] = {'type': match.group(2), 'required': match.group(3) == 'required'}
return options
@@ -45,6 +44,5 @@ def get_post_fields(page, cache):
if 'POST' in options_page.json['actions']:
return options_page.json['actions']['POST']
else:
- log.warning(
- "Insufficient privileges on %s, inferring POST fields from description.", options_page.endpoint)
+ log.warning("Insufficient privileges on %s, inferring POST fields from description.", options_page.endpoint)
return parse_description(options_page.json['description'])
diff --git a/awxkit/awxkit/awx/inventory.py b/awxkit/awxkit/awx/inventory.py
index b4872eb3e8..61a31c5754 100644
--- a/awxkit/awxkit/awx/inventory.py
+++ b/awxkit/awxkit/awx/inventory.py
@@ -17,13 +17,14 @@ def upload_inventory(ansible_runner, nhosts=10, ini=False):
copy_content = '''#!/bin/bash
cat <<EOF
%s
-EOF''' % json_inventory(nhosts)
+EOF''' % json_inventory(
+ nhosts
+ )
# Copy script to test system
contacted = ansible_runner.copy(dest=copy_dest, force=True, mode=copy_mode, content=copy_content)
for result in contacted.values():
- assert not result.get('failed', False), \
- "Failed to create inventory file: %s" % result
+ assert not result.get('failed', False), "Failed to create inventory file: %s" % result
return copy_dest
@@ -49,8 +50,7 @@ def generate_inventory(nhosts=100):
group_by_10s = 'group-%07dX.example.com' % (n / 10)
group_by_100s = 'group-%06dXX.example.com' % (n / 100)
group_by_1000s = 'group-%05dXXX.example.com' % (n / 1000)
- for group in [group_evens_odds, group_threes, group_fours, group_fives, group_sixes, group_sevens,
- group_eights, group_nines, group_tens, group_by_10s]:
+ for group in [group_evens_odds, group_threes, group_fours, group_fives, group_sixes, group_sevens, group_eights, group_nines, group_tens, group_by_10s]:
if not group:
continue
if group in inv_list:
@@ -58,11 +58,9 @@ def generate_inventory(nhosts=100):
else:
inv_list[group] = {'hosts': [hostname], 'children': [], 'vars': {'group_prefix': group.split('.')[0]}}
if group_by_1000s not in inv_list:
- inv_list[group_by_1000s] = {'hosts': [], 'children': [],
- 'vars': {'group_prefix': group_by_1000s.split('.')[0]}}
+ inv_list[group_by_1000s] = {'hosts': [], 'children': [], 'vars': {'group_prefix': group_by_1000s.split('.')[0]}}
if group_by_100s not in inv_list:
- inv_list[group_by_100s] = {'hosts': [], 'children': [],
- 'vars': {'group_prefix': group_by_100s.split('.')[0]}}
+ inv_list[group_by_100s] = {'hosts': [], 'children': [], 'vars': {'group_prefix': group_by_100s.split('.')[0]}}
if group_by_100s not in inv_list[group_by_1000s]['children']:
inv_list[group_by_1000s]['children'].append(group_by_100s)
if group_by_10s not in inv_list[group_by_100s]['children']:
diff --git a/awxkit/awxkit/awx/utils.py b/awxkit/awxkit/awx/utils.py
index d25e555ad6..f4aefeeca3 100644
--- a/awxkit/awxkit/awx/utils.py
+++ b/awxkit/awxkit/awx/utils.py
@@ -31,9 +31,22 @@ def _delete_all(endpoint):
def delete_all(v):
- for endpoint in (v.unified_jobs, v.job_templates, v.workflow_job_templates, v.notification_templates,
- v.projects, v.inventory, v.hosts, v.inventory_scripts, v.labels, v.credentials,
- v.teams, v.users, v.organizations, v.schedules):
+ for endpoint in (
+ v.unified_jobs,
+ v.job_templates,
+ v.workflow_job_templates,
+ v.notification_templates,
+ v.projects,
+ v.inventory,
+ v.hosts,
+ v.inventory_scripts,
+ v.labels,
+ v.credentials,
+ v.teams,
+ v.users,
+ v.organizations,
+ v.schedules,
+ ):
_delete_all(endpoint)
diff --git a/awxkit/awxkit/cli/__init__.py b/awxkit/awxkit/cli/__init__.py
index 6b20792eef..a73f18510d 100644
--- a/awxkit/awxkit/cli/__init__.py
+++ b/awxkit/awxkit/cli/__init__.py
@@ -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('')
diff --git a/awxkit/awxkit/cli/client.py b/awxkit/awxkit/cli/client.py
index f14d6df135..46dc4fa333 100755
--- a/awxkit/awxkit/cli/client.py
+++ b/awxkit/awxkit/cli/client.py
@@ -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)
diff --git a/awxkit/awxkit/cli/custom.py b/awxkit/awxkit/cli/custom.py
index 38d97d8895..d2b9bc6590 100644
--- a/awxkit/awxkit/cli/custom.py
+++ b/awxkit/awxkit/cli/custom.py
@@ -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.'
- )
+ 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)
- )
+ 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()
diff --git a/awxkit/awxkit/cli/docs/source/conf.py b/awxkit/awxkit/cli/docs/source/conf.py
index 7f4cf47185..75eb627103 100644
--- a/awxkit/awxkit/cli/docs/source/conf.py
+++ b/awxkit/awxkit/cli/docs/source/conf.py
@@ -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']
diff --git a/awxkit/awxkit/cli/format.py b/awxkit/awxkit/cli/format.py
index d35c61efbb..adf61337a4 100644
--- a/awxkit/awxkit/cli/format.py
+++ b/awxkit/awxkit/cli/format.py
@@ -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}
diff --git a/awxkit/awxkit/cli/options.py b/awxkit/awxkit/cli/options.py
index 4d292f3611..d5ad777feb 100644
--- a/awxkit/awxkit/cli/options.py
+++ b/awxkit/awxkit/cli/options.py
@@ -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
- )
- )
- raise argparse.ArgumentTypeError(
- 'Could not find any {0} with that {1}.'.format(
- model_name, identity
+ '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))
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():
diff --git a/awxkit/awxkit/cli/resource.py b/awxkit/awxkit/cli/resource.py
index 13419357d2..7aa7c32e0a 100644
--- a/awxkit/awxkit/cli/resource.py
+++ b/awxkit/awxkit/cli/resource.py
@@ -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:
diff --git a/awxkit/awxkit/cli/sphinx.py b/awxkit/awxkit/cli/sphinx.py
index 850d99e014..970f93700a 100644
--- a/awxkit/awxkit/cli/sphinx.py
+++ b/awxkit/awxkit/cli/sphinx.py
@@ -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()
diff --git a/awxkit/awxkit/cli/stdout.py b/awxkit/awxkit/cli/stdout.py
index 1cf18168e1..5de134ccc4 100644
--- a/awxkit/awxkit/cli/stdout.py
+++ b/awxkit/awxkit/cli/stdout.py
@@ -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:
diff --git a/awxkit/awxkit/cli/utils.py b/awxkit/awxkit/cli/utils.py
index d064158859..61c2fe8f5e 100644
--- a/awxkit/awxkit/cli/utils.py
+++ b/awxkit/awxkit/cli/utils.py
@@ -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)),
)
)
)
diff --git a/awxkit/awxkit/exceptions.py b/awxkit/awxkit/exceptions.py
index 596720b59d..84c37d615e 100644
--- a/awxkit/awxkit/exceptions.py
+++ b/awxkit/awxkit/exceptions.py
@@ -1,6 +1,4 @@
-
class Common(Exception):
-
def __init__(self, status_string='', message=''):
if isinstance(status_string, Exception):
self.status_string = ''
diff --git a/awxkit/awxkit/scripts/basic_session.py b/awxkit/awxkit/scripts/basic_session.py
index 4ede325214..5c58d70504 100755
--- a/awxkit/awxkit/scripts/basic_session.py
+++ b/awxkit/awxkit/scripts/basic_session.py
@@ -17,35 +17,26 @@ def parse_args():
parser.add_argument(
'--base-url',
dest='base_url',
- default=os.getenv(
- 'AWXKIT_BASE_URL',
- 'http://127.0.0.1:8013'),
- help='URL for AWX. Defaults to env var AWXKIT_BASE_URL or http://127.0.0.1:8013')
+ default=os.getenv('AWXKIT_BASE_URL', 'http://127.0.0.1:8013'),
+ help='URL for AWX. Defaults to env var AWXKIT_BASE_URL or http://127.0.0.1:8013',
+ )
parser.add_argument(
'-c',
'--credential-file',
dest='credential_file',
- default=os.getenv(
- 'AWXKIT_CREDENTIAL_FILE',
- utils.not_provided),
+ default=os.getenv('AWXKIT_CREDENTIAL_FILE', utils.not_provided),
help='Path for yml credential file. If not provided or set by AWXKIT_CREDENTIAL_FILE, set '
- 'AWXKIT_USER and AWXKIT_USER_PASSWORD env vars for awx user credentials.')
+ 'AWXKIT_USER and AWXKIT_USER_PASSWORD env vars for awx user credentials.',
+ )
parser.add_argument(
'-p',
'--project-file',
dest='project_file',
- default=os.getenv(
- 'AWXKIT_PROJECT_FILE'),
- help='Path for yml project config file.'
- 'If not provided or set by AWXKIT_PROJECT_FILE, projects will not have default SCM_URL')
- parser.add_argument('-f', '--file', dest='akit_script', default=False,
- help='akit script file to run in interactive session.')
- parser.add_argument(
- '-x',
- '--non-interactive',
- action='store_true',
- dest='non_interactive',
- help='Do not run in interactive mode.')
+ default=os.getenv('AWXKIT_PROJECT_FILE'),
+ help='Path for yml project config file.' 'If not provided or set by AWXKIT_PROJECT_FILE, projects will not have default SCM_URL',
+ )
+ parser.add_argument('-f', '--file', dest='akit_script', default=False, help='akit script file to run in interactive session.')
+ parser.add_argument('-x', '--non-interactive', action='store_true', dest='non_interactive', help='Do not run in interactive mode.')
return parser.parse_known_args()[0]
@@ -57,19 +48,14 @@ def main():
config.base_url = akit_args.base_url
if akit_args.credential_file != utils.not_provided:
- config.credentials = utils.load_credentials(
- akit_args.credential_file)
+ config.credentials = utils.load_credentials(akit_args.credential_file)
else:
- config.credentials = utils.PseudoNamespace({
- 'default': {
- 'username': os.getenv('AWXKIT_USER', 'admin'),
- 'password': os.getenv('AWXKIT_USER_PASSWORD', 'password')
- }
- })
+ config.credentials = utils.PseudoNamespace(
+ {'default': {'username': os.getenv('AWXKIT_USER', 'admin'), 'password': os.getenv('AWXKIT_USER_PASSWORD', 'password')}}
+ )
if akit_args.project_file != utils.not_provided:
- config.project_urls = utils.load_projects(
- akit_args.project_file)
+ config.project_urls = utils.load_projects(akit_args.project_file)
global root
root = api.Api()
@@ -106,6 +92,7 @@ def load_interactive():
try:
from IPython import start_ipython
+
basic_session_path = os.path.abspath(__file__)
if basic_session_path[-1] == 'c': # start_ipython doesn't work w/ .pyc
basic_session_path = basic_session_path[:-1]
@@ -115,6 +102,7 @@ def load_interactive():
return start_ipython(argv=sargs)
except ImportError:
from code import interact
+
main()
interact('', local=dict(globals(), **locals()))
diff --git a/awxkit/awxkit/utils/__init__.py b/awxkit/awxkit/utils/__init__.py
index d5f4055717..67442bee25 100644
--- a/awxkit/awxkit/utils/__init__.py
+++ b/awxkit/awxkit/utils/__init__.py
@@ -34,7 +34,8 @@ cloud_types = (
'rhv',
'satellite6',
'tower',
- 'vmware')
+ 'vmware',
+)
credential_type_kinds = ('cloud', 'net')
not_provided = 'xx__NOT_PROVIDED__xx'
@@ -52,7 +53,6 @@ class NoReloadError(Exception):
class PseudoNamespace(dict):
-
def __init__(self, _d=None, **loaded):
if not isinstance(_d, dict):
_d = {}
@@ -79,9 +79,7 @@ class PseudoNamespace(dict):
try:
return self.__getitem__(attr)
except KeyError:
- raise AttributeError(
- "{!r} has no attribute {!r}".format(
- self.__class__.__name__, attr))
+ raise AttributeError("{!r} has no attribute {!r}".format(self.__class__.__name__, attr))
def __setattr__(self, attr, value):
self.__setitem__(attr, value)
@@ -116,11 +114,7 @@ class PseudoNamespace(dict):
# PseudoNamespaces if applicable
def update(self, iterable=None, **kw):
if iterable:
- if (hasattr(iterable,
- 'keys') and isinstance(iterable.keys,
- (types.FunctionType,
- types.BuiltinFunctionType,
- types.MethodType))):
+ if hasattr(iterable, 'keys') and isinstance(iterable.keys, (types.FunctionType, types.BuiltinFunctionType, types.MethodType)):
for key in iterable:
self[key] = iterable[key]
else:
@@ -161,11 +155,7 @@ def filter_by_class(*item_class_tuples):
examined_item = item[0]
else:
examined_item = item
- if is_class_or_instance(
- examined_item,
- cls) or is_proper_subclass(
- examined_item,
- cls):
+ if is_class_or_instance(examined_item, cls) or is_proper_subclass(examined_item, cls):
results.append(item)
else:
updated = (cls, item[1]) if was_tuple else cls
@@ -249,7 +239,7 @@ def gen_utf_char():
is_char = False
b = 'b'
while not is_char:
- b = random.randint(32, 0x10ffff)
+ b = random.randint(32, 0x10FFFF)
is_char = chr(b).isprintable()
return chr(b)
@@ -266,20 +256,12 @@ def random_ipv4():
def random_ipv6():
"""Generates a random ipv6 address;; useful for testing."""
- return ':'.join(
- '{0:x}'.format(
- random.randint(
- 0,
- 2 ** 16 -
- 1)) for i in range(8))
+ return ':'.join('{0:x}'.format(random.randint(0, 2 ** 16 - 1)) for i in range(8))
def random_loopback_ip():
"""Generates a random loopback ipv4 address;; useful for testing."""
- return "127.{}.{}.{}".format(
- random_int(255),
- random_int(255),
- random_int(255))
+ return "127.{}.{}.{}".format(random_int(255), random_int(255), random_int(255))
def random_utf8(*args, **kwargs):
@@ -289,8 +271,7 @@ def random_utf8(*args, **kwargs):
"""
pattern = re.compile('[^\u0000-\uD7FF\uE000-\uFFFF]', re.UNICODE)
length = args[0] if len(args) else kwargs.get('length', 10)
- scrubbed = pattern.sub('\uFFFD', ''.join(
- [gen_utf_char() for _ in range(length)]))
+ scrubbed = pattern.sub('\uFFFD', ''.join([gen_utf_char() for _ in range(length)]))
return scrubbed
@@ -374,8 +355,10 @@ def is_proper_subclass(obj, cls):
def are_same_endpoint(first, second):
"""Equivalence check of two urls, stripped of query parameters"""
+
def strip(url):
return url.replace('www.', '').split('?')[0]
+
return strip(first) == strip(second)
@@ -421,10 +404,7 @@ class UTC(tzinfo):
return timedelta(0)
-def seconds_since_date_string(
- date_str,
- fmt='%Y-%m-%dT%H:%M:%S.%fZ',
- default_tz=UTC()):
+def seconds_since_date_string(date_str, fmt='%Y-%m-%dT%H:%M:%S.%fZ', default_tz=UTC()):
"""Return the number of seconds since the date and time indicated by a date
string and its corresponding format string.
diff --git a/awxkit/awxkit/utils/toposort.py b/awxkit/awxkit/utils/toposort.py
index 4874cd7ec9..58eec59646 100644
--- a/awxkit/awxkit/utils/toposort.py
+++ b/awxkit/awxkit/utils/toposort.py
@@ -42,18 +42,19 @@ class CircularDependencyError(ValueError):
def __init__(self, data):
# Sort the data just to make the output consistent, for use in
# error messages. That's convenient for doctests.
- s = 'Circular dependencies exist among these items: {{{}}}'.format(', '.join('{!r}:{!r}'.format(key, value) for key, value in sorted(data.items()))) # noqa
+ s = 'Circular dependencies exist among these items: {{{}}}'.format(
+ ', '.join('{!r}:{!r}'.format(key, value) for key, value in sorted(data.items()))
+ ) # noqa
super(CircularDependencyError, self).__init__(s)
self.data = data
def toposort(data):
"""Dependencies are expressed as a dictionary whose keys are items
-and whose values are a set of dependent items. Output is a list of
-sets in topological order. The first set consists of items with no
-dependences, each subsequent set consists of items that depend upon
-items in the preceeding sets.
-"""
+ and whose values are a set of dependent items. Output is a list of
+ sets in topological order. The first set consists of items with no
+ dependences, each subsequent set consists of items that depend upon
+ items in the preceeding sets."""
# Special case empty input.
if len(data) == 0:
@@ -74,9 +75,6 @@ items in the preceeding sets.
if not ordered:
break
yield ordered
- data = {
- item: (dep - ordered)
- for item, dep in data.items() if item not in ordered
- }
+ data = {item: (dep - ordered) for item, dep in data.items() if item not in ordered}
if len(data) != 0:
raise CircularDependencyError(data)
diff --git a/awxkit/awxkit/words.py b/awxkit/awxkit/words.py
index 940d71cd0f..eb3822db02 100644
--- a/awxkit/awxkit/words.py
+++ b/awxkit/awxkit/words.py
@@ -1,193 +1,1529 @@
# list of random English nouns used for resource name utilities
-words = ['People', 'History', 'Way', 'Art', 'World', 'Information', 'Map', 'Two',
- 'Family', 'Government', 'Health', 'System', 'Computer', 'Meat', 'Year', 'Thanks',
- 'Music', 'Person', 'Reading', 'Method', 'Data', 'Food', 'Understanding', 'Theory',
- 'Law', 'Bird', 'Literature', 'Problem', 'Software', 'Control', 'Knowledge', 'Power',
- 'Ability', 'Economics', 'Love', 'Internet', 'Television', 'Science', 'Library', 'Nature',
- 'Fact', 'Product', 'Idea', 'Temperature', 'Investment', 'Area', 'Society', 'Activity',
- 'Story', 'Industry', 'Media', 'Thing', 'Oven', 'Community', 'Definition', 'Safety',
- 'Quality', 'Development', 'Language', 'Management', 'Player', 'Variety', 'Video', 'Week',
- 'Security', 'Country', 'Exam', 'Movie', 'Organization', 'Equipment', 'Physics', 'Analysis',
- 'Policy', 'Series', 'Thought', 'Basis', 'Boyfriend', 'Direction', 'Strategy', 'Technology',
- 'Army', 'Camera', 'Freedom', 'Paper', 'Environment', 'Child', 'Instance', 'Month',
- 'Truth', 'Marketing', 'University', 'Writing', 'Article', 'Department', 'Difference', 'Goal',
- 'News', 'Audience', 'Fishing', 'Growth', 'Income', 'Marriage', 'User', 'Combination',
- 'Failure', 'Meaning', 'Medicine', 'Philosophy', 'Teacher', 'Communication', 'Night', 'Chemistry',
- 'Disease', 'Disk', 'Energy', 'Nation', 'Road', 'Role', 'Soup', 'Advertising',
- 'Location', 'Success', 'Addition', 'Apartment', 'Education', 'Math', 'Moment', 'Painting',
- 'Politics', 'Attention', 'Decision', 'Event', 'Property', 'Shopping', 'Student', 'Wood',
- 'Competition', 'Distribution', 'Entertainment', 'Office', 'Population', 'President', 'Unit', 'Category',
- 'Cigarette', 'Context', 'Introduction', 'Opportunity', 'Performance', 'Driver', 'Flight', 'Length',
- 'Magazine', 'Newspaper', 'Relationship', 'Teaching', 'Cell', 'Dealer', 'Debate', 'Finding',
- 'Lake', 'Member', 'Message', 'Phone', 'Scene', 'Appearance', 'Association', 'Concept',
- 'Customer', 'Death', 'Discussion', 'Housing', 'Inflation', 'Insurance', 'Mood', 'Woman',
- 'Advice', 'Blood', 'Effort', 'Expression', 'Importance', 'Opinion', 'Payment', 'Reality',
- 'Responsibility', 'Situation', 'Skill', 'Statement', 'Wealth', 'Application', 'City', 'County',
- 'Depth', 'Estate', 'Foundation', 'Grandmother', 'Heart', 'Perspective', 'Photo', 'Recipe',
- 'Studio', 'Topic', 'Collection', 'Depression', 'Imagination', 'Passion', 'Percentage', 'Resource',
- 'Setting', 'Ad', 'Agency', 'College', 'Connection', 'Criticism', 'Debt', 'Description',
- 'Memory', 'Patience', 'Secretary', 'Solution', 'Administration', 'Aspect', 'Attitude', 'Director',
- 'Personality', 'Psychology', 'Recommendation', 'Response', 'Selection', 'Storage', 'Version', 'Alcohol',
- 'Argument', 'Complaint', 'Contract', 'Emphasis', 'Highway', 'Loss', 'Membership', 'Possession',
- 'Preparation', 'Steak', 'Union', 'Agreement', 'Cancer', 'Currency', 'Employment', 'Engineering',
- 'Entry', 'Interaction', 'Limit', 'Mixture', 'Preference', 'Region', 'Republic', 'Seat',
- 'Tradition', 'Virus', 'Actor', 'Classroom', 'Delivery', 'Device', 'Difficulty', 'Drama',
- 'Election', 'Engine', 'Football', 'Guidance', 'Hotel', 'Match', 'Owner', 'Priority',
- 'Protection', 'Suggestion', 'Tension', 'Variation', 'Anxiety', 'Atmosphere', 'Awareness', 'Bread',
- 'Climate', 'Comparison', 'Confusion', 'Construction', 'Elevator', 'Emotion', 'Employee', 'Employer',
- 'Guest', 'Height', 'Leadership', 'Mall', 'Manager', 'Operation', 'Recording', 'Respect',
- 'Sample', 'Transportation', 'Boring', 'Charity', 'Cousin', 'Disaster', 'Editor', 'Efficiency',
- 'Excitement', 'Extent', 'Feedback', 'Guitar', 'Homework', 'Leader', 'Mom', 'Outcome',
- 'Permission', 'Presentation', 'Promotion', 'Reflection', 'Refrigerator', 'Resolution', 'Revenue', 'Session',
- 'Singer', 'Tennis', 'Basket', 'Bonus', 'Cabinet', 'Childhood', 'Church', 'Clothes',
- 'Coffee', 'Dinner', 'Drawing', 'Hair', 'Hearing', 'Initiative', 'Judgment', 'Lab',
- 'Measurement', 'Mode', 'Mud', 'Orange', 'Poetry', 'Police', 'Possibility', 'Procedure',
- 'Queen', 'Ratio', 'Relation', 'Restaurant', 'Satisfaction', 'Sector', 'Signature', 'Significance',
- 'Song', 'Tooth', 'Town', 'Vehicle', 'Volume', 'Wife', 'Accident', 'Airport',
- 'Appointment', 'Arrival', 'Assumption', 'Baseball', 'Chapter', 'Committee', 'Conversation', 'Database',
- 'Enthusiasm', 'Error', 'Explanation', 'Farmer', 'Gate', 'Girl', 'Hall', 'Historian',
- 'Hospital', 'Injury', 'Instruction', 'Maintenance', 'Manufacturer', 'Meal', 'Perception', 'Pie',
- 'Poem', 'Presence', 'Proposal', 'Reception', 'Replacement', 'Revolution', 'River', 'Son',
- 'Speech', 'Tea', 'Village', 'Warning', 'Winner', 'Worker', 'Writer', 'Assistance',
- 'Breath', 'Buyer', 'Chest', 'Chocolate', 'Conclusion', 'Contribution', 'Cookie', 'Courage',
- 'Dad', 'Desk', 'Drawer', 'Establishment', 'Examination', 'Garbage', 'Grocery', 'Honey',
- 'Impression', 'Improvement', 'Independence', 'Insect', 'Inspection', 'Inspector', 'King', 'Ladder',
- 'Menu', 'Penalty', 'Piano', 'Potato', 'Profession', 'Professor', 'Quantity', 'Reaction',
- 'Requirement', 'Salad', 'Sister', 'Supermarket', 'Tongue', 'Weakness', 'Wedding', 'Affair',
- 'Ambition', 'Analyst', 'Apple', 'Assignment', 'Assistant', 'Bathroom', 'Bedroom', 'Beer',
- 'Birthday', 'Celebration', 'Championship', 'Cheek', 'Client', 'Consequence', 'Departure', 'Diamond',
- 'Dirt', 'Ear', 'Fortune', 'Friendship', 'Snapewife', 'Funeral', 'Gene', 'Girlfriend', 'Hat',
- 'Indication', 'Intention', 'Lady', 'Midnight', 'Negotiation', 'Obligation', 'Passenger', 'Pizza',
- 'Platform', 'Poet', 'Pollution', 'Recognition', 'Reputation', 'Shirt', 'Sir', 'Speaker',
- 'Stranger', 'Surgery', 'Sympathy', 'Tale', 'Throat', 'Trainer', 'Uncle', 'Youth',
- 'Time', 'Work', 'Film', 'Water', 'Money', 'Example', 'While', 'Business',
- 'Study', 'Game', 'Life', 'Form', 'Air', 'Day', 'Place', 'Number',
- 'Part', 'Field', 'Fish', 'Back', 'Process', 'Heat', 'Hand', 'Experience',
- 'Job', 'Book', 'End', 'Point', 'Type', 'Home', 'Economy', 'Value',
- 'Body', 'Market', 'Guide', 'Interest', 'State', 'Radio', 'Course', 'Company',
- 'Price', 'Size', 'Card', 'List', 'Mind', 'Trade', 'Line', 'Care',
- 'Group', 'Risk', 'Word', 'Fat', 'Force', 'Key', 'Light', 'Training',
- 'Name', 'School', 'Top', 'Amount', 'Level', 'Order', 'Practice', 'Research',
- 'Sense', 'Service', 'Piece', 'Web', 'Boss', 'Sport', 'Fun', 'House',
- 'Page', 'Term', 'Test', 'Answer', 'Sound', 'Focus', 'Matter', 'Kind',
- 'Soil', 'Board', 'Oil', 'Picture', 'Access', 'Garden', 'Range', 'Rate',
- 'Reason', 'Future', 'Site', 'Demand', 'Exercise', 'Image', 'Case', 'Cause',
- 'Coast', 'Action', 'Age', 'Bad', 'Boat', 'Record', 'Result', 'Section',
- 'Building', 'Mouse', 'Cash', 'Class', 'Nothing', 'Period', 'Plan', 'Store',
- 'Tax', 'Side', 'Subject', 'Space', 'Rule', 'Stock', 'Weather', 'Chance',
- 'Figure', 'Man', 'Model', 'Source', 'Beginning', 'Earth', 'Program', 'Chicken',
- 'Design', 'Feature', 'Head', 'Material', 'Purpose', 'Question', 'Rock', 'Salt',
- 'Act', 'Birth', 'Car', 'Dog', 'Object', 'Scale', 'Sun', 'Note',
- 'Profit', 'Rent', 'Speed', 'Style', 'War', 'Bank', 'Craft', 'Half',
- 'Inside', 'Outside', 'Standard', 'Bus', 'Exchange', 'Eye', 'Fire', 'Position',
- 'Pressure', 'Stress', 'Advantage', 'Benefit', 'Box', 'Frame', 'Issue', 'Step',
- 'Cycle', 'Face', 'Item', 'Metal', 'Paint', 'Review', 'Room', 'Screen',
- 'Structure', 'View', 'Account', 'Ball', 'Discipline', 'Medium', 'Share', 'Balance',
- 'Bit', 'Black', 'Bottom', 'Choice', 'Gift', 'Impact', 'Machine', 'Shape',
- 'Tool', 'Wind', 'Address', 'Average', 'Career', 'Culture', 'Morning', 'Pot',
- 'Sign', 'Table', 'Task', 'Condition', 'Contact', 'Credit', 'Egg', 'Hope',
- 'Ice', 'Network', 'North', 'Square', 'Attempt', 'Date', 'Effect', 'Link',
- 'Post', 'Star', 'Voice', 'Capital', 'Challenge', 'Friend', 'Self', 'Shot',
- 'Brush', 'Couple', 'Exit', 'Front', 'Function', 'Lack', 'Living', 'Plant',
- 'Plastic', 'Spot', 'Summer', 'Taste', 'Theme', 'Track', 'Wing', 'Brain',
- 'Button', 'Click', 'Desire', 'Foot', 'Gas', 'Influence', 'Notice', 'Rain',
- 'Wall', 'Base', 'Damage', 'Distance', 'Feeling', 'Pair', 'Savings', 'Staff',
- 'Sugar', 'Target', 'Text', 'Animal', 'Author', 'Budget', 'Discount', 'File',
- 'Ground', 'Lesson', 'Minute', 'Officer', 'Phase', 'Reference', 'Register', 'Sky',
- 'Stage', 'Stick', 'Title', 'Trouble', 'Bowl', 'Bridge', 'Campaign', 'Character',
- 'Club', 'Edge', 'Evidence', 'Fan', 'Letter', 'Lock', 'Maximum', 'Novel',
- 'Option', 'Pack', 'Park', 'Plenty', 'Quarter', 'Skin', 'Sort', 'Weight',
- 'Baby', 'Background', 'Carry', 'Dish', 'Factor', 'Fruit', 'Glass', 'Joint',
- 'Master', 'Muscle', 'Red', 'Strength', 'Traffic', 'Trip', 'Vegetable', 'Appeal',
- 'Chart', 'Gear', 'Ideal', 'Kitchen', 'Land', 'Log', 'Mother', 'Net',
- 'Party', 'Principle', 'Relative', 'Sale', 'Season', 'Signal', 'Spirit', 'Street',
- 'Tree', 'Wave', 'Belt', 'Bench', 'Commission', 'Copy', 'Drop', 'Minimum',
- 'Path', 'Progress', 'Project', 'Sea', 'South', 'Status', 'Stuff', 'Ticket',
- 'Tour', 'Angle', 'Blue', 'Breakfast', 'Confidence', 'Daughter', 'Degree', 'Doctor',
- 'Dot', 'Dream', 'Duty', 'Essay', 'Father', 'Fee', 'Finance', 'Hour',
- 'Juice', 'Luck', 'Milk', 'Mouth', 'Peace', 'Pipe', 'Stable', 'Storm',
- 'Substance', 'Team', 'Trick', 'Afternoon', 'Bat', 'Beach', 'Blank', 'Catch',
- 'Chain', 'Consideration', 'Cream', 'Crew', 'Detail', 'Gold', 'Interview', 'Kid',
- 'Mark', 'Mission', 'Pain', 'Pleasure', 'Score', 'Screw', 'Gratitude', 'Shop',
- 'Shower', 'Suit', 'Tone', 'Window', 'Agent', 'Band', 'Bath', 'Block',
- 'Bone', 'Calendar', 'Candidate', 'Cap', 'Coat', 'Contest', 'Corner', 'Court',
- 'Cup', 'District', 'Door', 'East', 'Finger', 'Garage', 'Guarantee', 'Hole',
- 'Hook', 'Implement', 'Layer', 'Lecture', 'Lie', 'Manner', 'Meeting', 'Nose',
- 'Parking', 'Partner', 'Profile', 'Rice', 'Routine', 'Schedule', 'Swimming', 'Telephone',
- 'Tip', 'Winter', 'Airline', 'Bag', 'Battle', 'Bed', 'Bill', 'Bother',
- 'Cake', 'Code', 'Curve', 'Designer', 'Dimension', 'Dress', 'Ease', 'Emergency',
- 'Evening', 'Extension', 'Farm', 'Fight', 'Gap', 'Grade', 'Holiday', 'Horror',
- 'Horse', 'Host', 'Husband', 'Loan', 'Mistake', 'Mountain', 'Nail', 'Noise',
- 'Occasion', 'Package', 'Patient', 'Pause', 'Phrase', 'Proof', 'Race', 'Relief',
- 'Sand', 'Sentence', 'Shoulder', 'Smoke', 'Stomach', 'String', 'Tourist', 'Towel',
- 'Vacation', 'West', 'Wheel', 'Wine', 'Arm', 'Aside', 'Associate', 'Bet',
- 'Blow', 'Border', 'Branch', 'Breast', 'Brother', 'Buddy', 'Bunch', 'Chip',
- 'Coach', 'Cross', 'Document', 'Draft', 'Dust', 'Expert', 'Floor', 'God',
- 'Golf', 'Habit', 'Iron', 'Judge', 'Knife', 'Landscape', 'League', 'Mail',
- 'Mess', 'Native', 'Opening', 'Parent', 'Pattern', 'Pin', 'Pool', 'Pound',
- 'Request', 'Salary', 'Shame', 'Shelter', 'Shoe', 'Silver', 'Tackle', 'Tank',
- 'Trust', 'Assist', 'Bake', 'Bar', 'Bell', 'Bike', 'Blame', 'Boy',
- 'Brick', 'Chair', 'Closet', 'Clue', 'Collar', 'Comment', 'Conference', 'Devil',
- 'Diet', 'Fear', 'Fuel', 'Glove', 'Jacket', 'Lunch', 'Monitor', 'Mortgage',
- 'Nurse', 'Pace', 'Panic', 'Peak', 'Plane', 'Reward', 'Row', 'Sandwich',
- 'Shock', 'Spite', 'Spray', 'Surprise', 'Till', 'Transition', 'Weekend', 'Welcome',
- 'Yard', 'Alarm', 'Bend', 'Bicycle', 'Bite', 'Blind', 'Bottle', 'Cable',
- 'Candle', 'Clerk', 'Cloud', 'Concert', 'Counter', 'Flower', 'Grandfather', 'Harm',
- 'Knee', 'Lawyer', 'Leather', 'Load', 'Mirror', 'Neck', 'Pension', 'Plate',
- 'Purple', 'Ruin', 'Ship', 'Skirt', 'Slice', 'Snow', 'Specialist', 'Stroke',
- 'Switch', 'Trash', 'Tune', 'Zone', 'Anger', 'Award', 'Bid', 'Bitter',
- 'Boot', 'Bug', 'Camp', 'Candy', 'Carpet', 'Cat', 'Champion', 'Channel',
- 'Clock', 'Comfort', 'Cow', 'Crack', 'Engineer', 'Entrance', 'Fault', 'Grass',
- 'Guy', 'Hell', 'Highlight', 'Incident', 'Island', 'Joke', 'Jury', 'Leg',
- 'Lip', 'Mate', 'Motor', 'Nerve', 'Passage', 'Pen', 'Pride', 'Priest',
- 'Prize', 'Promise', 'Resident', 'Resort', 'Ring', 'Roof', 'Rope', 'Sail',
- 'Scheme', 'Script', 'Sock', 'Station', 'Toe', 'Tower', 'Truck', 'Witness',
- 'Asparagus', 'You', 'It', 'Can', 'Will', 'If', 'One', 'Many',
- 'Most', 'Other', 'Use', 'Make', 'Good', 'Look', 'Help', 'Go',
- 'Great', 'Being', 'Few', 'Might', 'Still', 'Public', 'Read', 'Keep',
- 'Start', 'Give', 'Human', 'Local', 'General', 'She', 'Specific', 'Long',
- 'Play', 'Feel', 'High', 'Tonight', 'Put', 'Common', 'Set', 'Change',
- 'Simple', 'Past', 'Big', 'Possible', 'Particular', 'Today', 'Major', 'Personal',
- 'Current', 'National', 'Cut', 'Natural', 'Physical', 'Show', 'Try', 'Check',
- 'Second', 'Call', 'Move', 'Pay', 'Let', 'Increase', 'Single', 'Individual',
- 'Turn', 'Ask', 'Buy', 'Guard', 'Hold', 'Main', 'Offer', 'Potential',
- 'Professional', 'International', 'Travel', 'Cook', 'Alternative', 'Following', 'Special', 'Working',
- 'Whole', 'Dance', 'Excuse', 'Cold', 'Commercial', 'Low', 'Purchase', 'Deal',
- 'Primary', 'Worth', 'Fall', 'Necessary', 'Positive', 'Produce', 'Search', 'Present',
- 'Spend', 'Talk', 'Creative', 'Tell', 'Cost', 'Drive', 'Green', 'Support',
- 'Glad', 'Remove', 'Return', 'Run', 'Complex', 'Due', 'Effective', 'Middle',
- 'Regular', 'Reserve', 'Independent', 'Leave', 'Original', 'Reach', 'Rest', 'Serve',
- 'Watch', 'Beautiful', 'Charge', 'Active', 'Break', 'Negative', 'Safe', 'Stay',
- 'Visit', 'Visual', 'Affect', 'Cover', 'Report', 'Rise', 'Walk', 'White',
- 'Beyond', 'Junior', 'Pick', 'Unique', 'Anything', 'Classic', 'Final', 'Lift',
- 'Mix', 'Private', 'Stop', 'Teach', 'Western', 'Concern', 'Familiar', 'Fly',
- 'Official', 'Broad', 'Comfortable', 'Gain', 'Maybe', 'Rich', 'Save', 'Stand',
- 'Young', 'Heavy', 'Hello', 'Lead', 'Listen', 'Valuable', 'Worry', 'Handle',
- 'Leading', 'Meet', 'Release', 'Sell', 'Finish', 'Normal', 'Press', 'Ride',
- 'Secret', 'Spread', 'Spring', 'Tough', 'Wait', 'Brown', 'Deep', 'Display',
- 'Flow', 'Hit', 'Objective', 'Shoot', 'Touch', 'Cancel', 'Chemical', 'Cry',
- 'Dump', 'Extreme', 'Push', 'Conflict', 'Eat', 'Fill', 'Formal', 'Jump',
- 'Kick', 'Opposite', 'Pass', 'Pitch', 'Remote', 'Total', 'Treat', 'Vast',
- 'Abuse', 'Beat', 'Burn', 'Deposit', 'Print', 'Raise', 'Sleep', 'Somewhere',
- 'Advance', 'Anywhere', 'Consist', 'Dark', 'Double', 'Draw', 'Equal', 'Fix',
- 'Hire', 'Internal', 'Join', 'Kill', 'Sensitive', 'Tap', 'Win', 'Attack',
- 'Claim', 'Constant', 'Drag', 'Drink', 'Guess', 'Minor', 'Pull', 'Raw',
- 'Soft', 'Solid', 'Wear', 'Weird', 'Wonder', 'Annual', 'Count', 'Dead',
- 'Doubt', 'Feed', 'Forever', 'Impress', 'Nobody', 'Repeat', 'Round', 'Sing',
- 'Slide', 'Strip', 'Whereas', 'Wish', 'Combine', 'Command', 'Dig', 'Divide',
- 'Equivalent', 'Hang', 'Hunt', 'Initial', 'March', 'Mention', 'Spiritual', 'Survey',
- 'Tie', 'Adult', 'Brief', 'Crazy', 'Escape', 'Gather', 'Hate', 'Prior',
- 'Repair', 'Rough', 'Sad', 'Scratch', 'Sick', 'Strike', 'Employ', 'External',
- 'Hurt', 'Illegal', 'Laugh', 'Lay', 'Mobile', 'Nasty', 'Ordinary', 'Respond',
- 'Royal', 'Senior', 'Split', 'Strain', 'Struggle', 'Swim', 'Train', 'Upper',
- 'Wash', 'Yellow', 'Convert', 'Crash', 'Dependent', 'Fold', 'Funny', 'Grab',
- 'Hide', 'Miss', 'Permit', 'Quote', 'Recover', 'Resolve', 'Roll', 'Sink',
- 'Slip', 'Spare', 'Suspect', 'Sweet', 'Swing', 'Twist', 'Upstairs', 'Usual',
- 'Abroad', 'Brave', 'Calm', 'Concentrate', 'Estimate', 'Grand', 'Male', 'Mine',
- 'Prompt', 'Quiet', 'Refuse', 'Regret', 'Reveal', 'Rush', 'Shake', 'Shift',
- 'Shine', 'Steal', 'Suck', 'Surround', 'Anybody', 'Bear', 'Brilliant', 'Dare',
- 'Dear', 'Delay', 'Drunk', 'Female', 'Hurry', 'Inevitable', 'Invite', 'Kiss',
- 'Neat', 'Pop', 'Punch', 'Quit', 'Reply', 'Representative', 'Resist', 'Rip',
- 'Rub', 'Silly', 'Smile', 'Spell', 'Stretch', 'Stupid', 'Tear', 'Temporary',
- 'Tomorrow', 'Wake', 'Wrap', 'Yesterday']
+words = [
+ 'People',
+ 'History',
+ 'Way',
+ 'Art',
+ 'World',
+ 'Information',
+ 'Map',
+ 'Two',
+ 'Family',
+ 'Government',
+ 'Health',
+ 'System',
+ 'Computer',
+ 'Meat',
+ 'Year',
+ 'Thanks',
+ 'Music',
+ 'Person',
+ 'Reading',
+ 'Method',
+ 'Data',
+ 'Food',
+ 'Understanding',
+ 'Theory',
+ 'Law',
+ 'Bird',
+ 'Literature',
+ 'Problem',
+ 'Software',
+ 'Control',
+ 'Knowledge',
+ 'Power',
+ 'Ability',
+ 'Economics',
+ 'Love',
+ 'Internet',
+ 'Television',
+ 'Science',
+ 'Library',
+ 'Nature',
+ 'Fact',
+ 'Product',
+ 'Idea',
+ 'Temperature',
+ 'Investment',
+ 'Area',
+ 'Society',
+ 'Activity',
+ 'Story',
+ 'Industry',
+ 'Media',
+ 'Thing',
+ 'Oven',
+ 'Community',
+ 'Definition',
+ 'Safety',
+ 'Quality',
+ 'Development',
+ 'Language',
+ 'Management',
+ 'Player',
+ 'Variety',
+ 'Video',
+ 'Week',
+ 'Security',
+ 'Country',
+ 'Exam',
+ 'Movie',
+ 'Organization',
+ 'Equipment',
+ 'Physics',
+ 'Analysis',
+ 'Policy',
+ 'Series',
+ 'Thought',
+ 'Basis',
+ 'Boyfriend',
+ 'Direction',
+ 'Strategy',
+ 'Technology',
+ 'Army',
+ 'Camera',
+ 'Freedom',
+ 'Paper',
+ 'Environment',
+ 'Child',
+ 'Instance',
+ 'Month',
+ 'Truth',
+ 'Marketing',
+ 'University',
+ 'Writing',
+ 'Article',
+ 'Department',
+ 'Difference',
+ 'Goal',
+ 'News',
+ 'Audience',
+ 'Fishing',
+ 'Growth',
+ 'Income',
+ 'Marriage',
+ 'User',
+ 'Combination',
+ 'Failure',
+ 'Meaning',
+ 'Medicine',
+ 'Philosophy',
+ 'Teacher',
+ 'Communication',
+ 'Night',
+ 'Chemistry',
+ 'Disease',
+ 'Disk',
+ 'Energy',
+ 'Nation',
+ 'Road',
+ 'Role',
+ 'Soup',
+ 'Advertising',
+ 'Location',
+ 'Success',
+ 'Addition',
+ 'Apartment',
+ 'Education',
+ 'Math',
+ 'Moment',
+ 'Painting',
+ 'Politics',
+ 'Attention',
+ 'Decision',
+ 'Event',
+ 'Property',
+ 'Shopping',
+ 'Student',
+ 'Wood',
+ 'Competition',
+ 'Distribution',
+ 'Entertainment',
+ 'Office',
+ 'Population',
+ 'President',
+ 'Unit',
+ 'Category',
+ 'Cigarette',
+ 'Context',
+ 'Introduction',
+ 'Opportunity',
+ 'Performance',
+ 'Driver',
+ 'Flight',
+ 'Length',
+ 'Magazine',
+ 'Newspaper',
+ 'Relationship',
+ 'Teaching',
+ 'Cell',
+ 'Dealer',
+ 'Debate',
+ 'Finding',
+ 'Lake',
+ 'Member',
+ 'Message',
+ 'Phone',
+ 'Scene',
+ 'Appearance',
+ 'Association',
+ 'Concept',
+ 'Customer',
+ 'Death',
+ 'Discussion',
+ 'Housing',
+ 'Inflation',
+ 'Insurance',
+ 'Mood',
+ 'Woman',
+ 'Advice',
+ 'Blood',
+ 'Effort',
+ 'Expression',
+ 'Importance',
+ 'Opinion',
+ 'Payment',
+ 'Reality',
+ 'Responsibility',
+ 'Situation',
+ 'Skill',
+ 'Statement',
+ 'Wealth',
+ 'Application',
+ 'City',
+ 'County',
+ 'Depth',
+ 'Estate',
+ 'Foundation',
+ 'Grandmother',
+ 'Heart',
+ 'Perspective',
+ 'Photo',
+ 'Recipe',
+ 'Studio',
+ 'Topic',
+ 'Collection',
+ 'Depression',
+ 'Imagination',
+ 'Passion',
+ 'Percentage',
+ 'Resource',
+ 'Setting',
+ 'Ad',
+ 'Agency',
+ 'College',
+ 'Connection',
+ 'Criticism',
+ 'Debt',
+ 'Description',
+ 'Memory',
+ 'Patience',
+ 'Secretary',
+ 'Solution',
+ 'Administration',
+ 'Aspect',
+ 'Attitude',
+ 'Director',
+ 'Personality',
+ 'Psychology',
+ 'Recommendation',
+ 'Response',
+ 'Selection',
+ 'Storage',
+ 'Version',
+ 'Alcohol',
+ 'Argument',
+ 'Complaint',
+ 'Contract',
+ 'Emphasis',
+ 'Highway',
+ 'Loss',
+ 'Membership',
+ 'Possession',
+ 'Preparation',
+ 'Steak',
+ 'Union',
+ 'Agreement',
+ 'Cancer',
+ 'Currency',
+ 'Employment',
+ 'Engineering',
+ 'Entry',
+ 'Interaction',
+ 'Limit',
+ 'Mixture',
+ 'Preference',
+ 'Region',
+ 'Republic',
+ 'Seat',
+ 'Tradition',
+ 'Virus',
+ 'Actor',
+ 'Classroom',
+ 'Delivery',
+ 'Device',
+ 'Difficulty',
+ 'Drama',
+ 'Election',
+ 'Engine',
+ 'Football',
+ 'Guidance',
+ 'Hotel',
+ 'Match',
+ 'Owner',
+ 'Priority',
+ 'Protection',
+ 'Suggestion',
+ 'Tension',
+ 'Variation',
+ 'Anxiety',
+ 'Atmosphere',
+ 'Awareness',
+ 'Bread',
+ 'Climate',
+ 'Comparison',
+ 'Confusion',
+ 'Construction',
+ 'Elevator',
+ 'Emotion',
+ 'Employee',
+ 'Employer',
+ 'Guest',
+ 'Height',
+ 'Leadership',
+ 'Mall',
+ 'Manager',
+ 'Operation',
+ 'Recording',
+ 'Respect',
+ 'Sample',
+ 'Transportation',
+ 'Boring',
+ 'Charity',
+ 'Cousin',
+ 'Disaster',
+ 'Editor',
+ 'Efficiency',
+ 'Excitement',
+ 'Extent',
+ 'Feedback',
+ 'Guitar',
+ 'Homework',
+ 'Leader',
+ 'Mom',
+ 'Outcome',
+ 'Permission',
+ 'Presentation',
+ 'Promotion',
+ 'Reflection',
+ 'Refrigerator',
+ 'Resolution',
+ 'Revenue',
+ 'Session',
+ 'Singer',
+ 'Tennis',
+ 'Basket',
+ 'Bonus',
+ 'Cabinet',
+ 'Childhood',
+ 'Church',
+ 'Clothes',
+ 'Coffee',
+ 'Dinner',
+ 'Drawing',
+ 'Hair',
+ 'Hearing',
+ 'Initiative',
+ 'Judgment',
+ 'Lab',
+ 'Measurement',
+ 'Mode',
+ 'Mud',
+ 'Orange',
+ 'Poetry',
+ 'Police',
+ 'Possibility',
+ 'Procedure',
+ 'Queen',
+ 'Ratio',
+ 'Relation',
+ 'Restaurant',
+ 'Satisfaction',
+ 'Sector',
+ 'Signature',
+ 'Significance',
+ 'Song',
+ 'Tooth',
+ 'Town',
+ 'Vehicle',
+ 'Volume',
+ 'Wife',
+ 'Accident',
+ 'Airport',
+ 'Appointment',
+ 'Arrival',
+ 'Assumption',
+ 'Baseball',
+ 'Chapter',
+ 'Committee',
+ 'Conversation',
+ 'Database',
+ 'Enthusiasm',
+ 'Error',
+ 'Explanation',
+ 'Farmer',
+ 'Gate',
+ 'Girl',
+ 'Hall',
+ 'Historian',
+ 'Hospital',
+ 'Injury',
+ 'Instruction',
+ 'Maintenance',
+ 'Manufacturer',
+ 'Meal',
+ 'Perception',
+ 'Pie',
+ 'Poem',
+ 'Presence',
+ 'Proposal',
+ 'Reception',
+ 'Replacement',
+ 'Revolution',
+ 'River',
+ 'Son',
+ 'Speech',
+ 'Tea',
+ 'Village',
+ 'Warning',
+ 'Winner',
+ 'Worker',
+ 'Writer',
+ 'Assistance',
+ 'Breath',
+ 'Buyer',
+ 'Chest',
+ 'Chocolate',
+ 'Conclusion',
+ 'Contribution',
+ 'Cookie',
+ 'Courage',
+ 'Dad',
+ 'Desk',
+ 'Drawer',
+ 'Establishment',
+ 'Examination',
+ 'Garbage',
+ 'Grocery',
+ 'Honey',
+ 'Impression',
+ 'Improvement',
+ 'Independence',
+ 'Insect',
+ 'Inspection',
+ 'Inspector',
+ 'King',
+ 'Ladder',
+ 'Menu',
+ 'Penalty',
+ 'Piano',
+ 'Potato',
+ 'Profession',
+ 'Professor',
+ 'Quantity',
+ 'Reaction',
+ 'Requirement',
+ 'Salad',
+ 'Sister',
+ 'Supermarket',
+ 'Tongue',
+ 'Weakness',
+ 'Wedding',
+ 'Affair',
+ 'Ambition',
+ 'Analyst',
+ 'Apple',
+ 'Assignment',
+ 'Assistant',
+ 'Bathroom',
+ 'Bedroom',
+ 'Beer',
+ 'Birthday',
+ 'Celebration',
+ 'Championship',
+ 'Cheek',
+ 'Client',
+ 'Consequence',
+ 'Departure',
+ 'Diamond',
+ 'Dirt',
+ 'Ear',
+ 'Fortune',
+ 'Friendship',
+ 'Snapewife',
+ 'Funeral',
+ 'Gene',
+ 'Girlfriend',
+ 'Hat',
+ 'Indication',
+ 'Intention',
+ 'Lady',
+ 'Midnight',
+ 'Negotiation',
+ 'Obligation',
+ 'Passenger',
+ 'Pizza',
+ 'Platform',
+ 'Poet',
+ 'Pollution',
+ 'Recognition',
+ 'Reputation',
+ 'Shirt',
+ 'Sir',
+ 'Speaker',
+ 'Stranger',
+ 'Surgery',
+ 'Sympathy',
+ 'Tale',
+ 'Throat',
+ 'Trainer',
+ 'Uncle',
+ 'Youth',
+ 'Time',
+ 'Work',
+ 'Film',
+ 'Water',
+ 'Money',
+ 'Example',
+ 'While',
+ 'Business',
+ 'Study',
+ 'Game',
+ 'Life',
+ 'Form',
+ 'Air',
+ 'Day',
+ 'Place',
+ 'Number',
+ 'Part',
+ 'Field',
+ 'Fish',
+ 'Back',
+ 'Process',
+ 'Heat',
+ 'Hand',
+ 'Experience',
+ 'Job',
+ 'Book',
+ 'End',
+ 'Point',
+ 'Type',
+ 'Home',
+ 'Economy',
+ 'Value',
+ 'Body',
+ 'Market',
+ 'Guide',
+ 'Interest',
+ 'State',
+ 'Radio',
+ 'Course',
+ 'Company',
+ 'Price',
+ 'Size',
+ 'Card',
+ 'List',
+ 'Mind',
+ 'Trade',
+ 'Line',
+ 'Care',
+ 'Group',
+ 'Risk',
+ 'Word',
+ 'Fat',
+ 'Force',
+ 'Key',
+ 'Light',
+ 'Training',
+ 'Name',
+ 'School',
+ 'Top',
+ 'Amount',
+ 'Level',
+ 'Order',
+ 'Practice',
+ 'Research',
+ 'Sense',
+ 'Service',
+ 'Piece',
+ 'Web',
+ 'Boss',
+ 'Sport',
+ 'Fun',
+ 'House',
+ 'Page',
+ 'Term',
+ 'Test',
+ 'Answer',
+ 'Sound',
+ 'Focus',
+ 'Matter',
+ 'Kind',
+ 'Soil',
+ 'Board',
+ 'Oil',
+ 'Picture',
+ 'Access',
+ 'Garden',
+ 'Range',
+ 'Rate',
+ 'Reason',
+ 'Future',
+ 'Site',
+ 'Demand',
+ 'Exercise',
+ 'Image',
+ 'Case',
+ 'Cause',
+ 'Coast',
+ 'Action',
+ 'Age',
+ 'Bad',
+ 'Boat',
+ 'Record',
+ 'Result',
+ 'Section',
+ 'Building',
+ 'Mouse',
+ 'Cash',
+ 'Class',
+ 'Nothing',
+ 'Period',
+ 'Plan',
+ 'Store',
+ 'Tax',
+ 'Side',
+ 'Subject',
+ 'Space',
+ 'Rule',
+ 'Stock',
+ 'Weather',
+ 'Chance',
+ 'Figure',
+ 'Man',
+ 'Model',
+ 'Source',
+ 'Beginning',
+ 'Earth',
+ 'Program',
+ 'Chicken',
+ 'Design',
+ 'Feature',
+ 'Head',
+ 'Material',
+ 'Purpose',
+ 'Question',
+ 'Rock',
+ 'Salt',
+ 'Act',
+ 'Birth',
+ 'Car',
+ 'Dog',
+ 'Object',
+ 'Scale',
+ 'Sun',
+ 'Note',
+ 'Profit',
+ 'Rent',
+ 'Speed',
+ 'Style',
+ 'War',
+ 'Bank',
+ 'Craft',
+ 'Half',
+ 'Inside',
+ 'Outside',
+ 'Standard',
+ 'Bus',
+ 'Exchange',
+ 'Eye',
+ 'Fire',
+ 'Position',
+ 'Pressure',
+ 'Stress',
+ 'Advantage',
+ 'Benefit',
+ 'Box',
+ 'Frame',
+ 'Issue',
+ 'Step',
+ 'Cycle',
+ 'Face',
+ 'Item',
+ 'Metal',
+ 'Paint',
+ 'Review',
+ 'Room',
+ 'Screen',
+ 'Structure',
+ 'View',
+ 'Account',
+ 'Ball',
+ 'Discipline',
+ 'Medium',
+ 'Share',
+ 'Balance',
+ 'Bit',
+ 'Black',
+ 'Bottom',
+ 'Choice',
+ 'Gift',
+ 'Impact',
+ 'Machine',
+ 'Shape',
+ 'Tool',
+ 'Wind',
+ 'Address',
+ 'Average',
+ 'Career',
+ 'Culture',
+ 'Morning',
+ 'Pot',
+ 'Sign',
+ 'Table',
+ 'Task',
+ 'Condition',
+ 'Contact',
+ 'Credit',
+ 'Egg',
+ 'Hope',
+ 'Ice',
+ 'Network',
+ 'North',
+ 'Square',
+ 'Attempt',
+ 'Date',
+ 'Effect',
+ 'Link',
+ 'Post',
+ 'Star',
+ 'Voice',
+ 'Capital',
+ 'Challenge',
+ 'Friend',
+ 'Self',
+ 'Shot',
+ 'Brush',
+ 'Couple',
+ 'Exit',
+ 'Front',
+ 'Function',
+ 'Lack',
+ 'Living',
+ 'Plant',
+ 'Plastic',
+ 'Spot',
+ 'Summer',
+ 'Taste',
+ 'Theme',
+ 'Track',
+ 'Wing',
+ 'Brain',
+ 'Button',
+ 'Click',
+ 'Desire',
+ 'Foot',
+ 'Gas',
+ 'Influence',
+ 'Notice',
+ 'Rain',
+ 'Wall',
+ 'Base',
+ 'Damage',
+ 'Distance',
+ 'Feeling',
+ 'Pair',
+ 'Savings',
+ 'Staff',
+ 'Sugar',
+ 'Target',
+ 'Text',
+ 'Animal',
+ 'Author',
+ 'Budget',
+ 'Discount',
+ 'File',
+ 'Ground',
+ 'Lesson',
+ 'Minute',
+ 'Officer',
+ 'Phase',
+ 'Reference',
+ 'Register',
+ 'Sky',
+ 'Stage',
+ 'Stick',
+ 'Title',
+ 'Trouble',
+ 'Bowl',
+ 'Bridge',
+ 'Campaign',
+ 'Character',
+ 'Club',
+ 'Edge',
+ 'Evidence',
+ 'Fan',
+ 'Letter',
+ 'Lock',
+ 'Maximum',
+ 'Novel',
+ 'Option',
+ 'Pack',
+ 'Park',
+ 'Plenty',
+ 'Quarter',
+ 'Skin',
+ 'Sort',
+ 'Weight',
+ 'Baby',
+ 'Background',
+ 'Carry',
+ 'Dish',
+ 'Factor',
+ 'Fruit',
+ 'Glass',
+ 'Joint',
+ 'Master',
+ 'Muscle',
+ 'Red',
+ 'Strength',
+ 'Traffic',
+ 'Trip',
+ 'Vegetable',
+ 'Appeal',
+ 'Chart',
+ 'Gear',
+ 'Ideal',
+ 'Kitchen',
+ 'Land',
+ 'Log',
+ 'Mother',
+ 'Net',
+ 'Party',
+ 'Principle',
+ 'Relative',
+ 'Sale',
+ 'Season',
+ 'Signal',
+ 'Spirit',
+ 'Street',
+ 'Tree',
+ 'Wave',
+ 'Belt',
+ 'Bench',
+ 'Commission',
+ 'Copy',
+ 'Drop',
+ 'Minimum',
+ 'Path',
+ 'Progress',
+ 'Project',
+ 'Sea',
+ 'South',
+ 'Status',
+ 'Stuff',
+ 'Ticket',
+ 'Tour',
+ 'Angle',
+ 'Blue',
+ 'Breakfast',
+ 'Confidence',
+ 'Daughter',
+ 'Degree',
+ 'Doctor',
+ 'Dot',
+ 'Dream',
+ 'Duty',
+ 'Essay',
+ 'Father',
+ 'Fee',
+ 'Finance',
+ 'Hour',
+ 'Juice',
+ 'Luck',
+ 'Milk',
+ 'Mouth',
+ 'Peace',
+ 'Pipe',
+ 'Stable',
+ 'Storm',
+ 'Substance',
+ 'Team',
+ 'Trick',
+ 'Afternoon',
+ 'Bat',
+ 'Beach',
+ 'Blank',
+ 'Catch',
+ 'Chain',
+ 'Consideration',
+ 'Cream',
+ 'Crew',
+ 'Detail',
+ 'Gold',
+ 'Interview',
+ 'Kid',
+ 'Mark',
+ 'Mission',
+ 'Pain',
+ 'Pleasure',
+ 'Score',
+ 'Screw',
+ 'Gratitude',
+ 'Shop',
+ 'Shower',
+ 'Suit',
+ 'Tone',
+ 'Window',
+ 'Agent',
+ 'Band',
+ 'Bath',
+ 'Block',
+ 'Bone',
+ 'Calendar',
+ 'Candidate',
+ 'Cap',
+ 'Coat',
+ 'Contest',
+ 'Corner',
+ 'Court',
+ 'Cup',
+ 'District',
+ 'Door',
+ 'East',
+ 'Finger',
+ 'Garage',
+ 'Guarantee',
+ 'Hole',
+ 'Hook',
+ 'Implement',
+ 'Layer',
+ 'Lecture',
+ 'Lie',
+ 'Manner',
+ 'Meeting',
+ 'Nose',
+ 'Parking',
+ 'Partner',
+ 'Profile',
+ 'Rice',
+ 'Routine',
+ 'Schedule',
+ 'Swimming',
+ 'Telephone',
+ 'Tip',
+ 'Winter',
+ 'Airline',
+ 'Bag',
+ 'Battle',
+ 'Bed',
+ 'Bill',
+ 'Bother',
+ 'Cake',
+ 'Code',
+ 'Curve',
+ 'Designer',
+ 'Dimension',
+ 'Dress',
+ 'Ease',
+ 'Emergency',
+ 'Evening',
+ 'Extension',
+ 'Farm',
+ 'Fight',
+ 'Gap',
+ 'Grade',
+ 'Holiday',
+ 'Horror',
+ 'Horse',
+ 'Host',
+ 'Husband',
+ 'Loan',
+ 'Mistake',
+ 'Mountain',
+ 'Nail',
+ 'Noise',
+ 'Occasion',
+ 'Package',
+ 'Patient',
+ 'Pause',
+ 'Phrase',
+ 'Proof',
+ 'Race',
+ 'Relief',
+ 'Sand',
+ 'Sentence',
+ 'Shoulder',
+ 'Smoke',
+ 'Stomach',
+ 'String',
+ 'Tourist',
+ 'Towel',
+ 'Vacation',
+ 'West',
+ 'Wheel',
+ 'Wine',
+ 'Arm',
+ 'Aside',
+ 'Associate',
+ 'Bet',
+ 'Blow',
+ 'Border',
+ 'Branch',
+ 'Breast',
+ 'Brother',
+ 'Buddy',
+ 'Bunch',
+ 'Chip',
+ 'Coach',
+ 'Cross',
+ 'Document',
+ 'Draft',
+ 'Dust',
+ 'Expert',
+ 'Floor',
+ 'God',
+ 'Golf',
+ 'Habit',
+ 'Iron',
+ 'Judge',
+ 'Knife',
+ 'Landscape',
+ 'League',
+ 'Mail',
+ 'Mess',
+ 'Native',
+ 'Opening',
+ 'Parent',
+ 'Pattern',
+ 'Pin',
+ 'Pool',
+ 'Pound',
+ 'Request',
+ 'Salary',
+ 'Shame',
+ 'Shelter',
+ 'Shoe',
+ 'Silver',
+ 'Tackle',
+ 'Tank',
+ 'Trust',
+ 'Assist',
+ 'Bake',
+ 'Bar',
+ 'Bell',
+ 'Bike',
+ 'Blame',
+ 'Boy',
+ 'Brick',
+ 'Chair',
+ 'Closet',
+ 'Clue',
+ 'Collar',
+ 'Comment',
+ 'Conference',
+ 'Devil',
+ 'Diet',
+ 'Fear',
+ 'Fuel',
+ 'Glove',
+ 'Jacket',
+ 'Lunch',
+ 'Monitor',
+ 'Mortgage',
+ 'Nurse',
+ 'Pace',
+ 'Panic',
+ 'Peak',
+ 'Plane',
+ 'Reward',
+ 'Row',
+ 'Sandwich',
+ 'Shock',
+ 'Spite',
+ 'Spray',
+ 'Surprise',
+ 'Till',
+ 'Transition',
+ 'Weekend',
+ 'Welcome',
+ 'Yard',
+ 'Alarm',
+ 'Bend',
+ 'Bicycle',
+ 'Bite',
+ 'Blind',
+ 'Bottle',
+ 'Cable',
+ 'Candle',
+ 'Clerk',
+ 'Cloud',
+ 'Concert',
+ 'Counter',
+ 'Flower',
+ 'Grandfather',
+ 'Harm',
+ 'Knee',
+ 'Lawyer',
+ 'Leather',
+ 'Load',
+ 'Mirror',
+ 'Neck',
+ 'Pension',
+ 'Plate',
+ 'Purple',
+ 'Ruin',
+ 'Ship',
+ 'Skirt',
+ 'Slice',
+ 'Snow',
+ 'Specialist',
+ 'Stroke',
+ 'Switch',
+ 'Trash',
+ 'Tune',
+ 'Zone',
+ 'Anger',
+ 'Award',
+ 'Bid',
+ 'Bitter',
+ 'Boot',
+ 'Bug',
+ 'Camp',
+ 'Candy',
+ 'Carpet',
+ 'Cat',
+ 'Champion',
+ 'Channel',
+ 'Clock',
+ 'Comfort',
+ 'Cow',
+ 'Crack',
+ 'Engineer',
+ 'Entrance',
+ 'Fault',
+ 'Grass',
+ 'Guy',
+ 'Hell',
+ 'Highlight',
+ 'Incident',
+ 'Island',
+ 'Joke',
+ 'Jury',
+ 'Leg',
+ 'Lip',
+ 'Mate',
+ 'Motor',
+ 'Nerve',
+ 'Passage',
+ 'Pen',
+ 'Pride',
+ 'Priest',
+ 'Prize',
+ 'Promise',
+ 'Resident',
+ 'Resort',
+ 'Ring',
+ 'Roof',
+ 'Rope',
+ 'Sail',
+ 'Scheme',
+ 'Script',
+ 'Sock',
+ 'Station',
+ 'Toe',
+ 'Tower',
+ 'Truck',
+ 'Witness',
+ 'Asparagus',
+ 'You',
+ 'It',
+ 'Can',
+ 'Will',
+ 'If',
+ 'One',
+ 'Many',
+ 'Most',
+ 'Other',
+ 'Use',
+ 'Make',
+ 'Good',
+ 'Look',
+ 'Help',
+ 'Go',
+ 'Great',
+ 'Being',
+ 'Few',
+ 'Might',
+ 'Still',
+ 'Public',
+ 'Read',
+ 'Keep',
+ 'Start',
+ 'Give',
+ 'Human',
+ 'Local',
+ 'General',
+ 'She',
+ 'Specific',
+ 'Long',
+ 'Play',
+ 'Feel',
+ 'High',
+ 'Tonight',
+ 'Put',
+ 'Common',
+ 'Set',
+ 'Change',
+ 'Simple',
+ 'Past',
+ 'Big',
+ 'Possible',
+ 'Particular',
+ 'Today',
+ 'Major',
+ 'Personal',
+ 'Current',
+ 'National',
+ 'Cut',
+ 'Natural',
+ 'Physical',
+ 'Show',
+ 'Try',
+ 'Check',
+ 'Second',
+ 'Call',
+ 'Move',
+ 'Pay',
+ 'Let',
+ 'Increase',
+ 'Single',
+ 'Individual',
+ 'Turn',
+ 'Ask',
+ 'Buy',
+ 'Guard',
+ 'Hold',
+ 'Main',
+ 'Offer',
+ 'Potential',
+ 'Professional',
+ 'International',
+ 'Travel',
+ 'Cook',
+ 'Alternative',
+ 'Following',
+ 'Special',
+ 'Working',
+ 'Whole',
+ 'Dance',
+ 'Excuse',
+ 'Cold',
+ 'Commercial',
+ 'Low',
+ 'Purchase',
+ 'Deal',
+ 'Primary',
+ 'Worth',
+ 'Fall',
+ 'Necessary',
+ 'Positive',
+ 'Produce',
+ 'Search',
+ 'Present',
+ 'Spend',
+ 'Talk',
+ 'Creative',
+ 'Tell',
+ 'Cost',
+ 'Drive',
+ 'Green',
+ 'Support',
+ 'Glad',
+ 'Remove',
+ 'Return',
+ 'Run',
+ 'Complex',
+ 'Due',
+ 'Effective',
+ 'Middle',
+ 'Regular',
+ 'Reserve',
+ 'Independent',
+ 'Leave',
+ 'Original',
+ 'Reach',
+ 'Rest',
+ 'Serve',
+ 'Watch',
+ 'Beautiful',
+ 'Charge',
+ 'Active',
+ 'Break',
+ 'Negative',
+ 'Safe',
+ 'Stay',
+ 'Visit',
+ 'Visual',
+ 'Affect',
+ 'Cover',
+ 'Report',
+ 'Rise',
+ 'Walk',
+ 'White',
+ 'Beyond',
+ 'Junior',
+ 'Pick',
+ 'Unique',
+ 'Anything',
+ 'Classic',
+ 'Final',
+ 'Lift',
+ 'Mix',
+ 'Private',
+ 'Stop',
+ 'Teach',
+ 'Western',
+ 'Concern',
+ 'Familiar',
+ 'Fly',
+ 'Official',
+ 'Broad',
+ 'Comfortable',
+ 'Gain',
+ 'Maybe',
+ 'Rich',
+ 'Save',
+ 'Stand',
+ 'Young',
+ 'Heavy',
+ 'Hello',
+ 'Lead',
+ 'Listen',
+ 'Valuable',
+ 'Worry',
+ 'Handle',
+ 'Leading',
+ 'Meet',
+ 'Release',
+ 'Sell',
+ 'Finish',
+ 'Normal',
+ 'Press',
+ 'Ride',
+ 'Secret',
+ 'Spread',
+ 'Spring',
+ 'Tough',
+ 'Wait',
+ 'Brown',
+ 'Deep',
+ 'Display',
+ 'Flow',
+ 'Hit',
+ 'Objective',
+ 'Shoot',
+ 'Touch',
+ 'Cancel',
+ 'Chemical',
+ 'Cry',
+ 'Dump',
+ 'Extreme',
+ 'Push',
+ 'Conflict',
+ 'Eat',
+ 'Fill',
+ 'Formal',
+ 'Jump',
+ 'Kick',
+ 'Opposite',
+ 'Pass',
+ 'Pitch',
+ 'Remote',
+ 'Total',
+ 'Treat',
+ 'Vast',
+ 'Abuse',
+ 'Beat',
+ 'Burn',
+ 'Deposit',
+ 'Print',
+ 'Raise',
+ 'Sleep',
+ 'Somewhere',
+ 'Advance',
+ 'Anywhere',
+ 'Consist',
+ 'Dark',
+ 'Double',
+ 'Draw',
+ 'Equal',
+ 'Fix',
+ 'Hire',
+ 'Internal',
+ 'Join',
+ 'Kill',
+ 'Sensitive',
+ 'Tap',
+ 'Win',
+ 'Attack',
+ 'Claim',
+ 'Constant',
+ 'Drag',
+ 'Drink',
+ 'Guess',
+ 'Minor',
+ 'Pull',
+ 'Raw',
+ 'Soft',
+ 'Solid',
+ 'Wear',
+ 'Weird',
+ 'Wonder',
+ 'Annual',
+ 'Count',
+ 'Dead',
+ 'Doubt',
+ 'Feed',
+ 'Forever',
+ 'Impress',
+ 'Nobody',
+ 'Repeat',
+ 'Round',
+ 'Sing',
+ 'Slide',
+ 'Strip',
+ 'Whereas',
+ 'Wish',
+ 'Combine',
+ 'Command',
+ 'Dig',
+ 'Divide',
+ 'Equivalent',
+ 'Hang',
+ 'Hunt',
+ 'Initial',
+ 'March',
+ 'Mention',
+ 'Spiritual',
+ 'Survey',
+ 'Tie',
+ 'Adult',
+ 'Brief',
+ 'Crazy',
+ 'Escape',
+ 'Gather',
+ 'Hate',
+ 'Prior',
+ 'Repair',
+ 'Rough',
+ 'Sad',
+ 'Scratch',
+ 'Sick',
+ 'Strike',
+ 'Employ',
+ 'External',
+ 'Hurt',
+ 'Illegal',
+ 'Laugh',
+ 'Lay',
+ 'Mobile',
+ 'Nasty',
+ 'Ordinary',
+ 'Respond',
+ 'Royal',
+ 'Senior',
+ 'Split',
+ 'Strain',
+ 'Struggle',
+ 'Swim',
+ 'Train',
+ 'Upper',
+ 'Wash',
+ 'Yellow',
+ 'Convert',
+ 'Crash',
+ 'Dependent',
+ 'Fold',
+ 'Funny',
+ 'Grab',
+ 'Hide',
+ 'Miss',
+ 'Permit',
+ 'Quote',
+ 'Recover',
+ 'Resolve',
+ 'Roll',
+ 'Sink',
+ 'Slip',
+ 'Spare',
+ 'Suspect',
+ 'Sweet',
+ 'Swing',
+ 'Twist',
+ 'Upstairs',
+ 'Usual',
+ 'Abroad',
+ 'Brave',
+ 'Calm',
+ 'Concentrate',
+ 'Estimate',
+ 'Grand',
+ 'Male',
+ 'Mine',
+ 'Prompt',
+ 'Quiet',
+ 'Refuse',
+ 'Regret',
+ 'Reveal',
+ 'Rush',
+ 'Shake',
+ 'Shift',
+ 'Shine',
+ 'Steal',
+ 'Suck',
+ 'Surround',
+ 'Anybody',
+ 'Bear',
+ 'Brilliant',
+ 'Dare',
+ 'Dear',
+ 'Delay',
+ 'Drunk',
+ 'Female',
+ 'Hurry',
+ 'Inevitable',
+ 'Invite',
+ 'Kiss',
+ 'Neat',
+ 'Pop',
+ 'Punch',
+ 'Quit',
+ 'Reply',
+ 'Representative',
+ 'Resist',
+ 'Rip',
+ 'Rub',
+ 'Silly',
+ 'Smile',
+ 'Spell',
+ 'Stretch',
+ 'Stupid',
+ 'Tear',
+ 'Temporary',
+ 'Tomorrow',
+ 'Wake',
+ 'Wrap',
+ 'Yesterday',
+]
diff --git a/awxkit/awxkit/ws.py b/awxkit/awxkit/ws.py
index ee39a5e990..41380d406e 100644
--- a/awxkit/awxkit/ws.py
+++ b/awxkit/awxkit/ws.py
@@ -84,12 +84,9 @@ class WSClient(object):
auth_cookie = ''
pref = 'wss://' if self._use_ssl else 'ws://'
url = '{0}{1.hostname}:{1.port}/websocket/'.format(pref, self)
- self.ws = websocket.WebSocketApp(url,
- on_open=self._on_open,
- on_message=self._on_message,
- on_error=self._on_error,
- on_close=self._on_close,
- cookie=auth_cookie)
+ self.ws = websocket.WebSocketApp(
+ url, on_open=self._on_open, on_message=self._on_message, on_error=self._on_error, on_close=self._on_close, cookie=auth_cookie
+ )
self._message_cache = []
self._should_subscribe_to_pending_job = False
self._pending_unsubscribe = threading.Event()
@@ -199,12 +196,8 @@ class WSClient(object):
message = json.loads(message)
log.debug('received message: {}'.format(message))
- if all([message.get('group_name') == 'jobs',
- message.get('status') == 'pending',
- message.get('unified_job_id'),
- self._should_subscribe_to_pending_job]):
- if bool(message.get('project_id')) == (
- self._should_subscribe_to_pending_job['events'] == 'project_update_events'):
+ if all([message.get('group_name') == 'jobs', message.get('status') == 'pending', message.get('unified_job_id'), self._should_subscribe_to_pending_job]):
+ if bool(message.get('project_id')) == (self._should_subscribe_to_pending_job['events'] == 'project_update_events'):
self._update_subscription(message['unified_job_id'])
ret = self._recv_queue.put(message)
diff --git a/awxkit/awxkit/yaml_file.py b/awxkit/awxkit/yaml_file.py
index 750ee3fbb1..49924cef93 100644
--- a/awxkit/awxkit/yaml_file.py
+++ b/awxkit/awxkit/yaml_file.py
@@ -12,7 +12,6 @@ file_path_cache = {}
class Loader(yaml.SafeLoader):
-
def __init__(self, stream):
self._root = os.path.split(stream.name)[0]
super(Loader, self).__init__(stream)
@@ -82,6 +81,7 @@ def load_file(filename):
random_thing: "{random_string:24}"
"""
from py.path import local
+
if filename is None:
this_file = os.path.abspath(__file__)
path = local(this_file).new(basename='../data.yaml')
diff --git a/awxkit/setup.py b/awxkit/setup.py
index b446ba0a90..23f4e161cb 100644
--- a/awxkit/setup.py
+++ b/awxkit/setup.py
@@ -68,11 +68,7 @@ setup(
'requests',
],
python_requires=">=3.6",
- extras_require={
- 'formatting': ['jq'],
- 'websockets': ['websocket-client==0.57.0'],
- 'crypto': ['cryptography']
- },
+ extras_require={'formatting': ['jq'], 'websockets': ['websocket-client==0.57.0'], 'crypto': ['cryptography']},
license='Apache 2.0',
classifiers=[
'Development Status :: 5 - Production/Stable',
@@ -87,10 +83,5 @@ setup(
'Topic :: System :: Software Distribution',
'Topic :: System :: Systems Administration',
],
- entry_points={
- 'console_scripts': [
- 'akit=awxkit.scripts.basic_session:load_interactive',
- 'awx=awxkit.cli:run'
- ]
- }
+ entry_points={'console_scripts': ['akit=awxkit.scripts.basic_session:load_interactive', 'awx=awxkit.cli:run']},
)
diff --git a/awxkit/test/cli/test_client.py b/awxkit/test/cli/test_client.py
index e792b6c267..9a63e37c55 100644
--- a/awxkit/test/cli/test_client.py
+++ b/awxkit/test/cli/test_client.py
@@ -7,7 +7,6 @@ from awxkit.cli import run, CLI
class MockedCLI(CLI):
-
def fetch_version_root(self):
pass
@@ -17,9 +16,7 @@ class MockedCLI(CLI):
@property
def json(self):
- return {
- 'users': None
- }
+ return {'users': None}
@pytest.mark.parametrize('help_param', ['-h', '--help'])
@@ -29,10 +26,7 @@ def test_help(capfd, help_param):
out, err = capfd.readouterr()
assert "usage:" in out
- for snippet in (
- '--conf.host https://example.awx.org]',
- '-v, --verbose'
- ):
+ for snippet in ('--conf.host https://example.awx.org]', '-v, --verbose'):
assert snippet in out
@@ -59,8 +53,5 @@ def test_list_resources(capfd, resource):
_, out = capfd.readouterr()
assert "usage:" in out
- for snippet in (
- '--conf.host https://example.awx.org]',
- '-v, --verbose'
- ):
+ for snippet in ('--conf.host https://example.awx.org]', '-v, --verbose'):
assert snippet in out
diff --git a/awxkit/test/cli/test_config.py b/awxkit/test/cli/test_config.py
index 3154fdb081..61b6b4c54d 100644
--- a/awxkit/test/cli/test_config.py
+++ b/awxkit/test/cli/test_config.py
@@ -4,16 +4,15 @@ from requests.exceptions import ConnectionError
from awxkit.cli import CLI
from awxkit import config
+
def test_host_from_environment():
cli = CLI()
- cli.parse_args(
- ['awx'],
- env={'TOWER_HOST': 'https://xyz.local'}
- )
+ cli.parse_args(['awx'], env={'TOWER_HOST': 'https://xyz.local'})
with pytest.raises(ConnectionError):
cli.connect()
assert config.base_url == 'https://xyz.local'
+
def test_host_from_argv():
cli = CLI()
cli.parse_args(['awx', '--conf.host', 'https://xyz.local'])
@@ -21,43 +20,30 @@ def test_host_from_argv():
cli.connect()
assert config.base_url == 'https://xyz.local'
+
def test_username_and_password_from_environment():
cli = CLI()
- cli.parse_args(
- ['awx'],
- env={
- 'TOWER_USERNAME': 'mary',
- 'TOWER_PASSWORD': 'secret'
- }
- )
+ cli.parse_args(['awx'], env={'TOWER_USERNAME': 'mary', 'TOWER_PASSWORD': 'secret'})
with pytest.raises(ConnectionError):
cli.connect()
assert config.credentials.default.username == 'mary'
assert config.credentials.default.password == 'secret'
+
def test_username_and_password_argv():
cli = CLI()
- cli.parse_args([
- 'awx', '--conf.username', 'mary', '--conf.password', 'secret'
- ])
+ cli.parse_args(['awx', '--conf.username', 'mary', '--conf.password', 'secret'])
with pytest.raises(ConnectionError):
cli.connect()
assert config.credentials.default.username == 'mary'
assert config.credentials.default.password == 'secret'
+
def test_config_precedence():
cli = CLI()
- cli.parse_args(
- [
- 'awx', '--conf.username', 'mary', '--conf.password', 'secret'
- ],
- env={
- 'TOWER_USERNAME': 'IGNORE',
- 'TOWER_PASSWORD': 'IGNORE'
- }
- )
+ cli.parse_args(['awx', '--conf.username', 'mary', '--conf.password', 'secret'], env={'TOWER_USERNAME': 'IGNORE', 'TOWER_PASSWORD': 'IGNORE'})
with pytest.raises(ConnectionError):
cli.connect()
diff --git a/awxkit/test/cli/test_format.py b/awxkit/test/cli/test_format.py
index 7327f91518..adbe0ef463 100644
--- a/awxkit/test/cli/test_format.py
+++ b/awxkit/test/cli/test_format.py
@@ -11,19 +11,17 @@ from awxkit.cli.resource import Import
def test_json_empty_list():
- page = Page.from_json({
- 'results': []
- })
+ page = Page.from_json({'results': []})
formatted = format_response(page)
assert json.loads(formatted) == {'results': []}
+
def test_yaml_empty_list():
- page = Page.from_json({
- 'results': []
- })
+ page = Page.from_json({'results': []})
formatted = format_response(page, fmt='yaml')
assert yaml.safe_load(formatted) == {'results': []}
+
def test_json_list():
users = {
'results': [
@@ -36,6 +34,7 @@ def test_json_list():
formatted = format_response(page)
assert json.loads(formatted) == users
+
def test_yaml_list():
users = {
'results': [
diff --git a/awxkit/test/cli/test_options.py b/awxkit/test/cli/test_options.py
index 83bb4ac36a..fc2e53f957 100644
--- a/awxkit/test/cli/test_options.py
+++ b/awxkit/test/cli/test_options.py
@@ -11,13 +11,11 @@ from awxkit.cli.options import ResourceOptionsParser
class ResourceOptionsParser(ResourceOptionsParser):
-
def get_allowed_options(self):
self.allowed_options = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
class OptionsPage(Page):
-
def options(self):
return self
@@ -33,30 +31,31 @@ class OptionsPage(Page):
class TestOptions(unittest.TestCase):
-
def setUp(self):
_parser = argparse.ArgumentParser()
self.parser = _parser.add_subparsers(help='action')
def test_list(self):
- page = OptionsPage.from_json({
- 'actions': {
- 'GET': {},
- 'POST': {},
+ page = OptionsPage.from_json(
+ {
+ 'actions': {
+ 'GET': {},
+ 'POST': {},
+ }
}
- })
+ )
ResourceOptionsParser(None, page, 'users', self.parser)
assert 'list' in self.parser.choices
def test_list_filtering(self):
- page = OptionsPage.from_json({
- 'actions': {
- 'GET': {},
- 'POST': {
- 'first_name': {'type': 'string'}
- },
+ page = OptionsPage.from_json(
+ {
+ 'actions': {
+ 'GET': {},
+ 'POST': {'first_name': {'type': 'string'}},
+ }
}
- })
+ )
options = ResourceOptionsParser(None, page, 'users', self.parser)
options.build_query_arguments('list', 'POST')
assert 'list' in self.parser.choices
@@ -66,14 +65,14 @@ class TestOptions(unittest.TestCase):
assert '--first_name TEXT' in out.getvalue()
def test_list_not_filterable(self):
- page = OptionsPage.from_json({
- 'actions': {
- 'GET': {},
- 'POST': {
- 'middle_name': {'type': 'string', 'filterable': False}
- },
+ page = OptionsPage.from_json(
+ {
+ 'actions': {
+ 'GET': {},
+ 'POST': {'middle_name': {'type': 'string', 'filterable': False}},
+ }
}
- })
+ )
options = ResourceOptionsParser(None, page, 'users', self.parser)
options.build_query_arguments('list', 'POST')
assert 'list' in self.parser.choices
@@ -83,16 +82,18 @@ class TestOptions(unittest.TestCase):
assert '--middle_name' not in out.getvalue()
def test_creation_optional_argument(self):
- page = OptionsPage.from_json({
- 'actions': {
- 'POST': {
- 'first_name': {
- 'type': 'string',
- 'help_text': 'Please specify your first name',
- }
- },
+ page = OptionsPage.from_json(
+ {
+ 'actions': {
+ 'POST': {
+ 'first_name': {
+ 'type': 'string',
+ 'help_text': 'Please specify your first name',
+ }
+ },
+ }
}
- })
+ )
options = ResourceOptionsParser(None, page, 'users', self.parser)
options.build_query_arguments('create', 'POST')
assert 'create' in self.parser.choices
@@ -102,17 +103,13 @@ class TestOptions(unittest.TestCase):
assert '--first_name TEXT Please specify your first name' in out.getvalue()
def test_creation_required_argument(self):
- page = OptionsPage.from_json({
- 'actions': {
- 'POST': {
- 'username': {
- 'type': 'string',
- 'help_text': 'Please specify a username',
- 'required': True
- }
- },
+ page = OptionsPage.from_json(
+ {
+ 'actions': {
+ 'POST': {'username': {'type': 'string', 'help_text': 'Please specify a username', 'required': True}},
+ }
}
- })
+ )
options = ResourceOptionsParser(None, page, 'users', self.parser)
options.build_query_arguments('create', 'POST')
assert 'create' in self.parser.choices
@@ -122,13 +119,13 @@ class TestOptions(unittest.TestCase):
assert '--username TEXT Please specify a username'
def test_integer_argument(self):
- page = OptionsPage.from_json({
- 'actions': {
- 'POST': {
- 'max_hosts': {'type': 'integer'}
- },
+ page = OptionsPage.from_json(
+ {
+ 'actions': {
+ 'POST': {'max_hosts': {'type': 'integer'}},
+ }
}
- })
+ )
options = ResourceOptionsParser(None, page, 'organizations', self.parser)
options.build_query_arguments('create', 'POST')
assert 'create' in self.parser.choices
@@ -138,13 +135,13 @@ class TestOptions(unittest.TestCase):
assert '--max_hosts INTEGER' in out.getvalue()
def test_boolean_argument(self):
- page = OptionsPage.from_json({
- 'actions': {
- 'POST': {
- 'diff_mode': {'type': 'boolean'}
- },
+ page = OptionsPage.from_json(
+ {
+ 'actions': {
+ 'POST': {'diff_mode': {'type': 'boolean'}},
+ }
}
- })
+ )
options = ResourceOptionsParser(None, page, 'users', self.parser)
options.build_query_arguments('create', 'POST')
assert 'create' in self.parser.choices
@@ -154,23 +151,25 @@ class TestOptions(unittest.TestCase):
assert '--diff_mode BOOLEAN' in out.getvalue()
def test_choices(self):
- page = OptionsPage.from_json({
- 'actions': {
- 'POST': {
- 'verbosity': {
- 'type': 'integer',
- 'choices': [
- (0, '0 (Normal)'),
- (1, '1 (Verbose)'),
- (2, '2 (More Verbose)'),
- (3, '3 (Debug)'),
- (4, '4 (Connection Debug)'),
- (5, '5 (WinRM Debug)'),
- ]
- }
- },
+ page = OptionsPage.from_json(
+ {
+ 'actions': {
+ 'POST': {
+ 'verbosity': {
+ 'type': 'integer',
+ 'choices': [
+ (0, '0 (Normal)'),
+ (1, '1 (Verbose)'),
+ (2, '2 (More Verbose)'),
+ (3, '3 (Debug)'),
+ (4, '4 (Connection Debug)'),
+ (5, '5 (WinRM Debug)'),
+ ],
+ }
+ },
+ }
}
- })
+ )
options = ResourceOptionsParser(None, page, 'users', self.parser)
options.build_query_arguments('create', 'POST')
assert 'create' in self.parser.choices
@@ -181,9 +180,7 @@ class TestOptions(unittest.TestCase):
def test_actions_with_primary_key(self):
for method in ('get', 'modify', 'delete'):
- page = OptionsPage.from_json({
- 'actions': {'GET': {}, 'POST': {}}
- })
+ page = OptionsPage.from_json({'actions': {'GET': {}, 'POST': {}}})
ResourceOptionsParser(None, page, 'jobs', self.parser)
assert method in self.parser.choices
@@ -193,19 +190,20 @@ class TestOptions(unittest.TestCase):
class TestSettingsOptions(unittest.TestCase):
-
def setUp(self):
_parser = argparse.ArgumentParser()
self.parser = _parser.add_subparsers(help='action')
def test_list(self):
- page = OptionsPage.from_json({
- 'actions': {
- 'GET': {},
- 'POST': {},
- 'PUT': {},
+ page = OptionsPage.from_json(
+ {
+ 'actions': {
+ 'GET': {},
+ 'POST': {},
+ 'PUT': {},
+ }
}
- })
+ )
page.endpoint = '/settings/all/'
ResourceOptionsParser(None, page, 'settings', self.parser)
assert 'list' in self.parser.choices
diff --git a/awxkit/test/test_credentials.py b/awxkit/test/test_credentials.py
index 714550119e..6adc5e9332 100644
--- a/awxkit/test/test_credentials.py
+++ b/awxkit/test/test_credentials.py
@@ -14,32 +14,39 @@ def set_config_cred_to_desired(config, location):
config_ref = config_ref[_location]
setattr(config_ref, split[-1], 'desired')
-class MockCredentialType(object):
+class MockCredentialType(object):
def __init__(self, name, kind, managed_by_tower=True):
self.name = name
self.kind = kind
self.managed_by_tower = managed_by_tower
-@pytest.mark.parametrize('field, kind, config_cred, desired_field, desired_value',
- [('field', 'ssh', PseudoNamespace(field=123), 'field', 123),
- ('subscription', 'azure', PseudoNamespace(subscription_id=123), 'subscription', 123),
- ('project_id', 'gce', PseudoNamespace(project=123), 'project', 123),
- ('authorize_password', 'net', PseudoNamespace(authorize=123), 'authorize_password', 123)])
+
+@pytest.mark.parametrize(
+ 'field, kind, config_cred, desired_field, desired_value',
+ [
+ ('field', 'ssh', PseudoNamespace(field=123), 'field', 123),
+ ('subscription', 'azure', PseudoNamespace(subscription_id=123), 'subscription', 123),
+ ('project_id', 'gce', PseudoNamespace(project=123), 'project', 123),
+ ('authorize_password', 'net', PseudoNamespace(authorize=123), 'authorize_password', 123),
+ ],
+)
def test_get_payload_field_and_value_from_config_cred(field, kind, config_cred, desired_field, desired_value):
- ret_field, ret_val = credentials.get_payload_field_and_value_from_kwargs_or_config_cred(field, kind, {},
- config_cred)
+ ret_field, ret_val = credentials.get_payload_field_and_value_from_kwargs_or_config_cred(field, kind, {}, config_cred)
assert ret_field == desired_field
assert ret_val == desired_value
-@pytest.mark.parametrize('field, kind, kwargs, desired_field, desired_value',
- [('field', 'ssh', dict(field=123), 'field', 123),
- ('subscription', 'azure', dict(subscription=123), 'subscription', 123),
- ('project_id', 'gce', dict(project_id=123), 'project', 123),
- ('authorize_password', 'net', dict(authorize_password=123), 'authorize_password', 123)])
+@pytest.mark.parametrize(
+ 'field, kind, kwargs, desired_field, desired_value',
+ [
+ ('field', 'ssh', dict(field=123), 'field', 123),
+ ('subscription', 'azure', dict(subscription=123), 'subscription', 123),
+ ('project_id', 'gce', dict(project_id=123), 'project', 123),
+ ('authorize_password', 'net', dict(authorize_password=123), 'authorize_password', 123),
+ ],
+)
def test_get_payload_field_and_value_from_kwarg(field, kind, kwargs, desired_field, desired_value):
- ret_field, ret_val = credentials.get_payload_field_and_value_from_kwargs_or_config_cred(field, kind, kwargs,
- PseudoNamespace())
+ ret_field, ret_val = credentials.get_payload_field_and_value_from_kwargs_or_config_cred(field, kind, kwargs, PseudoNamespace())
assert ret_field == desired_field
assert ret_val == desired_value
diff --git a/awxkit/test/test_dependency_resolver.py b/awxkit/test/test_dependency_resolver.py
index 4a7e85e3c4..b713a73adc 100644
--- a/awxkit/test/test_dependency_resolver.py
+++ b/awxkit/test/test_dependency_resolver.py
@@ -21,7 +21,6 @@ class MockHasCreate(has_create.HasCreate):
class A(MockHasCreate):
-
def create(self, **kw):
return self
@@ -87,13 +86,12 @@ class H(MockHasCreate):
optional_dependencies = [E, A]
- def create(self, a=None, e=None, **kw):
+ def create(self, a=None, e=None, **kw):
self.create_and_update_dependencies(*filter_by_class((a, A), (e, E)))
return self
class MultipleWordClassName(MockHasCreate):
-
def create(self, **kw):
return self
@@ -102,7 +100,7 @@ class AnotherMultipleWordClassName(MockHasCreate):
optional_dependencies = [MultipleWordClassName]
- def create(self, multiple_word_class_name=None, **kw):
+ def create(self, multiple_word_class_name=None, **kw):
self.create_and_update_dependencies(*filter_by_class((multiple_word_class_name, MultipleWordClassName)))
return self
@@ -183,19 +181,17 @@ def test_optional_dependency_graph_with_additional():
def test_creation_order():
"""confirms that `has_create.creation_order()` returns a valid creation order in the desired list of sets format"""
- dependency_graph = dict(eight=set(['seven', 'six']),
- seven=set(['five']),
- six=set(),
- five=set(['two', 'one']),
- four=set(['one']),
- three=set(['two']),
- two=set(['one']),
- one=set())
- desired = [set(['one', 'six']),
- set(['two', 'four']),
- set(['three', 'five']),
- set(['seven']),
- set(['eight'])]
+ dependency_graph = dict(
+ eight=set(['seven', 'six']),
+ seven=set(['five']),
+ six=set(),
+ five=set(['two', 'one']),
+ four=set(['one']),
+ three=set(['two']),
+ two=set(['one']),
+ one=set(),
+ )
+ desired = [set(['one', 'six']), set(['two', 'four']), set(['three', 'five']), set(['seven']), set(['eight'])]
assert has_create.creation_order(dependency_graph) == desired
@@ -203,14 +199,16 @@ def test_creation_order_with_loop():
"""confirms that `has_create.creation_order()` raises toposort.CircularDependencyError when evaluating
a cyclic dependency graph
"""
- dependency_graph = dict(eight=set(['seven', 'six']),
- seven=set(['five']),
- six=set(),
- five=set(['two', 'one']),
- four=set(['one']),
- three=set(['two']),
- two=set(['one']),
- one=set(['eight']))
+ dependency_graph = dict(
+ eight=set(['seven', 'six']),
+ seven=set(['five']),
+ six=set(),
+ five=set(['two', 'one']),
+ four=set(['one']),
+ three=set(['two']),
+ two=set(['one']),
+ one=set(['eight']),
+ )
with pytest.raises(CircularDependencyError):
assert has_create.creation_order(dependency_graph)
@@ -239,9 +237,11 @@ class Five(MockHasCreate):
class IsntAHasCreate(object):
pass
+
class Six(MockHasCreate, IsntAHasCreate):
dependencies = [Two]
+
class Seven(MockHasCreate):
dependencies = [IsntAHasCreate]
@@ -265,8 +265,7 @@ def test_separate_async_optionals_three_exist():
the class that has shared item as a dependency occurs first in a separate creation group
"""
order = has_create.creation_order(has_create.optional_dependency_graph(Five, Four, Three))
- assert has_create.separate_async_optionals(order) == [set([One]), set([Two]), set([Three]),
- set([Five]), set([Four])]
+ assert has_create.separate_async_optionals(order) == [set([One]), set([Two]), set([Three]), set([Five]), set([Four])]
def test_separate_async_optionals_not_has_create():
@@ -345,8 +344,7 @@ def test_dependency_resolution_complete():
for item in (h, a, e, d, c, b):
if item._dependency_store:
- assert all(item._dependency_store.values()
- ), "{0} missing dependency: {0._dependency_store}".format(item)
+ assert all(item._dependency_store.values()), "{0} missing dependency: {0._dependency_store}".format(item)
assert a == b._dependency_store[A], "Duplicate dependency detected"
assert a == c._dependency_store[A], "Duplicate dependency detected"
@@ -468,7 +466,6 @@ def test_teardown_ds_cleared():
class OneWithArgs(MockHasCreate):
-
def create(self, **kw):
self.kw = kw
return self
@@ -492,18 +489,17 @@ class ThreeWithArgs(MockHasCreate):
optional_dependencies = [TwoWithArgs]
def create(self, one_with_args=OneWithArgs, two_with_args=None, **kw):
- self.create_and_update_dependencies(*filter_by_class((one_with_args, OneWithArgs),
- (two_with_args, TwoWithArgs)))
+ self.create_and_update_dependencies(*filter_by_class((one_with_args, OneWithArgs), (two_with_args, TwoWithArgs)))
self.kw = kw
return self
+
class FourWithArgs(MockHasCreate):
dependencies = [TwoWithArgs, ThreeWithArgs]
def create(self, two_with_args=TwoWithArgs, three_with_args=ThreeWithArgs, **kw):
- self.create_and_update_dependencies(*filter_by_class((two_with_args, TwoWithArgs),
- (three_with_args, ThreeWithArgs)))
+ self.create_and_update_dependencies(*filter_by_class((two_with_args, TwoWithArgs), (three_with_args, ThreeWithArgs)))
self.kw = kw
return self
@@ -536,10 +532,9 @@ def test_no_tuple_for_class_arg_causes_shared_dependencies_nested_staggering():
def test_tuple_for_class_arg_causes_unshared_dependencies_when_downstream():
"""Confirms that provided arg-tuple for dependency type is applied instead of chained dependency"""
- three_wa = ThreeWithArgs().create(two_with_args=(TwoWithArgs, dict(one_with_args=False,
- make_one_with_args=True,
- two_with_args_kw_arg=234)),
- three_with_args_kw_arg=345)
+ three_wa = ThreeWithArgs().create(
+ two_with_args=(TwoWithArgs, dict(one_with_args=False, make_one_with_args=True, two_with_args_kw_arg=234)), three_with_args_kw_arg=345
+ )
assert isinstance(three_wa.ds.one_with_args, OneWithArgs)
assert isinstance(three_wa.ds.two_with_args, TwoWithArgs)
assert isinstance(three_wa.ds.two_with_args.ds.one_with_args, OneWithArgs)
@@ -552,13 +547,12 @@ def test_tuple_for_class_arg_causes_unshared_dependencies_when_downstream():
def test_tuples_for_class_arg_cause_unshared_dependencies_when_downstream():
"""Confirms that provided arg-tuple for dependency type is applied instead of chained dependency"""
- four_wa = FourWithArgs().create(two_with_args=(TwoWithArgs, dict(one_with_args=False,
- make_one_with_args=True,
- two_with_args_kw_arg=456)),
- # No shared dependencies with four_wa.ds.two_with_args
- three_with_args=(ThreeWithArgs, dict(one_with_args=(OneWithArgs, {}),
- two_with_args=False)),
- four_with_args_kw=567)
+ four_wa = FourWithArgs().create(
+ two_with_args=(TwoWithArgs, dict(one_with_args=False, make_one_with_args=True, two_with_args_kw_arg=456)),
+ # No shared dependencies with four_wa.ds.two_with_args
+ three_with_args=(ThreeWithArgs, dict(one_with_args=(OneWithArgs, {}), two_with_args=False)),
+ four_with_args_kw=567,
+ )
assert isinstance(four_wa.ds.two_with_args, TwoWithArgs)
assert isinstance(four_wa.ds.three_with_args, ThreeWithArgs)
assert isinstance(four_wa.ds.two_with_args.ds.one_with_args, OneWithArgs)
@@ -575,25 +569,21 @@ class NotHasCreate(object):
class MixinUserA(MockHasCreate, NotHasCreate):
-
def create(self, **kw):
return self
class MixinUserB(MockHasCreate, NotHasCreate):
-
def create(self, **kw):
return self
class MixinUserC(MixinUserB):
-
def create(self, **kw):
return self
class MixinUserD(MixinUserC):
-
def create(self, **kw):
return self
@@ -646,17 +636,12 @@ class DynamicallyDeclaresNotHasCreateDependency(MockHasCreate):
dependencies = [NotHasCreate]
def create(self, not_has_create=MixinUserA):
- dynamic_dependency = dict(mixinusera=MixinUserA,
- mixinuserb=MixinUserB,
- mixinuserc=MixinUserC)
+ dynamic_dependency = dict(mixinusera=MixinUserA, mixinuserb=MixinUserB, mixinuserc=MixinUserC)
self.create_and_update_dependencies(dynamic_dependency[not_has_create])
return self
-@pytest.mark.parametrize('dependency,dependency_class',
- [('mixinusera', MixinUserA),
- ('mixinuserb', MixinUserB),
- ('mixinuserc', MixinUserC)])
+@pytest.mark.parametrize('dependency,dependency_class', [('mixinusera', MixinUserA), ('mixinuserb', MixinUserB), ('mixinuserc', MixinUserC)])
def test_subclass_or_parent_dynamic_not_has_create_dependency_declaration(dependency, dependency_class):
"""Confirms that dependencies that dynamically declare dependencies subclassed from not HasCreate
are properly linked
@@ -670,17 +655,12 @@ class DynamicallyDeclaresHasCreateDependency(MockHasCreate):
dependencies = [MixinUserB]
def create(self, mixin_user_b=MixinUserB):
- dynamic_dependency = dict(mixinuserb=MixinUserB,
- mixinuserc=MixinUserC,
- mixinuserd=MixinUserD)
+ dynamic_dependency = dict(mixinuserb=MixinUserB, mixinuserc=MixinUserC, mixinuserd=MixinUserD)
self.create_and_update_dependencies(dynamic_dependency[mixin_user_b])
return self
-@pytest.mark.parametrize('dependency,dependency_class',
- [('mixinuserb', MixinUserB),
- ('mixinuserc', MixinUserC),
- ('mixinuserd', MixinUserD)])
+@pytest.mark.parametrize('dependency,dependency_class', [('mixinuserb', MixinUserB), ('mixinuserc', MixinUserC), ('mixinuserd', MixinUserD)])
def test_subclass_or_parent_dynamic_has_create_dependency_declaration(dependency, dependency_class):
"""Confirms that dependencies that dynamically declare dependencies subclassed from not HasCreate
are properly linked
diff --git a/awxkit/test/test_registry.py b/awxkit/test/test_registry.py
index bfc3ca09ff..b0f0f2527f 100644
--- a/awxkit/test/test_registry.py
+++ b/awxkit/test/test_registry.py
@@ -169,8 +169,7 @@ def test_wildcard_and_specific_method_registration_acts_as_default(reg):
def test_multiple_method_registrations_disallowed_for_single_path_single_registration(reg, method):
with pytest.raises(TypeError) as e:
reg.register((('some_path', method), ('some_path', method)), One)
- assert str(e.value) == ('"{0.pattern}" already has registered method "{1}"'
- .format(reg.url_pattern('some_path'), method))
+ assert str(e.value) == ('"{0.pattern}" already has registered method "{1}"'.format(reg.url_pattern('some_path'), method))
@pytest.mark.parametrize('method', ('method', '.*'))
@@ -178,8 +177,7 @@ def test_multiple_method_registrations_disallowed_for_single_path_multiple_regis
reg.register('some_path', method, One)
with pytest.raises(TypeError) as e:
reg.register('some_path', method, One)
- assert str(e.value) == ('"{0.pattern}" already has registered method "{1}"'
- .format(reg.url_pattern('some_path'), method))
+ assert str(e.value) == ('"{0.pattern}" already has registered method "{1}"'.format(reg.url_pattern('some_path'), method))
def test_paths_can_be_patterns(reg):
@@ -188,10 +186,9 @@ def test_paths_can_be_patterns(reg):
def test_mixed_form_single_registration(reg):
- reg.register([('some_path_one', 'method_one'),
- 'some_path_two',
- ('some_path_three', ('method_two', 'method_three')),
- 'some_path_four', 'some_path_five'], One)
+ reg.register(
+ [('some_path_one', 'method_one'), 'some_path_two', ('some_path_three', ('method_two', 'method_three')), 'some_path_four', 'some_path_five'], One
+ )
assert reg.get('some_path_one', 'method_one') is One
assert reg.get('some_path_one') is None
assert reg.get('some_path_one', 'nonexistent') is None
@@ -209,10 +206,9 @@ def test_mixed_form_single_registration(reg):
def test_mixed_form_single_registration_with_methodless_default(reg):
reg.setdefault(One)
- reg.register([('some_path_one', 'method_one'),
- 'some_path_two',
- ('some_path_three', ('method_two', 'method_three')),
- 'some_path_four', 'some_path_five'], Two)
+ reg.register(
+ [('some_path_one', 'method_one'), 'some_path_two', ('some_path_three', ('method_two', 'method_three')), 'some_path_four', 'some_path_five'], Two
+ )
assert reg.get('some_path_one', 'method_one') is Two
assert reg.get('some_path_one') is One
assert reg.get('some_path_one', 'nonexistent') is One
@@ -230,10 +226,9 @@ def test_mixed_form_single_registration_with_methodless_default(reg):
def test_mixed_form_single_registration_with_method_default(reg):
reg.setdefault('existent', One)
- reg.register([('some_path_one', 'method_one'),
- 'some_path_two',
- ('some_path_three', ('method_two', 'method_three')),
- 'some_path_four', 'some_path_five'], Two)
+ reg.register(
+ [('some_path_one', 'method_one'), 'some_path_two', ('some_path_three', ('method_two', 'method_three')), 'some_path_four', 'some_path_five'], Two
+ )
assert reg.get('some_path_one', 'method_one') is Two
assert reg.get('some_path_one') is None
assert reg.get('some_path_one', 'existent') is One
diff --git a/awxkit/test/test_utils.py b/awxkit/test/test_utils.py
index 5f497d2e93..1a0a5412f2 100644
--- a/awxkit/test/test_utils.py
+++ b/awxkit/test/test_utils.py
@@ -9,60 +9,68 @@ from awxkit import utils
from awxkit import exceptions as exc
-@pytest.mark.parametrize('inp, out',
- [[True, True],
- [False, False],
- [1, True],
- [0, False],
- [1.0, True],
- [0.0, False],
- ['TrUe', True],
- ['FalSe', False],
- ['yEs', True],
- ['No', False],
- ['oN', True],
- ['oFf', False],
- ['asdf', True],
- ['0', False],
- ['', False],
- [{1: 1}, True],
- [{}, False],
- [(0,), True],
- [(), False],
- [[1], True],
- [[], False]])
+@pytest.mark.parametrize(
+ 'inp, out',
+ [
+ [True, True],
+ [False, False],
+ [1, True],
+ [0, False],
+ [1.0, True],
+ [0.0, False],
+ ['TrUe', True],
+ ['FalSe', False],
+ ['yEs', True],
+ ['No', False],
+ ['oN', True],
+ ['oFf', False],
+ ['asdf', True],
+ ['0', False],
+ ['', False],
+ [{1: 1}, True],
+ [{}, False],
+ [(0,), True],
+ [(), False],
+ [[1], True],
+ [[], False],
+ ],
+)
def test_to_bool(inp, out):
assert utils.to_bool(inp) == out
-@pytest.mark.parametrize('inp, out',
- [["{}", {}],
- ["{'null': null}", {"null": None}],
- ["{'bool': true}", {"bool": True}],
- ["{'bool': false}", {"bool": False}],
- ["{'int': 0}", {"int": 0}],
- ["{'float': 1.0}", {"float": 1.0}],
- ["{'str': 'abc'}", {"str": "abc"}],
- ["{'obj': {}}", {"obj": {}}],
- ["{'list': []}", {"list": []}],
- ["---", None],
- ["---\n'null': null", {'null': None}],
- ["---\n'bool': true", {'bool': True}],
- ["---\n'bool': false", {'bool': False}],
- ["---\n'int': 0", {'int': 0}],
- ["---\n'float': 1.0", {'float': 1.0}],
- ["---\n'string': 'abc'", {'string': 'abc'}],
- ["---\n'obj': {}", {'obj': {}}],
- ["---\n'list': []", {'list': []}],
- ["", None],
- ["'null': null", {'null': None}],
- ["'bool': true", {'bool': True}],
- ["'bool': false", {'bool': False}],
- ["'int': 0", {'int': 0}],
- ["'float': 1.0", {'float': 1.0}],
- ["'string': 'abc'", {'string': 'abc'}],
- ["'obj': {}", {'obj': {}}],
- ["'list': []", {'list': []}]])
+@pytest.mark.parametrize(
+ 'inp, out',
+ [
+ ["{}", {}],
+ ["{'null': null}", {"null": None}],
+ ["{'bool': true}", {"bool": True}],
+ ["{'bool': false}", {"bool": False}],
+ ["{'int': 0}", {"int": 0}],
+ ["{'float': 1.0}", {"float": 1.0}],
+ ["{'str': 'abc'}", {"str": "abc"}],
+ ["{'obj': {}}", {"obj": {}}],
+ ["{'list': []}", {"list": []}],
+ ["---", None],
+ ["---\n'null': null", {'null': None}],
+ ["---\n'bool': true", {'bool': True}],
+ ["---\n'bool': false", {'bool': False}],
+ ["---\n'int': 0", {'int': 0}],
+ ["---\n'float': 1.0", {'float': 1.0}],
+ ["---\n'string': 'abc'", {'string': 'abc'}],
+ ["---\n'obj': {}", {'obj': {}}],
+ ["---\n'list': []", {'list': []}],
+ ["", None],
+ ["'null': null", {'null': None}],
+ ["'bool': true", {'bool': True}],
+ ["'bool': false", {'bool': False}],
+ ["'int': 0", {'int': 0}],
+ ["'float': 1.0", {'float': 1.0}],
+ ["'string': 'abc'", {'string': 'abc'}],
+ ["'obj': {}", {'obj': {}}],
+ ["'list': []", {'list': []}],
+ ],
+)
def test_load_valid_json_or_yaml(inp, out):
assert utils.load_json_or_yaml(inp) == out
@@ -74,19 +82,13 @@ def test_load_invalid_json_or_yaml(inp):
@pytest.mark.parametrize('non_ascii', [True, False])
-@pytest.mark.skipif(
- sys.version_info < (3, 6),
- reason='this is only intended to be used in py3, not the CLI'
-)
+@pytest.mark.skipif(sys.version_info < (3, 6), reason='this is only intended to be used in py3, not the CLI')
def test_random_titles_are_unicode(non_ascii):
assert isinstance(utils.random_title(non_ascii=non_ascii), str)
@pytest.mark.parametrize('non_ascii', [True, False])
-@pytest.mark.skipif(
- sys.version_info < (3, 6),
- reason='this is only intended to be used in py3, not the CLI'
-)
+@pytest.mark.skipif(sys.version_info < (3, 6), reason='this is only intended to be used in py3, not the CLI')
def test_random_titles_generates_correct_characters(non_ascii):
title = utils.random_title(non_ascii=non_ascii)
if non_ascii:
@@ -98,34 +100,39 @@ def test_random_titles_generates_correct_characters(non_ascii):
title.encode('utf-8')
-@pytest.mark.parametrize('inp, out',
- [['ClassNameShouldChange', 'class_name_should_change'],
- ['classnameshouldntchange', 'classnameshouldntchange'],
- ['Classspacingshouldntchange', 'classspacingshouldntchange'],
- ['Class1Name2Should3Change', 'class_1_name_2_should_3_change'],
- ['Class123name234should345change456', 'class_123_name_234_should_345_change_456']])
+@pytest.mark.parametrize(
+ 'inp, out',
+ [
+ ['ClassNameShouldChange', 'class_name_should_change'],
+ ['classnameshouldntchange', 'classnameshouldntchange'],
+ ['Classspacingshouldntchange', 'classspacingshouldntchange'],
+ ['Class1Name2Should3Change', 'class_1_name_2_should_3_change'],
+ ['Class123name234should345change456', 'class_123_name_234_should_345_change_456'],
+ ],
+)
def test_class_name_to_kw_arg(inp, out):
assert utils.class_name_to_kw_arg(inp) == out
-@pytest.mark.parametrize('first, second, expected',
- [['/api/v2/resources/', '/api/v2/resources/', True],
- ['/api/v2/resources/', '/api/v2/resources/?test=ignored', True],
- ['/api/v2/resources/?one=ignored', '/api/v2/resources/?two=ignored', True],
- ['http://one.com', 'http://one.com', True],
- ['http://one.com', 'http://www.one.com', True],
- ['http://one.com', 'http://one.com?test=ignored', True],
- ['http://one.com', 'http://www.one.com?test=ignored', True],
- ['http://one.com', 'https://one.com', False],
- ['http://one.com', 'https://one.com?test=ignored', False]])
+@pytest.mark.parametrize(
+ 'first, second, expected',
+ [
+ ['/api/v2/resources/', '/api/v2/resources/', True],
+ ['/api/v2/resources/', '/api/v2/resources/?test=ignored', True],
+ ['/api/v2/resources/?one=ignored', '/api/v2/resources/?two=ignored', True],
+ ['http://one.com', 'http://one.com', True],
+ ['http://one.com', 'http://www.one.com', True],
+ ['http://one.com', 'http://one.com?test=ignored', True],
+ ['http://one.com', 'http://www.one.com?test=ignored', True],
+ ['http://one.com', 'https://one.com', False],
+ ['http://one.com', 'https://one.com?test=ignored', False],
+ ],
+)
def test_are_same_endpoint(first, second, expected):
assert utils.are_same_endpoint(first, second) == expected
-@pytest.mark.parametrize('endpoint, expected',
- [['/api/v2/resources/', 'v2'],
- ['/api/v2000/resources/', 'v2000'],
- ['/api/', 'common']])
+@pytest.mark.parametrize('endpoint, expected', [['/api/v2/resources/', 'v2'], ['/api/v2000/resources/', 'v2000'], ['/api/', 'common']])
def test_version_from_endpoint(endpoint, expected):
assert utils.version_from_endpoint(endpoint) == expected
@@ -133,42 +140,51 @@ def test_version_from_endpoint(endpoint, expected):
class OneClass:
pass
+
class TwoClass:
pass
+
class ThreeClass:
pass
+
class FourClass(ThreeClass):
pass
+
def test_filter_by_class_with_subclass_class():
filtered = utils.filter_by_class((OneClass, OneClass), (FourClass, ThreeClass))
assert filtered == [OneClass, FourClass]
+
def test_filter_by_class_with_subclass_instance():
one = OneClass()
four = FourClass()
filtered = utils.filter_by_class((one, OneClass), (four, ThreeClass))
assert filtered == [one, four]
+
def test_filter_by_class_no_arg_tuples():
three = ThreeClass()
filtered = utils.filter_by_class((True, OneClass), (False, TwoClass), (three, ThreeClass))
assert filtered == [OneClass, None, three]
+
def test_filter_by_class_with_arg_tuples_containing_class():
one = OneClass()
three = (ThreeClass, dict(one=1, two=2))
filtered = utils.filter_by_class((one, OneClass), (False, TwoClass), (three, ThreeClass))
assert filtered == [one, None, three]
+
def test_filter_by_class_with_arg_tuples_containing_subclass():
one = OneClass()
three = (FourClass, dict(one=1, two=2))
filtered = utils.filter_by_class((one, OneClass), (False, TwoClass), (three, ThreeClass))
assert filtered == [one, None, three]
+
@pytest.mark.parametrize('truthy', (True, 123, 'yes'))
def test_filter_by_class_with_arg_tuples_containing_truthy(truthy):
one = OneClass()
@@ -177,18 +193,20 @@ def test_filter_by_class_with_arg_tuples_containing_truthy(truthy):
assert filtered == [one, None, (ThreeClass, dict(one=1, two=2))]
-@pytest.mark.parametrize('date_string,now,expected', [
- ('2017-12-20T00:00:01.5Z', datetime(2017, 12, 20, 0, 0, 2, 750000), 1.25),
- ('2017-12-20T00:00:01.5Z', datetime(2017, 12, 20, 0, 0, 1, 500000), 0.00),
- ('2017-12-20T00:00:01.5Z', datetime(2017, 12, 20, 0, 0, 0, 500000), -1.00),
-])
+@pytest.mark.parametrize(
+ 'date_string,now,expected',
+ [
+ ('2017-12-20T00:00:01.5Z', datetime(2017, 12, 20, 0, 0, 2, 750000), 1.25),
+ ('2017-12-20T00:00:01.5Z', datetime(2017, 12, 20, 0, 0, 1, 500000), 0.00),
+ ('2017-12-20T00:00:01.5Z', datetime(2017, 12, 20, 0, 0, 0, 500000), -1.00),
+ ],
+)
def test_seconds_since_date_string(date_string, now, expected):
with mock.patch('awxkit.utils.utcnow', return_value=now):
assert utils.seconds_since_date_string(date_string) == expected
class RecordingCallback(object):
-
def __init__(self, value=True):
self.call_count = 0
self.value = value
@@ -225,7 +243,6 @@ def test_suppress():
class TestPollUntil(object):
-
@pytest.mark.parametrize('timeout', [0, 0.0, -0.5, -1, -9999999])
def test_callback_called_once_for_non_positive_timeout(self, timeout):
with mock.patch('awxkit.utils.logged_sleep') as sleep:
@@ -246,7 +263,6 @@ class TestPollUntil(object):
class TestPseudoNamespace(object):
-
def test_set_item_check_item(self):
pn = utils.PseudoNamespace()
pn['key'] = 'value'
@@ -319,10 +335,7 @@ class TestPseudoNamespace(object):
assert pn == dict(one=[dict(two=2), dict(three=3)])
def test_instantiation_via_nested_dict_with_lists(self):
- pn = utils.PseudoNamespace(dict(one=[dict(two=2),
- dict(three=dict(four=4,
- five=[dict(six=6),
- dict(seven=7)]))]))
+ pn = utils.PseudoNamespace(dict(one=[dict(two=2), dict(three=dict(four=4, five=[dict(six=6), dict(seven=7)]))]))
assert pn.one[1].three.five[1].seven == 7
def test_instantiation_via_nested_dict_with_tuple(self):
@@ -332,10 +345,7 @@ class TestPseudoNamespace(object):
assert pn == dict(one=(dict(two=2), dict(three=3)))
def test_instantiation_via_nested_dict_with_tuples(self):
- pn = utils.PseudoNamespace(dict(one=(dict(two=2),
- dict(three=dict(four=4,
- five=(dict(six=6),
- dict(seven=7)))))))
+ pn = utils.PseudoNamespace(dict(one=(dict(two=2), dict(three=dict(four=4, five=(dict(six=6), dict(seven=7)))))))
assert pn.one[1].three.five[1].seven == 7
def test_update_with_nested_dict(self):
@@ -348,23 +358,16 @@ class TestPseudoNamespace(object):
def test_update_with_nested_dict_with_lists(self):
pn = utils.PseudoNamespace()
- pn.update(dict(one=[dict(two=2),
- dict(three=dict(four=4,
- five=[dict(six=6),
- dict(seven=7)]))]))
+ pn.update(dict(one=[dict(two=2), dict(three=dict(four=4, five=[dict(six=6), dict(seven=7)]))]))
assert pn.one[1].three.five[1].seven == 7
def test_update_with_nested_dict_with_tuples(self):
pn = utils.PseudoNamespace()
- pn.update(dict(one=(dict(two=2),
- dict(three=dict(four=4,
- five=(dict(six=6),
- dict(seven=7)))))))
+ pn.update(dict(one=(dict(two=2), dict(three=dict(four=4, five=(dict(six=6), dict(seven=7)))))))
assert pn.one[1].three.five[1].seven == 7
class TestUpdatePayload(object):
-
def test_empty_payload(self):
fields = ('one', 'two', 'three', 'four')
kwargs = dict(two=2, four=4)
diff --git a/awxkit/test/test_ws.py b/awxkit/test/test_ws.py
index afc6b42fc5..8e89cdc6f2 100644
--- a/awxkit/test/test_ws.py
+++ b/awxkit/test/test_ws.py
@@ -8,6 +8,7 @@ from awxkit.ws import WSClient
ParseResult = namedtuple("ParseResult", ["port", "hostname", "secure"])
+
def test_explicit_hostname():
client = WSClient("token", "some-hostname", 556, False)
assert client.port == 556
@@ -16,12 +17,15 @@ def test_explicit_hostname():
assert client.token == "token"
-@pytest.mark.parametrize('url, result',
- [['https://somename:123', ParseResult(123, "somename", True)],
- ['http://othername:456', ParseResult(456, "othername", False)],
- ['http://othername', ParseResult(80, "othername", False)],
- ['https://othername', ParseResult(443, "othername", True)],
-])
+@pytest.mark.parametrize(
+ 'url, result',
+ [
+ ['https://somename:123', ParseResult(123, "somename", True)],
+ ['http://othername:456', ParseResult(456, "othername", False)],
+ ['http://othername', ParseResult(80, "othername", False)],
+ ['https://othername', ParseResult(443, "othername", True)],
+ ],
+)
def test_urlparsing(url, result):
with patch("awxkit.ws.config") as mock_config:
mock_config.base_url = url