Make multimethods work with inheritance. (#3411)

Previously, this would fail with a NoSuchMethodError:

    class Package(object):
        # this is the default implementation
        def some_method(self):
            ...

    class Foo(Package):
        @when('platform=cray')
        def some_method(self):
            ...

        @when('platform=linux')
        def some_method(self):
            ...

This fixes the implementation of `@when` so that the superclass method
will be invoked when no subclass method matches.

Adds tests to ensure this works, as well.
This commit is contained in:
Todd Gamblin 2017-03-11 05:48:36 -08:00 committed by GitHub
parent 15f80ed15c
commit e3101808ae
4 changed files with 68 additions and 4 deletions

View File

@ -128,10 +128,16 @@ def __call__(self, package_self, *args, **kwargs):
if self.default:
return self.default(package_self, *args, **kwargs)
else:
raise NoSuchMethodError(
type(package_self), self.__name__, spec,
[m[0] for m in self.method_list])
superclass = super(package_self.__class__, package_self)
superclass_fn = getattr(superclass, self.__name__, None)
if callable(superclass_fn):
return superclass_fn(*args, **kwargs)
else:
raise NoSuchMethodError(
type(package_self), self.__name__, spec,
[m[0] for m in self.method_list])
def __str__(self):
return "SpecMultiMethod {\n\tdefault: %s,\n\tspecs: %s\n}" % (

View File

@ -118,3 +118,11 @@ def test_virtual_dep_match(builtin_mock):
pkg = spack.repo.get('multimethod^mpich@1.0')
assert pkg.different_by_virtual_dep() == 1
def test_multimethod_with_base_class(builtin_mock):
pkg = spack.repo.get('multimethod@3')
assert pkg.base_method() == "subclass_method"
pkg = spack.repo.get('multimethod@1')
assert pkg.base_method() == "base_method"

View File

@ -0,0 +1,40 @@
##############################################################################
# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://github.com/llnl/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 Lesser General Public License (as
# published by the Free Software Foundation) version 2.1, 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 Lesser 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
##############################################################################
from spack import *
class MultimethodBase(Package):
"""This is a base class for the Multimethod test case.
It tests whether mutlimethod properly invokes methods in a base
class when subclass multi-methods do not match.
"""
homepage = 'http://www.example.com/'
url = 'http://www.example.com/example-1.0.tar.gz'
def base_method(self):
return "base_method"

View File

@ -25,8 +25,10 @@
from spack import *
import spack.architecture
from spack.pkg.builtin.mock.multimethod_base import MultimethodBase
class Multimethod(Package):
class Multimethod(MultimethodBase):
"""This package is designed for use with Spack's multimethod test.
It has a bunch of test cases for the @when decorator that the
test uses.
@ -132,3 +134,11 @@ def different_by_virtual_dep(self):
@when('^mpi@2:')
def different_by_virtual_dep(self):
return 2
#
# Make sure methods with a default implementation in a superclass
# will invoke that method when none in the subclass match.
#
@when("@2:")
def base_method(self):
return "subclass_method"