试图抓住clang \ cindex.py

时间:2016-09-01 18:03:58

标签: python clang decorator python-decorators python-descriptors

这与我所拥有的其他question有关,但没有答案...... 我试图了解Python bindinglibclang的内幕是什么,并且真的很难这样做。

我已经在Python中阅读了关于decoratorsdescriptors的文章的TON,以了解CachedProperty class in clang/cindex.py的工作原理,但仍然无法全部这些碎片。

我见过的最相关的文字是one SO answer,以及ActiveState中的code recipe。这对我有点帮助,但是 - 正如我所提到的 - 我还没有。

所以,让我们切入追逐: 我想了解为什么我在创建CIndex时获得AssertionError。我将在这里仅发布相关代码(cindex.py长3646行..),我希望我不会错过任何与我相关的内容。 我的代码只有一个相关的行,即:

index = clang.cindex.Index.create()

这引用line 2291 in cindex.py,产生:

return Index(conf.lib.clang_createIndex(excludeDecls, 0))

从现在开始,有一系列函数调用,我无法解释为什么和WTH来自它们。我将列出与每个部分相关的问题的代码和pdb输出:

(重要的是要提前注意:conf.lib定义如下:)

class Config:
    ...snip..

    @CachedProperty
    def lib(self):
        lib = self.get_cindex_library()
        ...
        return lib

CachedProperty代码:

class CachedProperty(object):
    """Decorator that lazy-loads the value of a property.

    The first time the property is accessed, the original property function is
    executed. The value it returns is set as the new value of that instance's
    property, replacing the original method.
    """

    def __init__(self, wrapped):
        self.wrapped = wrapped
        try:
            self.__doc__ = wrapped.__doc__
        except:
            pass

    def __get__(self, instance, instance_type=None):
        if instance is None:
            return self

        value = self.wrapped(instance)
        setattr(instance, self.wrapped.__name__, value)

        return value

Pdb输出

-> return Index(conf.lib.clang_createIndex(excludeDecls, 0))
(Pdb) s
--Call--
> d:\project\clang\cindex.py(137)__get__()
-> def __get__(self, instance, instance_type=None):
(Pdb) p self
<clang.cindex.CachedProperty object at 0x00000000027982E8>
(Pdb) p self.wrapped
<function Config.lib at 0x0000000002793598>
  1. 为什么接下来打电话 Index(conf.lib.clang_createIndex(excludeDecls, 0))CachedProperty.__get__方法?怎么样__init__
  2. 如果__init__方法没有被调用,那么self.wrapped的方法怎么样? 值α
  3. Pdb输出

    (Pdb) r
    --Return--
    > d:\project\clang\cindex.py(144)__get__()-><CDLL 'libcla... at 0x27a1cc0>
    -> return value
    (Pdb) n
    --Call--
    > c:\program files\python35\lib\ctypes\__init__.py(357)__getattr__()
    -> def __getattr__(self, name):
    (Pdb) r
    --Return--
    > c:\program files\python35\lib\ctypes\__init__.py(362)__getattr__()-><_FuncPtr obj...000000296B458>
    -> return func
    (Pdb)
    
    1. CachedProperty.__get__应该将值返回到哪里? CDLL.__getattr__方法的调用来自何处?
    2. 最重要的部分,对我来说

      (Pdb) n
      --Call--
      > d:\project\clang\cindex.py(1970)__init__()
      -> def __init__(self, obj):
      (Pdb) p obj
      40998256
      

      这是creation of ClangObject,类索引继承自。{/ p>

      1. 但是 - 在哪里用__init__调用一个参数?这是conf.lib.clang_createIndex(excludeDecls, 0)返回的那个吗?
      2. 这个数字(40998256)来自哪里?我一遍又一遍地得到相同的号码。据我所知,它应该只是一个数字,而是一个clang.cindex.LP_c_void_p object,这就是断言失败的原因。
      3. 总结一下,对我来说最好的是对这里的函数调用的逐步指导,因为我在这一切中有点迷失......

1 个答案:

答案 0 :(得分:2)

CachedProperty对象是descriptor object;只要Python尝试访问仅在类上可用且具有__get__方法的实例上的属性,就会自动调用__get__方法。

使用CachedProperty作为装饰器意味着调用它并创建CachedProperty的实例来替换Config类上的原始函数对象。 @CachedProperty行导致CachedProperty.__init__被调用,实例最终作为Config传递到Config.lib类。请记住,语法

@CachedProperty
def lib(self):
    # ...

基本上以

执行
def lib(self):
    # ...
lib = CachedProperty(lib)

所以这会创建一个CachedProperty()的实例,lib作为wrapped参数传入,然后Config.lib设置为该对象。

你可以在调试器中看到这个;一步,你可以检查type(config).lib

(Pdb) type(config)
<class Config at 0x00000000027936E>
(Pdb) type(config).lib
<clang.cindex.CachedProperty object at 0x00000000027982E8>

在代码库的其余部分configConfig类的实例。首先,该实例在lib对象中没有__dict__名称,因此实例没有这样的属性:

(Pdb) 'lib' in config.__dict__
False

因此,尝试获取config.lib必须回到类,其中Python找到Config.lib属性,这是一个描述符对象。而不是直接使用Config.lib,Python在这种情况下返回调用Config.lib.__get__(config, Config)的结果。

然后__get__方法执行原始函数(由wrapped引用)并将其存储在config.__dict__中。所以对<{1}}的未来访问将找到该结果,并且之后不会使用该类上的描述符。

调用config.lib方法以满足__getattr__表达式中的 next 属性; conf.lib.clang_createIndex(excludeDecls, 0)config.lib(通过cdll.LoadLibrary())返回动态加载的库,specific object typectypes libary处理Accessing functions from loaded dlls。它会将属性转换为特定的C调用;这是CachedProperty.__get__()方法;见Index() class

clang_createIndex的调用完成后,生成的对象确实传递​​给conf.lib.clang_createIndex(excludeDecls, 0); ClangObject本身没有Index()方法,但基类ctypes.c_void_p data type有。

无论返回值是什么,它都有一个表示,看起来像一个整数。但是,它几乎肯定不是__init__。您可以使用int查看该对象的类型,查看它与type()具有的属性等等。我很确定它是Observe 代表一个dir()值(它是一个代表内存中实际C值的Python对象);它将表示为整数:

  

表示C clang.cindex.LP_c_void_p类型。该值表示为整数。构造函数接受可选的整数初始值设定项。

void * Python代码的其余部分只会将此值传递回clang代理的更多C调用。