summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2019-10-17 06:40:23 -0700
committerGitHub <noreply@github.com>2019-10-17 06:40:23 -0700
commitcf9de058aaa6bfde635868422b504e4fb1413359 (patch)
tree5736d8d8da67432902387d720b6fc2b4e454ce18 /lib
parent93c34039e488937e6803d20316c1d3607869067e (diff)
downloadspack-cf9de058aaa6bfde635868422b504e4fb1413359.tar.gz
spack-cf9de058aaa6bfde635868422b504e4fb1413359.tar.bz2
spack-cf9de058aaa6bfde635868422b504e4fb1413359.tar.xz
spack-cf9de058aaa6bfde635868422b504e4fb1413359.zip
multimethods: avoid calling caller_locals() in Python 3 (#13238)
Python 3 metaclasses have a `__prepare__` method that lets us save the class's dictionary before it is constructed. In Python 2 we had to walk up the stack using our `caller_locals()` method to get at this. Using `__prepare__` is much faster as it doesn't require us to use `inspect`. This makes multimethods use the faster `__prepare__` method in Python3, while still using `caller_locals()` in Python 2. We try to reduce the use of caller locals using caching to speed up Python 2 a little bit.
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/multimethod.py41
-rw-r--r--lib/spack/spack/package.py4
2 files changed, 35 insertions, 10 deletions
diff --git a/lib/spack/spack/multimethod.py b/lib/spack/spack/multimethod.py
index 085f243174..e0854a01c9 100644
--- a/lib/spack/spack/multimethod.py
+++ b/lib/spack/spack/multimethod.py
@@ -24,6 +24,7 @@ avoids overly complicated rat nests of if statements. Obviously,
depending on the scenario, regular old conditionals might be clearer,
so package authors should use their judgement.
"""
+
import functools
import inspect
@@ -34,6 +35,24 @@ import spack.error
from spack.spec import Spec
+class MultiMethodMeta(type):
+ """This allows us to track the class's dict during instantiation."""
+
+ #: saved dictionary of attrs on the class being constructed
+ _locals = None
+
+ @classmethod
+ def __prepare__(cls, name, bases, **kwargs):
+ """Save the dictionary that will be used for the class namespace."""
+ MultiMethodMeta._locals = dict()
+ return MultiMethodMeta._locals
+
+ def __init__(cls, name, bases, attr_dict):
+ """Clear out the cached locals dict once the class is built."""
+ MultiMethodMeta._locals = None
+ super(MultiMethodMeta, cls).__init__(name, bases, attr_dict)
+
+
class SpecMultiMethod(object):
"""This implements a multi-method for Spack specs. Packages are
instantiated with a particular spec, and you may want to
@@ -149,14 +168,15 @@ class when(object):
def install(self, prefix):
# Do default install
- @when('arch=chaos_5_x86_64_ib')
+ @when('target=x86_64:')
def install(self, prefix):
# This will be executed instead of the default install if
- # the package's platform() is chaos_5_x86_64_ib.
+ # the package's target is in the x86_64 family.
- @when('arch=bgqos_0")
+ @when('target=ppc64:')
def install(self, prefix):
- # This will be executed if the package's sys_type is bgqos_0
+ # This will be executed if the package's target is in
+ # the ppc64 family
This allows each package to have a default version of install() AND
specialized versions for particular platforms. The version that is
@@ -202,11 +222,14 @@ class when(object):
self.spec = Spec(condition)
def __call__(self, method):
- # Get the first definition of the method in the calling scope
- original_method = caller_locals().get(method.__name__)
-
- # Create a multimethod out of the original method if it
- # isn't one already.
+ # In Python 2, Get the first definition of the method in the
+ # calling scope by looking at the caller's locals. In Python 3,
+ # we handle this using MultiMethodMeta.__prepare__.
+ if MultiMethodMeta._locals is None:
+ MultiMethodMeta._locals = caller_locals()
+
+ # Create a multimethod with this name if there is not one already
+ original_method = MultiMethodMeta._locals.get(method.__name__)
if not type(original_method) == SpecMultiMethod:
original_method = SpecMultiMethod(original_method)
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index dbe6e38fa6..ecff265508 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -42,6 +42,7 @@ import spack.fetch_strategy as fs
import spack.hooks
import spack.mirror
import spack.mixins
+import spack.multimethod
import spack.repo
import spack.url
import spack.util.web
@@ -138,7 +139,8 @@ class InstallPhase(object):
class PackageMeta(
spack.directives.DirectiveMeta,
- spack.mixins.PackageMixinsMeta
+ spack.mixins.PackageMixinsMeta,
+ spack.multimethod.MultiMethodMeta
):
"""
Package metaclass for supporting directives (e.g., depends_on) and phases