diff --git a/.collections.py b/.collections.py deleted file mode 100644 index f8dc44f299..0000000000 --- a/.collections.py +++ /dev/null @@ -1,315 +0,0 @@ -from _abcoll import * -from _collections import deque, defaultdict -from itertools import imap as _imap -from operator import eq as _eq, itemgetter as _itemgetter -from keyword import iskeyword as _iskeyword -import sys as _sys - -try: - from thread import get_ident as _get_ident -except ImportError: - from dummy_thread import get_ident as _get_ident - - -def namedtuple(typename, field_names, verbose=False): - """Returns a new subclass of tuple with named fields. - - >>> Point = namedtuple('Point', 'x y') - >>> Point.__doc__ # docstring for the new class - 'Point(x, y)' - >>> p = Point(11, y=22) # instantiate with positional args or keywords - >>> p[0] + p[1] # indexable like a plain tuple - 33 - >>> x, y = p # unpack like a regular tuple - >>> x, y - (11, 22) - >>> p.x + p.y # fields also accessable by name - 33 - >>> d = p._asdict() # convert to a dictionary - >>> d['x'] - 11 - >>> Point(**d) # convert from a dictionary - Point(x=11, y=22) - >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields - Point(x=100, y=22) - - """ - - # Parse and validate the field names. Validation serves two purposes, - # generating informative error messages and preventing template injection attacks. - if isinstance(field_names, basestring): - field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas - field_names = tuple(map(str, field_names)) - for name in (typename,) + field_names: - if not all(c.isalnum() or c=='_' for c in name): - raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name) - if _iskeyword(name): - raise ValueError('Type names and field names cannot be a keyword: %r' % name) - if name[0].isdigit(): - raise ValueError('Type names and field names cannot start with a number: %r' % name) - seen_names = set() - for name in field_names: - if name.startswith('_'): - raise ValueError('Field names cannot start with an underscore: %r' % name) - if name in seen_names: - raise ValueError('Encountered duplicate field name: %r' % name) - seen_names.add(name) - - # Create and fill-in the class template - numfields = len(field_names) - argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes - reprtxt = ', '.join('%s=%%r' % name for name in field_names) - dicttxt = ', '.join('%r: t[%d]' % (name, pos) for pos, name in enumerate(field_names)) - template = '''class %(typename)s(tuple): - '%(typename)s(%(argtxt)s)' \n - __slots__ = () \n - _fields = %(field_names)r \n - def __new__(_cls, %(argtxt)s): - return _tuple.__new__(_cls, (%(argtxt)s)) \n - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - 'Make a new %(typename)s object from a sequence or iterable' - result = new(cls, iterable) - if len(result) != %(numfields)d: - raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) - return result \n - def __repr__(self): - return '%(typename)s(%(reprtxt)s)' %% self \n - def _asdict(t): - 'Return a new dict which maps field names to their values' - return {%(dicttxt)s} \n - def _replace(_self, **kwds): - 'Return a new %(typename)s object replacing specified fields with new values' - result = _self._make(map(kwds.pop, %(field_names)r, _self)) - if kwds: - raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) - return result \n - def __getnewargs__(self): - return tuple(self) \n\n''' % locals() - for i, name in enumerate(field_names): - template += ' %s = _property(_itemgetter(%d))\n' % (name, i) - if verbose: - print template - - # Execute the template string in a temporary namespace and - # support tracing utilities by setting a value for frame.f_globals['__name__'] - namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, - _property=property, _tuple=tuple) - try: - exec template in namespace - except SyntaxError, e: - raise SyntaxError(e.message + ':\n' + template) - result = namespace[typename] - - # For pickling to work, the __module__ variable needs to be set to the frame - # where the named tuple is created. Bypass this step in enviroments where - # sys._getframe is not defined (Jython for example). - if hasattr(_sys, '_getframe'): - result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') - - return result - - -class OrderedDict(dict): - 'Dictionary that remembers insertion order' - # An inherited dict maps keys to values. - # The inherited dict provides __getitem__, __len__, __contains__, and get. - # The remaining methods are order-aware. - # Big-O running times for all methods are the same as regular dictionaries. - - # The internal self.__map dict maps keys to links in a doubly linked list. - # The circular doubly linked list starts and ends with a sentinel element. - # The sentinel element never gets deleted (this simplifies the algorithm). - # Each link is stored as a list of length three: [PREV, NEXT, KEY]. - - def __init__(self, *args, **kwds): - '''Initialize an ordered dictionary. The signature is the same as - regular dictionaries, but keyword arguments are not recommended because - their insertion order is arbitrary. - - ''' - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__root = root = [] # sentinel node - root[:] = [root, root, None] - self.__map = {} - self.__update(*args, **kwds) - - def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' - # Setting a new item creates a new link at the end of the linked list, - # and the inherited dictionary is updated with the new key/value pair. - if key not in self: - root = self.__root - last = root[0] - last[1] = root[0] = self.__map[key] = [last, root, key] - return dict_setitem(self, key, value) - - def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' - # Deleting an existing item uses self.__map to find the link which gets - # removed by updating the links in the predecessor and successor nodes. - dict_delitem(self, key) - link_prev, link_next, _ = self.__map.pop(key) - link_prev[1] = link_next # update link_prev[NEXT] - link_next[0] = link_prev # update link_next[PREV] - - def __iter__(self): - 'od.__iter__() <==> iter(od)' - # Traverse the linked list in order. - root = self.__root - curr = root[1] # start at the first node - while curr is not root: - yield curr[2] # yield the curr[KEY] - curr = curr[1] # move to next node - - def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' - # Traverse the linked list in reverse order. - root = self.__root - curr = root[0] # start at the last node - while curr is not root: - yield curr[2] # yield the curr[KEY] - curr = curr[0] # move to previous node - - def clear(self): - 'od.clear() -> None. Remove all items from od.' - root = self.__root - root[:] = [root, root, None] - self.__map.clear() - dict.clear(self) - - # -- the following methods do not depend on the internal structure -- - - def keys(self): - 'od.keys() -> list of keys in od' - return list(self) - - def values(self): - 'od.values() -> list of values in od' - return [self[key] for key in self] - - def items(self): - 'od.items() -> list of (key, value) pairs in od' - return [(key, self[key]) for key in self] - - def iterkeys(self): - 'od.iterkeys() -> an iterator over the keys in od' - return iter(self) - - def itervalues(self): - 'od.itervalues -> an iterator over the values in od' - for k in self: - yield self[k] - - def iteritems(self): - 'od.iteritems -> an iterator over the (key, value) pairs in od' - for k in self: - yield (k, self[k]) - - update = MutableMapping.update - - __update = update # let subclasses override update without breaking __init__ - - __marker = object() - - def pop(self, key, default=__marker): - '''od.pop(k[,d]) -> v, remove specified key and return the corresponding - value. If key is not found, d is returned if given, otherwise KeyError - is raised. - - ''' - if key in self: - result = self[key] - del self[key] - return result - if default is self.__marker: - raise KeyError(key) - return default - - def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' - if key in self: - return self[key] - self[key] = default - return default - - def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if false. - - ''' - if not self: - raise KeyError('dictionary is empty') - key = next(reversed(self) if last else iter(self)) - value = self.pop(key) - return key, value - - def __repr__(self, _repr_running={}): - 'od.__repr__() <==> repr(od)' - call_key = id(self), _get_ident() - if call_key in _repr_running: - return '...' - _repr_running[call_key] = 1 - try: - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - finally: - del _repr_running[call_key] - - def __reduce__(self): - 'Return state information for pickling' - items = [[k, self[k]] for k in self] - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def copy(self): - 'od.copy() -> a shallow copy of od' - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S. - If not specified, the value defaults to None. - - ''' - self = cls() - for key in iterable: - self[key] = value - return self - - def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - if isinstance(other, OrderedDict): - return dict.__eq__(self, other) and all(_imap(_eq, self, other)) - return dict.__eq__(self, other) - - def __ne__(self, other): - 'od.__ne__(y) <==> od!=y' - return not self == other - - # -- the following methods support python 3.x style dictionary views -- - - def viewkeys(self): - "od.viewkeys() -> a set-like object providing a view on od's keys" - return KeysView(self) - - def viewvalues(self): - "od.viewvalues() -> an object providing a view on od's values" - return ValuesView(self) - - def viewitems(self): - "od.viewitems() -> a set-like object providing a view on od's items" - return ItemsView(self) - diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7ba53c1684..f1e1c72173 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,99 +64,6 @@ jobs: pip install -e .[testing] pytest - python26-test: - - runs-on: ubuntu-latest - container: - image: cronosmobi/python2.6:latest - env: - ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true - - steps: - - name: install system dependencies - run: | - apt-get install -y --no-install-recommends software-properties-common - apt-get install -y build-essential git curl unzip file tar - - name: clone the repo and checkout pull_request - run: | - git clone ${{ github.server_url }}/${{ github.repository }} - git -C insights-core fetch --no-tags --prune --no-recurse-submodules --depth=1 origin pull/${{ github.ref_name }}:${{ github.head_ref }} - git -C insights-core checkout ${{ github.head_ref }} - - name: build setuptools and pip - run: | - export PATH=$PATH:/github/home/.local/bin - CUR_DIR=$(pwd) - mkdir ../tools && cd ../tools - curl -L -O https://files.pythonhosted.org/packages/b8/04/be569e393006fa9a2c10ef72ea33133c2902baa115dd1d4279dae55c3b3b/setuptools-36.8.0.zip - unzip setuptools-36.8.0.zip && cd setuptools-36.8.0 - python setup.py install --user && cd .. - curl -L -O https://github.com/pypa/pip/archive/refs/tags/9.0.3.tar.gz - tar -xvzf 9.0.3.tar.gz && cd pip-9.0.3 - python setup.py install --user - cd ${CUR_DIR} - - name: install virtualenv depenencies - run: | - export PATH=$PATH:/github/home/.local/bin - CUR_DIR=$(pwd) - mkdir pip_packages && cd pip_packages - curl -L -O https://files.pythonhosted.org/packages/0c/5d/b077dbf309993d52c1d71e6bf6fe443a8029ea215135ebbe0b1b10e7aefc/pbr-3.1.1-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/31/77/3781f65cafe55480b56914def99022a5d2965a4bb269655c89ef2f1de3cd/importlib-1.0.4.zip - curl -L -O https://files.pythonhosted.org/packages/37/aa/111610d8bf5b1bb7a295a048fc648cec346347a8b0be5881defd2d1b4a52/oyaml-1.0-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/3b/f6/7a76333cf0b9251ecf49efff635015171843d9b977e4ffcf59f9c4428052/redis-2.10.6-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355746a97fab05a372e4a2c6a6b876165/idna-2.7-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz - curl -L -O https://files.pythonhosted.org/packages/53/25/ef88e8e45db141faa9598fbf7ad0062df8f50f881a36ed6a0073e1572126/ordereddict-1.1.tar.gz - curl -L -O https://files.pythonhosted.org/packages/53/67/9620edf7803ab867b175e4fd23c7b8bd8eba11cb761514dcd2e726ef07da/py-1.4.34-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/5a/4d/5a6bfb960738e503234f1d201eac5ed4eecbd6f66a8ed593097bd3306bec/coverage-4.3.4-cp26-cp26mu-manylinux1_x86_64.whl - curl -L -O https://files.pythonhosted.org/packages/5e/a0/5f06e1e1d463903cf0c0eebeb751791119ed7a4b3737fdc9a77f1cdfb51f/certifi-2020.12.5-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/65/47/7e02164a2a3db50ed6d8a6ab1d6d60b69c4c3fdf57a284257925dfc12bda/requests-2.19.1-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/65/e0/eb35e762802015cab1ccee04e8a277b03f1d8e53da3ec3106882ec42558b/Jinja2-2.10.3-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/69/be/9c322ed286263a93e5ee0ff575662d0709fc73ee906522e7cfa72b08b946/mccabe-0.5.3-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/69/cb/f5be453359271714c01b9bd06126eaf2e368f1fddfff30818754b5ac2328/funcsigs-1.0.2-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/6f/2c/a9386903ece2ea85e9807e0e062174dc26fdce8b05f216d00491be29fad5/enum34-1.1.10-py2-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/70/a9/9b66f22d038de51e05f92d5e677fd89d8f9c980db0b8a130621baad052f5/flake8-2.6.2-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/73/31/136a79364c1681a3c276796d1f5090833bd03461b78a1b037638d1a2c484/pycodestyle-2.0.0-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/74/55/98f59358be6d7240ba546b8a74813cc21841a9145a0c1a3a7998f50acbe7/pyflakes-1.2.3-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/75/5e/b84feba55e20f8da46ead76f14a3943c8cb722d40360702b2365b91dec00/PyYAML-3.11.tar.gz - curl -L -O https://files.pythonhosted.org/packages/85/e6/f041bcf77bcf7bf11ccb9b8a6cdb3a2ee70c1bd2ab49d87d2269cfd4f3e0/pytest_cov-2.4.0-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/87/1c/17f3e3935a913dfe2a5ca85fa5ccbef366bfd82eb318b1f75dadbf0affca/defusedxml-0.5.0-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/8a/20/6eca772d1a5830336f84aca1d8198e5a3f4715cd1c7fc36d3cc7f7185091/msgpack-python-0.5.6.tar.gz - curl -L -O https://files.pythonhosted.org/packages/8c/2d/aad7f16146f4197a11f8e91fb81df177adcc2073d36a17b1491fd09df6ed/pycparser-2.18.tar.gz - curl -L -O https://files.pythonhosted.org/packages/98/f5/76619a63f0e4a1d2f5a1792ebc233a395c648c63d3461dc0331479ef120a/CacheControl-0.12.4.tar.gz - curl -L -O https://files.pythonhosted.org/packages/ab/1a/ec151e5e703ac80041eaccef923611bbcec2b667c20383655a06962732e9/configparser-3.8.1-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/ac/95/a05b56bb975efa78d3557efa36acaf9cf5d2fd0ee0062060493687432e03/pip-9.0.3-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/bd/c9/6fdd990019071a4a32a5e7cb78a1d92c53851ef4f56f62a3486e6a7d8ffb/urllib3-1.23-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/c4/17/73e8eda8fbc18b8421a8b16be8bbbb2a461f4d2405f3628beb8e5d2ca567/pytest-3.0.6-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/c8/22/9460e311f340cb62d26a38c419b1381b8593b0bb6b5d1f056938b086d362/lockfile-0.12.2-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/cb/85/8a1588a04172e0853352ecfe214264c65a62ab35374d9ad9c569cf94c2a3/python_gnupg-0.4.6-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/e6/35/f187bdf23be87092bd0f1200d43d23076cee4d0dec109f195173fd3ebc79/mock-2.0.0-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/f2/94/3af39d34be01a24a6e65433d19e107099374224905f1e0cc6bbe1fd22a2f/argparse-1.4.0-py2.py3-none-any.whl - curl -L -O https://files.pythonhosted.org/packages/86/84/6bd1384196a6871a9108157ec934a1e1ee0078582cd208b43352566a86dc/pytest_catchlog-1.2.2-py2.py3-none-any.whl - cd ${CUR_DIR} - mkdir ../collections_module - curl -L -o ./../collections_module/collections.py https://raw.githubusercontent.com/RedHatInsights/insights-core/5c8ca0f2fb3de45908e8d931d40758af34a7997a/.collections.py - - name: flake8 - run: | - cd insights-core - # show the branch - git --no-pager branch - export PATH=$PATH:/github/home/.local/bin - pip install --user -e .[linting] --no-index -f ../pip_packages - flake8 . - cd - - - name: pytest - run: | - cd insights-core - # show the branch - git --no-pager branch - export PATH=$PATH:/github/home/.local/bin - pip install --user -e .[testing] --no-index -f ../pip_packages - export PYTHONPATH=${PYTHONPATH}:../../collections_module - pytest - cd - - docs-test: runs-on: ubuntu-latest diff --git a/Jenkinsfile b/Jenkinsfile index ea42da6d48..b57b79f3f4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -10,33 +10,6 @@ pipeline { } stage('Build and Test Insights Core') { parallel { - stage('Build RHEL6') { - agent { - node { - label 'python26' - } - } - steps { - echo "Testing with Pytest..." - sh """ - virtualenv .testenv - source .testenv/bin/activate - pip install /pip_packages/pip-9.0.3-py2.py3-none-any.whl - pip install -r /var/lib/jenkins/ci_requirements.txt -f /pip_packages - pip install -e .[testing] -f /pip_packages - pytest - """ - echo "Testing with Linter..." - sh """ - virtualenv .lintenv - source .lintenv/bin/activate - pip install /pip_packages/pip-9.0.3-py2.py3-none-any.whl - pip install -r /var/lib/jenkins/ci_requirements.txt -f /pip_packages - pip install -e .[linting] -f /pip_packages - flake8 - """ - } - } stage('Build RHEL7 Python 2.7') { agent { node { @@ -104,7 +77,7 @@ pipeline { pip install -e .[docs] sphinx-build -W -b html -qa -E docs docs/_build/html """ - } + } } stage('Nofity Github - Docs Check Passed') { steps { diff --git a/README.rst b/README.rst index a36ce2e443..9aa9dce45a 100644 --- a/README.rst +++ b/README.rst @@ -24,7 +24,7 @@ Features -------- * Over 200 Enterprise Linux data parsers -* Support for Python 2.6+ and 3.3+ +* Support for Python 2.7+ and 3.3+ * Built in support for local host collection * Data collection support for several archive formats diff --git a/build.sh b/build.sh index 339e9554c4..bd7481cc12 100755 --- a/build.sh +++ b/build.sh @@ -2,24 +2,18 @@ set -ev -if [ "`python -V 2>&1`" == "Python 2.6.9" ]; then - cp .collections.py /home/travis/virtualenv/python2.6.9/lib/python2.6/collections.py -fi - py.test -if [ "`python -V 2>&1`" != "Python 2.6.9" ]; then - flake8; - sphinx-build -W -b html -qa -E docs docs/_build/html; - if [ "${TRAVIS_PULL_REQUEST}" == "false" ] && [ -n "$DOCKER_USER" ] && [ -n "$DOCKER_PASS" ]; then - docker login -u $DOCKER_USER -p $DOCKER_PASS - export REPO=jhjaggars/insights-core - docker build -f Dockerfile -t $REPO:$COMMIT . - docker tag $REPO:$COMMIT $REPO:$TRAVIS_BRANCH - if [ "$TRAVIS_BRANCH" == "master" ]; then - docker tag $REPO:$COMMIT $REPO:latest - fi - docker push $REPO +flake8 + +sphinx-build -W -b html -qa -E docs docs/_build/html; +if [ "${TRAVIS_PULL_REQUEST}" == "false" ] && [ -n "$DOCKER_USER" ] && [ -n "$DOCKER_PASS" ]; then + docker login -u $DOCKER_USER -p $DOCKER_PASS + export REPO=jhjaggars/insights-core + docker build -f Dockerfile -t $REPO:$COMMIT . + docker tag $REPO:$COMMIT $REPO:$TRAVIS_BRANCH + if [ "$TRAVIS_BRANCH" == "master" ]; then + docker tag $REPO:$COMMIT $REPO:latest fi + docker push $REPO fi - diff --git a/insights/core/__init__.py b/insights/core/__init__.py index ae070a9e03..1df1e9b9b0 100644 --- a/insights/core/__init__.py +++ b/insights/core/__init__.py @@ -21,21 +21,15 @@ try: from yaml import CSafeLoader as SafeLoader -except ImportError: +except ImportError: # pragma: no cover from yaml import SafeLoader -# Since XPath expression is not supported by the ElementTree in Python 2.6, -# import insights.contrib.ElementTree when running python is prior to 2.6 for compatibility. -# Script insights.contrib.ElementTree is the same with xml.etree.ElementTree in Python 2.7.14 -# Otherwise, import defusedxml.ElementTree to avoid XML vulnerabilities, +# Import defusedxml.ElementTree firstly to avoid XML vulnerabilities, # if dependency not installed import xml.etree.ElementTree instead. -if sys.version_info[0] == 2 and sys.version_info[1] <= 6: - import insights.contrib.ElementTree as ET -else: - try: - import defusedxml.ElementTree as ET - except: - import xml.etree.ElementTree as ET +try: + import defusedxml.ElementTree as ET +except: # pragma: no cover + import xml.etree.ElementTree as ET log = logging.getLogger(__name__) diff --git a/insights/parsers/nftables.py b/insights/parsers/nftables.py index 8b95703c3b..7510c75b6a 100644 --- a/insights/parsers/nftables.py +++ b/insights/parsers/nftables.py @@ -83,14 +83,14 @@ class NftListRuleSet(JSONParser): Examples: >>> type(nft_obj) - >>> [str(item) for item in nft_obj.tables('ip')] # change unicode to string to be compatible with python2.6/2.7 + >>> [str(item) for item in nft_obj.tables('ip')] # change unicode to string to be compatible with python2.7 ['table1'] >>> chains = nft_obj.chains('ip', 'table1') >>> len(chains) 1 - >>> str(chains[0]['name'].value) # change unicode to string to be compatible with python2.6/2.7 + >>> str(chains[0]['name'].value) # change unicode to string to be compatible with python2.7 'chain1' - >>> str(chains[0]['type'].value) # change unicode to string to be compatible with python2.6/2.7 + >>> str(chains[0]['type'].value) # change unicode to string to be compatible with python2.7 'filter' >>> rules = nft_obj.rules('ip', 'table1', 'chain1') >>> len(rules) diff --git a/insights/parsr/query/boolean.py b/insights/parsr/query/boolean.py index 6e9db5a7df..75a7906cb7 100644 --- a/insights/parsr/query/boolean.py +++ b/insights/parsr/query/boolean.py @@ -39,9 +39,9 @@ def is_positive(n): gt_five_and_lt_10 = gt(5) & lt(10) """ + from itertools import count import six -import sys class Boolean(object): @@ -61,10 +61,6 @@ def __call__(self, value): return self.test(value) def to_pyfunc(self): - ver = sys.version_info - if ver[0] == 2 and ver[1] == 6: - return self.test - env = {} ids = count() @@ -100,7 +96,9 @@ def predicate(value): return {body} except Exception as ex: return False - """.format(body=expr(self)) + """.format( + body=expr(self) + ) six.exec_(func, env, env) return env["predicate"] @@ -169,6 +167,7 @@ def inner(val): if ignore_case: return CaselessPredicate(func, val.lower()) return Predicate(func, val) + return inner diff --git a/insights/tests/client/apps/test_playbook_verifier.py b/insights/tests/client/apps/test_playbook_verifier.py index bd56240c2a..176052b3d8 100644 --- a/insights/tests/client/apps/test_playbook_verifier.py +++ b/insights/tests/client/apps/test_playbook_verifier.py @@ -12,20 +12,14 @@ from mock.mock import patch from pytest import raises +from insights.client.constants import InsightsConstants as constants +from insights.client.apps.ansible import playbook_verifier +from insights.client.apps.ansible.playbook_verifier import verify, PlaybookVerificationError, get_play_revocation_list, normalize_play_py2, load_playbook_yaml # noqa -# Because pytest 3 does not allow module-level skips, we have to import these -# modules conditionally and skip each individual module here. -if sys.version_info > (2, 6): - from insights.client.constants import InsightsConstants as constants - from insights.client.apps.ansible import playbook_verifier - from insights.client.apps.ansible.playbook_verifier import verify, PlaybookVerificationError, get_play_revocation_list, normalize_play_py2, load_playbook_yaml # noqa - -SKIP_BELOW_27 = pytest.mark.skipif(sys.version_info < (2, 7), reason="Unsupported; needs Python 2.7+ or 3.6+") SKIP_ON_3 = pytest.mark.skipif(sys.version_info[0] > 2, reason="Only required in Python 2") -@SKIP_BELOW_27 class TestErrors: @patch("insights.client.apps.ansible.playbook_verifier.get_play_revocation_list", return_value=[]) def test_vars_not_found_error(self, mock_method): @@ -206,7 +200,6 @@ def test_revoked_playbook(self, call_1, call_2): @SKIP_ON_3 -@SKIP_BELOW_27 def test_normalize_snippet(): playbook = '''task: when: @@ -227,7 +220,6 @@ def test_normalize_snippet(): assert normalize_play_py2(snippet) == want -@SKIP_BELOW_27 class TestExcludeDynamicElements: def test_ok_signature(self): source = { @@ -320,7 +312,6 @@ def test_invalid_exclusion_key(self): assert excinfo.value.message == expected -@SKIP_BELOW_27 class TestPlaybookSerializer: def test_list(self): @@ -374,7 +365,6 @@ def test_strings(self, source, expected): assert result == expected -@SKIP_BELOW_27 class TestSerializePlaybookSnippet: def test_serialize_dictionary(self): raw = "\n".join([ @@ -438,7 +428,6 @@ def test_real(self, filename): assert result == expected -@SKIP_BELOW_27 class TestGetPlaybookSnippetRevocationList: @mock.patch( "insights.client.apps.ansible.playbook_verifier.verify_play", @@ -494,7 +483,6 @@ def test_invalid_signature(self, mocked_verify_play): assert mocked_verify_play.call_count == 1 -@SKIP_BELOW_27 class TestHashPlaybookSnippets: @pytest.mark.parametrize("filename", ("insights_remove", "document-from-hell", "unicode")) def test_real(self, filename): diff --git a/insights/tests/client/test_utilities.py b/insights/tests/client/test_utilities.py index f8ede1ea96..30e4ec69ea 100644 --- a/insights/tests/client/test_utilities.py +++ b/insights/tests/client/test_utilities.py @@ -1,5 +1,4 @@ import os -import sys from tarfile import open as tar_open import tarfile import tempfile @@ -345,7 +344,6 @@ def test_get_tags_nonexist(): assert got is None -@pytest.mark.skipif(sys.version_info < (2, 7), reason='Playbook verifier uses oyaml library which is incompatable with this test') def test_write_tags(): tags = {'foo': 'bar'} fp = tempfile.NamedTemporaryFile() diff --git a/insights/tests/datasources/malware_detection/test_malware_detection.py b/insights/tests/datasources/malware_detection/test_malware_detection.py index f62b68f681..ebebcba771 100644 --- a/insights/tests/datasources/malware_detection/test_malware_detection.py +++ b/insights/tests/datasources/malware_detection/test_malware_detection.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import os import re -import sys import pytest import yaml @@ -71,9 +70,6 @@ TEST_GET_DISABLED_RULES_FAILURE = getenv_bool("TEST_GET_DISABLED_RULES_FAILURE", False) TEST_ALL = getenv_bool("TEST_ALL", False) -# Are we running on RHEL6? (well actually, with python 2.6) -IS_RHEL6 = sys.version_info < (2, 7) -SKIP_IF_RHEL6_REASON = "The malware-detection client isn't supported on RHEL6 / python 2.6" # Is the best way to determine if we are running in a container? IS_CONTAINER = os.path.exists("/.dockerenv") or os.path.exists("/run/.containerenv") or '1' not in call('pidof init systemd').strip().split() SKIP_IF_CONTAINER_REASON = "This test uses running process that may not exist when run in a container" @@ -111,7 +107,6 @@ def extract_tmp_files(): ####################################################################### # The following section tests functionality that does not require yara ####################################################################### -@pytest.mark.skipif(IS_RHEL6, reason=SKIP_IF_RHEL6_REASON) class TestsNotUtilizingYara: def test_default_spec(self): @@ -323,7 +318,6 @@ def test_process_include_exclude_tmp_files(self, extract_tmp_files): ################################################################################################### # The following section tests functionality that requires yara but can do with its execution faked ################################################################################################### -@pytest.mark.skipif(IS_RHEL6, reason=SKIP_IF_RHEL6_REASON) class TestsUtilizingFakeYara: @patch(BUILD_YARA_COMMAND_TARGET) @@ -2289,7 +2283,6 @@ def create_test_files_real_yara(): yield os.system('rm -rf %s' % TEMP_TEST_DIR) - @pytest.mark.skipif(IS_RHEL6, reason=SKIP_IF_RHEL6_REASON) class TestsUtilizingRealYara: @patch(CONFIG_FILE_TARGET, TEMP_CONFIG_FILE) diff --git a/insights/tests/test_formats.py b/insights/tests/test_formats.py index 90c03c872c..918919f4f2 100644 --- a/insights/tests/test_formats.py +++ b/insights/tests/test_formats.py @@ -1,4 +1,3 @@ -import sys import pytest from six import StringIO from insights import dr, make_fail, rule @@ -81,7 +80,6 @@ def test_syslog_format_archive(): assert SL_PATH in data -@pytest.mark.skipif(sys.version_info < (2, 7), reason='Playbook verifier code uses oyaml library which is incompatable with this test') def test_yaml_format(): broker = dr.Broker() output = StringIO() diff --git a/insights/tests/test_remote_resource.py b/insights/tests/test_remote_resource.py index ae66eb26c5..377d9a62c3 100644 --- a/insights/tests/test_remote_resource.py +++ b/insights/tests/test_remote_resource.py @@ -1,8 +1,6 @@ from cachecontrol.cache import DictCache from insights.core.remote_resource import RemoteResource, CachedRemoteResource from insights.tests.mock_web_server import TestMockServer -import sys -import pytest GOOD_PAYLOAD = b'Successful return from Mock Service' NOT_FOUND = b'{"error":{"code": "404", "message": "Not Found"}}' @@ -35,7 +33,6 @@ def test_get_cached_remote_resource(self): assert GOOD_PAYLOAD in rtn.content # Test CachedRemoteResource returns cached response - @pytest.mark.skipif(sys.version_info < (2, 7), reason="CacheControl requires python 2.7 or higher") def test_get_cached_remote_resource_cached(self): crr = CachedRemoteResource() diff --git a/insights/tests/tools/test_apply_spec_filters.py b/insights/tests/tools/test_apply_spec_filters.py index 7a325d3423..06126543a2 100644 --- a/insights/tests/tools/test_apply_spec_filters.py +++ b/insights/tests/tools/test_apply_spec_filters.py @@ -1,7 +1,5 @@ import json import os -import pytest -import sys import yaml from collections import defaultdict @@ -85,7 +83,6 @@ def teardown_function(): filters.FILTERS = defaultdict(dict) -@pytest.mark.skipif(sys.version_info < (2, 7), reason='Skip py26') def test_apply_specs_filters_json(): apply_spec_filters.apply_filters("json", 'insights.parsers', JSON_file) diff --git a/insights/tests/util/test_util.py b/insights/tests/util/test_util.py index 6181e0921e..f99feeae11 100644 --- a/insights/tests/util/test_util.py +++ b/insights/tests/util/test_util.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import pytest -import sys import warnings from insights.tests import deep_compare @@ -256,7 +255,6 @@ def test_case_variants(): assert case_variants('hosts:') == ['hosts:', 'HOSTS:', 'Hosts:'] -@pytest.mark.skipif(sys.version_info < (2, 7), reason='Code with PYTEST_CURRENT_TEST is incompatible with py26') def test_deprecated(): def normal_fn(): return 1 diff --git a/setup.py b/setup.py index c8f10e3956..a71c6086e7 100644 --- a/setup.py +++ b/setup.py @@ -35,8 +35,7 @@ 'lockfile', 'jinja2<=2.11.3; python_version <= "2.7"', 'jinja2; python_version > "2.7"', - 'pyyaml>=3.10,<=3.13; python_version < "2.7"', - 'pyyaml; python_version >= "2.7"', + 'pyyaml', 'setuptools; python_version >= "3.12"', ] ) @@ -82,17 +81,12 @@ def maybe_require(pkg): ] ) -# python 2.6 requires setuptools~=36.8.0 to support this syntax testing = set( [ - 'coverage==4.3.4; python_version < "2.7"', - 'coverage; python_version >= "2.7"', - 'pytest==3.0.6; python_version < "2.7"', - 'pytest_catchlog; python_version < "2.7"', + 'coverage', 'pytest~=4.6.0; python_version == "2.7"', 'pytest; python_version >= "3"', - 'pytest-cov==2.4.0; python_version < "2.7"', - 'pytest-cov; python_version >= "2.7"', + 'pytest-cov', 'mock==2.0.0', ] ) @@ -107,7 +101,7 @@ def maybe_require(pkg): openshift = set(['openshift']) -linting = set(['flake8==2.6.2; python_version < "2.7"', 'flake8; python_version >= "2.7"']) +linting = set(['flake8']) optional = set( [ @@ -152,7 +146,6 @@ def maybe_require(pkg): 'Natural Language :: English', 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', diff --git a/shippable.yml b/shippable.yml deleted file mode 100644 index b3bcdccb3e..0000000000 --- a/shippable.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: python - -python: - - 2.6 - - 2.7 - - 3.3 - -build: - ci: - - if [ $SHIPPABLE_PYTHON_VERSION == "2.6" ]; then cp .collections.py /usr/lib/python2.6/collections.py; fi - - pip install -e .[develop] - - pip install --upgrade setuptools - - mkdir -p shippable/testresults shippable/codecoverage - - py.test --cov=insights --cov-report=xml --junit-xml=shippable/testresults/results.xml - - mv coverage.xml shippable/codecoverage - - flake8 - - if [ $SHIPPABLE_PYTHON_VERSION == "2.7" ]; then apt-get -y install pandoc && sphinx-build -W -b html -qa -E docs docs/_build/html; fi