Slide 1

Slide 1 text

Adam Hitchcock @northisup My First Linter (plugin)

Slide 2

Slide 2 text

or… how I got sick of crashing production with stupid mistakes

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

what is an AST anyways? ๏ _ast.Call ๏ args ๏ col_offset ๏ func ๏ keywords ๏ kwargs ๏ lineno ๏ starargs http://en.wikipedia.org/wiki/Abstract_syntax_tree

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Example!

Slide 9

Slide 9 text

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)!

Slide 10

Slide 10 text

“Dude, I usually use flake8”

Slide 11

Slide 11 text

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)

Slide 12

Slide 12 text

get it now ๏ github.com/NorthIsUp/dont-fudge-up ๏ pip install dont-fudge-up

Slide 13

Slide 13 text

psst, we’re hiring disqus.com/jobs If this was interesting to you... dev-ops, django, js, iOS