Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

brain_builtin_inference.py 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. # Copyright (c) 2014-2016 Claudiu Popa <pcmanticore@gmail.com>
  2. # Copyright (c) 2015-2016 Cara Vinson <ceridwenv@gmail.com>
  3. # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
  4. # For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
  5. """Astroid hooks for various builtins."""
  6. from functools import partial
  7. import sys
  8. from textwrap import dedent
  9. import six
  10. from astroid import (MANAGER, UseInferenceDefault, AttributeInferenceError,
  11. inference_tip, InferenceError, NameInferenceError)
  12. from astroid import arguments
  13. from astroid.builder import AstroidBuilder
  14. from astroid import helpers
  15. from astroid import nodes
  16. from astroid import objects
  17. from astroid import scoped_nodes
  18. from astroid import util
  19. OBJECT_DUNDER_NEW = 'object.__new__'
  20. def _extend_str(class_node, rvalue):
  21. """function to extend builtin str/unicode class"""
  22. # TODO(cpopa): this approach will make astroid to believe
  23. # that some arguments can be passed by keyword, but
  24. # unfortunately, strings and bytes don't accept keyword arguments.
  25. code = dedent('''
  26. class whatever(object):
  27. def join(self, iterable):
  28. return {rvalue}
  29. def replace(self, old, new, count=None):
  30. return {rvalue}
  31. def format(self, *args, **kwargs):
  32. return {rvalue}
  33. def encode(self, encoding='ascii', errors=None):
  34. return ''
  35. def decode(self, encoding='ascii', errors=None):
  36. return u''
  37. def capitalize(self):
  38. return {rvalue}
  39. def title(self):
  40. return {rvalue}
  41. def lower(self):
  42. return {rvalue}
  43. def upper(self):
  44. return {rvalue}
  45. def swapcase(self):
  46. return {rvalue}
  47. def index(self, sub, start=None, end=None):
  48. return 0
  49. def find(self, sub, start=None, end=None):
  50. return 0
  51. def count(self, sub, start=None, end=None):
  52. return 0
  53. def strip(self, chars=None):
  54. return {rvalue}
  55. def lstrip(self, chars=None):
  56. return {rvalue}
  57. def rstrip(self, chars=None):
  58. return {rvalue}
  59. def rjust(self, width, fillchar=None):
  60. return {rvalue}
  61. def center(self, width, fillchar=None):
  62. return {rvalue}
  63. def ljust(self, width, fillchar=None):
  64. return {rvalue}
  65. ''')
  66. code = code.format(rvalue=rvalue)
  67. fake = AstroidBuilder(MANAGER).string_build(code)['whatever']
  68. for method in fake.mymethods():
  69. class_node.locals[method.name] = [method]
  70. method.parent = class_node
  71. def extend_builtins(class_transforms):
  72. from astroid.bases import BUILTINS
  73. builtin_ast = MANAGER.astroid_cache[BUILTINS]
  74. for class_name, transform in class_transforms.items():
  75. transform(builtin_ast[class_name])
  76. if sys.version_info > (3, 0):
  77. extend_builtins({'bytes': partial(_extend_str, rvalue="b''"),
  78. 'str': partial(_extend_str, rvalue="''")})
  79. else:
  80. extend_builtins({'str': partial(_extend_str, rvalue="''"),
  81. 'unicode': partial(_extend_str, rvalue="u''")})
  82. def register_builtin_transform(transform, builtin_name):
  83. """Register a new transform function for the given *builtin_name*.
  84. The transform function must accept two parameters, a node and
  85. an optional context.
  86. """
  87. def _transform_wrapper(node, context=None):
  88. result = transform(node, context=context)
  89. if result:
  90. if not result.parent:
  91. # Let the transformation function determine
  92. # the parent for its result. Otherwise,
  93. # we set it to be the node we transformed from.
  94. result.parent = node
  95. result.lineno = node.lineno
  96. result.col_offset = node.col_offset
  97. return iter([result])
  98. MANAGER.register_transform(nodes.Call,
  99. inference_tip(_transform_wrapper),
  100. lambda n: (isinstance(n.func, nodes.Name) and
  101. n.func.name == builtin_name))
  102. def _generic_inference(node, context, node_type, transform):
  103. args = node.args
  104. if not args:
  105. return node_type()
  106. if len(node.args) > 1:
  107. raise UseInferenceDefault()
  108. arg, = args
  109. transformed = transform(arg)
  110. if not transformed:
  111. try:
  112. inferred = next(arg.infer(context=context))
  113. except (InferenceError, StopIteration):
  114. raise UseInferenceDefault()
  115. if inferred is util.Uninferable:
  116. raise UseInferenceDefault()
  117. transformed = transform(inferred)
  118. if not transformed or transformed is util.Uninferable:
  119. raise UseInferenceDefault()
  120. return transformed
  121. def _generic_transform(arg, klass, iterables, build_elts):
  122. if isinstance(arg, klass):
  123. return arg
  124. elif isinstance(arg, iterables):
  125. if not all(isinstance(elt, nodes.Const)
  126. for elt in arg.elts):
  127. # TODO(cpopa): Don't support heterogenous elements.
  128. # Not yet, though.
  129. raise UseInferenceDefault()
  130. elts = [elt.value for elt in arg.elts]
  131. elif isinstance(arg, nodes.Dict):
  132. if not all(isinstance(elt[0], nodes.Const)
  133. for elt in arg.items):
  134. raise UseInferenceDefault()
  135. elts = [item[0].value for item in arg.items]
  136. elif (isinstance(arg, nodes.Const) and
  137. isinstance(arg.value, (six.string_types, six.binary_type))):
  138. elts = arg.value
  139. else:
  140. return
  141. return klass.from_constants(elts=build_elts(elts))
  142. def _infer_builtin(node, context,
  143. klass=None, iterables=None,
  144. build_elts=None):
  145. transform_func = partial(
  146. _generic_transform,
  147. klass=klass,
  148. iterables=iterables,
  149. build_elts=build_elts)
  150. return _generic_inference(node, context, klass, transform_func)
  151. # pylint: disable=invalid-name
  152. infer_tuple = partial(
  153. _infer_builtin,
  154. klass=nodes.Tuple,
  155. iterables=(nodes.List, nodes.Set, objects.FrozenSet,
  156. objects.DictItems, objects.DictKeys,
  157. objects.DictValues),
  158. build_elts=tuple)
  159. infer_list = partial(
  160. _infer_builtin,
  161. klass=nodes.List,
  162. iterables=(nodes.Tuple, nodes.Set, objects.FrozenSet,
  163. objects.DictItems, objects.DictKeys,
  164. objects.DictValues),
  165. build_elts=list)
  166. infer_set = partial(
  167. _infer_builtin,
  168. klass=nodes.Set,
  169. iterables=(nodes.List, nodes.Tuple, objects.FrozenSet,
  170. objects.DictKeys),
  171. build_elts=set)
  172. infer_frozenset = partial(
  173. _infer_builtin,
  174. klass=objects.FrozenSet,
  175. iterables=(nodes.List, nodes.Tuple, nodes.Set, objects.FrozenSet,
  176. objects.DictKeys),
  177. build_elts=frozenset)
  178. def _get_elts(arg, context):
  179. is_iterable = lambda n: isinstance(n,
  180. (nodes.List, nodes.Tuple, nodes.Set))
  181. try:
  182. inferred = next(arg.infer(context))
  183. except (InferenceError, NameInferenceError):
  184. raise UseInferenceDefault()
  185. if isinstance(inferred, nodes.Dict):
  186. items = inferred.items
  187. elif is_iterable(inferred):
  188. items = []
  189. for elt in inferred.elts:
  190. # If an item is not a pair of two items,
  191. # then fallback to the default inference.
  192. # Also, take in consideration only hashable items,
  193. # tuples and consts. We are choosing Names as well.
  194. if not is_iterable(elt):
  195. raise UseInferenceDefault()
  196. if len(elt.elts) != 2:
  197. raise UseInferenceDefault()
  198. if not isinstance(elt.elts[0],
  199. (nodes.Tuple, nodes.Const, nodes.Name)):
  200. raise UseInferenceDefault()
  201. items.append(tuple(elt.elts))
  202. else:
  203. raise UseInferenceDefault()
  204. return items
  205. def infer_dict(node, context=None):
  206. """Try to infer a dict call to a Dict node.
  207. The function treats the following cases:
  208. * dict()
  209. * dict(mapping)
  210. * dict(iterable)
  211. * dict(iterable, **kwargs)
  212. * dict(mapping, **kwargs)
  213. * dict(**kwargs)
  214. If a case can't be inferred, we'll fallback to default inference.
  215. """
  216. call = arguments.CallSite.from_call(node)
  217. if call.has_invalid_arguments() or call.has_invalid_keywords():
  218. raise UseInferenceDefault
  219. args = call.positional_arguments
  220. kwargs = list(call.keyword_arguments.items())
  221. if not args and not kwargs:
  222. # dict()
  223. return nodes.Dict()
  224. elif kwargs and not args:
  225. # dict(a=1, b=2, c=4)
  226. items = [(nodes.Const(key), value) for key, value in kwargs]
  227. elif len(args) == 1 and kwargs:
  228. # dict(some_iterable, b=2, c=4)
  229. elts = _get_elts(args[0], context)
  230. keys = [(nodes.Const(key), value) for key, value in kwargs]
  231. items = elts + keys
  232. elif len(args) == 1:
  233. items = _get_elts(args[0], context)
  234. else:
  235. raise UseInferenceDefault()
  236. value = nodes.Dict(col_offset=node.col_offset,
  237. lineno=node.lineno,
  238. parent=node.parent)
  239. value.postinit(items)
  240. return value
  241. def infer_super(node, context=None):
  242. """Understand super calls.
  243. There are some restrictions for what can be understood:
  244. * unbounded super (one argument form) is not understood.
  245. * if the super call is not inside a function (classmethod or method),
  246. then the default inference will be used.
  247. * if the super arguments can't be inferred, the default inference
  248. will be used.
  249. """
  250. if len(node.args) == 1:
  251. # Ignore unbounded super.
  252. raise UseInferenceDefault
  253. scope = node.scope()
  254. if not isinstance(scope, nodes.FunctionDef):
  255. # Ignore non-method uses of super.
  256. raise UseInferenceDefault
  257. if scope.type not in ('classmethod', 'method'):
  258. # Not interested in staticmethods.
  259. raise UseInferenceDefault
  260. cls = scoped_nodes.get_wrapping_class(scope)
  261. if not len(node.args):
  262. mro_pointer = cls
  263. # In we are in a classmethod, the interpreter will fill
  264. # automatically the class as the second argument, not an instance.
  265. if scope.type == 'classmethod':
  266. mro_type = cls
  267. else:
  268. mro_type = cls.instantiate_class()
  269. else:
  270. # TODO(cpopa): support flow control (multiple inference values).
  271. try:
  272. mro_pointer = next(node.args[0].infer(context=context))
  273. except InferenceError:
  274. raise UseInferenceDefault
  275. try:
  276. mro_type = next(node.args[1].infer(context=context))
  277. except InferenceError:
  278. raise UseInferenceDefault
  279. if mro_pointer is util.Uninferable or mro_type is util.Uninferable:
  280. # No way we could understand this.
  281. raise UseInferenceDefault
  282. super_obj = objects.Super(mro_pointer=mro_pointer,
  283. mro_type=mro_type,
  284. self_class=cls,
  285. scope=scope)
  286. super_obj.parent = node
  287. return super_obj
  288. def _infer_getattr_args(node, context):
  289. if len(node.args) not in (2, 3):
  290. # Not a valid getattr call.
  291. raise UseInferenceDefault
  292. try:
  293. # TODO(cpopa): follow all the values of the first argument?
  294. obj = next(node.args[0].infer(context=context))
  295. attr = next(node.args[1].infer(context=context))
  296. except InferenceError:
  297. raise UseInferenceDefault
  298. if obj is util.Uninferable or attr is util.Uninferable:
  299. # If one of the arguments is something we can't infer,
  300. # then also make the result of the getattr call something
  301. # which is unknown.
  302. return util.Uninferable, util.Uninferable
  303. is_string = (isinstance(attr, nodes.Const) and
  304. isinstance(attr.value, six.string_types))
  305. if not is_string:
  306. raise UseInferenceDefault
  307. return obj, attr.value
  308. def infer_getattr(node, context=None):
  309. """Understand getattr calls
  310. If one of the arguments is an Uninferable object, then the
  311. result will be an Uninferable object. Otherwise, the normal attribute
  312. lookup will be done.
  313. """
  314. obj, attr = _infer_getattr_args(node, context)
  315. if obj is util.Uninferable or attr is util.Uninferable or not hasattr(obj, 'igetattr'):
  316. return util.Uninferable
  317. try:
  318. return next(obj.igetattr(attr, context=context))
  319. except (StopIteration, InferenceError, AttributeInferenceError):
  320. if len(node.args) == 3:
  321. # Try to infer the default and return it instead.
  322. try:
  323. return next(node.args[2].infer(context=context))
  324. except InferenceError:
  325. raise UseInferenceDefault
  326. raise UseInferenceDefault
  327. def infer_hasattr(node, context=None):
  328. """Understand hasattr calls
  329. This always guarantees three possible outcomes for calling
  330. hasattr: Const(False) when we are sure that the object
  331. doesn't have the intended attribute, Const(True) when
  332. we know that the object has the attribute and Uninferable
  333. when we are unsure of the outcome of the function call.
  334. """
  335. try:
  336. obj, attr = _infer_getattr_args(node, context)
  337. if obj is util.Uninferable or attr is util.Uninferable or not hasattr(obj, 'getattr'):
  338. return util.Uninferable
  339. obj.getattr(attr, context=context)
  340. except UseInferenceDefault:
  341. # Can't infer something from this function call.
  342. return util.Uninferable
  343. except AttributeInferenceError:
  344. # Doesn't have it.
  345. return nodes.Const(False)
  346. return nodes.Const(True)
  347. def infer_callable(node, context=None):
  348. """Understand callable calls
  349. This follows Python's semantics, where an object
  350. is callable if it provides an attribute __call__,
  351. even though that attribute is something which can't be
  352. called.
  353. """
  354. if len(node.args) != 1:
  355. # Invalid callable call.
  356. raise UseInferenceDefault
  357. argument = node.args[0]
  358. try:
  359. inferred = next(argument.infer(context=context))
  360. except InferenceError:
  361. return util.Uninferable
  362. if inferred is util.Uninferable:
  363. return util.Uninferable
  364. return nodes.Const(inferred.callable())
  365. def infer_bool(node, context=None):
  366. """Understand bool calls."""
  367. if len(node.args) > 1:
  368. # Invalid bool call.
  369. raise UseInferenceDefault
  370. if not node.args:
  371. return nodes.Const(False)
  372. argument = node.args[0]
  373. try:
  374. inferred = next(argument.infer(context=context))
  375. except InferenceError:
  376. return util.Uninferable
  377. if inferred is util.Uninferable:
  378. return util.Uninferable
  379. bool_value = inferred.bool_value()
  380. if bool_value is util.Uninferable:
  381. return util.Uninferable
  382. return nodes.Const(bool_value)
  383. def infer_type(node, context=None):
  384. """Understand the one-argument form of *type*."""
  385. if len(node.args) != 1:
  386. raise UseInferenceDefault
  387. return helpers.object_type(node.args[0], context)
  388. def infer_slice(node, context=None):
  389. """Understand `slice` calls."""
  390. args = node.args
  391. if not 0 < len(args) <= 3:
  392. raise UseInferenceDefault
  393. args = list(map(helpers.safe_infer, args))
  394. for arg in args:
  395. if not arg or arg is util.Uninferable:
  396. raise UseInferenceDefault
  397. if not isinstance(arg, nodes.Const):
  398. raise UseInferenceDefault
  399. if not isinstance(arg.value, (type(None), int)):
  400. raise UseInferenceDefault
  401. if len(args) < 3:
  402. # Make sure we have 3 arguments.
  403. args.extend([None] * (3 - len(args)))
  404. slice_node = nodes.Slice(lineno=node.lineno,
  405. col_offset=node.col_offset,
  406. parent=node.parent)
  407. slice_node.postinit(*args)
  408. return slice_node
  409. def _infer_object__new__decorator(node, context=None):
  410. # Instantiate class immediately
  411. # since that's what @object.__new__ does
  412. return iter((node.instantiate_class(),))
  413. def _infer_object__new__decorator_check(node):
  414. """Predicate before inference_tip
  415. Check if the given ClassDef has a @object.__new__ decorator
  416. """
  417. if not node.decorators:
  418. return False
  419. for decorator in node.decorators.nodes:
  420. if isinstance(decorator, nodes.Attribute):
  421. if decorator.as_string() == OBJECT_DUNDER_NEW:
  422. return True
  423. return False
  424. # Builtins inference
  425. register_builtin_transform(infer_bool, 'bool')
  426. register_builtin_transform(infer_super, 'super')
  427. register_builtin_transform(infer_callable, 'callable')
  428. register_builtin_transform(infer_getattr, 'getattr')
  429. register_builtin_transform(infer_hasattr, 'hasattr')
  430. register_builtin_transform(infer_tuple, 'tuple')
  431. register_builtin_transform(infer_set, 'set')
  432. register_builtin_transform(infer_list, 'list')
  433. register_builtin_transform(infer_dict, 'dict')
  434. register_builtin_transform(infer_frozenset, 'frozenset')
  435. register_builtin_transform(infer_type, 'type')
  436. register_builtin_transform(infer_slice, 'slice')
  437. # Infer object.__new__ calls
  438. MANAGER.register_transform(
  439. nodes.ClassDef,
  440. inference_tip(_infer_object__new__decorator),
  441. _infer_object__new__decorator_check
  442. )