param icon indicating copy to clipboard operation
param copied to clipboard

concrete_descendents ignoring abstract flag?

Open jbednar opened this issue 4 years ago • 1 comments

As far as I can tell, param.concrete_descendents is ignoring whether a class is declared abstract:

image

Here the definition of concrete_descendents is:

def _is_abstract(class_):
    try:
        return class_.abstract
    except AttributeError:
        return False

def concrete_descendents(parentclass):
    return dict((c.__name__,c) for c in descendents(parentclass)
                if not _is_abstract(c))

and the definition of SelectorBase is:

class SelectorBase(Parameter):
    __abstract = True
    def get_range(self):
        raise NotImplementedError("get_range() must be implemented in subclasses.")

SelectorBase appears be declaring itself abstract, but that doesn't get picked up by concrete_descendents, as SelectorBase is not excluded from the list returned. I can't quite see how this was ever meant to work, given that none of this code appears to have changed in the past 9 years. Or am I just confused?

jbednar avatar Aug 19 '21 21:08 jbednar

Parameterized classes inherit the abstract property from the ParameterizedMetaclass metaclass (instances don't inherit this property). So _is_abstract above works fine when the class is a Parameterized subclass. SelectorBase.__abstract = True should be removed as Parameters aren't Parameterized subclasses.

class ParameterizedMetaclass(type):

    # Should use the official Python 2.6+ abstract base classes; see
    # https://github.com/holoviz/param/issues/84
    def __is_abstract(mcs):
        """
        Return True if the class has an attribute __abstract set to True.
        Subclasses will return False unless they themselves have
        __abstract set to true.  This mechanism allows a class to
        declare itself to be abstract (e.g. to avoid it being offered
        as an option in a GUI), without the "abstract" property being
        inherited by its subclasses (at least one of which is
        presumably not abstract).
        """
        # Can't just do ".__abstract", because that is mangled to
        # _ParameterizedMetaclass__abstract before running, but
        # the actual class object will have an attribute
        # _ClassName__abstract.  So, we have to mangle it ourselves at
        # runtime. Mangling follows description in
        # https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references
        try:
            return getattr(mcs,'_%s__abstract'%mcs.__name__.lstrip("_"))
        except AttributeError:
            return False

    abstract = property(__is_abstract)

maximlt avatar Feb 16 '25 08:02 maximlt