diff --git a/awxkit/awxkit/api/mixins/has_status.py b/awxkit/awxkit/api/mixins/has_status.py index 8844e131fa..bd76400baa 100644 --- a/awxkit/awxkit/api/mixins/has_status.py +++ b/awxkit/awxkit/api/mixins/has_status.py @@ -1,8 +1,6 @@ from datetime import datetime import json -import six - from awxkit.utils import poll_until from awxkit.exceptions import WaitUntilTimeout @@ -50,7 +48,7 @@ class HasStatus(object): return self.wait_until_status(self.started_statuses, interval=interval, timeout=timeout) def assert_status(self, status_list, msg=None): - if isinstance(status_list, six.text_type): + if isinstance(status_list, str): status_list = [status_list] if self.status in status_list: # include corner cases in is_successful logic diff --git a/awxkit/awxkit/api/pages/credentials.py b/awxkit/awxkit/api/pages/credentials.py index d9d039c837..7d3ca009ec 100644 --- a/awxkit/awxkit/api/pages/credentials.py +++ b/awxkit/awxkit/api/pages/credentials.py @@ -1,6 +1,6 @@ import logging -from six.moves import http_client as http +import http.client as http import awxkit.exceptions as exc from awxkit.api.mixins import DSAdapter, HasCopy, HasCreate @@ -19,10 +19,7 @@ from awxkit.utils import ( from . import base, page from .page import exception_from_status_code -try: - from urllib.parse import urljoin -except ImportError: - from urlparse import urljoin +from urllib.parse import urljoin log = logging.getLogger(__name__) diff --git a/awxkit/awxkit/api/pages/page.py b/awxkit/awxkit/api/pages/page.py index 653974cb71..fb21c26ea2 100644 --- a/awxkit/awxkit/api/pages/page.py +++ b/awxkit/awxkit/api/pages/page.py @@ -4,8 +4,7 @@ import json import re from requests import Response -import six -from six.moves import http_client as http +import http.client as http from awxkit.utils import ( PseudoNamespace, @@ -170,10 +169,7 @@ class Page(object): def from_json(cls, raw): resp = Response() data = json.dumps(raw) - if six.PY3: - resp._content = bytes(data, 'utf-8') - else: - resp._content = data + resp._content = bytes(data, 'utf-8') resp.encoding = 'utf-8' resp.status_code = 200 return cls(r=resp) diff --git a/awxkit/awxkit/cli/client.py b/awxkit/awxkit/cli/client.py index 6a7c4c3f51..8013523921 100755 --- a/awxkit/awxkit/cli/client.py +++ b/awxkit/awxkit/cli/client.py @@ -6,7 +6,6 @@ import pkg_resources import sys from requests.exceptions import RequestException -import six from .custom import handle_custom_actions from .format import (add_authentication_arguments, @@ -203,15 +202,7 @@ class CLI(object): if hasattr(response, 'rc'): raise SystemExit(response.rc) else: - if six.PY3: - self.parser.print_help() - elif six.PY2 and not self.help: - # Unfortunately, argparse behavior between py2 and py3 - # changed in a notable way when required subparsers - # have invalid (or missing) arguments specified - # see: https://github.com/python/cpython/commit/f97c59aaba2d93e48cbc6d25f7ff9f9c87f8d0b2 - print('\nargument resource: invalid choice') - raise SystemExit(2) + self.parser.print_help() def parse_action(self, page, from_sphinx=False): """Perform an HTTP OPTIONS request diff --git a/awxkit/awxkit/cli/custom.py b/awxkit/awxkit/cli/custom.py index 0f876579bc..b398b68a9a 100644 --- a/awxkit/awxkit/cli/custom.py +++ b/awxkit/awxkit/cli/custom.py @@ -1,8 +1,6 @@ import functools import json -from six import with_metaclass, PY3 - from .stdout import monitor, monitor_workflow from .utils import CustomRegistryMeta, color_enabled from awxkit import api @@ -24,7 +22,7 @@ class CustomActionRegistryMeta(CustomRegistryMeta): return ' '.join([self.resource, self.action]) -class CustomAction(with_metaclass(CustomActionRegistryMeta)): +class CustomAction(metaclass=CustomActionRegistryMeta): """Base class for defining a custom action for a resource.""" def __init__(self, page): @@ -549,11 +547,8 @@ class SettingsModify(CustomAction): return resp.from_json({'key': key, 'value': resp[key]}) def is_json(self, data): - err = ValueError - if PY3: - err = json.decoder.JSONDecodeError try: json.loads(data) - except err: + except json.decoder.JSONDecodeError: return False return True diff --git a/awxkit/awxkit/cli/format.py b/awxkit/awxkit/cli/format.py index f87afc07d3..ca91a822f4 100644 --- a/awxkit/awxkit/cli/format.py +++ b/awxkit/awxkit/cli/format.py @@ -2,7 +2,6 @@ import locale import json from distutils.util import strtobool -import six import yaml from awxkit.cli.utils import colored @@ -81,7 +80,7 @@ def add_output_formatting_arguments(parser, env): def format_response(response, fmt='json', filter='.', changed=False): if response is None: return # HTTP 204 - if isinstance(response, six.text_type): + if isinstance(response, str): return response if 'results' in response.__dict__: @@ -115,7 +114,7 @@ def format_jq(output, fmt): results = [] for x in jq.jq(fmt).transform(output, multiple_output=True): if x not in (None, ''): - if isinstance(x, six.text_type): + if isinstance(x, str): results.append(x) else: results.append(json.dumps(x)) diff --git a/awxkit/awxkit/cli/options.py b/awxkit/awxkit/cli/options.py index 6cc784b51d..8b33a0d086 100644 --- a/awxkit/awxkit/cli/options.py +++ b/awxkit/awxkit/cli/options.py @@ -7,7 +7,6 @@ import sys import yaml from distutils.util import strtobool -import six from .custom import CustomAction from .format import add_output_formatting_arguments @@ -182,7 +181,7 @@ class ResourceOptionsParser(object): for k, v in parsed.items(): # add support for file reading at top-level JSON keys # (to make things like SSH key data easier to work with) - if isinstance(v, six.text_type) and v.startswith('@'): + if isinstance(v, str) and v.startswith('@'): path = os.path.expanduser(v[1:]) parsed[k] = open(path).read() diff --git a/awxkit/awxkit/cli/resource.py b/awxkit/awxkit/cli/resource.py index 20e08c6f18..6937ab1f95 100644 --- a/awxkit/awxkit/cli/resource.py +++ b/awxkit/awxkit/cli/resource.py @@ -1,7 +1,5 @@ import os -from six import PY3, with_metaclass - from awxkit import api, config from awxkit.utils import to_str from awxkit.api.pages import Page @@ -45,7 +43,7 @@ DEPRECATED_RESOURCES_REVERSE = dict( ) -class CustomCommand(with_metaclass(CustomRegistryMeta)): +class CustomCommand(metaclass=CustomRegistryMeta): """Base class for implementing custom commands. Custom commands represent static code which should run - they are @@ -153,18 +151,7 @@ def parse_resource(client, skip_deprecated=False): k, help='', **kwargs ) - try: - resource = client.parser.parse_known_args()[0].resource - except SystemExit: - if PY3: - raise - else: - # Unfortunately, argparse behavior between py2 and py3 - # changed in a notable way when required subparsers - # have invalid (or missing) arguments specified - # see: https://github.com/python/cpython/commit/f97c59aaba2d93e48cbc6d25f7ff9f9c87f8d0b2 - # In py2, this raises a SystemExit; which we want to _ignore_ - resource = None + resource = client.parser.parse_known_args()[0].resource if resource in DEPRECATED_RESOURCES.values(): client.argv[ client.argv.index(resource) diff --git a/awxkit/awxkit/cli/utils.py b/awxkit/awxkit/cli/utils.py index 3cb2a251b1..d064158859 100644 --- a/awxkit/awxkit/cli/utils.py +++ b/awxkit/awxkit/cli/utils.py @@ -5,8 +5,6 @@ import os import sys import threading -import six - _color = threading.local() _color.enabled = True @@ -38,29 +36,6 @@ class CustomRegistryMeta(type): class HelpfulArgumentParser(ArgumentParser): - def __init__(self, *args, **kwargs): - super(HelpfulArgumentParser, self).__init__(*args, **kwargs) - if six.PY2: - # backport parser aliases support to py2 - # see: https://github.com/python/cpython/commit/fd311a712d5876c3a3efff265978452eea759f85 - SubParsersAction = self._registries['action']['parsers'] - - class _SubParsersAction(SubParsersAction): - - def add_parser(self, name, **kwargs): - aliases = kwargs.pop('aliases', []) - parser = super(_SubParsersAction, self).add_parser(name, **kwargs) - if aliases: - self._choices_actions[-1].metavar = ' '.join([ - name, - '({})'.format(', '.join(aliases)) - ]) - for alias in aliases: - self._name_parser_map[alias] = parser - return parser - - self._registries['action']['parsers'] = _SubParsersAction - def error(self, message): # pragma: nocover """Prints a usage message incorporating the message to stderr and exits. diff --git a/awxkit/awxkit/utils/__init__.py b/awxkit/awxkit/utils/__init__.py index 2526983817..32f45c3e82 100644 --- a/awxkit/awxkit/utils/__init__.py +++ b/awxkit/awxkit/utils/__init__.py @@ -10,7 +10,6 @@ import sys import re import os -import six import yaml from awxkit.words import words @@ -132,7 +131,7 @@ class PseudoNamespace(dict): def is_relative_endpoint(candidate): - return isinstance(candidate, (six.text_type,)) and candidate.startswith('/api/') + return isinstance(candidate, (str,)) and candidate.startswith('/api/') def is_class_or_instance(obj, cls): @@ -321,19 +320,9 @@ def update_payload(payload, fields, kwargs): def to_str(obj): - if six.PY3: - if isinstance(obj, bytes): - return obj.decode('utf-8') - return obj - if not isinstance(obj, six.text_type): - try: - return str(obj) - except UnicodeDecodeError: - try: - obj = six.text_type(obj, 'utf8') - except UnicodeDecodeError: - obj = obj.decode('latin1') - return obj.encode('utf8') + if isinstance(obj, bytes): + return obj.decode('utf-8') + return obj def to_bool(obj): diff --git a/awxkit/awxkit/ws.py b/awxkit/awxkit/ws.py index 4136b7c278..ee39a5e990 100644 --- a/awxkit/awxkit/ws.py +++ b/awxkit/awxkit/ws.py @@ -4,8 +4,8 @@ import atexit import json import ssl -from six.moves.queue import Queue, Empty -from six.moves.urllib.parse import urlparse +from queue import Queue, Empty +from urllib.parse import urlparse from awxkit.config import config diff --git a/awxkit/requirements.txt b/awxkit/requirements.txt index dd2d30b1de..6c9fdba970 100644 --- a/awxkit/requirements.txt +++ b/awxkit/requirements.txt @@ -1,3 +1,2 @@ PyYAML requests -six diff --git a/awxkit/setup.py b/awxkit/setup.py index 2c972162b1..2f304f2600 100644 --- a/awxkit/setup.py +++ b/awxkit/setup.py @@ -67,7 +67,7 @@ setup( }, include_package_data=True, install_requires=requirements, - python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*", + python_requires=">=3.6", extras_require={ 'formatting': ['jq'], 'websockets': ['websocket-client>0.54.0'], diff --git a/awxkit/test/cli/test_options.py b/awxkit/test/cli/test_options.py index 3eb5e51f47..83bb4ac36a 100644 --- a/awxkit/test/cli/test_options.py +++ b/awxkit/test/cli/test_options.py @@ -1,10 +1,7 @@ import argparse import json import unittest -try: - from StringIO import StringIO -except ImportError: - from io import StringIO +from io import StringIO import pytest from requests import Response diff --git a/awxkit/test/test_credentials.py b/awxkit/test/test_credentials.py index bd1331016d..714550119e 100644 --- a/awxkit/test/test_credentials.py +++ b/awxkit/test/test_credentials.py @@ -1,7 +1,4 @@ -try: - from unittest.mock import patch -except ImportError: - from mock import patch +from unittest.mock import patch import pytest diff --git a/awxkit/test/test_utils.py b/awxkit/test/test_utils.py index 20efa2c640..5f497d2e93 100644 --- a/awxkit/test/test_utils.py +++ b/awxkit/test/test_utils.py @@ -2,12 +2,8 @@ from datetime import datetime import sys -try: - from unittest import mock -except ImportError: - import mock +from unittest import mock import pytest -import six from awxkit import utils from awxkit import exceptions as exc @@ -83,7 +79,7 @@ def test_load_invalid_json_or_yaml(inp): 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), six.text_type) + assert isinstance(utils.random_title(non_ascii=non_ascii), str) @pytest.mark.parametrize('non_ascii', [True, False]) diff --git a/awxkit/test/test_ws.py b/awxkit/test/test_ws.py index ea55ba8ffa..afc6b42fc5 100644 --- a/awxkit/test/test_ws.py +++ b/awxkit/test/test_ws.py @@ -1,10 +1,7 @@ # -*- coding: utf-8 -*- from collections import namedtuple -try: - from unittest.mock import patch -except ImportError: - from mock import patch +from unittest.mock import patch import pytest from awxkit.ws import WSClient diff --git a/awxkit/tox.ini b/awxkit/tox.ini index 7931be5491..42c72bd608 100644 --- a/awxkit/tox.ini +++ b/awxkit/tox.ini @@ -8,6 +8,7 @@ skip_missing_interpreters = true # skipsdist = true [testenv] +basepython = python3.6 passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH setenv = PYTHONPATH = {toxinidir}:{env:PYTHONPATH:}:. @@ -21,7 +22,6 @@ deps = commands = coverage run --parallel --source awxkit -m pytest --doctest-glob='*.md' --junit-xml=report.xml {posargs} [testenv:lint] -basepython = python3.6 deps = {[testenv]deps} flake8 @@ -32,7 +32,6 @@ commands = - coverage erase [testenv:coveralls] -basepython = python3.6 commands= - coverage combine - coverage report -m @@ -43,4 +42,4 @@ max-line-length = 120 [pytest] addopts = -v --tb=native -junit_family=xunit2 \ No newline at end of file +junit_family=xunit2