PageRenderTime 27ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/Packages/SublimeCodeIntel/libs/codeintel2/tree_python.py

https://bitbucket.org/luobailiang/sublime-config
Python | 767 lines | 721 code | 6 blank | 40 comment | 10 complexity | aac548de6254095a6fa70ec174f00ff3 MD5 | raw file
  1. #!/usr/bin/env python
  2. # ***** BEGIN LICENSE BLOCK *****
  3. # Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. #
  5. # The contents of this file are subject to the Mozilla Public License
  6. # Version 1.1 (the "License"); you may not use this file except in
  7. # compliance with the License. You may obtain a copy of the License at
  8. # http://www.mozilla.org/MPL/
  9. #
  10. # Software distributed under the License is distributed on an "AS IS"
  11. # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing rights and limitations
  13. # under the License.
  14. #
  15. # The Original Code is Komodo code.
  16. #
  17. # The Initial Developer of the Original Code is ActiveState Software Inc.
  18. # Portions created by ActiveState Software Inc are Copyright (C) 2000-2007
  19. # ActiveState Software Inc. All Rights Reserved.
  20. #
  21. # Contributor(s):
  22. # ActiveState Software Inc
  23. #
  24. # Alternatively, the contents of this file may be used under the terms of
  25. # either the GNU General Public License Version 2 or later (the "GPL"), or
  26. # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27. # in which case the provisions of the GPL or the LGPL are applicable instead
  28. # of those above. If you wish to allow use of your version of this file only
  29. # under the terms of either the GPL or the LGPL, and not to allow others to
  30. # use your version of this file under the terms of the MPL, indicate your
  31. # decision by deleting the provisions above and replace them with the notice
  32. # and other provisions required by the GPL or the LGPL. If you do not delete
  33. # the provisions above, a recipient may use your version of this file under
  34. # the terms of any one of the MPL, the GPL or the LGPL.
  35. #
  36. # ***** END LICENSE BLOCK *****
  37. """Completion evaluation code for Python"""
  38. from os.path import basename, dirname, join, exists, isdir
  39. from codeintel2.common import *
  40. from codeintel2.tree import TreeEvaluator
  41. base_exception_class_completions = [
  42. ("class", "BaseException"),
  43. ("class", "Exception"),
  44. ("class", "StandardError"),
  45. ("class", "ArithmeticError"),
  46. ("class", "LookupError"),
  47. ("class", "EnvironmentError"),
  48. ("class", "AssertionError"),
  49. ("class", "AttributeError"),
  50. ("class", "EOFError"),
  51. ("class", "FloatingPointError"),
  52. ("class", "GeneratorExit"),
  53. ("class", "IOError"),
  54. ("class", "ImportError"),
  55. ("class", "IndexError"),
  56. ("class", "KeyError"),
  57. ("class", "KeyboardInterrupt"),
  58. ("class", "MemoryError"),
  59. ("class", "NameError"),
  60. ("class", "NotImplementedError"),
  61. ("class", "OSError"),
  62. ("class", "OverflowError"),
  63. ("class", "ReferenceError"),
  64. ("class", "RuntimeError"),
  65. ("class", "StopIteration"),
  66. ("class", "SyntaxError"),
  67. ("class", "SystemError"),
  68. ("class", "SystemExit"),
  69. ("class", "TypeError"),
  70. ("class", "UnboundLocalError"),
  71. ("class", "UnicodeError"),
  72. ("class", "UnicodeEncodeError"),
  73. ("class", "UnicodeDecodeError"),
  74. ("class", "UnicodeTranslateError"),
  75. ("class", "ValueError"),
  76. ("class", "VMSError"),
  77. ("class", "WindowsError"),
  78. ("class", "ZeroDivisionError"),
  79. # Warning category exceptions.
  80. ("class", "Warning"),
  81. ("class", "UserWarning"),
  82. ("class", "DeprecationWarning"),
  83. ("class", "PendingDeprecationWarning"),
  84. ("class", "SyntaxWarning"),
  85. ("class", "RuntimeWarning"),
  86. ("class", "FutureWarning"),
  87. ("class", "ImportWarning"),
  88. ("class", "UnicodeWarning"),
  89. ]
  90. class PythonImportLibGenerator(object):
  91. """A lazily loading lib generator.
  92. To be used for Komodo's import lookup handling. This generator will return
  93. libraries as needed, then when the given set of libraries runs out (i.e.
  94. when there were no matches in the given libraries), to then try and find
  95. other possible directories (libraries) that could offer a match."""
  96. def __init__(self, mgr, lang, bufpath, imp_prefix, libs):
  97. self.mgr = mgr
  98. self.lang = lang
  99. self.imp_prefix = imp_prefix
  100. self.bufpath = bufpath
  101. self.libs = libs
  102. self.index = 0
  103. def __iter__(self):
  104. self.index = 0
  105. return self
  106. def next(self):
  107. if self.index < len(self.libs):
  108. # Return the regular libs.
  109. try:
  110. return self.libs[self.index]
  111. finally:
  112. self.index += 1
  113. elif self.index == len(self.libs):
  114. # Try to find a matching parent directory to use.
  115. #print "Lazily loading the parent import libs: %r" % (self.imp_prefix, )
  116. self.index += 1
  117. lookuppath = dirname(self.bufpath)
  118. parent_dirs_left = 10
  119. while lookuppath and parent_dirs_left > 0:
  120. #print 'lookuppath: %r' % (lookuppath, )
  121. parent_dirs_left -= 1
  122. parent_name = basename(lookuppath)
  123. if parent_name == self.imp_prefix[0] and \
  124. exists(join(lookuppath, "__init__.py")):
  125. # Matching directory - return that as a library.
  126. lookuppath = dirname(lookuppath)
  127. #print "Adding parent dir lib: %r" % (lookuppath)
  128. return self.mgr.db.get_lang_lib(self.lang, "parentdirlib",
  129. [lookuppath])
  130. lookuppath = dirname(lookuppath)
  131. # No match found - we're done.
  132. raise StopIteration
  133. else:
  134. raise StopIteration
  135. class PythonTreeEvaluator(TreeEvaluator):
  136. # Own copy of libs (that shadows the real self.buf.libs) - this is required
  137. # in order to properly adjust the "reldirlib" libraries as they hit imports
  138. # from different directories - i.e. to correctly deal with relative imports.
  139. _libs = None
  140. @property
  141. def libs(self):
  142. if self._libs is None:
  143. self._libs = self.buf.libs
  144. return self._libs
  145. @libs.setter
  146. def libs(self, value):
  147. self._libs = value
  148. def eval_cplns(self):
  149. self.log_start()
  150. if self.trg.type == 'available-exceptions':
  151. # TODO: Should perform a lookup to determine all available exception
  152. # classes.
  153. return base_exception_class_completions
  154. start_scoperef = self.get_start_scoperef()
  155. self.info("start scope is %r", start_scoperef)
  156. #if self.trg.type == 'available-classes':
  157. # return self._available_classes(start_scoperef, self.trg.extra["consumed"])
  158. hit = self._hit_from_citdl(self.expr, start_scoperef)
  159. return list(self._members_from_hit(hit))
  160. def eval_calltips(self):
  161. self.log_start()
  162. start_scoperef = self.get_start_scoperef()
  163. self.info("start scope is %r", start_scoperef)
  164. hit = self._hit_from_citdl(self.expr, start_scoperef)
  165. return [ self._calltip_from_hit(hit) ]
  166. def eval_defns(self):
  167. self.log_start()
  168. start_scoperef = self.get_start_scoperef()
  169. self.info("start scope is %r", start_scoperef)
  170. hit = self._hit_from_citdl(self.expr, start_scoperef, defn_only=True)
  171. return [ self._defn_from_hit(hit) ]
  172. def _defn_from_hit(self, hit):
  173. defn = TreeEvaluator._defn_from_hit(self, hit)
  174. if not defn.path:
  175. # Locate the module in the users own Python stdlib,
  176. # bug 65296.
  177. langintel = self.buf.langintel
  178. info = langintel.python_info_from_env(self.buf.env)
  179. ver, prefix, libdir, sitelibdir, sys_path = info
  180. if libdir:
  181. elem, (blob, lpath) = hit
  182. path = join(libdir, blob.get("name"))
  183. if exists(path + ".py"):
  184. defn.path = path + ".py"
  185. elif isdir(path) and exists(join(path, "__init__.py")):
  186. defn.path = join(path, "__init__.py")
  187. return defn
  188. #def _available_classes(self, scoperef, consumed):
  189. # matches = set()
  190. # blob = scoperef[0] # XXX??
  191. # for elem in blob:
  192. # if elem.tag == 'scope' and elem.get('ilk') == 'class':
  193. # matches.add(elem.get('name'))
  194. # matches.difference_update(set(consumed))
  195. # matches_list = sorted(list(matches))
  196. # return [('class', m) for m in matches_list]
  197. def _tokenize_citdl_expr(self, citdl):
  198. for token in citdl.split('.'):
  199. if token.endswith('()'):
  200. yield token[:-2]
  201. yield '()'
  202. else:
  203. yield token
  204. def _join_citdl_expr(self, tokens):
  205. return '.'.join(tokens)
  206. def _calltip_from_func(self, elem, scoperef, class_name=None):
  207. # See "Determining a Function CallTip" in the spec for a
  208. # discussion of this algorithm.
  209. signature = elem.get("signature")
  210. ctlines = []
  211. if not signature:
  212. name = class_name or elem.get("name")
  213. ctlines = [name + "(...)"]
  214. else:
  215. ctlines = signature.splitlines(0)
  216. doc = elem.get("doc")
  217. if doc:
  218. ctlines += doc.splitlines(0)
  219. return '\n'.join(ctlines)
  220. def _calltip_from_class(self, elem, scoperef):
  221. # If the class has a defined signature then use that.
  222. signature = elem.get("signature")
  223. if signature:
  224. doc = elem.get("doc")
  225. ctlines = signature.splitlines(0)
  226. if doc:
  227. ctlines += doc.splitlines(0)
  228. return '\n'.join(ctlines)
  229. else:
  230. ctor_hit = self._ctor_hit_from_class(elem, scoperef)
  231. if ctor_hit and (ctor_hit[0].get("doc")
  232. or ctor_hit[0].get("signature")):
  233. self.log("ctor is %r on %r", *ctor_hit)
  234. return self._calltip_from_func(ctor_hit[0], ctor_hit[1],
  235. class_name=elem.get("name"))
  236. else:
  237. doc = elem.get("doc")
  238. if doc:
  239. ctlines = [ln for ln in doc.splitlines(0) if ln]
  240. else:
  241. ctlines = [elem.get("name") + "()"]
  242. return '\n'.join(ctlines)
  243. def _ctor_hit_from_class(self, elem, scoperef):
  244. """Return the Python ctor for the given class element, or None."""
  245. if "__init__" in elem.names:
  246. class_scoperef = (scoperef[0], scoperef[1]+[elem.get("name")])
  247. return elem.names["__init__"], class_scoperef
  248. else:
  249. for classref in elem.get("classrefs", "").split():
  250. try:
  251. basehit = self._hit_from_type_inference(classref, scoperef)
  252. except CodeIntelError, ex:
  253. self.warn(str(ex))
  254. else:
  255. ctor_hit = self._ctor_hit_from_class(*basehit)
  256. if ctor_hit:
  257. return ctor_hit
  258. return None
  259. def _calltip_from_hit(self, hit):
  260. # TODO: compare with CitadelEvaluator._getSymbolCallTips()
  261. elem, scoperef = hit
  262. if elem.tag == "variable":
  263. XXX
  264. elif elem.tag == "scope":
  265. ilk = elem.get("ilk")
  266. if ilk == "function":
  267. calltip = self._calltip_from_func(elem, scoperef)
  268. elif ilk == "class":
  269. calltip = self._calltip_from_class(elem, scoperef)
  270. else:
  271. raise NotImplementedError("unexpected scope ilk for "
  272. "calltip hit: %r" % elem)
  273. else:
  274. raise NotImplementedError("unexpected elem for calltip "
  275. "hit: %r" % elem)
  276. return calltip
  277. def _members_from_elem(self, elem):
  278. """Return the appropriate set of autocomplete completions for
  279. the given element. Typically this is just one, but can be more for
  280. '*'-imports
  281. """
  282. members = set()
  283. if elem.tag == "import":
  284. alias = elem.get("alias")
  285. symbol_name = elem.get("symbol")
  286. module_name = elem.get("module")
  287. if symbol_name:
  288. import_handler = self.citadel.import_handler_from_lang(self.trg.lang)
  289. blob = import_handler.import_blob_name(
  290. module_name, self.libs, self.ctlr)
  291. if symbol_name == "*":
  292. for m_name, m_elem in blob.names.items():
  293. m_type = m_elem.get("ilk") or m_elem.tag
  294. members.add( (m_type, m_name) )
  295. elif symbol_name in blob.names:
  296. symbol = blob.names[symbol_name]
  297. member_type = (symbol.get("ilk") or symbol.tag)
  298. members.add( (member_type, alias or symbol_name) )
  299. else:
  300. hit, nconsumed \
  301. = self._hit_from_elem_imports([symbol_name], blob)
  302. if hit:
  303. symbol = hit[0]
  304. member_type = (symbol.get("ilk") or symbol.tag)
  305. members.add( (member_type, alias or symbol_name) )
  306. else:
  307. self.warn("could not resolve %r", elem)
  308. else:
  309. cpln_name = alias or module_name.split('.', 1)[0]
  310. members.add( ("module", cpln_name) )
  311. else:
  312. members.add( (elem.get("ilk") or elem.tag, elem.get("name")) )
  313. return members
  314. def _members_from_hit(self, hit):
  315. elem, scoperef = hit
  316. members = set()
  317. for child in elem:
  318. if "__hidden__" not in child.get("attributes", "").split():
  319. try:
  320. members.update(self._members_from_elem(child))
  321. except CodeIntelError, ex:
  322. self.warn("%s (skipping members for %s)", ex, child)
  323. if elem.get("ilk") == "class":
  324. for classref in elem.get("classrefs", "").split():
  325. try:
  326. subhit = self._hit_from_type_inference(classref, scoperef)
  327. except CodeIntelError, ex:
  328. # Continue with what we *can* resolve.
  329. self.warn(str(ex))
  330. else:
  331. members.update(self._members_from_hit(subhit))
  332. # Add special __class__ attribute.
  333. members.add(("variable", "__class__"))
  334. # Add special __doc__ attribute.
  335. members.add(("variable", "__doc__"))
  336. return members
  337. def _hit_from_citdl(self, expr, scoperef, defn_only=False):
  338. """Resolve the given CITDL expression (starting at the given
  339. scope) down to a non-import/non-variable hit.
  340. """
  341. self._check_infinite_recursion(expr)
  342. tokens = list(self._tokenize_citdl_expr(expr))
  343. #self.log("expr tokens: %r", tokens)
  344. # First part...
  345. hit, nconsumed = self._hit_from_first_part(tokens, scoperef)
  346. if not hit:
  347. #TODO: Add the fallback Buffer-specific near-by hunt
  348. # for a symbol for the first token. See my spiral-bound
  349. # book for some notes.
  350. raise CodeIntelError("could not resolve first part of '%s'" % expr)
  351. self.debug("_hit_from_citdl: first part: %r -> %r",
  352. tokens[:nconsumed], hit)
  353. # ...the remainder.
  354. remaining_tokens = tokens[nconsumed:]
  355. while remaining_tokens:
  356. self.debug("_hit_from_citdl: resolve %r on %r in %r",
  357. remaining_tokens, *hit)
  358. if remaining_tokens[0] == "()":
  359. new_hit = self._hit_from_call(*hit)
  360. nconsumed = 1
  361. else:
  362. new_hit, nconsumed \
  363. = self._hit_from_getattr(remaining_tokens, *hit)
  364. remaining_tokens = remaining_tokens[nconsumed:]
  365. hit = new_hit
  366. # Resolve any variable type inferences.
  367. #TODO: Need to *recursively* resolve hits.
  368. elem, scoperef = hit
  369. if elem.tag == "variable" and not defn_only:
  370. elem, scoperef = self._hit_from_variable_type_inference(elem, scoperef)
  371. self.info("'%s' is %s on %s", expr, elem, scoperef)
  372. return (elem, scoperef)
  373. def _hit_from_first_part(self, tokens, scoperef):
  374. """Find a hit for the first part of the tokens.
  375. Returns (<hit>, <num-tokens-consumed>) or (None, None) if could
  376. not resolve.
  377. Example for 'os.sep':
  378. tokens: ('os', 'sep')
  379. retval: ((<variable 'sep'>, (<blob 'os', [])), 1)
  380. Example for 'os.path':
  381. tokens: ('os', 'path')
  382. retval: ((<import os.path>, (<blob 'os', [])), 2)
  383. """
  384. first_token = tokens[0]
  385. self.log("find '%s ...' starting at %s:", first_token, scoperef)
  386. # pythoncile will sometimes give a citdl expression of "__builtins__",
  387. # check for this now, bug:
  388. # http://bugs.activestate.com/show_bug.cgi?id=71972
  389. if first_token == "__builtins__":
  390. # __builtins__ is the same as the built_in_blob, return it.
  391. scoperef = (self.built_in_blob, [])
  392. return (self.built_in_blob, scoperef), 1
  393. while 1:
  394. elem = self._elem_from_scoperef(scoperef)
  395. if first_token in elem.names:
  396. #TODO: skip __hidden__ names
  397. self.log("is '%s' accessible on %s? yes: %s",
  398. first_token, scoperef, elem.names[first_token])
  399. return (elem.names[first_token], scoperef), 1
  400. hit, nconsumed \
  401. = self._hit_from_elem_imports(tokens, elem)
  402. if hit is not None:
  403. self.log("is '%s' accessible on %s? yes: %s",
  404. '.'.join(tokens[:nconsumed]), scoperef, hit[0])
  405. return hit, nconsumed
  406. self.log("is '%s' accessible on %s? no", first_token, scoperef)
  407. scoperef = self.parent_scoperef_from_scoperef(scoperef)
  408. if not scoperef:
  409. return None, None
  410. def _set_reldirlib_from_blob(self, blob):
  411. """Set the relative import directory to be this blob's location."""
  412. # See bug 45822 and bug 88971 for examples of why this is necessary.
  413. if blob is None:
  414. return
  415. blob_src = blob.get("src")
  416. if blob_src and blob.get("ilk") == "blob":
  417. reldirpath = dirname(blob_src)
  418. reldirlib = self.mgr.db.get_lang_lib(self.trg.lang, "reldirlib",
  419. [reldirpath])
  420. newlibs = self.libs[:] # Make a copy of the libs.
  421. if newlibs[0].name == "reldirlib":
  422. # Update the existing reldirlib location.
  423. newlibs[0] = reldirlib
  424. else:
  425. # Add in the relative directory lib.
  426. newlibs.insert(0, reldirlib)
  427. self.log("imports:: setting reldirlib to: %r", reldirpath)
  428. self.libs = newlibs
  429. def _add_parentdirlib(self, libs, tokens):
  430. """Add a lazily loaded parent directory import library."""
  431. if isinstance(libs, PythonImportLibGenerator):
  432. # Reset to the original libs.
  433. libs = libs.libs
  434. libs = PythonImportLibGenerator(self.mgr, self.trg.lang, self.buf.path,
  435. tokens, libs)
  436. return libs
  437. def __hit_from_elem_imports(self, tokens, elem):
  438. """See if token is from one of the imports on this <scope> elem.
  439. Returns (<hit>, <num-tokens-consumed>) or (None, None) if not found.
  440. XXX import_handler.import_blob_name() calls all have potential
  441. to raise CodeIntelError.
  442. """
  443. #PERF: just have a .import_handler property on the evalr?
  444. import_handler = self.citadel.import_handler_from_lang(self.trg.lang)
  445. #PERF: Add .imports method to ciElementTree for quick iteration
  446. # over them. Or perhaps some cache to speed this method.
  447. #TODO: The right answer here is to not resolve the <import>,
  448. # just return it. It is complicated enough that the
  449. # construction of members has to know the original context.
  450. # See the "Foo.mypackage.<|>mymodule.yo" part of test
  451. # python/cpln/wacky_imports.
  452. # XXX Not totally confident that this is the right answer.
  453. first_token = tokens[0]
  454. self._check_infinite_recursion(first_token)
  455. orig_libs = self.libs
  456. for imp_elem in (i for i in elem if i.tag == "import"):
  457. libs = orig_libs # reset libs back to the original
  458. self.debug("'%s ...' from %r?", tokens[0], imp_elem)
  459. alias = imp_elem.get("alias")
  460. symbol_name = imp_elem.get("symbol")
  461. module_name = imp_elem.get("module")
  462. allow_parentdirlib = True
  463. if module_name.startswith("."):
  464. allow_parentdirlib = False
  465. # Need a different curdirlib.
  466. lookuppath = self.buf.path
  467. while module_name.startswith("."):
  468. lookuppath = dirname(lookuppath)
  469. module_name = module_name[1:]
  470. libs = [self.mgr.db.get_lang_lib("Python", "curdirlib",
  471. [lookuppath])]
  472. if not module_name:
  473. module_name = symbol_name
  474. symbol_name = None
  475. if symbol_name:
  476. # from module import symbol, from module import symbol as alias
  477. # from module import submod, from module import submod as alias
  478. if (alias and alias == first_token) \
  479. or (not alias and symbol_name == first_token):
  480. # Try 'from module import symbol/from module import
  481. # symbol as alias' first.
  482. if allow_parentdirlib:
  483. libs = self._add_parentdirlib(libs, module_name.split("."))
  484. try:
  485. blob = import_handler.import_blob_name(
  486. module_name, libs, self.ctlr)
  487. if symbol_name in blob.names:
  488. return (blob.names[symbol_name], (blob, [])), 1
  489. else:
  490. self._set_reldirlib_from_blob(blob)
  491. hit, nconsumed = self._hit_from_elem_imports(
  492. [first_token] + tokens[1:], blob)
  493. if hit:
  494. return hit, nconsumed
  495. except CodeIntelError:
  496. pass
  497. # That didn't work, try 'from module import
  498. # submod/from module import submod as alias'.
  499. submodule_name = import_handler.sep.join(
  500. [module_name, symbol_name])
  501. if allow_parentdirlib:
  502. libs = self._add_parentdirlib(libs, (module_name, symbol_name))
  503. try:
  504. subblob = import_handler.import_blob_name(
  505. submodule_name, libs, self.ctlr)
  506. return (subblob, (subblob, [])), 1
  507. except CodeIntelError:
  508. # That didn't work either. Give up.
  509. self.warn("could not import '%s' from %s",
  510. first_token, imp_elem)
  511. # from module import *
  512. elif symbol_name == "*":
  513. try:
  514. if allow_parentdirlib:
  515. libs = self._add_parentdirlib(libs, module_name.split("."))
  516. blob = import_handler.import_blob_name(
  517. module_name, libs, self.ctlr)
  518. except CodeIntelError:
  519. pass # don't freak out: might not be our import anyway
  520. else:
  521. self._set_reldirlib_from_blob(blob)
  522. try:
  523. hit, nconsumed = self._hit_from_getattr(
  524. tokens, blob, (blob, []))
  525. except CodeIntelError:
  526. pass
  527. else:
  528. if hit:
  529. return hit, nconsumed
  530. elif (alias and alias == first_token) \
  531. or (not alias and module_name == first_token):
  532. if allow_parentdirlib:
  533. libs = self._add_parentdirlib(libs, module_name.split("."))
  534. blob = import_handler.import_blob_name(
  535. module_name, libs, self.ctlr)
  536. return (blob, (blob, [])), 1
  537. elif '.' in module_name:
  538. # E.g., might be looking up ('os', 'path', ...) and
  539. # have <import os.path>.
  540. module_tokens = module_name.split('.')
  541. if allow_parentdirlib:
  542. libs = self._add_parentdirlib(libs, module_tokens)
  543. if module_tokens == tokens[:len(module_tokens)]:
  544. # E.g. tokens: ('os', 'path', ...)
  545. # imp_elem: <import os.path>
  546. # return: <blob 'os.path'> for first two tokens
  547. blob = import_handler.import_blob_name(
  548. module_name, libs, self.ctlr)
  549. #XXX Is this correct scoperef for module object?
  550. return (blob, (blob, [])), len(module_tokens)
  551. else:
  552. # E.g. tokens: ('os', 'sep', ...)
  553. # imp_elem: <import os.path>
  554. # return: <blob 'os'> for first token
  555. for i in range(len(module_tokens)-1, 0, -1):
  556. if module_tokens[:i] == tokens[:i]:
  557. blob = import_handler.import_blob_name(
  558. '.'.join(module_tokens[:i]),
  559. libs, self.ctlr)
  560. #XXX Is this correct scoperef for module object?
  561. return (blob, (blob, [])), i
  562. return None, None
  563. def _hit_from_elem_imports(self, tokens, elem):
  564. """See if token is from one of the imports on this <scope> elem.
  565. Returns (<hit>, <num-tokens-consumed>) or (None, None) if not found.
  566. XXX import_handler.import_blob_name() calls all have potential
  567. to raise CodeIntelError.
  568. """
  569. # This is a wrapper function around the real __hit_from_elem_imports,
  570. # that will update the relative dir libs appropriately when an import
  571. # hit is made - see bug 88971 for why this is necessary.
  572. hit, nconsumed = self.__hit_from_elem_imports(tokens, elem)
  573. if hit is not None:
  574. self._set_reldirlib_from_blob(hit[0])
  575. return hit, nconsumed
  576. def _hit_from_call(self, elem, scoperef):
  577. """Resolve the function call inference for 'elem' at 'scoperef'."""
  578. # This might be a variable, in that case we keep resolving the variable
  579. # until we get to the final function/class element that is to be called.
  580. while elem.tag == "variable":
  581. elem, scoperef = self._hit_from_variable_type_inference(elem, scoperef)
  582. ilk = elem.get("ilk")
  583. if ilk == "class":
  584. # Return the class element.
  585. self.log("_hit_from_call: resolved to class '%s'", elem.get("name"))
  586. return (elem, scoperef)
  587. if ilk == "function":
  588. citdl = elem.get("returns")
  589. if citdl:
  590. self.log("_hit_from_call: function with citdl %r",
  591. citdl)
  592. # scoperef has to be set to the function called
  593. func_scoperef = (scoperef[0], scoperef[1]+[elem.get("name")])
  594. return self._hit_from_citdl(citdl, func_scoperef)
  595. raise CodeIntelError("no return type info for %r" % elem)
  596. def _hit_from_getattr(self, tokens, elem, scoperef):
  597. """Return a hit for a getattr on the given element.
  598. Returns (<hit>, <num-tokens-consumed>) or raises an CodeIntelError.
  599. Typically this just does a getattr of tokens[0], but handling
  600. some multi-level imports can result in multiple tokens being
  601. consumed.
  602. """
  603. #TODO: On failure, call a hook to make an educated guess. Some
  604. # attribute names are strong signals as to the object type
  605. # -- typically those for common built-in classes.
  606. first_token = tokens[0]
  607. self.log("resolve getattr '%s' on %r in %r:", first_token,
  608. elem, scoperef)
  609. if elem.tag == "variable":
  610. elem, scoperef = self._hit_from_variable_type_inference(elem, scoperef)
  611. assert elem.tag == "scope"
  612. ilk = elem.get("ilk")
  613. if ilk == "function":
  614. # Internal function arguments and variable should
  615. # *not* resolve. And we don't support function
  616. # attributes.
  617. pass
  618. elif ilk == "class":
  619. attr = elem.names.get(first_token)
  620. if attr is not None:
  621. self.log("attr is %r in %r", attr, elem)
  622. # update the scoperef, we are now inside the class.
  623. scoperef = (scoperef[0], scoperef[1] + [elem.get("name")])
  624. return (attr, scoperef), 1
  625. # When looking for a __class__ on a class instance, we match the
  626. # class itself - bug .
  627. if first_token == "__class__":
  628. self.log("attr is class %r", elem)
  629. return (elem, scoperef), 1
  630. self.debug("look for %r from imports in %r", tokens, elem)
  631. hit, nconsumed \
  632. = self._hit_from_elem_imports(tokens, elem)
  633. if hit is not None:
  634. return hit, nconsumed
  635. for classref in elem.get("classrefs", "").split():
  636. try:
  637. self.log("is '%s' from base class: %r?", first_token,
  638. classref)
  639. base_elem, base_scoperef \
  640. = self._hit_from_type_inference(classref, scoperef)
  641. return self._hit_from_getattr(tokens, base_elem,
  642. base_scoperef)
  643. except CodeIntelError, ex:
  644. self.log("could not resolve classref '%s' on scoperef %r",
  645. classref, scoperef, )
  646. # Was not available, try the next class then.
  647. elif ilk == "blob":
  648. attr = elem.names.get(first_token)
  649. if attr is not None:
  650. self.log("attr is %r in %r", attr, elem)
  651. return (attr, scoperef), 1
  652. hit, nconsumed \
  653. = self._hit_from_elem_imports(tokens, elem)
  654. if hit is not None:
  655. return hit, nconsumed
  656. else:
  657. raise NotImplementedError("unexpected scope ilk: %r" % ilk)
  658. raise CodeIntelError("could not resolve '%s' getattr on %r in %r"
  659. % (first_token, elem, scoperef))
  660. def _hit_from_variable_type_inference(self, elem, scoperef):
  661. """Resolve the type inference for 'elem' at 'scoperef'."""
  662. citdl = elem.get("citdl")
  663. if not citdl:
  664. raise CodeIntelError("no type-inference info for %r" % elem)
  665. self.log("resolve '%s' type inference for %r:", citdl, elem)
  666. return self._hit_from_citdl(citdl, scoperef)
  667. def _hit_from_type_inference(self, citdl, scoperef):
  668. """Resolve the 'citdl' type inference at 'scoperef'."""
  669. self.log("resolve '%s' type inference:", citdl)
  670. return self._hit_from_citdl(citdl, scoperef)
  671. _built_in_blob = None
  672. @property
  673. def built_in_blob(self):
  674. if self._built_in_blob is None:
  675. #XXX Presume last lib is stdlib.
  676. self._built_in_blob = self.buf.libs[-1].get_blob("*")
  677. return self._built_in_blob
  678. def parent_scoperef_from_scoperef(self, scoperef):
  679. blob, lpath = scoperef
  680. if lpath:
  681. parent_lpath = lpath[:-1]
  682. if parent_lpath:
  683. elem = self._elem_from_scoperef((blob, parent_lpath))
  684. if elem.get("ilk") == "class":
  685. # Python eval shouldn't consider the class-level
  686. # scope as a parent scope when resolving from the
  687. # top-level. (test python/cpln/skip_class_scope)
  688. parent_lpath = parent_lpath[:-1]
  689. return (blob, parent_lpath)
  690. elif blob is self._built_in_blob:
  691. return None
  692. else:
  693. return (self.built_in_blob, [])
  694. def _elem_from_scoperef(self, scoperef):
  695. """A scoperef is (<blob>, <lpath>). Return the actual elem in
  696. the <blob> ciElementTree being referred to.
  697. """
  698. elem = scoperef[0]
  699. for lname in scoperef[1]:
  700. elem = elem.names[lname]
  701. return elem