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.

lint.py 58KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412
  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2006-2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
  3. # Copyright (c) 2008 Fabrice Douchant <Fabrice.Douchant@logilab.fr>
  4. # Copyright (c) 2009 Vincent
  5. # Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com>
  6. # Copyright (c) 2011-2014 Google, Inc.
  7. # Copyright (c) 2012 David Pursehouse <david.pursehouse@sonymobile.com>
  8. # Copyright (c) 2012 Kevin Jing Qiu <kevin.jing.qiu@gmail.com>
  9. # Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
  10. # Copyright (c) 2012 JT Olds <jtolds@xnet5.com>
  11. # Copyright (c) 2014-2017 Claudiu Popa <pcmanticore@gmail.com>
  12. # Copyright (c) 2014-2015 Michal Nowikowski <godfryd@gmail.com>
  13. # Copyright (c) 2014 Brett Cannon <brett@python.org>
  14. # Copyright (c) 2014 Alexandru Coman <fcoman@bitdefender.com>
  15. # Copyright (c) 2014 Daniel Harding <dharding@living180.net>
  16. # Copyright (c) 2014 Arun Persaud <arun@nubati.net>
  17. # Copyright (c) 2014 Dan Goldsmith <djgoldsmith@googlemail.com>
  18. # Copyright (c) 2015-2016 Florian Bruhin <me@the-compiler.org>
  19. # Copyright (c) 2015 Aru Sahni <arusahni@gmail.com>
  20. # Copyright (c) 2015 Steven Myint <hg@stevenmyint.com>
  21. # Copyright (c) 2015 Simu Toni <simutoni@gmail.com>
  22. # Copyright (c) 2015 Mihai Balint <balint.mihai@gmail.com>
  23. # Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
  24. # Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
  25. # Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
  26. # Copyright (c) 2016 Alan Evangelista <alanoe@linux.vnet.ibm.com>
  27. # Copyright (c) 2017 Daniel Miller <millerdev@gmail.com>
  28. # Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
  29. # Copyright (c) 2017 Roman Ivanov <me@roivanov.com>
  30. # Copyright (c) 2017 Ned Batchelder <ned@nedbatchelder.com>
  31. # Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
  32. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  33. # For details: https://github.com/PyCQA/pylint/blob/master/COPYING
  34. """ %prog [options] modules_or_packages
  35. Check that module(s) satisfy a coding standard (and more !).
  36. %prog --help
  37. Display this help message and exit.
  38. %prog --help-msg <msg-id>[,<msg-id>]
  39. Display help messages about given message identifiers and exit.
  40. """
  41. from __future__ import print_function
  42. import collections
  43. import contextlib
  44. import operator
  45. import os
  46. try:
  47. import multiprocessing
  48. except ImportError:
  49. multiprocessing = None
  50. import sys
  51. import tokenize
  52. import warnings
  53. import six
  54. import astroid
  55. from astroid.__pkginfo__ import version as astroid_version
  56. from astroid import modutils
  57. from pylint import checkers
  58. from pylint import interfaces
  59. from pylint import reporters
  60. from pylint import exceptions
  61. from pylint import utils
  62. from pylint import config
  63. from pylint.__pkginfo__ import version
  64. from pylint.reporters.ureports import nodes as report_nodes
  65. MANAGER = astroid.MANAGER
  66. def _get_new_args(message):
  67. location = (
  68. message.abspath,
  69. message.path,
  70. message.module,
  71. message.obj,
  72. message.line,
  73. message.column,
  74. )
  75. return (
  76. message.msg_id,
  77. message.symbol,
  78. location,
  79. message.msg,
  80. message.confidence,
  81. )
  82. def _get_python_path(filepath):
  83. dirname = os.path.realpath(os.path.expanduser(filepath))
  84. if not os.path.isdir(dirname):
  85. dirname = os.path.dirname(dirname)
  86. while True:
  87. if not os.path.exists(os.path.join(dirname, "__init__.py")):
  88. return dirname
  89. old_dirname = dirname
  90. dirname = os.path.dirname(dirname)
  91. if old_dirname == dirname:
  92. return os.getcwd()
  93. return None
  94. def _merge_stats(stats):
  95. merged = {}
  96. by_msg = collections.Counter()
  97. for stat in stats:
  98. message_stats = stat.pop('by_msg', {})
  99. by_msg.update(message_stats)
  100. for key, item in six.iteritems(stat):
  101. if key not in merged:
  102. merged[key] = item
  103. else:
  104. if isinstance(item, dict):
  105. merged[key].update(item)
  106. else:
  107. merged[key] = merged[key] + item
  108. merged['by_msg'] = by_msg
  109. return merged
  110. @contextlib.contextmanager
  111. def _patch_sysmodules():
  112. # Context manager that permits running pylint, on Windows, with -m switch
  113. # and with --jobs, as in 'python -2 -m pylint .. --jobs'.
  114. # For more details why this is needed,
  115. # see Python issue http://bugs.python.org/issue10845.
  116. mock_main = __name__ != '__main__' # -m switch
  117. if mock_main:
  118. sys.modules['__main__'] = sys.modules[__name__]
  119. try:
  120. yield
  121. finally:
  122. if mock_main:
  123. sys.modules.pop('__main__')
  124. # Python Linter class #########################################################
  125. MSGS = {
  126. 'F0001': ('%s',
  127. 'fatal',
  128. 'Used when an error occurred preventing the analysis of a \
  129. module (unable to find it for instance).'),
  130. 'F0002': ('%s: %s',
  131. 'astroid-error',
  132. 'Used when an unexpected error occurred while building the '
  133. 'Astroid representation. This is usually accompanied by a '
  134. 'traceback. Please report such errors !'),
  135. 'F0010': ('error while code parsing: %s',
  136. 'parse-error',
  137. 'Used when an exception occurred while building the Astroid '
  138. 'representation which could be handled by astroid.'),
  139. 'I0001': ('Unable to run raw checkers on built-in module %s',
  140. 'raw-checker-failed',
  141. 'Used to inform that a built-in module has not been checked '
  142. 'using the raw checkers.'),
  143. 'I0010': ('Unable to consider inline option %r',
  144. 'bad-inline-option',
  145. 'Used when an inline option is either badly formatted or can\'t '
  146. 'be used inside modules.'),
  147. 'I0011': ('Locally disabling %s (%s)',
  148. 'locally-disabled',
  149. 'Used when an inline option disables a message or a messages '
  150. 'category.'),
  151. 'I0012': ('Locally enabling %s (%s)',
  152. 'locally-enabled',
  153. 'Used when an inline option enables a message or a messages '
  154. 'category.'),
  155. 'I0013': ('Ignoring entire file',
  156. 'file-ignored',
  157. 'Used to inform that the file will not be checked'),
  158. 'I0020': ('Suppressed %s (from line %d)',
  159. 'suppressed-message',
  160. 'A message was triggered on a line, but suppressed explicitly '
  161. 'by a disable= comment in the file. This message is not '
  162. 'generated for messages that are ignored due to configuration '
  163. 'settings.'),
  164. 'I0021': ('Useless suppression of %s',
  165. 'useless-suppression',
  166. 'Reported when a message is explicitly disabled for a line or '
  167. 'a block of code, but never triggered.'),
  168. 'I0022': ('Pragma "%s" is deprecated, use "%s" instead',
  169. 'deprecated-pragma',
  170. 'Some inline pylint options have been renamed or reworked, '
  171. 'only the most recent form should be used. '
  172. 'NOTE:skip-all is only available with pylint >= 0.26',
  173. {'old_names': [('I0014', 'deprecated-disable-all')]}),
  174. 'E0001': ('%s',
  175. 'syntax-error',
  176. 'Used when a syntax error is raised for a module.'),
  177. 'E0011': ('Unrecognized file option %r',
  178. 'unrecognized-inline-option',
  179. 'Used when an unknown inline option is encountered.'),
  180. 'E0012': ('Bad option value %r',
  181. 'bad-option-value',
  182. 'Used when a bad value for an inline option is encountered.'),
  183. }
  184. if multiprocessing is not None:
  185. class ChildLinter(multiprocessing.Process):
  186. def run(self):
  187. # pylint: disable=no-member, unbalanced-tuple-unpacking
  188. tasks_queue, results_queue, self._config = self._args
  189. self._config["jobs"] = 1 # Child does not parallelize any further.
  190. self._python3_porting_mode = self._config.pop(
  191. 'python3_porting_mode', None)
  192. self._plugins = self._config.pop('plugins', None)
  193. # Run linter for received files/modules.
  194. for file_or_module in iter(tasks_queue.get, 'STOP'):
  195. try:
  196. result = self._run_linter(file_or_module[0])
  197. results_queue.put(result)
  198. except Exception as ex:
  199. print("internal error with sending report for module %s" %
  200. file_or_module, file=sys.stderr)
  201. print(ex, file=sys.stderr)
  202. results_queue.put({})
  203. def _run_linter(self, file_or_module):
  204. linter = PyLinter()
  205. # Register standard checkers.
  206. linter.load_default_plugins()
  207. # Load command line plugins.
  208. if self._plugins:
  209. linter.load_plugin_modules(self._plugins)
  210. linter.load_configuration_from_config(self._config)
  211. linter.set_reporter(reporters.CollectingReporter())
  212. # Enable the Python 3 checker mode. This option is
  213. # passed down from the parent linter up to here, since
  214. # the Python 3 porting flag belongs to the Run class,
  215. # instead of the Linter class.
  216. if self._python3_porting_mode:
  217. linter.python3_porting_mode()
  218. # Run the checks.
  219. linter.check(file_or_module)
  220. msgs = [_get_new_args(m) for m in linter.reporter.messages]
  221. return (file_or_module, linter.file_state.base_name, linter.current_name,
  222. msgs, linter.stats, linter.msg_status)
  223. class PyLinter(config.OptionsManagerMixIn,
  224. utils.MessagesHandlerMixIn,
  225. utils.ReportsHandlerMixIn,
  226. checkers.BaseTokenChecker):
  227. """lint Python modules using external checkers.
  228. This is the main checker controlling the other ones and the reports
  229. generation. It is itself both a raw checker and an astroid checker in order
  230. to:
  231. * handle message activation / deactivation at the module level
  232. * handle some basic but necessary stats'data (number of classes, methods...)
  233. IDE plugin developers: you may have to call
  234. `astroid.builder.MANAGER.astroid_cache.clear()` across runs if you want
  235. to ensure the latest code version is actually checked.
  236. """
  237. __implements__ = (interfaces.ITokenChecker, )
  238. name = 'master'
  239. priority = 0
  240. level = 0
  241. msgs = MSGS
  242. @staticmethod
  243. def make_options():
  244. return (('ignore',
  245. {'type' : 'csv', 'metavar' : '<file>[,<file>...]',
  246. 'dest' : 'black_list', 'default' : ('CVS',),
  247. 'help' : 'Add files or directories to the blacklist. '
  248. 'They should be base names, not paths.'}),
  249. ('ignore-patterns',
  250. {'type' : 'regexp_csv', 'metavar' : '<pattern>[,<pattern>...]',
  251. 'dest' : 'black_list_re', 'default' : (),
  252. 'help' : 'Add files or directories matching the regex patterns to the'
  253. ' blacklist. The regex matches against base names, not paths.'}),
  254. ('persistent',
  255. {'default': True, 'type' : 'yn', 'metavar' : '<y_or_n>',
  256. 'level': 1,
  257. 'help' : 'Pickle collected data for later comparisons.'}),
  258. ('load-plugins',
  259. {'type' : 'csv', 'metavar' : '<modules>', 'default' : (),
  260. 'level': 1,
  261. 'help' : 'List of plugins (as comma separated values of '
  262. 'python modules names) to load, usually to register '
  263. 'additional checkers.'}),
  264. ('output-format',
  265. {'default': 'text', 'type': 'string', 'metavar' : '<format>',
  266. 'short': 'f',
  267. 'group': 'Reports',
  268. 'help' : 'Set the output format. Available formats are text,'
  269. ' parseable, colorized, json and msvs (visual studio).'
  270. 'You can also give a reporter class, eg mypackage.mymodule.'
  271. 'MyReporterClass.'}),
  272. ('reports',
  273. {'default': False, 'type' : 'yn', 'metavar' : '<y_or_n>',
  274. 'short': 'r',
  275. 'group': 'Reports',
  276. 'help' : 'Tells whether to display a full report or only the '
  277. 'messages'}),
  278. ('evaluation',
  279. {'type' : 'string', 'metavar' : '<python_expression>',
  280. 'group': 'Reports', 'level': 1,
  281. 'default': '10.0 - ((float(5 * error + warning + refactor + '
  282. 'convention) / statement) * 10)',
  283. 'help' : 'Python expression which should return a note less '
  284. 'than 10 (10 is the highest note). You have access '
  285. 'to the variables errors warning, statement which '
  286. 'respectively contain the number of errors / '
  287. 'warnings messages and the total number of '
  288. 'statements analyzed. This is used by the global '
  289. 'evaluation report (RP0004).'}),
  290. ('score',
  291. {'default': True, 'type': 'yn', 'metavar': '<y_or_n>',
  292. 'short': 's',
  293. 'group': 'Reports',
  294. 'help': 'Activate the evaluation score.'}),
  295. ('confidence',
  296. {'type' : 'multiple_choice', 'metavar': '<levels>',
  297. 'default': '',
  298. 'choices': [c.name for c in interfaces.CONFIDENCE_LEVELS],
  299. 'group': 'Messages control',
  300. 'help' : 'Only show warnings with the listed confidence levels.'
  301. ' Leave empty to show all. Valid levels: %s' % (
  302. ', '.join(c.name for c in interfaces.CONFIDENCE_LEVELS),)}),
  303. ('enable',
  304. {'type' : 'csv', 'metavar': '<msg ids>',
  305. 'short': 'e',
  306. 'group': 'Messages control',
  307. 'help' : 'Enable the message, report, category or checker with the '
  308. 'given id(s). You can either give multiple identifier '
  309. 'separated by comma (,) or put this option multiple time '
  310. '(only on the command line, not in the configuration file '
  311. 'where it should appear only once). '
  312. 'See also the "--disable" option for examples. '}),
  313. ('disable',
  314. {'type' : 'csv', 'metavar': '<msg ids>',
  315. 'short': 'd',
  316. 'group': 'Messages control',
  317. 'help' : 'Disable the message, report, category or checker '
  318. 'with the given id(s). You can either give multiple identifiers'
  319. ' separated by comma (,) or put this option multiple times '
  320. '(only on the command line, not in the configuration file '
  321. 'where it should appear only once).'
  322. 'You can also use "--disable=all" to disable everything first '
  323. 'and then reenable specific checks. For example, if you want '
  324. 'to run only the similarities checker, you can use '
  325. '"--disable=all --enable=similarities". '
  326. 'If you want to run only the classes checker, but have no '
  327. 'Warning level messages displayed, use'
  328. '"--disable=all --enable=classes --disable=W"'}),
  329. ('msg-template',
  330. {'type' : 'string', 'metavar': '<template>',
  331. 'group': 'Reports',
  332. 'help' : ('Template used to display messages. '
  333. 'This is a python new-style format string '
  334. 'used to format the message information. '
  335. 'See doc for all details')
  336. }),
  337. ('jobs',
  338. {'type' : 'int', 'metavar': '<n-processes>',
  339. 'short': 'j',
  340. 'default': 1,
  341. 'help' : '''Use multiple processes to speed up Pylint.''',
  342. }),
  343. ('unsafe-load-any-extension',
  344. {'type': 'yn', 'metavar': '<yn>', 'default': False, 'hide': True,
  345. 'help': ('Allow loading of arbitrary C extensions. Extensions'
  346. ' are imported into the active Python interpreter and'
  347. ' may run arbitrary code.')}),
  348. ('extension-pkg-whitelist',
  349. {'type': 'csv', 'metavar': '<pkg[,pkg]>', 'default': [],
  350. 'help': ('A comma-separated list of package or module names'
  351. ' from where C extensions may be loaded. Extensions are'
  352. ' loading into the active Python interpreter and may run'
  353. ' arbitrary code')}),
  354. ('suggestion-mode',
  355. {'type': 'yn', 'metavar': '<yn>', 'default': True,
  356. 'help': ('When enabled, pylint would attempt to guess common '
  357. 'misconfiguration and emit user-friendly hints instead '
  358. 'of false-positive error messages')}),
  359. )
  360. option_groups = (
  361. ('Messages control', 'Options controlling analysis messages'),
  362. ('Reports', 'Options related to output formatting and reporting'),
  363. )
  364. def __init__(self, options=(), reporter=None, option_groups=(),
  365. pylintrc=None):
  366. # some stuff has to be done before ancestors initialization...
  367. #
  368. # messages store / checkers / reporter / astroid manager
  369. self.msgs_store = utils.MessagesStore()
  370. self.reporter = None
  371. self._reporter_name = None
  372. self._reporters = {}
  373. self._checkers = collections.defaultdict(list)
  374. self._pragma_lineno = {}
  375. self._ignore_file = False
  376. # visit variables
  377. self.file_state = utils.FileState()
  378. self.current_name = None
  379. self.current_file = None
  380. self.stats = None
  381. # init options
  382. self._external_opts = options
  383. self.options = options + PyLinter.make_options()
  384. self.option_groups = option_groups + PyLinter.option_groups
  385. self._options_methods = {
  386. 'enable': self.enable,
  387. 'disable': self.disable}
  388. self._bw_options_methods = {'disable-msg': self.disable,
  389. 'enable-msg': self.enable}
  390. full_version = '%%prog %s, \nastroid %s\nPython %s' % (
  391. version, astroid_version, sys.version)
  392. utils.MessagesHandlerMixIn.__init__(self)
  393. utils.ReportsHandlerMixIn.__init__(self)
  394. super(PyLinter, self).__init__(
  395. usage=__doc__,
  396. version=full_version,
  397. config_file=pylintrc or config.PYLINTRC)
  398. checkers.BaseTokenChecker.__init__(self)
  399. # provided reports
  400. self.reports = (('RP0001', 'Messages by category',
  401. report_total_messages_stats),
  402. ('RP0002', '% errors / warnings by module',
  403. report_messages_by_module_stats),
  404. ('RP0003', 'Messages',
  405. report_messages_stats),
  406. )
  407. self.register_checker(self)
  408. self._dynamic_plugins = set()
  409. self._python3_porting_mode = False
  410. self._error_mode = False
  411. self.load_provider_defaults()
  412. if reporter:
  413. self.set_reporter(reporter)
  414. def load_default_plugins(self):
  415. checkers.initialize(self)
  416. reporters.initialize(self)
  417. # Make sure to load the default reporter, because
  418. # the option has been set before the plugins had been loaded.
  419. if not self.reporter:
  420. self._load_reporter()
  421. def load_plugin_modules(self, modnames):
  422. """take a list of module names which are pylint plugins and load
  423. and register them
  424. """
  425. for modname in modnames:
  426. if modname in self._dynamic_plugins:
  427. continue
  428. self._dynamic_plugins.add(modname)
  429. module = modutils.load_module_from_name(modname)
  430. module.register(self)
  431. def _load_reporter(self):
  432. name = self._reporter_name.lower()
  433. if name in self._reporters:
  434. self.set_reporter(self._reporters[name]())
  435. else:
  436. try:
  437. reporter_class = self._load_reporter_class()
  438. except (ImportError, AttributeError):
  439. raise exceptions.InvalidReporterError(name)
  440. else:
  441. self.set_reporter(reporter_class())
  442. def _load_reporter_class(self):
  443. qname = self._reporter_name
  444. module = modutils.load_module_from_name(
  445. modutils.get_module_part(qname))
  446. class_name = qname.split('.')[-1]
  447. reporter_class = getattr(module, class_name)
  448. return reporter_class
  449. def set_reporter(self, reporter):
  450. """set the reporter used to display messages and reports"""
  451. self.reporter = reporter
  452. reporter.linter = self
  453. def set_option(self, optname, value, action=None, optdict=None):
  454. """overridden from config.OptionsProviderMixin to handle some
  455. special options
  456. """
  457. if optname in self._options_methods or \
  458. optname in self._bw_options_methods:
  459. if value:
  460. try:
  461. meth = self._options_methods[optname]
  462. except KeyError:
  463. meth = self._bw_options_methods[optname]
  464. warnings.warn('%s is deprecated, replace it by %s' % (optname,
  465. optname.split('-')[0]),
  466. DeprecationWarning)
  467. value = utils._check_csv(value)
  468. if isinstance(value, (list, tuple)):
  469. for _id in value:
  470. meth(_id, ignore_unknown=True)
  471. else:
  472. meth(value)
  473. return # no need to call set_option, disable/enable methods do it
  474. elif optname == 'output-format':
  475. self._reporter_name = value
  476. # If the reporters are already available, load
  477. # the reporter class.
  478. if self._reporters:
  479. self._load_reporter()
  480. try:
  481. checkers.BaseTokenChecker.set_option(self, optname,
  482. value, action, optdict)
  483. except config.UnsupportedAction:
  484. print('option %s can\'t be read from config file' % \
  485. optname, file=sys.stderr)
  486. def register_reporter(self, reporter_class):
  487. self._reporters[reporter_class.name] = reporter_class
  488. def report_order(self):
  489. reports = sorted(self._reports, key=lambda x: getattr(x, 'name', ''))
  490. try:
  491. # Remove the current reporter and add it
  492. # at the end of the list.
  493. reports.pop(reports.index(self))
  494. except ValueError:
  495. pass
  496. else:
  497. reports.append(self)
  498. return reports
  499. # checkers manipulation methods ############################################
  500. def register_checker(self, checker):
  501. """register a new checker
  502. checker is an object implementing IRawChecker or / and IAstroidChecker
  503. """
  504. assert checker.priority <= 0, 'checker priority can\'t be >= 0'
  505. self._checkers[checker.name].append(checker)
  506. for r_id, r_title, r_cb in checker.reports:
  507. self.register_report(r_id, r_title, r_cb, checker)
  508. self.register_options_provider(checker)
  509. if hasattr(checker, 'msgs'):
  510. self.msgs_store.register_messages(checker)
  511. checker.load_defaults()
  512. # Register the checker, but disable all of its messages.
  513. # TODO(cpopa): we should have a better API for this.
  514. if not getattr(checker, 'enabled', True):
  515. self.disable(checker.name)
  516. def disable_noerror_messages(self):
  517. for msgcat, msgids in six.iteritems(self.msgs_store._msgs_by_category):
  518. # enable only messages with 'error' severity and above ('fatal')
  519. if msgcat in ['E', 'F']:
  520. for msgid in msgids:
  521. self.enable(msgid)
  522. else:
  523. for msgid in msgids:
  524. self.disable(msgid)
  525. def disable_reporters(self):
  526. """disable all reporters"""
  527. for _reporters in six.itervalues(self._reports):
  528. for report_id, _, _ in _reporters:
  529. self.disable_report(report_id)
  530. def error_mode(self):
  531. """error mode: enable only errors; no reports, no persistent"""
  532. self._error_mode = True
  533. self.disable_noerror_messages()
  534. self.disable('miscellaneous')
  535. if self._python3_porting_mode:
  536. self.disable('all')
  537. for msg_id in self._checker_messages('python3'):
  538. if msg_id.startswith('E'):
  539. self.enable(msg_id)
  540. config_parser = self.cfgfile_parser
  541. if config_parser.has_option('MESSAGES CONTROL', 'disable'):
  542. value = config_parser.get('MESSAGES CONTROL', 'disable')
  543. self.global_set_option('disable', value)
  544. else:
  545. self.disable('python3')
  546. self.set_option('reports', False)
  547. self.set_option('persistent', False)
  548. self.set_option('score', False)
  549. def python3_porting_mode(self):
  550. """Disable all other checkers and enable Python 3 warnings."""
  551. self.disable('all')
  552. self.enable('python3')
  553. if self._error_mode:
  554. # The error mode was activated, using the -E flag.
  555. # So we'll need to enable only the errors from the
  556. # Python 3 porting checker.
  557. for msg_id in self._checker_messages('python3'):
  558. if msg_id.startswith('E'):
  559. self.enable(msg_id)
  560. else:
  561. self.disable(msg_id)
  562. config_parser = self.cfgfile_parser
  563. if config_parser.has_option('MESSAGES CONTROL', 'disable'):
  564. value = config_parser.get('MESSAGES CONTROL', 'disable')
  565. self.global_set_option('disable', value)
  566. self._python3_porting_mode = True
  567. # block level option handling #############################################
  568. #
  569. # see func_block_disable_msg.py test case for expected behaviour
  570. def process_tokens(self, tokens):
  571. """process tokens from the current module to search for module/block
  572. level options
  573. """
  574. control_pragmas = {'disable', 'enable'}
  575. for (tok_type, content, start, _, _) in tokens:
  576. if tok_type != tokenize.COMMENT:
  577. continue
  578. match = utils.OPTION_RGX.search(content)
  579. if match is None:
  580. continue
  581. if match.group(1).strip() == "disable-all" or \
  582. match.group(1).strip() == 'skip-file':
  583. if match.group(1).strip() == "disable-all":
  584. self.add_message('deprecated-pragma', line=start[0],
  585. args=('disable-all', 'skip-file'))
  586. self.add_message('file-ignored', line=start[0])
  587. self._ignore_file = True
  588. return
  589. try:
  590. opt, value = match.group(1).split('=', 1)
  591. except ValueError:
  592. self.add_message('bad-inline-option', args=match.group(1).strip(),
  593. line=start[0])
  594. continue
  595. opt = opt.strip()
  596. if opt in self._options_methods or opt in self._bw_options_methods:
  597. try:
  598. meth = self._options_methods[opt]
  599. except KeyError:
  600. meth = self._bw_options_methods[opt]
  601. # found a "(dis|en)able-msg" pragma deprecated suppression
  602. self.add_message('deprecated-pragma', line=start[0],
  603. args=(opt, opt.replace('-msg', '')))
  604. for msgid in utils._splitstrip(value):
  605. # Add the line where a control pragma was encountered.
  606. if opt in control_pragmas:
  607. self._pragma_lineno[msgid] = start[0]
  608. try:
  609. if (opt, msgid) == ('disable', 'all'):
  610. self.add_message('deprecated-pragma', line=start[0],
  611. args=('disable=all', 'skip-file'))
  612. self.add_message('file-ignored', line=start[0])
  613. self._ignore_file = True
  614. return
  615. meth(msgid, 'module', start[0])
  616. except exceptions.UnknownMessageError:
  617. self.add_message('bad-option-value', args=msgid, line=start[0])
  618. else:
  619. self.add_message('unrecognized-inline-option', args=opt, line=start[0])
  620. # code checking methods ###################################################
  621. def get_checkers(self):
  622. """return all available checkers as a list"""
  623. return [self] + [c for _checkers in six.itervalues(self._checkers)
  624. for c in _checkers if c is not self]
  625. def prepare_checkers(self):
  626. """return checkers needed for activated messages and reports"""
  627. if not self.config.reports:
  628. self.disable_reporters()
  629. # get needed checkers
  630. neededcheckers = [self]
  631. for checker in self.get_checkers()[1:]:
  632. messages = set(msg for msg in checker.msgs
  633. if self.is_message_enabled(msg))
  634. if (messages or
  635. any(self.report_is_enabled(r[0]) for r in checker.reports)):
  636. neededcheckers.append(checker)
  637. # Sort checkers by priority
  638. neededcheckers = sorted(neededcheckers,
  639. key=operator.attrgetter('priority'),
  640. reverse=True)
  641. return neededcheckers
  642. # pylint: disable=unused-argument
  643. @staticmethod
  644. def should_analyze_file(modname, path, is_argument=False):
  645. """Returns whether or not a module should be checked.
  646. This implementation returns True for all python source file, indicating
  647. that all files should be linted.
  648. Subclasses may override this method to indicate that modules satisfying
  649. certain conditions should not be linted.
  650. :param str modname: The name of the module to be checked.
  651. :param str path: The full path to the source code of the module.
  652. :param bool is_argument: Whetter the file is an argument to pylint or not.
  653. Files which respect this property are always
  654. checked, since the user requested it explicitly.
  655. :returns: True if the module should be checked.
  656. :rtype: bool
  657. """
  658. if is_argument:
  659. return True
  660. return path.endswith('.py')
  661. # pylint: enable=unused-argument
  662. def check(self, files_or_modules):
  663. """main checking entry: check a list of files or modules from their
  664. name.
  665. """
  666. # initialize msgs_state now that all messages have been registered into
  667. # the store
  668. for msg in self.msgs_store.messages:
  669. if not msg.may_be_emitted():
  670. self._msgs_state[msg.msgid] = False
  671. if not isinstance(files_or_modules, (list, tuple)):
  672. files_or_modules = (files_or_modules,)
  673. if self.config.jobs == 1:
  674. self._do_check(files_or_modules)
  675. else:
  676. with _patch_sysmodules():
  677. self._parallel_check(files_or_modules)
  678. def _get_jobs_config(self):
  679. child_config = collections.OrderedDict()
  680. filter_options = {'long-help'}
  681. filter_options.update((opt_name for opt_name, _ in self._external_opts))
  682. for opt_providers in six.itervalues(self._all_options):
  683. for optname, optdict, val in opt_providers.options_and_values():
  684. if optdict.get('deprecated'):
  685. continue
  686. if optname not in filter_options:
  687. child_config[optname] = utils._format_option_value(
  688. optdict, val)
  689. child_config['python3_porting_mode'] = self._python3_porting_mode
  690. child_config['plugins'] = self._dynamic_plugins
  691. return child_config
  692. def _parallel_task(self, files_or_modules):
  693. # Prepare configuration for child linters.
  694. child_config = self._get_jobs_config()
  695. children = []
  696. manager = multiprocessing.Manager()
  697. tasks_queue = manager.Queue()
  698. results_queue = manager.Queue()
  699. # Send files to child linters.
  700. expanded_files = self.expand_files(files_or_modules)
  701. # do not start more jobs than needed
  702. for _ in range(min(self.config.jobs, len(expanded_files))):
  703. child_linter = ChildLinter(args=(tasks_queue, results_queue,
  704. child_config))
  705. child_linter.start()
  706. children.append(child_linter)
  707. for files_or_module in expanded_files:
  708. path = files_or_module['path']
  709. tasks_queue.put([path])
  710. # collect results from child linters
  711. failed = False
  712. for _ in expanded_files:
  713. try:
  714. result = results_queue.get()
  715. except Exception as ex:
  716. print("internal error while receiving results from child linter",
  717. file=sys.stderr)
  718. print(ex, file=sys.stderr)
  719. failed = True
  720. break
  721. yield result
  722. # Stop child linters and wait for their completion.
  723. for _ in range(self.config.jobs):
  724. tasks_queue.put('STOP')
  725. for child in children:
  726. child.join()
  727. if failed:
  728. print("Error occurred, stopping the linter.", file=sys.stderr)
  729. sys.exit(32)
  730. def _parallel_check(self, files_or_modules):
  731. # Reset stats.
  732. self.open()
  733. all_stats = []
  734. module = None
  735. for result in self._parallel_task(files_or_modules):
  736. if not result:
  737. continue
  738. (
  739. _,
  740. self.file_state.base_name,
  741. module,
  742. messages,
  743. stats,
  744. msg_status
  745. ) = result
  746. for msg in messages:
  747. msg = utils.Message(*msg)
  748. self.set_current_module(module)
  749. self.reporter.handle_message(msg)
  750. all_stats.append(stats)
  751. self.msg_status |= msg_status
  752. self.stats = _merge_stats(all_stats)
  753. self.current_name = module
  754. # Insert stats data to local checkers.
  755. for checker in self.get_checkers():
  756. if checker is not self:
  757. checker.stats = self.stats
  758. def _do_check(self, files_or_modules):
  759. walker = utils.PyLintASTWalker(self)
  760. _checkers = self.prepare_checkers()
  761. tokencheckers = [c for c in _checkers
  762. if interfaces.implements(c, interfaces.ITokenChecker)
  763. and c is not self]
  764. rawcheckers = [c for c in _checkers
  765. if interfaces.implements(c, interfaces.IRawChecker)]
  766. # notify global begin
  767. for checker in _checkers:
  768. checker.open()
  769. if interfaces.implements(checker, interfaces.IAstroidChecker):
  770. walker.add_checker(checker)
  771. # build ast and check modules or packages
  772. for descr in self.expand_files(files_or_modules):
  773. modname, filepath, is_arg = descr['name'], descr['path'], descr['isarg']
  774. if not self.should_analyze_file(modname, filepath, is_argument=is_arg):
  775. continue
  776. self.set_current_module(modname, filepath)
  777. # get the module representation
  778. ast_node = self.get_ast(filepath, modname)
  779. if ast_node is None:
  780. continue
  781. # XXX to be correct we need to keep module_msgs_state for every
  782. # analyzed module (the problem stands with localized messages which
  783. # are only detected in the .close step)
  784. self.file_state = utils.FileState(descr['basename'])
  785. self._ignore_file = False
  786. # fix the current file (if the source file was not available or
  787. # if it's actually a c extension)
  788. self.current_file = ast_node.file # pylint: disable=maybe-no-member
  789. self.check_astroid_module(ast_node, walker, rawcheckers, tokencheckers)
  790. # warn about spurious inline messages handling
  791. spurious_messages = self.file_state.iter_spurious_suppression_messages(self.msgs_store)
  792. for msgid, line, args in spurious_messages:
  793. self.add_message(msgid, line, None, args)
  794. # notify global end
  795. self.stats['statement'] = walker.nbstatements
  796. for checker in reversed(_checkers):
  797. checker.close()
  798. def expand_files(self, modules):
  799. """get modules and errors from a list of modules and handle errors
  800. """
  801. result, errors = utils.expand_modules(modules, self.config.black_list,
  802. self.config.black_list_re)
  803. for error in errors:
  804. message = modname = error["mod"]
  805. key = error["key"]
  806. self.set_current_module(modname)
  807. if key == "fatal":
  808. message = str(error["ex"]).replace(os.getcwd() + os.sep, '')
  809. self.add_message(key, args=message)
  810. return result
  811. def set_current_module(self, modname, filepath=None):
  812. """set the name of the currently analyzed module and
  813. init statistics for it
  814. """
  815. if not modname and filepath is None:
  816. return
  817. self.reporter.on_set_current_module(modname, filepath)
  818. self.current_name = modname
  819. self.current_file = filepath or modname
  820. self.stats['by_module'][modname] = {}
  821. self.stats['by_module'][modname]['statement'] = 0
  822. for msg_cat in six.itervalues(utils.MSG_TYPES):
  823. self.stats['by_module'][modname][msg_cat] = 0
  824. def get_ast(self, filepath, modname):
  825. """return a ast(roid) representation for a module"""
  826. try:
  827. return MANAGER.ast_from_file(filepath, modname, source=True)
  828. except astroid.AstroidSyntaxError as ex:
  829. self.add_message('syntax-error',
  830. line=getattr(ex.error, 'lineno', 0),
  831. args=str(ex.error))
  832. except astroid.AstroidBuildingException as ex:
  833. self.add_message('parse-error', args=ex)
  834. except Exception as ex: # pylint: disable=broad-except
  835. import traceback
  836. traceback.print_exc()
  837. self.add_message('astroid-error', args=(ex.__class__, ex))
  838. def check_astroid_module(self, ast_node, walker,
  839. rawcheckers, tokencheckers):
  840. """Check a module from its astroid representation."""
  841. try:
  842. tokens = utils.tokenize_module(ast_node)
  843. except tokenize.TokenError as ex:
  844. self.add_message('syntax-error', line=ex.args[1][0], args=ex.args[0])
  845. return None
  846. if not ast_node.pure_python:
  847. self.add_message('raw-checker-failed', args=ast_node.name)
  848. else:
  849. #assert astroid.file.endswith('.py')
  850. # invoke ITokenChecker interface on self to fetch module/block
  851. # level options
  852. self.process_tokens(tokens)
  853. if self._ignore_file:
  854. return False
  855. # walk ast to collect line numbers
  856. self.file_state.collect_block_lines(self.msgs_store, ast_node)
  857. # run raw and tokens checkers
  858. for checker in rawcheckers:
  859. checker.process_module(ast_node)
  860. for checker in tokencheckers:
  861. checker.process_tokens(tokens)
  862. # generate events to astroid checkers
  863. walker.walk(ast_node)
  864. return True
  865. # IAstroidChecker interface #################################################
  866. def open(self):
  867. """initialize counters"""
  868. self.stats = {'by_module' : {},
  869. 'by_msg' : {},
  870. }
  871. MANAGER.always_load_extensions = self.config.unsafe_load_any_extension
  872. MANAGER.extension_package_whitelist.update(
  873. self.config.extension_pkg_whitelist)
  874. for msg_cat in six.itervalues(utils.MSG_TYPES):
  875. self.stats[msg_cat] = 0
  876. def generate_reports(self):
  877. """close the whole package /module, it's time to make reports !
  878. if persistent run, pickle results for later comparison
  879. """
  880. # Display whatever messages are left on the reporter.
  881. self.reporter.display_messages(report_nodes.Section())
  882. if self.file_state.base_name is not None:
  883. # load previous results if any
  884. previous_stats = config.load_results(self.file_state.base_name)
  885. # XXX code below needs refactoring to be more reporter agnostic
  886. self.reporter.on_close(self.stats, previous_stats)
  887. if self.config.reports:
  888. sect = self.make_reports(self.stats, previous_stats)
  889. else:
  890. sect = report_nodes.Section()
  891. if self.config.reports:
  892. self.reporter.display_reports(sect)
  893. self._report_evaluation()
  894. # save results if persistent run
  895. if self.config.persistent:
  896. config.save_results(self.stats, self.file_state.base_name)
  897. else:
  898. self.reporter.on_close(self.stats, {})
  899. def _report_evaluation(self):
  900. """make the global evaluation report"""
  901. # check with at least check 1 statements (usually 0 when there is a
  902. # syntax error preventing pylint from further processing)
  903. previous_stats = config.load_results(self.file_state.base_name)
  904. if self.stats['statement'] == 0:
  905. return
  906. # get a global note for the code
  907. evaluation = self.config.evaluation
  908. try:
  909. note = eval(evaluation, {}, self.stats) # pylint: disable=eval-used
  910. except Exception as ex: # pylint: disable=broad-except
  911. msg = 'An exception occurred while rating: %s' % ex
  912. else:
  913. self.stats['global_note'] = note
  914. msg = 'Your code has been rated at %.2f/10' % note
  915. pnote = previous_stats.get('global_note')
  916. if pnote is not None:
  917. msg += ' (previous run: %.2f/10, %+.2f)' % (pnote, note - pnote)
  918. if self.config.score:
  919. sect = report_nodes.EvaluationSection(msg)
  920. self.reporter.display_reports(sect)
  921. # some reporting functions ####################################################
  922. def report_total_messages_stats(sect, stats, previous_stats):
  923. """make total errors / warnings report"""
  924. lines = ['type', 'number', 'previous', 'difference']
  925. lines += checkers.table_lines_from_stats(stats, previous_stats,
  926. ('convention', 'refactor',
  927. 'warning', 'error'))
  928. sect.append(report_nodes.Table(children=lines, cols=4, rheaders=1))
  929. def report_messages_stats(sect, stats, _):
  930. """make messages type report"""
  931. if not stats['by_msg']:
  932. # don't print this report when we didn't detected any errors
  933. raise exceptions.EmptyReportError()
  934. in_order = sorted([(value, msg_id)
  935. for msg_id, value in six.iteritems(stats['by_msg'])
  936. if not msg_id.startswith('I')])
  937. in_order.reverse()
  938. lines = ('message id', 'occurrences')
  939. for value, msg_id in in_order:
  940. lines += (msg_id, str(value))
  941. sect.append(report_nodes.Table(children=lines, cols=2, rheaders=1))
  942. def report_messages_by_module_stats(sect, stats, _):
  943. """make errors / warnings by modules report"""
  944. if len(stats['by_module']) == 1:
  945. # don't print this report when we are analysing a single module
  946. raise exceptions.EmptyReportError()
  947. by_mod = collections.defaultdict(dict)
  948. for m_type in ('fatal', 'error', 'warning', 'refactor', 'convention'):
  949. total = stats[m_type]
  950. for module in six.iterkeys(stats['by_module']):
  951. mod_total = stats['by_module'][module][m_type]
  952. if total == 0:
  953. percent = 0
  954. else:
  955. percent = float((mod_total)*100) / total
  956. by_mod[module][m_type] = percent
  957. sorted_result = []
  958. for module, mod_info in six.iteritems(by_mod):
  959. sorted_result.append((mod_info['error'],
  960. mod_info['warning'],
  961. mod_info['refactor'],
  962. mod_info['convention'],
  963. module))
  964. sorted_result.sort()
  965. sorted_result.reverse()
  966. lines = ['module', 'error', 'warning', 'refactor', 'convention']
  967. for line in sorted_result:
  968. # Don't report clean modules.
  969. if all(entry == 0 for entry in line[:-1]):
  970. continue
  971. lines.append(line[-1])
  972. for val in line[:-1]:
  973. lines.append('%.2f' % val)
  974. if len(lines) == 5:
  975. raise exceptions.EmptyReportError()
  976. sect.append(report_nodes.Table(children=lines, cols=5, rheaders=1))
  977. # utilities ###################################################################
  978. class ArgumentPreprocessingError(Exception):
  979. """Raised if an error occurs during argument preprocessing."""
  980. def preprocess_options(args, search_for):
  981. """look for some options (keys of <search_for>) which have to be processed
  982. before others
  983. values of <search_for> are callback functions to call when the option is
  984. found
  985. """
  986. i = 0
  987. while i < len(args):
  988. arg = args[i]
  989. if arg.startswith('--'):
  990. try:
  991. option, val = arg[2:].split('=', 1)
  992. except ValueError:
  993. option, val = arg[2:], None
  994. try:
  995. cb, takearg = search_for[option]
  996. except KeyError:
  997. i += 1
  998. else:
  999. del args[i]
  1000. if takearg and val is None:
  1001. if i >= len(args) or args[i].startswith('-'):
  1002. msg = 'Option %s expects a value' % option
  1003. raise ArgumentPreprocessingError(msg)
  1004. val = args[i]
  1005. del args[i]
  1006. elif not takearg and val is not None:
  1007. msg = "Option %s doesn't expects a value" % option
  1008. raise ArgumentPreprocessingError(msg)
  1009. cb(option, val)
  1010. else:
  1011. i += 1
  1012. @contextlib.contextmanager
  1013. def fix_import_path(args):
  1014. """Prepare sys.path for running the linter checks.
  1015. Within this context, each of the given arguments is importable.
  1016. Paths are added to sys.path in corresponding order to the arguments.
  1017. We avoid adding duplicate directories to sys.path.
  1018. `sys.path` is reset to its original value upon exiting this context.
  1019. """
  1020. orig = list(sys.path)
  1021. changes = []
  1022. for arg in args:
  1023. path = _get_python_path(arg)
  1024. if path in changes:
  1025. continue
  1026. else:
  1027. changes.append(path)
  1028. sys.path[:] = changes + ["."] + sys.path
  1029. try:
  1030. yield
  1031. finally:
  1032. sys.path[:] = orig
  1033. class Run(object):
  1034. """helper class to use as main for pylint :
  1035. run(*sys.argv[1:])
  1036. """
  1037. LinterClass = PyLinter
  1038. option_groups = (
  1039. ('Commands', 'Options which are actually commands. Options in this \
  1040. group are mutually exclusive.'),
  1041. )
  1042. def __init__(self, args, reporter=None, exit=True):
  1043. self._rcfile = None
  1044. self._plugins = []
  1045. try:
  1046. preprocess_options(args, {
  1047. # option: (callback, takearg)
  1048. 'init-hook': (cb_init_hook, True),
  1049. 'rcfile': (self.cb_set_rcfile, True),
  1050. 'load-plugins': (self.cb_add_plugins, True),
  1051. })
  1052. except ArgumentPreprocessingError as ex:
  1053. print(ex, file=sys.stderr)
  1054. sys.exit(32)
  1055. self.linter = linter = self.LinterClass((
  1056. ('rcfile',
  1057. {'action' : 'callback', 'callback' : lambda *args: 1,
  1058. 'type': 'string', 'metavar': '<file>',
  1059. 'help' : 'Specify a configuration file.'}),
  1060. ('init-hook',
  1061. {'action' : 'callback', 'callback' : lambda *args: 1,
  1062. 'type' : 'string', 'metavar': '<code>',
  1063. 'level': 1,
  1064. 'help' : 'Python code to execute, usually for sys.path '
  1065. 'manipulation such as pygtk.require().'}),
  1066. ('help-msg',
  1067. {'action' : 'callback', 'type' : 'string', 'metavar': '<msg-id>',
  1068. 'callback' : self.cb_help_message,
  1069. 'group': 'Commands',
  1070. 'help' : 'Display a help message for the given message id and '
  1071. 'exit. The value may be a comma separated list of message ids.'}),
  1072. ('list-msgs',
  1073. {'action' : 'callback', 'metavar': '<msg-id>',
  1074. 'callback' : self.cb_list_messages,
  1075. 'group': 'Commands', 'level': 1,
  1076. 'help' : "Generate pylint's messages."}),
  1077. ('list-conf-levels',
  1078. {'action' : 'callback',
  1079. 'callback' : cb_list_confidence_levels,
  1080. 'group': 'Commands', 'level': 1,
  1081. 'help' : "Generate pylint's messages."}),
  1082. ('full-documentation',
  1083. {'action' : 'callback', 'metavar': '<msg-id>',
  1084. 'callback' : self.cb_full_documentation,
  1085. 'group': 'Commands', 'level': 1,
  1086. 'help' : "Generate pylint's full documentation."}),
  1087. ('generate-rcfile',
  1088. {'action' : 'callback', 'callback' : self.cb_generate_config,
  1089. 'group': 'Commands',
  1090. 'help' : 'Generate a sample configuration file according to '
  1091. 'the current configuration. You can put other options '
  1092. 'before this one to get them in the generated '
  1093. 'configuration.'}),
  1094. ('generate-man',
  1095. {'action' : 'callback', 'callback' : self.cb_generate_manpage,
  1096. 'group': 'Commands',
  1097. 'help' : "Generate pylint's man page.", 'hide': True}),
  1098. ('errors-only',
  1099. {'action' : 'callback', 'callback' : self.cb_error_mode,
  1100. 'short': 'E',
  1101. 'help' : 'In error mode, checkers without error messages are '
  1102. 'disabled and for others, only the ERROR messages are '
  1103. 'displayed, and no reports are done by default'''}),
  1104. ('py3k',
  1105. {'action' : 'callback', 'callback' : self.cb_python3_porting_mode,
  1106. 'help' : 'In Python 3 porting mode, all checkers will be '
  1107. 'disabled and only messages emitted by the porting '
  1108. 'checker will be displayed'}),
  1109. ), option_groups=self.option_groups, pylintrc=self._rcfile)
  1110. # register standard checkers
  1111. linter.load_default_plugins()
  1112. # load command line plugins
  1113. linter.load_plugin_modules(self._plugins)
  1114. # add some help section
  1115. linter.add_help_section('Environment variables', config.ENV_HELP, level=1)
  1116. # pylint: disable=bad-continuation
  1117. linter.add_help_section('Output',
  1118. 'Using the default text output, the message format is : \n'
  1119. ' \n'
  1120. ' MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE \n'
  1121. ' \n'
  1122. 'There are 5 kind of message types : \n'
  1123. ' * (C) convention, for programming standard violation \n'
  1124. ' * (R) refactor, for bad code smell \n'
  1125. ' * (W) warning, for python specific problems \n'
  1126. ' * (E) error, for probable bugs in the code \n'
  1127. ' * (F) fatal, if an error occurred which prevented pylint from doing further\n'
  1128. 'processing.\n'
  1129. , level=1)
  1130. linter.add_help_section('Output status code',
  1131. 'Pylint should leave with following status code: \n'
  1132. ' * 0 if everything went fine \n'
  1133. ' * 1 if a fatal message was issued \n'
  1134. ' * 2 if an error message was issued \n'
  1135. ' * 4 if a warning message was issued \n'
  1136. ' * 8 if a refactor message was issued \n'
  1137. ' * 16 if a convention message was issued \n'
  1138. ' * 32 on usage error \n'
  1139. ' \n'
  1140. 'status 1 to 16 will be bit-ORed so you can know which different categories has\n'
  1141. 'been issued by analysing pylint output status code\n',
  1142. level=1)
  1143. # read configuration
  1144. linter.disable('I')
  1145. linter.enable('c-extension-no-member')
  1146. linter.read_config_file()
  1147. config_parser = linter.cfgfile_parser
  1148. # run init hook, if present, before loading plugins
  1149. if config_parser.has_option('MASTER', 'init-hook'):
  1150. cb_init_hook('init-hook',
  1151. utils._unquote(config_parser.get('MASTER',
  1152. 'init-hook')))
  1153. # is there some additional plugins in the file configuration, in
  1154. if config_parser.has_option('MASTER', 'load-plugins'):
  1155. plugins = utils._splitstrip(
  1156. config_parser.get('MASTER', 'load-plugins'))
  1157. linter.load_plugin_modules(plugins)
  1158. # now we can load file config and command line, plugins (which can
  1159. # provide options) have been registered
  1160. linter.load_config_file()
  1161. if reporter:
  1162. # if a custom reporter is provided as argument, it may be overridden
  1163. # by file parameters, so re-set it here, but before command line
  1164. # parsing so it's still overrideable by command line option
  1165. linter.set_reporter(reporter)
  1166. try:
  1167. args = linter.load_command_line_configuration(args)
  1168. except SystemExit as exc:
  1169. if exc.code == 2: # bad options
  1170. exc.code = 32
  1171. raise
  1172. if not args:
  1173. print(linter.help())
  1174. sys.exit(32)
  1175. if linter.config.jobs < 0:
  1176. print("Jobs number (%d) should be greater than 0"
  1177. % linter.config.jobs, file=sys.stderr)
  1178. sys.exit(32)
  1179. if linter.config.jobs > 1 or linter.config.jobs == 0:
  1180. if multiprocessing is None:
  1181. print("Multiprocessing library is missing, "
  1182. "fallback to single process", file=sys.stderr)
  1183. linter.set_option("jobs", 1)
  1184. else:
  1185. if linter.config.jobs == 0:
  1186. linter.config.jobs = multiprocessing.cpu_count()
  1187. # insert current working directory to the python path to have a correct
  1188. # behaviour
  1189. with fix_import_path(args):
  1190. linter.check(args)
  1191. linter.generate_reports()
  1192. if exit:
  1193. sys.exit(self.linter.msg_status)
  1194. def cb_set_rcfile(self, name, value):
  1195. """callback for option preprocessing (i.e. before option parsing)"""
  1196. self._rcfile = value
  1197. def cb_add_plugins(self, name, value):
  1198. """callback for option preprocessing (i.e. before option parsing)"""
  1199. self._plugins.extend(utils._splitstrip(value))
  1200. def cb_error_mode(self, *args, **kwargs):
  1201. """error mode:
  1202. * disable all but error messages
  1203. * disable the 'miscellaneous' checker which can be safely deactivated in
  1204. debug
  1205. * disable reports
  1206. * do not save execution information
  1207. """
  1208. self.linter.error_mode()
  1209. def cb_generate_config(self, *args, **kwargs):
  1210. """optik callback for sample config file generation"""
  1211. self.linter.generate_config(skipsections=('COMMANDS',))
  1212. sys.exit(0)
  1213. def cb_generate_manpage(self, *args, **kwargs):
  1214. """optik callback for sample config file generation"""
  1215. from pylint import __pkginfo__
  1216. self.linter.generate_manpage(__pkginfo__)
  1217. sys.exit(0)
  1218. def cb_help_message(self, option, optname, value, parser):
  1219. """optik callback for printing some help about a particular message"""
  1220. self.linter.msgs_store.help_message(utils._splitstrip(value))
  1221. sys.exit(0)
  1222. def cb_full_documentation(self, option, optname, value, parser):
  1223. """optik callback for printing full documentation"""
  1224. self.linter.print_full_documentation()
  1225. sys.exit(0)
  1226. def cb_list_messages(self, option, optname, value, parser): # FIXME
  1227. """optik callback for printing available messages"""
  1228. self.linter.msgs_store.list_messages()
  1229. sys.exit(0)
  1230. def cb_python3_porting_mode(self, *args, **kwargs):
  1231. """Activate only the python3 porting checker."""
  1232. self.linter.python3_porting_mode()
  1233. def cb_list_confidence_levels(option, optname, value, parser):
  1234. for level in interfaces.CONFIDENCE_LEVELS:
  1235. print('%-18s: %s' % level)
  1236. sys.exit(0)
  1237. def cb_init_hook(optname, value):
  1238. """exec arbitrary code to set sys.path for instance"""
  1239. exec(value) # pylint: disable=exec-used
  1240. if __name__ == '__main__':
  1241. Run(sys.argv[1:])