utils.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. # pylint: disable=W0611
  2. #
  3. # Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
  4. # http://www.logilab.fr/ -- mailto:contact@logilab.fr
  5. #
  6. # This program is free software; you can redistribute it and/or modify it under
  7. # the terms of the GNU General Public License as published by the Free Software
  8. # Foundation; either version 2 of the License, or (at your option) any later
  9. # version.
  10. #
  11. # This program is distributed in the hope that it will be useful, but WITHOUT
  12. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  13. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along with
  16. # this program; if not, write to the Free Software Foundation, Inc.,
  17. # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. """some functions that may be useful for various checkers
  19. """
  20. import re
  21. import string
  22. from logilab import astng
  23. from logilab.astng import scoped_nodes
  24. from logilab.common.compat import builtins
  25. BUILTINS_NAME = builtins.__name__
  26. COMP_NODE_TYPES = astng.ListComp, astng.SetComp, astng.DictComp, astng.GenExpr
  27. def is_inside_except(node):
  28. """Returns true if node is inside the name of an except handler."""
  29. current = node
  30. while current and not isinstance(current.parent, astng.ExceptHandler):
  31. current = current.parent
  32. return current and current is current.parent.name
  33. def get_all_elements(node):
  34. """Recursively returns all atoms in nested lists and tuples."""
  35. if isinstance(node, (astng.Tuple, astng.List)):
  36. for child in node.elts:
  37. for e in get_all_elements(child):
  38. yield e
  39. else:
  40. yield node
  41. def clobber_in_except(node):
  42. """Checks if an assignment node in an except handler clobbers an existing
  43. variable.
  44. Returns (True, args for W0623) if assignment clobbers an existing variable,
  45. (False, None) otherwise.
  46. """
  47. if isinstance(node, astng.AssAttr):
  48. return (True, (node.attrname, 'object %r' % (node.expr.name,)))
  49. elif isinstance(node, astng.AssName):
  50. name = node.name
  51. if is_builtin(name):
  52. return (True, (name, 'builtins'))
  53. else:
  54. scope, stmts = node.lookup(name)
  55. if (stmts and
  56. not isinstance(stmts[0].ass_type(),
  57. (astng.Assign, astng.AugAssign, astng.ExceptHandler))):
  58. return (True, (name, 'outer scope (line %i)' % (stmts[0].lineno,)))
  59. return (False, None)
  60. def safe_infer(node):
  61. """return the inferred value for the given node.
  62. Return None if inference failed or if there is some ambiguity (more than
  63. one node has been inferred)
  64. """
  65. try:
  66. inferit = node.infer()
  67. value = inferit.next()
  68. except astng.InferenceError:
  69. return
  70. try:
  71. inferit.next()
  72. return # None if there is ambiguity on the inferred node
  73. except astng.InferenceError:
  74. return # there is some kind of ambiguity
  75. except StopIteration:
  76. return value
  77. def is_super(node):
  78. """return True if the node is referencing the "super" builtin function
  79. """
  80. if getattr(node, 'name', None) == 'super' and \
  81. node.root().name == BUILTINS_NAME:
  82. return True
  83. return False
  84. def is_error(node):
  85. """return true if the function does nothing but raising an exception"""
  86. for child_node in node.get_children():
  87. if isinstance(child_node, astng.Raise):
  88. return True
  89. return False
  90. def is_raising(body):
  91. """return true if the given statement node raise an exception"""
  92. for node in body:
  93. if isinstance(node, astng.Raise):
  94. return True
  95. return False
  96. def is_empty(body):
  97. """return true if the given node does nothing but 'pass'"""
  98. return len(body) == 1 and isinstance(body[0], astng.Pass)
  99. builtins = builtins.__dict__.copy()
  100. SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__')
  101. def is_builtin(name): # was is_native_builtin
  102. """return true if <name> could be considered as a builtin defined by python
  103. """
  104. if name in builtins:
  105. return True
  106. if name in SPECIAL_BUILTINS:
  107. return True
  108. return False
  109. def is_defined_before(var_node):
  110. """return True if the variable node is defined by a parent node (list,
  111. set, dict, or generator comprehension, lambda) or in a previous sibling
  112. node on the same line (statement_defining ; statement_using)
  113. """
  114. varname = var_node.name
  115. _node = var_node.parent
  116. while _node:
  117. if isinstance(_node, COMP_NODE_TYPES):
  118. for ass_node in _node.nodes_of_class(astng.AssName):
  119. if ass_node.name == varname:
  120. return True
  121. elif isinstance(_node, astng.For):
  122. for ass_node in _node.target.nodes_of_class(astng.AssName):
  123. if ass_node.name == varname:
  124. return True
  125. elif isinstance(_node, astng.With):
  126. if _node.vars is None:
  127. # quickfix : case in which 'with' is used without 'as'
  128. return False
  129. if _node.vars.name == varname:
  130. return True
  131. elif isinstance(_node, (astng.Lambda, astng.Function)):
  132. if _node.args.is_argument(varname):
  133. return True
  134. if getattr(_node, 'name', None) == varname:
  135. return True
  136. break
  137. _node = _node.parent
  138. # possibly multiple statements on the same line using semi colon separator
  139. stmt = var_node.statement()
  140. _node = stmt.previous_sibling()
  141. lineno = stmt.fromlineno
  142. while _node and _node.fromlineno == lineno:
  143. for ass_node in _node.nodes_of_class(astng.AssName):
  144. if ass_node.name == varname:
  145. return True
  146. for imp_node in _node.nodes_of_class( (astng.From, astng.Import)):
  147. if varname in [name[1] or name[0] for name in imp_node.names]:
  148. return True
  149. _node = _node.previous_sibling()
  150. return False
  151. def is_func_default(node):
  152. """return true if the given Name node is used in function default argument's
  153. value
  154. """
  155. parent = node.scope()
  156. if isinstance(parent, astng.Function):
  157. for default_node in parent.args.defaults:
  158. for default_name_node in default_node.nodes_of_class(astng.Name):
  159. if default_name_node is node:
  160. return True
  161. return False
  162. def is_func_decorator(node):
  163. """return true if the name is used in function decorator"""
  164. parent = node.parent
  165. while parent is not None:
  166. if isinstance(parent, astng.Decorators):
  167. return True
  168. if (parent.is_statement or
  169. isinstance(parent, astng.Lambda) or
  170. isinstance(parent, (scoped_nodes.ComprehensionScope,
  171. scoped_nodes.ListComp))):
  172. break
  173. parent = parent.parent
  174. return False
  175. def is_ancestor_name(frame, node):
  176. """return True if `frame` is a astng.Class node with `node` in the
  177. subtree of its bases attribute
  178. """
  179. try:
  180. bases = frame.bases
  181. except AttributeError:
  182. return False
  183. for base in bases:
  184. if node in base.nodes_of_class(astng.Name):
  185. return True
  186. return False
  187. def assign_parent(node):
  188. """return the higher parent which is not an AssName, Tuple or List node
  189. """
  190. while node and isinstance(node, (astng.AssName,
  191. astng.Tuple,
  192. astng.List)):
  193. node = node.parent
  194. return node
  195. def overrides_an_abstract_method(class_node, name):
  196. """return True if pnode is a parent of node"""
  197. for ancestor in class_node.ancestors():
  198. if name in ancestor and isinstance(ancestor[name], astng.Function) and \
  199. ancestor[name].is_abstract(pass_is_abstract=False):
  200. return True
  201. return False
  202. def overrides_a_method(class_node, name):
  203. """return True if <name> is a method overridden from an ancestor"""
  204. for ancestor in class_node.ancestors():
  205. if name in ancestor and isinstance(ancestor[name], astng.Function):
  206. return True
  207. return False
  208. PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__',
  209. '__str__', '__repr__',
  210. '__len__', '__iter__',
  211. '__delete__', '__get__', '__set__',
  212. '__getitem__', '__setitem__', '__delitem__', '__contains__',
  213. '__getattribute__', '__getattr__', '__setattr__', '__delattr__',
  214. '__call__',
  215. '__enter__', '__exit__',
  216. '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__',
  217. '__nonzero__', '__neg__', '__invert__',
  218. '__mul__', '__imul__', '__rmul__',
  219. '__div__', '__idiv__', '__rdiv__',
  220. '__add__', '__iadd__', '__radd__',
  221. '__sub__', '__isub__', '__rsub__',
  222. '__pow__', '__ipow__', '__rpow__',
  223. '__mod__', '__imod__', '__rmod__',
  224. '__and__', '__iand__', '__rand__',
  225. '__or__', '__ior__', '__ror__',
  226. '__xor__', '__ixor__', '__rxor__',
  227. # XXX To be continued
  228. ))
  229. def check_messages(*messages):
  230. """decorator to store messages that are handled by a checker method"""
  231. def store_messages(func):
  232. func.checks_msgs = messages
  233. return func
  234. return store_messages
  235. class IncompleteFormatString(Exception):
  236. """A format string ended in the middle of a format specifier."""
  237. pass
  238. class UnsupportedFormatCharacter(Exception):
  239. """A format character in a format string is not one of the supported
  240. format characters."""
  241. def __init__(self, index):
  242. Exception.__init__(self, index)
  243. self.index = index
  244. def parse_format_string(format_string):
  245. """Parses a format string, returning a tuple of (keys, num_args), where keys
  246. is the set of mapping keys in the format string, and num_args is the number
  247. of arguments required by the format string. Raises
  248. IncompleteFormatString or UnsupportedFormatCharacter if a
  249. parse error occurs."""
  250. keys = set()
  251. num_args = 0
  252. def next_char(i):
  253. i += 1
  254. if i == len(format_string):
  255. raise IncompleteFormatString
  256. return (i, format_string[i])
  257. i = 0
  258. while i < len(format_string):
  259. c = format_string[i]
  260. if c == '%':
  261. i, c = next_char(i)
  262. # Parse the mapping key (optional).
  263. key = None
  264. if c == '(':
  265. depth = 1
  266. i, c = next_char(i)
  267. key_start = i
  268. while depth != 0:
  269. if c == '(':
  270. depth += 1
  271. elif c == ')':
  272. depth -= 1
  273. i, c = next_char(i)
  274. key_end = i - 1
  275. key = format_string[key_start:key_end]
  276. # Parse the conversion flags (optional).
  277. while c in '#0- +':
  278. i, c = next_char(i)
  279. # Parse the minimum field width (optional).
  280. if c == '*':
  281. num_args += 1
  282. i, c = next_char(i)
  283. else:
  284. while c in string.digits:
  285. i, c = next_char(i)
  286. # Parse the precision (optional).
  287. if c == '.':
  288. i, c = next_char(i)
  289. if c == '*':
  290. num_args += 1
  291. i, c = next_char(i)
  292. else:
  293. while c in string.digits:
  294. i, c = next_char(i)
  295. # Parse the length modifier (optional).
  296. if c in 'hlL':
  297. i, c = next_char(i)
  298. # Parse the conversion type (mandatory).
  299. if c not in 'diouxXeEfFgGcrs%':
  300. raise UnsupportedFormatCharacter(i)
  301. if key:
  302. keys.add(key)
  303. elif c != '%':
  304. num_args += 1
  305. i += 1
  306. return keys, num_args
  307. def is_attr_protected(attrname):
  308. """return True if attribute name is protected (start with _ and some other
  309. details), False otherwise.
  310. """
  311. return attrname[0] == '_' and not attrname == '_' and not (
  312. attrname.startswith('__') and attrname.endswith('__'))
  313. def node_frame_class(node):
  314. """return klass node for a method node (or a staticmethod or a
  315. classmethod), return null otherwise
  316. """
  317. klass = node.frame()
  318. while klass is not None and not isinstance(klass, astng.Class):
  319. if klass.parent is None:
  320. klass = None
  321. else:
  322. klass = klass.parent.frame()
  323. return klass
  324. def is_super_call(expr):
  325. """return True if expression node is a function call and if function name
  326. is super. Check before that you're in a method.
  327. """
  328. return (isinstance(expr, astng.CallFunc) and
  329. isinstance(expr.func, astng.Name) and
  330. expr.func.name == 'super')
  331. def is_attr_private(attrname):
  332. """Check that attribute name is private (at least two leading underscores,
  333. at most one trailing underscore)
  334. """
  335. regex = re.compile('^_{2,}.*[^_]+_?$')
  336. return regex.match(attrname)