Upgrade to Pro — share decks privately, control downloads, hide ads and more …

My First Linter (plugin)

Adam
May 15, 2014

My First Linter (plugin)

Lightning talk for the Python Meetup on making a linter plugin for PyLint and Flake8.

Adam

May 15, 2014
Tweet

More Decks by Adam

Other Decks in Technology

Transcript

  1. problem: pdb.set_trace() ๏ sometimes you are debugging ๏ and solve

    the problem! ๏ and forget to remove all of your debug code ๏ then code review ๏ or worse… git push heroku
  2. solution! ๏ ack --py "import pdb; pdb.set_trace()” ๏ fragile, prone

    to regex errors ๏ not really cool enough for a lightning talk ! ๏ custom linter! ๏ testable ๏ cool enough for a lightning talk
  3. what is a linter ๏ tells you that you are

    bad at your job ๏ stylistically (pep8) ๏ unused variables ๏ just plain incorrect syntax ๏ overly complicated code ๏ not a compiler ๏ tries to look at the code structure ๏ builds an AST
  4. what is an AST anyways? ๏ _ast.Call ๏ args ๏

    col_offset ๏ func ๏ keywords ๏ kwargs ๏ lineno ๏ starargs http://en.wikipedia.org/wiki/Abstract_syntax_tree
  5. linter plugins are hard ๏ What took me so long?

    ๏ bad (and hard to find) docs ๏ not on readthedocs ๏ lots about how to use, not how they work ๏ https://launchpad.net/pyflakes ๏ https://launchpad.net/pylint ๏ Flake8 extension framework is minimal and new-ish ๏ pep8-naming extension did good work to make this manageable #copypasta
  6. PyLint example class DFUChecker(BaseChecker):! ! __implements__ = IAstroidChecker! ! name

    = 'dont-fudge-up'! msgs = {! 'W4201': ('print at %s:%d', 'print', 'you left a print in your code'),! 'W4202': ('pdb imported %s:%d', 'import-pdb', 'pdb imported, probably not nee 'F4203': ('import debug at %s:%d', 'import-debug', 'import debug will be fata 'F4204': ('pdb.set_trace at %s:%d', 'pdb-set_trace', 'pdb.set_trace will be f }! ! # this is important so that your checker is executed before others! priority = -1! ! def visit_print(self, node):! self.add_message('W4201', node=node, args=snip)! ! def visit_callfunc(self, node):! if node.as_string() == 'pdb.set_trace()':! self.add_message('F4204', node=node, args=snip)! ! def visit_import(self, node):! if node.as_string() == 'import pdb':! self.add_message('W4202', node=node, args=snip)! elif node.as_string() == 'import debug':! self.add_message('F4203', node=node, args=snip)!
  7. Flake8 example class DebuggerCheck(DFUASTCheck):! ! cautioned_set_trace = 'D102'! forbidden_set_trace =

    'D502'! ! def visit_call(self, node, parrents):! func_name = getattr(node.func, 'attr', None) or \! ! ! ! getattr(node.func, 'id', None)! func_module = getattr(node.func, 'value', None)! ! if func_name == 'set_trace':! if func_module and func_module.id in ('pdb', 'ipdb'):! yield self.err(node, self.forbidden_set_trace)! else:! yield self.err(node, self.cautioned_set_trace)