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.
This commit is contained in:
parent
93c34039e4
commit
cf9de058aa
@ -24,6 +24,7 @@
|
||||
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 @@
|
||||
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 SomePackage(Package):
|
||||
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 @@ def __init__(self, condition):
|
||||
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__)
|
||||
# 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 out of the original method if it
|
||||
# isn't one already.
|
||||
# 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)
|
||||
|
||||
|
@ -42,6 +42,7 @@
|
||||
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 @@ def copy(self):
|
||||
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user