PageRenderTime 143ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/comtypes/client/lazybind.py

https://gitlab.com/hoiwai930/comtypes
Python | 267 lines | 182 code | 29 blank | 56 comment | 49 complexity | eb8887fe6cb206e80628e5c66d01ddd4 MD5 | raw file
  1. import comtypes
  2. import comtypes.automation
  3. from comtypes.automation import IEnumVARIANT
  4. from comtypes.automation import DISPATCH_METHOD
  5. from comtypes.automation import DISPATCH_PROPERTYGET
  6. from comtypes.automation import DISPATCH_PROPERTYPUT
  7. from comtypes.automation import DISPATCH_PROPERTYPUTREF
  8. from comtypes.automation import DISPID_VALUE
  9. from comtypes.automation import DISPID_NEWENUM
  10. from comtypes.typeinfo import FUNC_PUREVIRTUAL, FUNC_DISPATCH
  11. class FuncDesc(object):
  12. """Stores important FUNCDESC properties by copying them from a
  13. real FUNCDESC instance.
  14. """
  15. def __init__(self, **kw):
  16. self.__dict__.update(kw)
  17. # What is missing?
  18. #
  19. # Should NamedProperty support __call__()?
  20. _all_slice = slice(None, None, None)
  21. class NamedProperty(object):
  22. def __init__(self, disp, get, put, putref):
  23. self.get = get
  24. self.put = put
  25. self.putref = putref
  26. self.disp = disp
  27. def __getitem__(self, arg):
  28. if self.get is None:
  29. raise TypeError("unsubscriptable object")
  30. if isinstance(arg, tuple):
  31. return self.disp._comobj._invoke(self.get.memid,
  32. self.get.invkind,
  33. 0,
  34. *arg)
  35. elif arg == _all_slice:
  36. return self.disp._comobj._invoke(self.get.memid,
  37. self.get.invkind,
  38. 0)
  39. return self.disp._comobj._invoke(self.get.memid,
  40. self.get.invkind,
  41. 0,
  42. *[arg])
  43. def __call__(self, *args):
  44. if self.get is None:
  45. raise TypeError("object is not callable")
  46. return self.disp._comobj._invoke(self.get.memid,
  47. self.get.invkind,
  48. 0,
  49. *args)
  50. def __setitem__(self, name, value):
  51. # See discussion in Dispatch.__setattr__ below.
  52. if self.put is None and self.putref is None:
  53. raise TypeError("object does not support item assignment")
  54. if comtypes._is_object(value):
  55. descr = self.putref or self.put
  56. else:
  57. descr = self.put or self.putref
  58. if isinstance(name, tuple):
  59. self.disp._comobj._invoke(descr.memid,
  60. descr.invkind,
  61. 0,
  62. *(name + (value,)))
  63. elif name == _all_slice:
  64. self.disp._comobj._invoke(descr.memid,
  65. descr.invkind,
  66. 0,
  67. value)
  68. else:
  69. self.disp._comobj._invoke(descr.memid,
  70. descr.invkind,
  71. 0,
  72. name,
  73. value)
  74. def __iter__(self):
  75. """ Explicitly disallow iteration. """
  76. msg = "%r is not iterable" % self.disp
  77. raise TypeError(msg)
  78. # The following 'Dispatch' class, returned from
  79. # CreateObject(progid, dynamic=True)
  80. # differ in behaviour from objects created with
  81. # CreateObject(progid, dynamic=False)
  82. # (let us call the latter 'Custom' objects for this discussion):
  83. #
  84. #
  85. # 1. Dispatch objects support __call__(), custom objects do not
  86. #
  87. # 2. Custom objects method support named arguments, Dispatch
  88. # objects do not (could be added, would probably be expensive)
  89. class Dispatch(object):
  90. """Dynamic dispatch for an object the exposes type information.
  91. Binding at runtime is done via ITypeComp::Bind calls.
  92. """
  93. def __init__(self, comobj, tinfo):
  94. self.__dict__["_comobj"] = comobj
  95. self.__dict__["_tinfo"] = tinfo
  96. self.__dict__["_tcomp"] = tinfo.GetTypeComp()
  97. self.__dict__["_tdesc"] = {}
  98. ## self.__dict__["_iid"] = tinfo.GetTypeAttr().guid
  99. def __bind(self, name, invkind):
  100. """Bind (name, invkind) and return a FuncDesc instance or
  101. None. Results (even unsuccessful ones) are cached."""
  102. # We could cache the info in the class instead of the
  103. # instance, but we would need an additional key for that:
  104. # self._iid
  105. try:
  106. return self._tdesc[(name, invkind)]
  107. except KeyError:
  108. try:
  109. descr = self._tcomp.Bind(name, invkind)[1]
  110. except comtypes.COMError:
  111. info = None
  112. else:
  113. # Using a separate instance to store interesting
  114. # attributes of descr avoids that the typecomp instance is
  115. # kept alive...
  116. info = FuncDesc(memid=descr.memid,
  117. invkind=descr.invkind,
  118. cParams=descr.cParams,
  119. funckind=descr.funckind)
  120. self._tdesc[(name, invkind)] = info
  121. return info
  122. def QueryInterface(self, *args):
  123. "QueryInterface is forwarded to the real com object."
  124. return self._comobj.QueryInterface(*args)
  125. def __cmp__(self, other):
  126. if not isinstance(other, Dispatch):
  127. return 1
  128. return cmp(self._comobj, other._comobj)
  129. def __eq__(self, other):
  130. return isinstance(other, Dispatch) and \
  131. self._comobj == other._comobj
  132. def __hash__(self):
  133. return hash(self._comobj)
  134. def __getattr__(self, name):
  135. """Get a COM attribute."""
  136. if name.startswith("__") and name.endswith("__"):
  137. raise AttributeError(name)
  138. # check for propget or method
  139. descr = self.__bind(name, DISPATCH_METHOD | DISPATCH_PROPERTYGET)
  140. if descr is None:
  141. raise AttributeError(name)
  142. if descr.invkind == DISPATCH_PROPERTYGET:
  143. # DISPATCH_PROPERTYGET
  144. if descr.funckind == FUNC_DISPATCH:
  145. if descr.cParams == 0:
  146. return self._comobj._invoke(descr.memid, descr.invkind, 0)
  147. elif descr.funckind == FUNC_PUREVIRTUAL:
  148. # FUNC_PUREVIRTUAL descriptions contain the property
  149. # itself as a parameter.
  150. if descr.cParams == 1:
  151. return self._comobj._invoke(descr.memid, descr.invkind, 0)
  152. else:
  153. raise RuntimeError("funckind %d not yet implemented" % descr.funckind)
  154. put = self.__bind(name, DISPATCH_PROPERTYPUT)
  155. putref = self.__bind(name, DISPATCH_PROPERTYPUTREF)
  156. return NamedProperty(self, descr, put, putref)
  157. else:
  158. # DISPATCH_METHOD
  159. def caller(*args):
  160. return self._comobj._invoke(descr.memid, descr.invkind, 0, *args)
  161. try:
  162. caller.__name__ = name
  163. except TypeError:
  164. # In Python 2.3, __name__ is readonly
  165. pass
  166. return caller
  167. def __setattr__(self, name, value):
  168. # Hm, this can be a propput, a propputref, or 'both' property.
  169. # (Or nothing at all.)
  170. #
  171. # Whether propput or propputref is called will depend on what
  172. # is available, and on the type of 'value' as determined by
  173. # comtypes._is_object(value).
  174. #
  175. # I think that the following table MAY be correct; although I
  176. # have no idea whether the cases marked (?) are really valid.
  177. #
  178. # invkind available | _is_object(value) | invkind we should use
  179. # ---------------------------------------------------------------
  180. # put | True | put (?)
  181. # put | False | put
  182. # putref | True | putref
  183. # putref | False | putref (?)
  184. # put, putref | True | putref
  185. # put, putref | False | put
  186. put = self.__bind(name, DISPATCH_PROPERTYPUT)
  187. putref = self.__bind(name, DISPATCH_PROPERTYPUTREF)
  188. if not put and not putref:
  189. raise AttributeError(name)
  190. if comtypes._is_object(value):
  191. descr = putref or put
  192. else:
  193. descr = put or putref
  194. if descr.cParams == 1:
  195. self._comobj._invoke(descr.memid, descr.invkind, 0, value)
  196. return
  197. raise AttributeError(name)
  198. def __call__(self, *args):
  199. return self._comobj._invoke(DISPID_VALUE,
  200. DISPATCH_METHOD | DISPATCH_PROPERTYGET,
  201. 0,
  202. *args)
  203. def __getitem__(self, arg):
  204. if isinstance(arg, tuple):
  205. args = arg
  206. elif arg == _all_slice:
  207. args = ()
  208. else:
  209. args = (arg,)
  210. try:
  211. return self._comobj._invoke(DISPID_VALUE,
  212. DISPATCH_METHOD | DISPATCH_PROPERTYGET,
  213. 0,
  214. *args)
  215. except comtypes.COMError:
  216. return iter(self)[arg]
  217. def __setitem__(self, name, value):
  218. if comtypes._is_object(value):
  219. invkind = DISPATCH_PROPERTYPUTREF
  220. else:
  221. invkind = DISPATCH_PROPERTYPUT
  222. if isinstance(name, tuple):
  223. args = name + (value,)
  224. elif name == _all_slice:
  225. args = (value,)
  226. else:
  227. args = (name, value)
  228. return self._comobj._invoke(DISPID_VALUE,
  229. invkind,
  230. 0,
  231. *args)
  232. def __iter__(self):
  233. punk = self._comobj._invoke(DISPID_NEWENUM,
  234. DISPATCH_METHOD | DISPATCH_PROPERTYGET,
  235. 0)
  236. enum = punk.QueryInterface(IEnumVARIANT)
  237. enum._dynamic = True
  238. return enum