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.

stdlib.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
  3. # Copyright (c) 2013-2014 Google, Inc.
  4. # Copyright (c) 2014-2017 Claudiu Popa <pcmanticore@gmail.com>
  5. # Copyright (c) 2014 Cosmin Poieana <cmin@ropython.org>
  6. # Copyright (c) 2014 Vlad Temian <vladtemian@gmail.com>
  7. # Copyright (c) 2014 Arun Persaud <arun@nubati.net>
  8. # Copyright (c) 2015 Cezar <celnazli@bitdefender.com>
  9. # Copyright (c) 2015 Chris Rebert <code@rebertia.com>
  10. # Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
  11. # Copyright (c) 2016 Jared Garst <cultofjared@gmail.com>
  12. # Copyright (c) 2017 Martin <MartinBasti@users.noreply.github.com>
  13. # Copyright (c) 2017 Christopher Zurcher <zurcher@users.noreply.github.com>
  14. # Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
  15. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  16. # For details: https://github.com/PyCQA/pylint/blob/master/COPYING
  17. """Checkers for various standard library functions."""
  18. import sys
  19. import six
  20. import astroid
  21. from astroid.bases import Instance
  22. from pylint.interfaces import IAstroidChecker
  23. from pylint.checkers import BaseChecker
  24. from pylint.checkers import utils
  25. OPEN_FILES = {'open', 'file'}
  26. UNITTEST_CASE = 'unittest.case'
  27. THREADING_THREAD = 'threading.Thread'
  28. COPY_COPY = 'copy.copy'
  29. OS_ENVIRON = 'os._Environ'
  30. if sys.version_info >= (3, 0):
  31. OPEN_MODULE = '_io'
  32. else:
  33. OPEN_MODULE = '__builtin__'
  34. def _check_mode_str(mode):
  35. # check type
  36. if not isinstance(mode, six.string_types):
  37. return False
  38. # check syntax
  39. modes = set(mode)
  40. _mode = "rwatb+U"
  41. creating = False
  42. if six.PY3:
  43. _mode += "x"
  44. creating = "x" in modes
  45. if modes - set(_mode) or len(mode) > len(modes):
  46. return False
  47. # check logic
  48. reading = "r" in modes
  49. writing = "w" in modes
  50. appending = "a" in modes
  51. text = "t" in modes
  52. binary = "b" in modes
  53. if "U" in modes:
  54. if writing or appending or creating and six.PY3:
  55. return False
  56. reading = True
  57. if not six.PY3:
  58. binary = True
  59. if text and binary:
  60. return False
  61. total = reading + writing + appending + (creating if six.PY3 else 0)
  62. if total > 1:
  63. return False
  64. if not (reading or writing or appending or creating and six.PY3):
  65. return False
  66. # other 2.x constraints
  67. if not six.PY3:
  68. if "U" in mode:
  69. mode = mode.replace("U", "")
  70. if "r" not in mode:
  71. mode = "r" + mode
  72. return mode[0] in ("r", "w", "a", "U")
  73. return True
  74. class StdlibChecker(BaseChecker):
  75. __implements__ = (IAstroidChecker,)
  76. name = 'stdlib'
  77. msgs = {
  78. 'W1501': ('"%s" is not a valid mode for open.',
  79. 'bad-open-mode',
  80. 'Python supports: r, w, a[, x] modes with b, +, '
  81. 'and U (only with r) options. '
  82. 'See http://docs.python.org/2/library/functions.html#open'),
  83. 'W1502': ('Using datetime.time in a boolean context.',
  84. 'boolean-datetime',
  85. 'Using datetime.time in a boolean context can hide '
  86. 'subtle bugs when the time they represent matches '
  87. 'midnight UTC. This behaviour was fixed in Python 3.5. '
  88. 'See http://bugs.python.org/issue13936 for reference.',
  89. {'maxversion': (3, 5)}),
  90. 'W1503': ('Redundant use of %s with constant '
  91. 'value %r',
  92. 'redundant-unittest-assert',
  93. 'The first argument of assertTrue and assertFalse is '
  94. 'a condition. If a constant is passed as parameter, that '
  95. 'condition will be always true. In this case a warning '
  96. 'should be emitted.'),
  97. 'W1505': ('Using deprecated method %s()',
  98. 'deprecated-method',
  99. 'The method is marked as deprecated and will be removed in '
  100. 'a future version of Python. Consider looking for an '
  101. 'alternative in the documentation.'),
  102. 'W1506': ('threading.Thread needs the target function',
  103. 'bad-thread-instantiation',
  104. 'The warning is emitted when a threading.Thread class '
  105. 'is instantiated without the target function being passed. '
  106. 'By default, the first parameter is the group param, not the target param. '),
  107. 'W1507': ('Using copy.copy(os.environ). Use os.environ.copy() '
  108. 'instead. ',
  109. 'shallow-copy-environ',
  110. 'os.environ is not a dict object but proxy object, so '
  111. 'shallow copy has still effects on original object. '
  112. 'See https://bugs.python.org/issue15373 for reference. '),
  113. }
  114. deprecated = {
  115. 0: [
  116. 'cgi.parse_qs', 'cgi.parse_qsl',
  117. 'ctypes.c_buffer',
  118. 'distutils.command.register.register.check_metadata',
  119. 'distutils.command.sdist.sdist.check_metadata',
  120. 'tkinter.Misc.tk_menuBar',
  121. 'tkinter.Menu.tk_bindForTraversal',
  122. ],
  123. 2: {
  124. (2, 6, 0): [
  125. 'commands.getstatus',
  126. 'os.popen2',
  127. 'os.popen3',
  128. 'os.popen4',
  129. 'macostools.touched',
  130. ],
  131. (2, 7, 0): [
  132. 'unittest.case.TestCase.assertEquals',
  133. 'unittest.case.TestCase.assertNotEquals',
  134. 'unittest.case.TestCase.assertAlmostEquals',
  135. 'unittest.case.TestCase.assertNotAlmostEquals',
  136. 'unittest.case.TestCase.assert_',
  137. 'xml.etree.ElementTree.Element.getchildren',
  138. 'xml.etree.ElementTree.Element.getiterator',
  139. 'xml.etree.ElementTree.XMLParser.getiterator',
  140. 'xml.etree.ElementTree.XMLParser.doctype',
  141. ],
  142. },
  143. 3: {
  144. (3, 0, 0): [
  145. 'inspect.getargspec',
  146. 'unittest.case.TestCase._deprecate.deprecated_func',
  147. ],
  148. (3, 1, 0): [
  149. 'base64.encodestring', 'base64.decodestring',
  150. 'ntpath.splitunc',
  151. ],
  152. (3, 2, 0): [
  153. 'cgi.escape',
  154. 'configparser.RawConfigParser.readfp',
  155. 'xml.etree.ElementTree.Element.getchildren',
  156. 'xml.etree.ElementTree.Element.getiterator',
  157. 'xml.etree.ElementTree.XMLParser.getiterator',
  158. 'xml.etree.ElementTree.XMLParser.doctype',
  159. ],
  160. (3, 3, 0): [
  161. 'inspect.getmoduleinfo',
  162. 'logging.warn', 'logging.Logger.warn',
  163. 'logging.LoggerAdapter.warn',
  164. 'nntplib._NNTPBase.xpath',
  165. 'platform.popen',
  166. ],
  167. (3, 4, 0): [
  168. 'importlib.find_loader',
  169. 'plistlib.readPlist', 'plistlib.writePlist',
  170. 'plistlib.readPlistFromBytes',
  171. 'plistlib.writePlistToBytes',
  172. ],
  173. (3, 4, 4): [
  174. 'asyncio.tasks.async',
  175. ],
  176. (3, 5, 0): [
  177. 'fractions.gcd',
  178. 'inspect.getargvalues',
  179. 'inspect.formatargspec', 'inspect.formatargvalues',
  180. 'inspect.getcallargs',
  181. 'platform.linux_distribution', 'platform.dist',
  182. ],
  183. (3, 6, 0): [
  184. 'importlib._bootstrap_external.FileLoader.load_module',
  185. ],
  186. },
  187. }
  188. def _check_bad_thread_instantiation(self, node):
  189. if not node.kwargs and node.args:
  190. self.add_message('bad-thread-instantiation', node=node)
  191. def _check_shallow_copy_environ(self, node):
  192. arg = utils.get_argument_from_call(node, position=0)
  193. for inferred in arg.inferred():
  194. if inferred.qname() == OS_ENVIRON:
  195. self.add_message('shallow-copy-environ', node=node)
  196. break
  197. @utils.check_messages('bad-open-mode', 'redundant-unittest-assert',
  198. 'deprecated-method',
  199. 'bad-thread-instantiation',
  200. 'shallow-copy-environ')
  201. def visit_call(self, node):
  202. """Visit a Call node."""
  203. try:
  204. for inferred in node.func.infer():
  205. if inferred is astroid.Uninferable:
  206. continue
  207. if inferred.root().name == OPEN_MODULE:
  208. if getattr(node.func, 'name', None) in OPEN_FILES:
  209. self._check_open_mode(node)
  210. if inferred.root().name == UNITTEST_CASE:
  211. self._check_redundant_assert(node, inferred)
  212. if isinstance(inferred, astroid.ClassDef) and inferred.qname() == THREADING_THREAD:
  213. self._check_bad_thread_instantiation(node)
  214. if isinstance(inferred, astroid.FunctionDef) and inferred.qname() == COPY_COPY:
  215. self._check_shallow_copy_environ(node)
  216. self._check_deprecated_method(node, inferred)
  217. except astroid.InferenceError:
  218. return
  219. @utils.check_messages('boolean-datetime')
  220. def visit_unaryop(self, node):
  221. if node.op == 'not':
  222. self._check_datetime(node.operand)
  223. @utils.check_messages('boolean-datetime')
  224. def visit_if(self, node):
  225. self._check_datetime(node.test)
  226. @utils.check_messages('boolean-datetime')
  227. def visit_ifexp(self, node):
  228. self._check_datetime(node.test)
  229. @utils.check_messages('boolean-datetime')
  230. def visit_boolop(self, node):
  231. for value in node.values:
  232. self._check_datetime(value)
  233. def _check_deprecated_method(self, node, inferred):
  234. py_vers = sys.version_info[0]
  235. if isinstance(node.func, astroid.Attribute):
  236. func_name = node.func.attrname
  237. elif isinstance(node.func, astroid.Name):
  238. func_name = node.func.name
  239. else:
  240. # Not interested in other nodes.
  241. return
  242. # Reject nodes which aren't of interest to us.
  243. acceptable_nodes = (astroid.BoundMethod,
  244. astroid.UnboundMethod,
  245. astroid.FunctionDef)
  246. if not isinstance(inferred, acceptable_nodes):
  247. return
  248. qname = inferred.qname()
  249. if qname in self.deprecated[0]:
  250. self.add_message('deprecated-method', node=node,
  251. args=(func_name, ))
  252. else:
  253. for since_vers, func_list in self.deprecated[py_vers].items():
  254. if since_vers <= sys.version_info and qname in func_list:
  255. self.add_message('deprecated-method', node=node,
  256. args=(func_name, ))
  257. break
  258. def _check_redundant_assert(self, node, infer):
  259. if (isinstance(infer, astroid.BoundMethod) and
  260. node.args and isinstance(node.args[0], astroid.Const) and
  261. infer.name in ['assertTrue', 'assertFalse']):
  262. self.add_message('redundant-unittest-assert',
  263. args=(infer.name, node.args[0].value, ),
  264. node=node)
  265. def _check_datetime(self, node):
  266. """ Check that a datetime was infered.
  267. If so, emit boolean-datetime warning.
  268. """
  269. try:
  270. infered = next(node.infer())
  271. except astroid.InferenceError:
  272. return
  273. if (isinstance(infered, Instance) and
  274. infered.qname() == 'datetime.time'):
  275. self.add_message('boolean-datetime', node=node)
  276. def _check_open_mode(self, node):
  277. """Check that the mode argument of an open or file call is valid."""
  278. try:
  279. mode_arg = utils.get_argument_from_call(node, position=1,
  280. keyword='mode')
  281. except utils.NoSuchArgumentError:
  282. return
  283. if mode_arg:
  284. mode_arg = utils.safe_infer(mode_arg)
  285. if (isinstance(mode_arg, astroid.Const)
  286. and not _check_mode_str(mode_arg.value)):
  287. self.add_message('bad-open-mode', node=node,
  288. args=mode_arg.value)
  289. def register(linter):
  290. """required method to auto register this checker """
  291. linter.register_checker(StdlibChecker(linter))