robot 源码解读3【testsuite相关】

  1. testsuite相关代码中,描述符类装饰器比较难以理解,简化源码方便理解如下

'''robot 源码解读3【testsuite相关】
目的:
    1. 查看类的继承关系
    2. setter类的理解
'''

#  描述符类, 描述符是作为类的属性而不是实例属性存在的
#  将一个类中的方法装饰成类中的属性
class setter(object):

    def __init__(self, method):
        self.method = method
        self.attr_name = '_setter__' + method.__name__
        self.__doc__ = method.__doc__
        print('setter: __init__:' ,method)

    def __get__(self, instance, owner):  # instance:TestSuite object ;owner:class TestSuite
        if instance is None:
            return self
        print('setter: __get__:', instance, owner)
        return getattr(instance, self.attr_name)    # 返回的是TestSuite的attr_name
        # try:
        #     print('setter: __get__:', instance, owner)
        #     return getattr(instance, self.attr_name)
        # except AttributeError:
        #     raise AttributeError(self.method.__name__)

    def __set__(self, instance, value):
        if instance is None:
            return
        print('setter: __set__:', instance,value )
        # 相当于TestSuite 执行 test(self, Keyword)
        setattr(instance, self.attr_name, self.method(instance, value))



class SetterAwareType(type):

    def __new__(cls, name, bases, dct):
        slots = dct.get('__slots__')
        if slots is not None:
            for item in dct.values():
                if isinstance(item, setter):
                    slots.append(item.attr_name)
        return type.__new__(cls, name, bases, dct)



def with_metaclass(meta, *bases):
    """Create a base class with a metaclass."""
    # This requires a bit of explanation: the basic idea is to make a
    # dummy metaclass for one level of class instantiation that replaces
    # itself with the actual metaclass.
    class metaclass(type):
        def __new__(cls, name, this_bases, d):
            return meta(name, bases, d)
    return type.__new__(metaclass, 'temporary_class', (), {})


class ModelObject(with_metaclass(SetterAwareType, object)):

    __slots__ = []    # __slots__变量,来限制该class实例能添加的属性

    def copy(self, **attributes):
        pass


class Keyword(ModelObject):

    @setter
    def parent(self, parent):
        """Parent test suite, test case or keyword."""
        if parent and parent is not self.parent:
            self._sort_key = getattr(parent, '_child_sort_key', -1)
        return parent

    @setter
    def tags(self, tags):
        """Keyword tags as a :class:`~.model.tags.Tags` object."""
        return tags


class TestSuite(ModelObject):

    def __init__(self, name='', doc='', metadata=None, source=None):
        self.parent = None  #: Parent suite. ``None`` with the root suite.
        self._name = name

    # test = setter(test) , setter是描述符类,描述符是作为类的属性而不是实例属性存在的
    @setter
    def test(self, Keyword):
        print('TestSuite: test: 赋值的时候调用',Keyword)
        return Keyword

    # _setter__test = 'amize'


    # 源码中都是如下的suites,tests,keywords等
    # @setter
    # def suites(self, suites):
    #     """Child suites as a :class:`~.TestSuites` object."""
    #     return TestSuites(self.__class__, self, suites)
    #
    # @setter
    # def tests(self, tests):
    #     """Tests as a :class:`~.TestCases` object."""
    #     return TestCases(self.test_class, self, tests)
    #
    # @setter
    # def keywords(self, keywords):
    #     """Suite setup and teardown as a :class:`~.Keywords` object."""
    #     return Keywords(self.keyword_class, self, keywords)


''' 以下不实例化也会打印
setter: __init__: <function Keyword.parent at 0x0000020B824D7E58>
setter: __init__: <function Keyword.tags at 0x0000020B824D7EE8>
setter: __init__: <function TestSuite.test at 0x0000020B82520048>'''


# 不执行下面的语句也会打印上面的
suite = TestSuite('name')

keyword = Keyword()

#  @setter 保证suite.test必须赋值后才能运用,因为__get__ 返回的是__init__里面的值
# print(suite.test)  #  这里会报错:'TestSuite' object has no attribute '_setter__test'
# print(suite._setter__test) # 同上,报错:'TestSuite' object has no attribute '_setter__test'


suite.test = keyword  # 【1. setter: __set__:函数】 ,【2. TestSuite: test: 函数被调用】


# 已经赋值了 suite.test = keyword ,不能看class setter中的 __init__了,这里会报错:'Keyword' object has no attribute 'method'
# print(type(suite.test.method))


# _setter__test == suite.test ,区别是 print(suite.test)还要调用__get__
print(suite._setter__test)
print(suite.test)        # 【1. setter: __get__ 调用】

print(suite.__dict__)      # {'parent': None, '_name': 'name', '_setter__test': <__main__.Keyword object at 0x0000026458EF11C8>}
print(dir(suite))



原文地址:https://www.cnblogs.com/amize/p/14598025.html