From d333e147214b513ef8d96e1987d3785a6feb5e1f Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Tue, 24 Dec 2019 18:28:33 +0100 Subject: tests: check min required python version with vermin (#14289) This commit removes the `python_version.py` unit test module and the vendored dependencies `pyqver2.py` and `pyqver3.py`. It substitutes them with an equivalent check done using `vermin` that is run as a separate workflow via Github Actions. This allows us to delete 2 vendored dependencies that are unmaintained and substitutes them with a maintained tool. Also, updates the list of vendored dependencies. --- .github/workflows/minimum_python_versions.yaml | 30 +++ lib/spack/external/__init__.py | 8 - lib/spack/external/pyqver2.py | 344 ------------------------- lib/spack/external/pyqver3.py | 248 ------------------ lib/spack/llnl/util/cpu/microarchitecture.py | 2 +- lib/spack/llnl/util/cpu/schema.py | 2 +- lib/spack/llnl/util/lang.py | 8 +- lib/spack/spack/mirror.py | 2 +- lib/spack/spack/repo.py | 2 +- lib/spack/spack/test/python_version.py | 159 ------------ lib/spack/spack/test/schema.py | 2 +- lib/spack/spack/util/imp/importlib_importer.py | 2 +- lib/spack/spack/variant.py | 2 +- 13 files changed, 42 insertions(+), 769 deletions(-) create mode 100644 .github/workflows/minimum_python_versions.yaml delete mode 100755 lib/spack/external/pyqver2.py delete mode 100755 lib/spack/external/pyqver3.py delete mode 100644 lib/spack/spack/test/python_version.py diff --git a/.github/workflows/minimum_python_versions.yaml b/.github/workflows/minimum_python_versions.yaml new file mode 100644 index 0000000000..f0a5736849 --- /dev/null +++ b/.github/workflows/minimum_python_versions.yaml @@ -0,0 +1,30 @@ +name: Minimum Python Versions + +on: + push: + branches: + - master + - develop + pull_request: + branches: + - master + - develop +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: 2.7 + - name: Install Python Packages + run: | + pip install --upgrade pip + pip install --upgrade vermin + - name: Minimum Version (Spack's Core) + run: vermin --backport argparse -t=2.6- -t=3.5- -v lib/spack/spack/ lib/spack/llnl/ bin/ + - name: Minimum Version (Repositories) + run: vermin --backport argparse -t=2.6- -t=3.5- -v var/spack/repos diff --git a/lib/spack/external/__init__.py b/lib/spack/external/__init__.py index 1001ff5fff..3550a4235f 100644 --- a/lib/spack/external/__init__.py +++ b/lib/spack/external/__init__.py @@ -82,14 +82,6 @@ py ini-parsing, io, code, and log facilities. * Version: 1.4.34 (last version supporting Python 2.6) -pyqver ------- - -* Homepage: https://github.com/ghewgill/pyqver -* Usage: External script to query required python version of - python source code. Used for ensuring 2.6 compatibility. -* Version: Unversioned - pytest ------ diff --git a/lib/spack/external/pyqver2.py b/lib/spack/external/pyqver2.py deleted file mode 100755 index 07b191425b..0000000000 --- a/lib/spack/external/pyqver2.py +++ /dev/null @@ -1,344 +0,0 @@ -#!/usr/bin/env python -# -# pyqver2.py -# by Greg Hewgill -# https://github.com/ghewgill/pyqver -# -# This software is provided 'as-is', without any express or implied -# warranty. In no event will the author be held liable for any damages -# arising from the use of this software. -# -# Permission is granted to anyone to use this software for any purpose, -# including commercial applications, and to alter it and redistribute it -# freely, subject to the following restrictions: -# -# 1. The origin of this software must not be misrepresented; you must not -# claim that you wrote the original software. If you use this software -# in a product, an acknowledgment in the product documentation would be -# appreciated but is not required. -# 2. Altered source versions must be plainly marked as such, and must not be -# misrepresented as being the original software. -# 3. This notice may not be removed or altered from any source distribution. -# -# Copyright (c) 2009-2013 Greg Hewgill http://hewgill.com -# - -import compiler -import platform -import sys - -StandardModules = { - "__future__": (2, 1), - "abc": (2, 6), -# skip argparse now that it's in lib/spack/external -# "argparse": (2, 7), - "ast": (2, 6), - "atexit": (2, 0), - "bz2": (2, 3), - "cgitb": (2, 2), - "collections": (2, 4), - "contextlib": (2, 5), - "cookielib": (2, 4), - "cProfile": (2, 5), - "csv": (2, 3), - "ctypes": (2, 5), - "datetime": (2, 3), - "decimal": (2, 4), - "difflib": (2, 1), - "DocXMLRPCServer": (2, 3), - "dummy_thread": (2, 3), - "dummy_threading": (2, 3), - "email": (2, 2), - "fractions": (2, 6), - "functools": (2, 5), - "future_builtins": (2, 6), - "hashlib": (2, 5), - "heapq": (2, 3), - "hmac": (2, 2), - "hotshot": (2, 2), - "HTMLParser": (2, 2), - "importlib": (2, 7), - "inspect": (2, 1), - "io": (2, 6), - "itertools": (2, 3), - "json": (2, 6), - "logging": (2, 3), - "modulefinder": (2, 3), - "msilib": (2, 5), - "multiprocessing": (2, 6), - "netrc": (1, 5, 2), - "numbers": (2, 6), - "optparse": (2, 3), - "ossaudiodev": (2, 3), - "pickletools": (2, 3), - "pkgutil": (2, 3), - "platform": (2, 3), - "pydoc": (2, 1), - "runpy": (2, 5), - "sets": (2, 3), - "shlex": (1, 5, 2), - "SimpleXMLRPCServer": (2, 2), - "spwd": (2, 5), - "sqlite3": (2, 5), - "ssl": (2, 6), - "stringprep": (2, 3), - "subprocess": (2, 4), - "sysconfig": (2, 7), - "tarfile": (2, 3), - "textwrap": (2, 3), - "timeit": (2, 3), - "unittest": (2, 1), - "uuid": (2, 5), - "warnings": (2, 1), - "weakref": (2, 1), - "winsound": (1, 5, 2), - "wsgiref": (2, 5), - "xml.dom": (2, 0), - "xml.dom.minidom": (2, 0), - "xml.dom.pulldom": (2, 0), - "xml.etree.ElementTree": (2, 5), - "xml.parsers.expat":(2, 0), - "xml.sax": (2, 0), - "xml.sax.handler": (2, 0), - "xml.sax.saxutils": (2, 0), - "xml.sax.xmlreader":(2, 0), - "xmlrpclib": (2, 2), - "zipfile": (1, 6), - "zipimport": (2, 3), - "_ast": (2, 5), - "_winreg": (2, 0), -} - -Functions = { - "all": (2, 5), - "any": (2, 5), - "collections.Counter": (2, 7), - "collections.defaultdict": (2, 5), - "collections.OrderedDict": (2, 7), - "functools.total_ordering": (2, 7), - "enumerate": (2, 3), - "frozenset": (2, 4), - "itertools.compress": (2, 7), - "math.erf": (2, 7), - "math.erfc": (2, 7), - "math.expm1": (2, 7), - "math.gamma": (2, 7), - "math.lgamma": (2, 7), - "memoryview": (2, 7), - "next": (2, 6), - "os.getresgid": (2, 7), - "os.getresuid": (2, 7), - "os.initgroups": (2, 7), - "os.setresgid": (2, 7), - "os.setresuid": (2, 7), - "reversed": (2, 4), - "set": (2, 4), - "subprocess.check_call": (2, 5), - "subprocess.check_output": (2, 7), - "sum": (2, 3), - "symtable.is_declared_global": (2, 7), - "weakref.WeakSet": (2, 7), -} - -Identifiers = { - "False": (2, 2), - "True": (2, 2), -} - -def uniq(a): - if len(a) == 0: - return [] - else: - return [a[0]] + uniq([x for x in a if x != a[0]]) - -class NodeChecker(object): - def __init__(self): - self.vers = dict() - self.vers[(2,0)] = [] - def add(self, node, ver, msg): - if ver not in self.vers: - self.vers[ver] = [] - self.vers[ver].append((node.lineno, msg)) - def default(self, node): - for child in node.getChildNodes(): - self.visit(child) - def visitCallFunc(self, node): - def rollup(n): - if isinstance(n, compiler.ast.Name): - return n.name - elif isinstance(n, compiler.ast.Const): - return type(n.value).__name__ - elif isinstance(n, compiler.ast.Getattr): - r = rollup(n.expr) - if r: - return r + "." + n.attrname - name = rollup(node.node) - if name: - # Special handling for empty format strings, which aren't - # allowed in Python 2.6 - if name in ('unicode.format', 'str.format'): - n = node.node - if isinstance(n, compiler.ast.Getattr): - n = n.expr - if isinstance(n, compiler.ast.Const): - if '{}' in n.value: - self.add(node, (2,7), name + ' with {} format string') - - v = Functions.get(name) - if v is not None: - self.add(node, v, name) - self.default(node) - def visitClass(self, node): - if node.bases: - self.add(node, (2,2), "new-style class") - if node.decorators: - self.add(node, (2,6), "class decorator") - self.default(node) - def visitDictComp(self, node): - self.add(node, (2,7), "dictionary comprehension") - self.default(node) - def visitFloorDiv(self, node): - self.add(node, (2,2), "// operator") - self.default(node) - def visitFrom(self, node): - v = StandardModules.get(node.modname) - if v is not None: - self.add(node, v, node.modname) - for n in node.names: - name = node.modname + "." + n[0] - v = Functions.get(name) - if v is not None: - self.add(node, v, name) - def visitFunction(self, node): - if node.decorators: - self.add(node, (2,4), "function decorator") - self.default(node) - def visitGenExpr(self, node): - self.add(node, (2,4), "generator expression") - self.default(node) - def visitGetattr(self, node): - if (isinstance(node.expr, compiler.ast.Const) - and isinstance(node.expr.value, str) - and node.attrname == "format"): - self.add(node, (2,6), "string literal .format()") - self.default(node) - def visitIfExp(self, node): - self.add(node, (2,5), "inline if expression") - self.default(node) - def visitImport(self, node): - for n in node.names: - v = StandardModules.get(n[0]) - if v is not None: - self.add(node, v, n[0]) - self.default(node) - def visitName(self, node): - v = Identifiers.get(node.name) - if v is not None: - self.add(node, v, node.name) - self.default(node) - def visitSet(self, node): - self.add(node, (2,7), "set literal") - self.default(node) - def visitSetComp(self, node): - self.add(node, (2,7), "set comprehension") - self.default(node) - def visitTryFinally(self, node): - # try/finally with a suite generates a Stmt node as the body, - # but try/except/finally generates a TryExcept as the body - if isinstance(node.body, compiler.ast.TryExcept): - self.add(node, (2,5), "try/except/finally") - self.default(node) - def visitWith(self, node): - if isinstance(node.body, compiler.ast.With): - self.add(node, (2,7), "with statement with multiple contexts") - else: - self.add(node, (2,5), "with statement") - self.default(node) - def visitYield(self, node): - self.add(node, (2,2), "yield expression") - self.default(node) - -def get_versions(source, filename=None): - """Return information about the Python versions required for specific features. - - The return value is a dictionary with keys as a version number as a tuple - (for example Python 2.6 is (2,6)) and the value are a list of features that - require the indicated Python version. - """ - tree = compiler.parse(source) - checker = compiler.walk(tree, NodeChecker()) - return checker.vers - -def v27(source): - if sys.version_info >= (2, 7): - return qver(source) - else: - print >>sys.stderr, "Not all features tested, run --test with Python 2.7" - return (2, 7) - -def qver(source): - """Return the minimum Python version required to run a particular bit of code. - - >>> qver('print "hello world"') - (2, 0) - >>> qver('class test(object): pass') - (2, 2) - >>> qver('yield 1') - (2, 2) - >>> qver('a // b') - (2, 2) - >>> qver('True') - (2, 2) - >>> qver('enumerate(a)') - (2, 3) - >>> qver('total = sum') - (2, 0) - >>> qver('sum(a)') - (2, 3) - >>> qver('(x*x for x in range(5))') - (2, 4) - >>> qver('class C:\\n @classmethod\\n def m(): pass') - (2, 4) - >>> qver('y if x else z') - (2, 5) - >>> qver('import hashlib') - (2, 5) - >>> qver('from hashlib import md5') - (2, 5) - >>> qver('import xml.etree.ElementTree') - (2, 5) - >>> qver('try:\\n try: pass;\\n except: pass;\\nfinally: pass') - (2, 0) - >>> qver('try: pass;\\nexcept: pass;\\nfinally: pass') - (2, 5) - >>> qver('from __future__ import with_statement\\nwith x: pass') - (2, 5) - >>> qver('collections.defaultdict(list)') - (2, 5) - >>> qver('from collections import defaultdict') - (2, 5) - >>> qver('"{0}".format(0)') - (2, 6) - >>> qver('memoryview(x)') - (2, 7) - >>> v27('{1, 2, 3}') - (2, 7) - >>> v27('{x for x in s}') - (2, 7) - >>> v27('{x: y for x in s}') - (2, 7) - >>> qver('from __future__ import with_statement\\nwith x:\\n with y: pass') - (2, 5) - >>> v27('from __future__ import with_statement\\nwith x, y: pass') - (2, 7) - >>> qver('@decorator\\ndef f(): pass') - (2, 4) - >>> qver('@decorator\\nclass test:\\n pass') - (2, 6) - - #>>> qver('0o0') - #(2, 6) - #>>> qver('@foo\\nclass C: pass') - #(2, 6) - """ - return max(get_versions(source).keys()) diff --git a/lib/spack/external/pyqver3.py b/lib/spack/external/pyqver3.py deleted file mode 100755 index b63576a064..0000000000 --- a/lib/spack/external/pyqver3.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python3 -# -# pyqver3.py -# by Greg Hewgill -# https://github.com/ghewgill/pyqver -# -# This software is provided 'as-is', without any express or implied -# warranty. In no event will the author be held liable for any damages -# arising from the use of this software. -# -# Permission is granted to anyone to use this software for any purpose, -# including commercial applications, and to alter it and redistribute it -# freely, subject to the following restrictions: -# -# 1. The origin of this software must not be misrepresented; you must not -# claim that you wrote the original software. If you use this software -# in a product, an acknowledgment in the product documentation would be -# appreciated but is not required. -# 2. Altered source versions must be plainly marked as such, and must not be -# misrepresented as being the original software. -# 3. This notice may not be removed or altered from any source distribution. -# -# Copyright (c) 2009-2013 Greg Hewgill http://hewgill.com -# -import ast -import platform -import sys - -StandardModules = { -# skip argparse now that it's in lib/spack/external -# "argparse": (3, 2), - "faulthandler": (3, 3), - "importlib": (3, 1), - "ipaddress": (3, 3), - "lzma": (3, 3), - "tkinter.ttk": (3, 1), - "unittest.mock": (3, 3), - "venv": (3, 3), -} - -Functions = { - "bytearray.maketrans": (3, 1), - "bytes.maketrans": (3, 1), - "bz2.open": (3, 3), - "collections.Counter": (3, 1), - "collections.OrderedDict": (3, 1), - "crypt.mksalt": (3, 3), - "email.generator.BytesGenerator": (3, 2), - "email.message_from_binary_file": (3, 2), - "email.message_from_bytes": (3, 2), - "functools.lru_cache": (3, 2), - "gzip.compress": (3, 2), - "gzip.decompress": (3, 2), - "inspect.getclosurevars": (3, 3), - "inspect.getgeneratorlocals": (3, 3), - "inspect.getgeneratorstate": (3, 2), - "itertools.combinations_with_replacement": (3, 1), - "itertools.compress": (3, 1), - "logging.config.dictConfig": (3, 2), - "logging.NullHandler": (3, 1), - "math.erf": (3, 2), - "math.erfc": (3, 2), - "math.expm1": (3, 2), - "math.gamma": (3, 2), - "math.isfinite": (3, 2), - "math.lgamma": (3, 2), - "math.log2": (3, 3), - "os.environb": (3, 2), - "os.fsdecode": (3, 2), - "os.fsencode": (3, 2), - "os.fwalk": (3, 3), - "os.getenvb": (3, 2), - "os.get_exec_path": (3, 2), - "os.getgrouplist": (3, 3), - "os.getpriority": (3, 3), - "os.getresgid": (3, 2), - "os.getresuid": (3, 2), - "os.get_terminal_size": (3, 3), - "os.getxattr": (3, 3), - "os.initgroups": (3, 2), - "os.listxattr": (3, 3), - "os.lockf": (3, 3), - "os.pipe2": (3, 3), - "os.posix_fadvise": (3, 3), - "os.posix_fallocate": (3, 3), - "os.pread": (3, 3), - "os.pwrite": (3, 3), - "os.readv": (3, 3), - "os.removexattr": (3, 3), - "os.replace": (3, 3), - "os.sched_get_priority_max": (3, 3), - "os.sched_get_priority_min": (3, 3), - "os.sched_getaffinity": (3, 3), - "os.sched_getparam": (3, 3), - "os.sched_getscheduler": (3, 3), - "os.sched_rr_get_interval": (3, 3), - "os.sched_setaffinity": (3, 3), - "os.sched_setparam": (3, 3), - "os.sched_setscheduler": (3, 3), - "os.sched_yield": (3, 3), - "os.sendfile": (3, 3), - "os.setpriority": (3, 3), - "os.setresgid": (3, 2), - "os.setresuid": (3, 2), - "os.setxattr": (3, 3), - "os.sync": (3, 3), - "os.truncate": (3, 3), - "os.waitid": (3, 3), - "os.writev": (3, 3), - "shutil.chown": (3, 3), - "shutil.disk_usage": (3, 3), - "shutil.get_archive_formats": (3, 3), - "shutil.get_terminal_size": (3, 3), - "shutil.get_unpack_formats": (3, 3), - "shutil.make_archive": (3, 3), - "shutil.register_archive_format": (3, 3), - "shutil.register_unpack_format": (3, 3), - "shutil.unpack_archive": (3, 3), - "shutil.unregister_archive_format": (3, 3), - "shutil.unregister_unpack_format": (3, 3), - "shutil.which": (3, 3), - "signal.pthread_kill": (3, 3), - "signal.pthread_sigmask": (3, 3), - "signal.sigpending": (3, 3), - "signal.sigtimedwait": (3, 3), - "signal.sigwait": (3, 3), - "signal.sigwaitinfo": (3, 3), - "socket.CMSG_LEN": (3, 3), - "socket.CMSG_SPACE": (3, 3), - "socket.fromshare": (3, 3), - "socket.if_indextoname": (3, 3), - "socket.if_nameindex": (3, 3), - "socket.if_nametoindex": (3, 3), - "socket.sethostname": (3, 3), - "ssl.match_hostname": (3, 2), - "ssl.RAND_bytes": (3, 3), - "ssl.RAND_pseudo_bytes": (3, 3), - "ssl.SSLContext": (3, 2), - "ssl.SSLEOFError": (3, 3), - "ssl.SSLSyscallError": (3, 3), - "ssl.SSLWantReadError": (3, 3), - "ssl.SSLWantWriteError": (3, 3), - "ssl.SSLZeroReturnError": (3, 3), - "stat.filemode": (3, 3), - "textwrap.indent": (3, 3), - "threading.get_ident": (3, 3), - "time.clock_getres": (3, 3), - "time.clock_gettime": (3, 3), - "time.clock_settime": (3, 3), - "time.get_clock_info": (3, 3), - "time.monotonic": (3, 3), - "time.perf_counter": (3, 3), - "time.process_time": (3, 3), - "types.new_class": (3, 3), - "types.prepare_class": (3, 3), -} - -def uniq(a): - if len(a) == 0: - return [] - else: - return [a[0]] + uniq([x for x in a if x != a[0]]) - -class NodeChecker(ast.NodeVisitor): - def __init__(self): - self.vers = dict() - self.vers[(3,0)] = [] - def add(self, node, ver, msg): - if ver not in self.vers: - self.vers[ver] = [] - self.vers[ver].append((node.lineno, msg)) - def visit_Call(self, node): - def rollup(n): - if isinstance(n, ast.Name): - return n.id - elif isinstance(n, ast.Attribute): - r = rollup(n.value) - if r: - return r + "." + n.attr - name = rollup(node.func) - if name: - v = Functions.get(name) - if v is not None: - self.add(node, v, name) - self.generic_visit(node) - def visit_Import(self, node): - for n in node.names: - v = StandardModules.get(n.name) - if v is not None: - self.add(node, v, n.name) - self.generic_visit(node) - def visit_ImportFrom(self, node): - v = StandardModules.get(node.module) - if v is not None: - self.add(node, v, node.module) - for n in node.names: - name = node.module + "." + n.name - v = Functions.get(name) - if v is not None: - self.add(node, v, name) - def visit_Raise(self, node): - if isinstance(node.cause, ast.Name) and node.cause.id == "None": - self.add(node, (3,3), "raise ... from None") - def visit_YieldFrom(self, node): - self.add(node, (3,3), "yield from") - -def get_versions(source, filename=None): - """Return information about the Python versions required for specific features. - - The return value is a dictionary with keys as a version number as a tuple - (for example Python 3.1 is (3,1)) and the value are a list of features that - require the indicated Python version. - """ - tree = ast.parse(source, filename=filename) - checker = NodeChecker() - checker.visit(tree) - return checker.vers - -def v33(source): - if sys.version_info >= (3, 3): - return qver(source) - else: - print("Not all features tested, run --test with Python 3.3", file=sys.stderr) - return (3, 3) - -def qver(source): - """Return the minimum Python version required to run a particular bit of code. - - >>> qver('print("hello world")') - (3, 0) - >>> qver("import importlib") - (3, 1) - >>> qver("from importlib import x") - (3, 1) - >>> qver("import tkinter.ttk") - (3, 1) - >>> qver("from collections import Counter") - (3, 1) - >>> qver("collections.OrderedDict()") - (3, 1) - >>> qver("import functools\\n@functools.lru_cache()\\ndef f(x): x*x") - (3, 2) - >>> v33("yield from x") - (3, 3) - >>> v33("raise x from None") - (3, 3) - """ - return max(get_versions(source).keys()) diff --git a/lib/spack/llnl/util/cpu/microarchitecture.py b/lib/spack/llnl/util/cpu/microarchitecture.py index 3d1590376a..759bff5058 100644 --- a/lib/spack/llnl/util/cpu/microarchitecture.py +++ b/lib/spack/llnl/util/cpu/microarchitecture.py @@ -8,7 +8,7 @@ import re import warnings try: - from collections.abc import Sequence + from collections.abc import Sequence # novm except ImportError: from collections import Sequence diff --git a/lib/spack/llnl/util/cpu/schema.py b/lib/spack/llnl/util/cpu/schema.py index cc15cb64ba..14cdf4e00f 100644 --- a/lib/spack/llnl/util/cpu/schema.py +++ b/lib/spack/llnl/util/cpu/schema.py @@ -6,7 +6,7 @@ import json import os.path try: - from collections.abc import MutableMapping + from collections.abc import MutableMapping # novm except ImportError: from collections import MutableMapping diff --git a/lib/spack/llnl/util/lang.py b/lib/spack/llnl/util/lang.py index 11820a7f58..bdb551dcad 100644 --- a/lib/spack/llnl/util/lang.py +++ b/lib/spack/llnl/util/lang.py @@ -612,12 +612,14 @@ def load_module_from_file(module_name, module_path): """ if sys.version_info[0] == 3 and sys.version_info[1] >= 5: import importlib.util - spec = importlib.util.spec_from_file_location(module_name, module_path) - module = importlib.util.module_from_spec(spec) + spec = importlib.util.spec_from_file_location( # novm + module_name, module_path) + module = importlib.util.module_from_spec(spec) # novm spec.loader.exec_module(module) elif sys.version_info[0] == 3 and sys.version_info[1] < 5: import importlib.machinery - loader = importlib.machinery.SourceFileLoader(module_name, module_path) + loader = importlib.machinery.SourceFileLoader( # novm + module_name, module_path) module = loader.load_module() elif sys.version_info[0] == 2: import imp diff --git a/lib/spack/spack/mirror.py b/lib/spack/spack/mirror.py index ceb53801ce..650b7bd409 100644 --- a/lib/spack/spack/mirror.py +++ b/lib/spack/spack/mirror.py @@ -24,7 +24,7 @@ import ruamel.yaml.error as yaml_error from ordereddict_backport import OrderedDict try: - from collections.abc import Mapping + from collections.abc import Mapping # novm except ImportError: from collections import Mapping diff --git a/lib/spack/spack/repo.py b/lib/spack/spack/repo.py index 3ea196dd4d..8b124a4a18 100644 --- a/lib/spack/spack/repo.py +++ b/lib/spack/spack/repo.py @@ -20,7 +20,7 @@ import traceback from six import string_types, add_metaclass try: - from collections.abc import Mapping + from collections.abc import Mapping # novm except ImportError: from collections import Mapping diff --git a/lib/spack/spack/test/python_version.py b/lib/spack/spack/test/python_version.py deleted file mode 100644 index 6875aa4655..0000000000 --- a/lib/spack/spack/test/python_version.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other -# Spack Project Developers. See the top-level COPYRIGHT file for details. -# -# SPDX-License-Identifier: (Apache-2.0 OR MIT) - -"""Check that Spack complies with minimum supported python versions. - -We ensure that all Spack files work with Python2 >= 2.6 and Python3 >= 3.0. - -We'd like to drop 2.6 support at some point, but there are still many HPC -systems that ship with RHEL6/CentOS 6, which have Python 2.6 as the -default version. Once those go away, we can likely drop 2.6 and increase -the minimum supported Python 3 version, as well. -""" -from __future__ import print_function - -import os -import sys -import re - -import pytest - -import llnl.util.tty as tty - -import spack.paths -from spack.paths import lib_path as spack_lib_path - - -# -# This test uses pyqver, by Greg Hewgill, which is a dual-source module. -# That means we need to do different checks depending on whether we're -# running Python 2 or Python 3. -# -if sys.version_info[0] < 3: - import pyqver2 as pyqver - spack_min_supported = (2, 6) - - # Exclude Python 3 versions of dual-source modules when using Python 2 - exclude_paths = [ - # Jinja 2 has some 'async def' functions that are not treated correctly - # by pyqver.py - os.path.join(spack_lib_path, 'external', 'jinja2', 'asyncfilters.py'), - os.path.join(spack_lib_path, 'external', 'jinja2', 'asyncsupport.py'), - os.path.join(spack_lib_path, 'external', 'yaml', 'lib3'), - os.path.join(spack_lib_path, 'external', 'pyqver3.py'), - # Uses importlib - os.path.join(spack_lib_path, 'spack', 'test', 'schema.py') - ] - -else: - import pyqver3 as pyqver - spack_min_supported = (3, 0) - - # Exclude Python 2 versions of dual-source modules when using Python 3 - exclude_paths = [ - # Jinja 2 has some 'async def' functions that are not treated correctly - # by pyqver.py - os.path.join(spack_lib_path, 'external', 'jinja2', 'asyncfilters.py'), - os.path.join(spack_lib_path, 'external', 'jinja2', 'asyncsupport.py'), - os.path.join(spack_lib_path, 'external', 'yaml', 'lib'), - os.path.join(spack_lib_path, 'external', 'pyqver2.py'), - # Uses importlib - os.path.join(spack_lib_path, 'spack', 'test', 'schema.py') - ] - - -def pyfiles(search_paths, exclude=()): - """Generator that yields all the python files in the search paths. - - Args: - search_paths (list of str): list of paths to search for python files - exclude (list of str): file paths to exclude from search - - Yields: - python files in the search path. - """ - # first file is the spack script. - yield spack.paths.spack_script - - # Iterate through the whole spack source tree. - for path in search_paths: - for root, dirnames, filenames in os.walk(path): - for filename in filenames: - realpath = os.path.realpath(os.path.join(root, filename)) - if any(realpath.startswith(p) for p in exclude): - continue - - if re.match(r'^[^.#].*\.py$', filename): - yield os.path.join(root, filename) - - -def check_python_versions(files): - """Check that a set of Python files works with supported Ptyhon versions""" - # This is a dict dict mapping: - # version -> filename -> reasons - # - # Reasons are tuples of (lineno, string), where the string is the - # cause for a version incompatibility. - all_issues = {} - - # Parse files and run pyqver on each file. - for path in files: - with open(path) as pyfile: - full_text = pyfile.read() - versions = pyqver.get_versions(full_text, path) - - for ver, reasons in versions.items(): - if ver <= spack_min_supported: - continue - - # Record issues. Mark exceptions with '# nopyqver' comment - for lineno, cause in reasons: - lines = full_text.split('\n') - if not re.search(r'#\s*nopyqver\s*$', lines[lineno - 1]): - all_issues.setdefault(ver, {})[path] = reasons - - # Print a message if there are are issues - if all_issues: - tty.msg("Spack must remain compatible with Python version %d.%d" - % spack_min_supported) - - # Print out a table showing which files/linenos require which - # python version, and a string describing why. - for v in sorted(all_issues.keys(), reverse=True): - messages = [] - for path in sorted(all_issues[v].keys()): - short_path = path - if path.startswith(spack.paths.prefix): - short_path = path[len(spack.paths.prefix):] - - reasons = [r for r in set(all_issues[v][path]) if r] - for lineno, cause in reasons: - file_line = "%s:%s" % (short_path.lstrip('/'), lineno) - messages.append((file_line, cause)) - - print() - tty.msg("These files require version %d.%d:" % v) - maxlen = max(len(f) for f, prob in messages) - fmt = "%%-%ds%%s" % (maxlen + 3) - print(fmt % ('File', 'Reason')) - print(fmt % ('-' * (maxlen), '-' * 20)) - for msg in messages: - print(fmt % msg) - - # Fail this test if there were issues. - assert not all_issues - - -@pytest.mark.maybeslow -def test_core_module_compatibility(): - """Test that all core spack modules work with supported Python versions.""" - check_python_versions( - pyfiles([spack_lib_path], exclude=exclude_paths)) - - -@pytest.mark.maybeslow -def test_package_module_compatibility(): - """Test that all spack packages work with supported Python versions.""" - check_python_versions(pyfiles([spack.paths.packages_path])) diff --git a/lib/spack/spack/test/schema.py b/lib/spack/spack/test/schema.py index 9149b77b37..c86964000d 100644 --- a/lib/spack/spack/test/schema.py +++ b/lib/spack/spack/test/schema.py @@ -107,7 +107,7 @@ def test_module_suffixes(module_suffixes_schema): 'repos' ]) def test_schema_validation(meta_schema, config_name): - import importlib + import importlib # novm module_name = 'spack.schema.{0}'.format(config_name) module = importlib.import_module(module_name) schema = getattr(module, 'schema') diff --git a/lib/spack/spack/util/imp/importlib_importer.py b/lib/spack/spack/util/imp/importlib_importer.py index 33c50cb601..fb4ad58aa6 100644 --- a/lib/spack/spack/util/imp/importlib_importer.py +++ b/lib/spack/spack/util/imp/importlib_importer.py @@ -7,7 +7,7 @@ ``importlib`` is only fully implemented in Python 3. """ -from importlib.machinery import SourceFileLoader +from importlib.machinery import SourceFileLoader # novm class PrependFileLoader(SourceFileLoader): diff --git a/lib/spack/spack/variant.py b/lib/spack/spack/variant.py index bdee9d3552..250843f115 100644 --- a/lib/spack/spack/variant.py +++ b/lib/spack/spack/variant.py @@ -21,7 +21,7 @@ import spack.directives import spack.error as error try: - from collections.abc import Sequence + from collections.abc import Sequence # novm except ImportError: from collections import Sequence -- cgit v1.2.3-60-g2f50