diff options
author | Todd Gamblin <tgamblin@llnl.gov> | 2014-08-10 11:45:32 -0700 |
---|---|---|
committer | Todd Gamblin <tgamblin@llnl.gov> | 2014-08-10 11:45:32 -0700 |
commit | 8ab793a3a67a6ef4573b0b2b1e68b293b6d9fd2e (patch) | |
tree | 9bc193e855074dae5da387e98531bb1f1a3b19b1 /lib | |
parent | 884a4fecd18eb983ae02ead366d638a735e5170b (diff) | |
download | spack-8ab793a3a67a6ef4573b0b2b1e68b293b6d9fd2e.tar.gz spack-8ab793a3a67a6ef4573b0b2b1e68b293b6d9fd2e.tar.bz2 spack-8ab793a3a67a6ef4573b0b2b1e68b293b6d9fd2e.tar.xz spack-8ab793a3a67a6ef4573b0b2b1e68b293b6d9fd2e.zip |
Add external package with pyqver2 tool
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/external/__init__.py | 33 | ||||
-rwxr-xr-x | lib/spack/external/pyqver2.py | 392 |
2 files changed, 425 insertions, 0 deletions
diff --git a/lib/spack/external/__init__.py b/lib/spack/external/__init__.py new file mode 100644 index 0000000000..1cc981930a --- /dev/null +++ b/lib/spack/external/__init__.py @@ -0,0 +1,33 @@ +############################################################################## +# Copyright (c) 2013, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://scalability-llnl.github.io/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License (as published by +# the Free Software Foundation) version 2.1 dated February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +""" +This module contains external, potentially separately licensed, +packages that are included in spack. + +So far: + argparse: We include our own version to be Python 2.6 compatible. + pyqver2: External script to query required python version of python source code. + Used for ensuring 2.6 compatibility. +""" diff --git a/lib/spack/external/pyqver2.py b/lib/spack/external/pyqver2.py new file mode 100755 index 0000000000..875f8837fb --- /dev/null +++ b/lib/spack/external/pyqver2.py @@ -0,0 +1,392 @@ +#!/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), + "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), + "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.Getattr): + r = rollup(n.expr) + if r: + return r + "." + n.attrname + name = rollup(node.node) + if name: + 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): + """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()) + + +if __name__ == '__main__': + + Verbose = False + MinVersion = (2, 3) + Lint = False + + files = [] + i = 1 + while i < len(sys.argv): + a = sys.argv[i] + if a == "--test": + import doctest + doctest.testmod() + sys.exit(0) + if a == "-v" or a == "--verbose": + Verbose = True + elif a == "-l" or a == "--lint": + Lint = True + elif a == "-m" or a == "--min-version": + i += 1 + MinVersion = tuple(map(int, sys.argv[i].split("."))) + else: + files.append(a) + i += 1 + + if not files: + print >>sys.stderr, """Usage: %s [options] source ... + + Report minimum Python version required to run given source files. + + -m x.y or --min-version x.y (default 2.3) + report version triggers at or above version x.y in verbose mode + -v or --verbose + print more detailed report of version triggers for each version + """ % sys.argv[0] + sys.exit(1) + + for fn in files: + try: + f = open(fn) + source = f.read() + f.close() + ver = get_versions(source) + if Verbose: + print fn + for v in sorted([k for k in ver.keys() if k >= MinVersion], reverse=True): + reasons = [x for x in uniq(ver[v]) if x] + if reasons: + # each reason is (lineno, message) + print "\t%s\t%s" % (".".join(map(str, v)), ", ".join([x[1] for x in reasons])) + elif Lint: + for v in sorted([k for k in ver.keys() if k >= MinVersion], reverse=True): + reasons = [x for x in uniq(ver[v]) if x] + for r in reasons: + # each reason is (lineno, message) + print "%s:%s: %s %s" % (fn, r[0], ".".join(map(str, v)), r[1]) + else: + print "%s\t%s" % (".".join(map(str, max(ver.keys()))), fn) + except SyntaxError, x: + print "%s: syntax error compiling with Python %s: %s" % (fn, platform.python_version(), x) |