summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2014-08-10 11:45:32 -0700
committerTodd Gamblin <tgamblin@llnl.gov>2014-08-10 11:45:32 -0700
commit8ab793a3a67a6ef4573b0b2b1e68b293b6d9fd2e (patch)
tree9bc193e855074dae5da387e98531bb1f1a3b19b1
parent884a4fecd18eb983ae02ead366d638a735e5170b (diff)
downloadspack-8ab793a3a67a6ef4573b0b2b1e68b293b6d9fd2e.tar.gz
spack-8ab793a3a67a6ef4573b0b2b1e68b293b6d9fd2e.tar.bz2
spack-8ab793a3a67a6ef4573b0b2b1e68b293b6d9fd2e.tar.xz
spack-8ab793a3a67a6ef4573b0b2b1e68b293b6d9fd2e.zip
Add external package with pyqver2 tool
-rw-r--r--lib/spack/external/__init__.py33
-rwxr-xr-xlib/spack/external/pyqver2.py392
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)