Well, it's been said than imitation is the sincerest form of flattery, so be flattered, Michele!

In order to gain a better understanding of the whole metaclass issue, I decided to make my own implementation, targeting Python 3. I figured I could leave out a bunch of the complexity required to support Python 2.

The more I learn of 3, the more I love it. Many improvements in simplicity and elegance.

At any rate, what I have is below. My (very limited) initial tests are working fine. super() appears to work as is.

Feedback appreciated!

~Ethan~

8<-----------------------------------------------------------------
"""Traits -- used instead of multiple inheritance, inspired my Michele
Simionato's Simple Traits experiment

Python Version: 3.x

Intended use:
    To combine a single base class, as many traits as needed/desired,
    glue code to combine together succussfully.  While traits are kept
    in classes, they should be functions only: no state information
    should be kept.  If different traits from different classes having
    the same name are combined into one one class, that class must
    specify which one it wants, or an error will be raised at class
    creation time.  If __magic_methods__ are part of the traits, their
    names must be specified in the trait classes __magic_traits__ list
    attribute; like-wise, if traits have required attributes that must
    be supplied by the composed class/other traits, their names must be
    in the __traits_required__ list attribute.

Name resolution order:
    Least - Base class
            Traits
    Most -  Current (composed) class"""

class Trait(type):
    def __init__(yo, *args, **kwargs):
        super().__init__(*args)
    def __new__(metacls, cls_name, cls_bases, cls_dict, traits=tuple()):
        if len(cls_bases) > 1:
            raise TypeError("multiple bases not allowed with Traits")
result_class = type.__new__(metacls, cls_name, cls_bases,
                                                             cls_dict)

        conflicts = False
        for trait in result_class.__trait_conflicts__:
            if getattr(result_class, trait, None) is None:
                if not conflicts:
                    print()
                conflicts = True
                print("conflict found: %r is in %s" %
                        (trait,result_class.__trait_conflicts__[trait]))
        if conflicts:
            print()
            raise TypeError("conflicts must be resolved")
        delattr(result_class, '__trait_conflicts__')

        missing_required = False
        for trait in result_class.__required_traits__:
            if getattr(result_class, trait, None) is None:
                if not missing_required:
                    print()
                missing_required = True
                print("missing requirement: %r from %s" %
                        (trait,result_class.__required_traits__[trait]))
        if missing_required:
            print()
            raise TypeError("requirements not met")
        delattr(result_class, '__required_traits__')

        return result_class

    @classmethod
    def __prepare__(metacls, name, bases, traits=tuple()):
        class _Dict(dict):
            "Normal dict with traits attribute"
        class _Traits:
            "container for trait bundles"
            def __repr__(yo):
                return "(%s)" % ", ".join([str(getattr(yo, trait))
                        for trait in dir(yo)
                        if not trait.startswith('__')
                        or not trait.endswith('__')])
        if not traits:
            raise TypeError("no traits specified... what's the point?")
        elif type(traits) != tuple:
            traits = (traits, )
        class_dict = _Dict()
        # for direct . access here
        setattr(class_dict, 'traits', _Traits())
        # to survive proxification
        class_dict['traits'] = class_dict.traits
        setattr(class_dict, '__trait_conflicts__', dict())
        class_dict['__trait_conflicts__']=class_dict.__trait_conflicts__
        setattr(class_dict, '__magic_traits__', set())
        class_dict['__magic_traits__'] = class_dict.__magic_traits__
        setattr(class_dict, '__required_traits__', dict())
        class_dict['__required_traits__']=class_dict.__required_traits__
        for trait_bundle in traits:
            setattr(class_dict.traits,
                    trait_bundle.__name__,
                    trait_bundle)
        traits_dict = {}
        for trait_bundle in traits:
            metacls._integrate_traits(class_dict,
                                      traits_dict,
                                      trait_bundle)
        metacls._check_conflicts(class_dict, traits_dict)
        return class_dict

    @staticmethod
    def _check_conflicts(class_dict, traits_dict):
        for trait, cls_list in traits_dict.items():
            if len(cls_list) == 1:
                class_dict[trait] = getattr(cls_list[0], trait)
            else:
                first_trait = getattr(cls_list[0], trait)
                for next_class in cls_list[1:]:
                    next_trait = getattr(next_class, trait)
                    if first_trait is not next_trait:
                        trouble = True
                        class_dict.__trait_conflicts__[trait] = cls_list
                        break
                    else:
                        class_dict[trait] = first_trait

    @staticmethod
    def _integrate_traits(class_dict, traits_dict, trait_bundle):
        magic_traits=getattr(trait_bundle, '__magic_traits__', tuple())
        for trait in magic_traits:
            class_dict.__magic_traits__.add(trait)
            if trait not in traits_dict:
                traits_dict[trait] = [trait_bundle]
            else:
                traits_dict[trait].append(trait_bundle)
        required = getattr(trait_bundle, '__required_traits__', tuple())
        for trait in required:
            #class_dict.__required_traits__.add(trait)
            if trait not in class_dict.__required_traits__:
                class_dict.__required_traits__[trait] = [trait_bundle]
            else:
                # may have to fix following line...
                class_dict.__required_traits__[trait].\
                            append(trait_bundle)
        for trait in [t for t in dir(trait_bundle)
                if not t.startswith('__') or not t.endswith('__')]:
            if trait not in traits_dict:
                traits_dict[trait] = [trait_bundle]
            else:
                traits_dict[trait].append(trait_bundle)

# test stuff
class TBundle1():
    def repeat(yo, text, count):
        return "%s " % text * count

class BaseClass():
    def repeat(yo, text, count):
        return "----%s----" % text * count
    def whatsit(yo, arg1):
        return "Got a %s!!" % arg1

class DerivedClass(BaseClass, metaclass=Trait, traits=TBundle1):
    def repeat(yo, text, count):
        print('whatever...')
    def whatsit(yo, arg1):
        print("calling baseclass's whatsit...")
        print(super().whatsit(arg1))

8< ------------------------------------------------------------------------

My apologies for the formatting. With the exception of the one line above that may need to be recombined, it should compile (um, interpret? ;) correctly as-is.
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to