mirror of https://github.com/akelge/zsh
Added vim-flake8 (pyflakes+pep8+mccabe)
Updated vcs plugin
This commit is contained in:
parent
1b3b3cb2dc
commit
f4e66759ec
|
@ -8,5 +8,5 @@ ScriptID SourceID Filename
|
||||||
2324 9247 :AutoInstall: TextFormat
|
2324 9247 :AutoInstall: TextFormat
|
||||||
1658 17123 :AutoInstall: NERD_tree.vim
|
1658 17123 :AutoInstall: NERD_tree.vim
|
||||||
1218 14455 :AutoInstall: NERD_commenter.vim
|
1218 14455 :AutoInstall: NERD_commenter.vim
|
||||||
90 17031 :AutoInstall: vcscommand.vim
|
90 19809 :AutoInstall: vcscommand.vim
|
||||||
2896 11941 :AutoInstall: open_terminal.vim
|
2896 11941 :AutoInstall: open_terminal.vim
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from mccabe.mccabe import get_module_complexity
|
||||||
|
from pyflakes import checker, messages
|
||||||
|
import _ast
|
||||||
|
from pep8 import pep8 as p8
|
||||||
|
from pep8.autopep8 import fix_file as pep8_fix, fix_lines as pep8_fix_lines
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class Pep8Options():
|
||||||
|
verbose = 0
|
||||||
|
diff = False
|
||||||
|
in_place = True
|
||||||
|
recursive = False
|
||||||
|
pep8_passes = 100
|
||||||
|
max_line_length = 79
|
||||||
|
ignore = ''
|
||||||
|
select = ''
|
||||||
|
aggressive = False
|
||||||
|
|
||||||
|
|
||||||
|
class MccabeOptions():
|
||||||
|
complexity = 10
|
||||||
|
|
||||||
|
flake_code_mapping = {
|
||||||
|
'W402': (messages.UnusedImport,),
|
||||||
|
'W403': (messages.ImportShadowedByLoopVar,),
|
||||||
|
'W404': (messages.ImportStarUsed,),
|
||||||
|
'W405': (messages.LateFutureImport,),
|
||||||
|
'W801': (messages.RedefinedWhileUnused,
|
||||||
|
messages.RedefinedInListComp,),
|
||||||
|
'W802': (messages.UndefinedName,),
|
||||||
|
'W803': (messages.UndefinedExport,),
|
||||||
|
'W804': (messages.UndefinedLocal,
|
||||||
|
messages.UnusedVariable,),
|
||||||
|
'W805': (messages.DuplicateArgument,),
|
||||||
|
'W806': (messages.Redefined,),
|
||||||
|
}
|
||||||
|
|
||||||
|
flake_class_mapping = dict(
|
||||||
|
(k, c) for (c, v) in flake_code_mapping.items() for k in v)
|
||||||
|
|
||||||
|
|
||||||
|
def fix_file(filename):
|
||||||
|
pep8_fix(filename, Pep8Options)
|
||||||
|
|
||||||
|
|
||||||
|
def fix_lines(lines):
|
||||||
|
return pep8_fix_lines(lines, Pep8Options)
|
||||||
|
|
||||||
|
|
||||||
|
def run_checkers(filename, checkers, ignore):
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for c in checkers:
|
||||||
|
|
||||||
|
checker_fun = globals().get(c)
|
||||||
|
if not checker:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
for e in checker_fun(filename):
|
||||||
|
e.update(
|
||||||
|
col=e.get('col') or 0,
|
||||||
|
text="{0} [{1}]".format(
|
||||||
|
e.get('text', '').strip(
|
||||||
|
).replace("'", "\"").splitlines()[0],
|
||||||
|
c),
|
||||||
|
filename=os.path.normpath(filename),
|
||||||
|
type=e.get('type') or 'W',
|
||||||
|
bufnr=0,
|
||||||
|
)
|
||||||
|
result.append(e)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
result = filter(lambda e: _ignore_error(e, ignore), result)
|
||||||
|
return sorted(result, key=lambda x: x['lnum'])
|
||||||
|
|
||||||
|
|
||||||
|
def mccabe(filename):
|
||||||
|
return get_module_complexity(filename, min=MccabeOptions.complexity)
|
||||||
|
|
||||||
|
|
||||||
|
def pep8(filename):
|
||||||
|
style = PEP8 or _init_pep8()
|
||||||
|
return style.input_file(filename)
|
||||||
|
|
||||||
|
|
||||||
|
def pyflakes(filename):
|
||||||
|
codeString = file(filename, 'U').read() + '\n'
|
||||||
|
errors = []
|
||||||
|
try:
|
||||||
|
tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST)
|
||||||
|
except SyntaxError as e:
|
||||||
|
errors.append(dict(
|
||||||
|
lnum=e.lineno or 0,
|
||||||
|
col=e.offset or 0,
|
||||||
|
text=getattr(e, 'msg', None) or str(e),
|
||||||
|
type='E'
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
w = checker.Checker(tree, filename)
|
||||||
|
w.messages.sort(lambda a, b: cmp(a.lineno, b.lineno))
|
||||||
|
for w in w.messages:
|
||||||
|
errors.append(dict(
|
||||||
|
lnum=w.lineno,
|
||||||
|
col=0,
|
||||||
|
text=u'{0} {1}'.format(
|
||||||
|
flake_class_mapping.get(w.__class__, ''),
|
||||||
|
w.message % w.message_args),
|
||||||
|
type='E'
|
||||||
|
))
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
PEP8 = None
|
||||||
|
|
||||||
|
|
||||||
|
def _init_pep8():
|
||||||
|
global PEP8
|
||||||
|
|
||||||
|
class _PEP8Report(p8.BaseReport):
|
||||||
|
|
||||||
|
def init_file(self, filename, lines, expected, line_offset):
|
||||||
|
super(_PEP8Report, self).init_file(
|
||||||
|
filename, lines, expected, line_offset)
|
||||||
|
self.errors = []
|
||||||
|
|
||||||
|
def error(self, line_number, offset, text, check):
|
||||||
|
code = super(_PEP8Report, self).error(
|
||||||
|
line_number, offset, text, check)
|
||||||
|
|
||||||
|
self.errors.append(dict(
|
||||||
|
text=text,
|
||||||
|
type=code,
|
||||||
|
col=offset + 1,
|
||||||
|
lnum=line_number,
|
||||||
|
))
|
||||||
|
|
||||||
|
def get_file_results(self):
|
||||||
|
return self.errors
|
||||||
|
|
||||||
|
PEP8 = p8.StyleGuide(reporter=_PEP8Report)
|
||||||
|
return PEP8
|
||||||
|
|
||||||
|
|
||||||
|
def _ignore_error(e, ignore):
|
||||||
|
for i in ignore:
|
||||||
|
if e['text'].startswith(i):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
for r in run_checkers(
|
||||||
|
'/home/andrew/devel/vim/bundle/flake8-vim/ftplugin/python/flake8.py',
|
||||||
|
checkers=['mccabe', 'pyflakes', 'pep8'], ignore=[]):
|
||||||
|
print r
|
Binary file not shown.
|
@ -0,0 +1,188 @@
|
||||||
|
" Check python support
|
||||||
|
if !has('python')
|
||||||
|
echo "Error: PyFlake.vim required vim compiled with +python."
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !exists('g:PyFlakeRangeCommand')
|
||||||
|
let g:PyFlakeRangeCommand = 'Q'
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !exists('b:PyFlake_initialized')
|
||||||
|
let b:PyFlake_initialized = 1
|
||||||
|
|
||||||
|
au BufWritePost <buffer> call flake8#on_write()
|
||||||
|
au CursorHold <buffer> call flake8#get_message()
|
||||||
|
au CursorMoved <buffer> call flake8#get_message()
|
||||||
|
|
||||||
|
" Commands
|
||||||
|
command! -buffer PyFlakeToggle :let b:PyFlake_disabled = exists('b:PyFlake_disabled') ? b:PyFlake_disabled ? 0 : 1 : 1
|
||||||
|
command! -buffer PyFlake :call flake8#run()
|
||||||
|
command! -buffer -range=% PyFlakeAuto :call flake8#auto(<line1>,<line2>)
|
||||||
|
|
||||||
|
" Keymaps
|
||||||
|
if g:PyFlakeRangeCommand != ''
|
||||||
|
exec 'vnoremap <buffer> <silent> ' . g:PyFlakeRangeCommand . ' :PyFlakeAuto<CR>'
|
||||||
|
endif
|
||||||
|
|
||||||
|
let b:showing_message = 0
|
||||||
|
|
||||||
|
" Signs definition
|
||||||
|
sign define W text=WW texthl=Todo
|
||||||
|
sign define C text=CC texthl=Comment
|
||||||
|
sign define R text=RR texthl=Visual
|
||||||
|
sign define E text=EE texthl=Error
|
||||||
|
endif
|
||||||
|
|
||||||
|
"Check for flake8 plugin is loaded
|
||||||
|
if exists("g:PyFlakeDirectory")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !exists('g:PyFlakeOnWrite')
|
||||||
|
let g:PyFlakeOnWrite = 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Init variables
|
||||||
|
let g:PyFlakeDirectory = expand('<sfile>:p:h')
|
||||||
|
|
||||||
|
if !exists('g:PyFlakeCheckers')
|
||||||
|
let g:PyFlakeCheckers = 'pep8,mccabe,pyflakes'
|
||||||
|
endif
|
||||||
|
if !exists('g:PyFlakeDefaultComplexity')
|
||||||
|
let g:PyFlakeDefaultComplexity=10
|
||||||
|
endif
|
||||||
|
if !exists('g:PyFlakeDisabledMessages')
|
||||||
|
let g:PyFlakeDisabledMessages = 'E501'
|
||||||
|
endif
|
||||||
|
if !exists('g:PyFlakeCWindow')
|
||||||
|
let g:PyFlakeCWindow = 6
|
||||||
|
endif
|
||||||
|
if !exists('g:PyFlakeSigns')
|
||||||
|
let g:PyFlakeSigns = 1
|
||||||
|
endif
|
||||||
|
if !exists('g:PyFlakeMaxLineLength')
|
||||||
|
let g:PyFlakeMaxLineLength = 100
|
||||||
|
endif
|
||||||
|
|
||||||
|
python << EOF
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import vim
|
||||||
|
|
||||||
|
sys.path.insert(0, vim.eval("g:PyFlakeDirectory"))
|
||||||
|
from flake8 import run_checkers, fix_lines, Pep8Options, MccabeOptions
|
||||||
|
|
||||||
|
def flake8_check():
|
||||||
|
checkers=vim.eval('g:PyFlakeCheckers').split(',')
|
||||||
|
ignore=vim.eval('g:PyFlakeDisabledMessages').split(',')
|
||||||
|
MccabeOptions.complexity=int(vim.eval('g:PyFlakeDefaultComplexity'))
|
||||||
|
Pep8Options.max_line_length=int(vim.eval('g:PyFlakeMaxLineLength'))
|
||||||
|
filename=vim.current.buffer.name
|
||||||
|
parse_result(run_checkers(filename, checkers, ignore))
|
||||||
|
|
||||||
|
def parse_result(result):
|
||||||
|
vim.command('let g:qf_list = {}'.format(json.dumps(result, ensure_ascii=False)))
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
function! flake8#on_write()
|
||||||
|
if !g:PyFlakeOnWrite || exists("b:PyFlake_disabled") && b:PyFlake_disabled
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
call flake8#check()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! flake8#run()
|
||||||
|
if &modifiable && &modified
|
||||||
|
write
|
||||||
|
endif
|
||||||
|
call flake8#check()
|
||||||
|
endfun
|
||||||
|
|
||||||
|
function! flake8#check()
|
||||||
|
py flake8_check()
|
||||||
|
let s:matchDict = {}
|
||||||
|
for err in g:qf_list
|
||||||
|
let s:matchDict[err.lnum] = err.text
|
||||||
|
endfor
|
||||||
|
call setqflist(g:qf_list, 'r')
|
||||||
|
|
||||||
|
" Place signs
|
||||||
|
if g:PyFlakeSigns
|
||||||
|
call flake8#place_signs()
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Open cwindow
|
||||||
|
if g:PyFlakeCWindow
|
||||||
|
cclose
|
||||||
|
if len(g:qf_list)
|
||||||
|
let l:winsize = len(g:qf_list) > g:PyFlakeCWindow ? g:PyFlakeCWindow : len(g:qf_list)
|
||||||
|
exec l:winsize . 'cwindow'
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! flake8#auto(l1, l2) "{{{
|
||||||
|
cclose
|
||||||
|
sign unplace *
|
||||||
|
let s:matchDict = {}
|
||||||
|
call setqflist([])
|
||||||
|
|
||||||
|
python << EOF
|
||||||
|
start, end = int(vim.eval('a:l1'))-1, int(vim.eval('a:l2'))
|
||||||
|
enc = vim.eval('&enc')
|
||||||
|
lines = fix_lines(vim.current.buffer[start:end]).splitlines()
|
||||||
|
res = [ln.encode(enc, 'replace') for ln in lines]
|
||||||
|
vim.current.buffer[start:end] = res
|
||||||
|
EOF
|
||||||
|
endfunction "}}}
|
||||||
|
|
||||||
|
function! flake8#place_signs()
|
||||||
|
"first remove all sings
|
||||||
|
sign unplace *
|
||||||
|
|
||||||
|
"now we place one sign for every quickfix line
|
||||||
|
let l:id = 1
|
||||||
|
for item in getqflist()
|
||||||
|
execute(':sign place '.l:id.' name='.l:item.type.' line='.l:item.lnum.' buffer='.l:item.bufnr)
|
||||||
|
let l:id = l:id + 1
|
||||||
|
endfor
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" keep track of whether or not we are showing a message
|
||||||
|
" WideMsg() prints [long] message up to (&columns-1) length
|
||||||
|
" guaranteed without "Press Enter" prompt.
|
||||||
|
function! flake8#wide_msg(msg)
|
||||||
|
let x=&ruler | let y=&showcmd
|
||||||
|
set noruler noshowcmd
|
||||||
|
redraw
|
||||||
|
echo strpart(a:msg, 0, &columns-1)
|
||||||
|
let &ruler=x | let &showcmd=y
|
||||||
|
endfun
|
||||||
|
|
||||||
|
|
||||||
|
function! flake8#get_message()
|
||||||
|
let s:cursorPos = getpos(".")
|
||||||
|
|
||||||
|
" Bail if RunPyflakes hasn't been called yet.
|
||||||
|
if !exists('s:matchDict')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" if there's a message for the line the cursor is currently on, echo
|
||||||
|
" it to the console
|
||||||
|
if has_key(s:matchDict, s:cursorPos[1])
|
||||||
|
let s:pyflakesMatch = get(s:matchDict, s:cursorPos[1])
|
||||||
|
call flake8#wide_msg(s:pyflakesMatch)
|
||||||
|
let b:showing_message = 1
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" otherwise, if we're showing a message, clear it
|
||||||
|
if b:showing_message == 1
|
||||||
|
echo
|
||||||
|
let b:showing_message = 0
|
||||||
|
endif
|
||||||
|
endfunction
|
|
@ -0,0 +1,293 @@
|
||||||
|
""" Meager code path measurement tool.
|
||||||
|
Ned Batchelder
|
||||||
|
http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html
|
||||||
|
MIT License.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from compiler import parse # NOQA
|
||||||
|
iter_child_nodes = None # NOQA
|
||||||
|
except ImportError:
|
||||||
|
from ast import parse, iter_child_nodes # NOQA
|
||||||
|
|
||||||
|
import optparse
|
||||||
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
WARNING_CODE = "W901"
|
||||||
|
|
||||||
|
|
||||||
|
class ASTVisitor:
|
||||||
|
|
||||||
|
VERBOSE = 0
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.node = None
|
||||||
|
self._cache = {}
|
||||||
|
|
||||||
|
def default(self, node, *args):
|
||||||
|
if hasattr(node, 'getChildNodes'):
|
||||||
|
children = node.getChildNodes()
|
||||||
|
else:
|
||||||
|
children = iter_child_nodes(node)
|
||||||
|
|
||||||
|
for child in children:
|
||||||
|
self.dispatch(child, *args)
|
||||||
|
|
||||||
|
def dispatch(self, node, *args):
|
||||||
|
self.node = node
|
||||||
|
klass = node.__class__
|
||||||
|
meth = self._cache.get(klass)
|
||||||
|
if meth is None:
|
||||||
|
className = klass.__name__
|
||||||
|
meth = getattr(self.visitor, 'visit' + className, self.default)
|
||||||
|
self._cache[klass] = meth
|
||||||
|
|
||||||
|
return meth(node, *args)
|
||||||
|
|
||||||
|
def preorder(self, tree, visitor, *args):
|
||||||
|
"""Do preorder walk of tree using visitor"""
|
||||||
|
self.visitor = visitor
|
||||||
|
visitor.visit = self.dispatch
|
||||||
|
self.dispatch(tree, *args) # XXX *args make sense?
|
||||||
|
|
||||||
|
|
||||||
|
class PathNode:
|
||||||
|
def __init__(self, name, look="circle"):
|
||||||
|
self.name = name
|
||||||
|
self.look = look
|
||||||
|
|
||||||
|
def to_dot(self):
|
||||||
|
print('node [shape=%s,label="%s"] %d;' % \
|
||||||
|
(self.look, self.name, self.dot_id()))
|
||||||
|
|
||||||
|
def dot_id(self):
|
||||||
|
return id(self)
|
||||||
|
|
||||||
|
|
||||||
|
class PathGraph:
|
||||||
|
def __init__(self, name, entity, lineno):
|
||||||
|
self.name = name
|
||||||
|
self.entity = entity
|
||||||
|
self.lineno = lineno
|
||||||
|
self.nodes = defaultdict(list)
|
||||||
|
|
||||||
|
def connect(self, n1, n2):
|
||||||
|
self.nodes[n1].append(n2)
|
||||||
|
|
||||||
|
def to_dot(self):
|
||||||
|
print('subgraph {')
|
||||||
|
for node in self.nodes:
|
||||||
|
node.to_dot()
|
||||||
|
for node, nexts in self.nodes.items():
|
||||||
|
for next in nexts:
|
||||||
|
print('%s -- %s;' % (node.dot_id(), next.dot_id()))
|
||||||
|
print('}')
|
||||||
|
|
||||||
|
def complexity(self):
|
||||||
|
""" Return the McCabe complexity for the graph.
|
||||||
|
V-E+2
|
||||||
|
"""
|
||||||
|
num_edges = sum([len(n) for n in self.nodes.values()])
|
||||||
|
num_nodes = len(self.nodes)
|
||||||
|
return num_edges - num_nodes + 2
|
||||||
|
|
||||||
|
|
||||||
|
class PathGraphingAstVisitor(ASTVisitor):
|
||||||
|
""" A visitor for a parsed Abstract Syntax Tree which finds executable
|
||||||
|
statements.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
ASTVisitor.__init__(self)
|
||||||
|
self.classname = ""
|
||||||
|
self.graphs = {}
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.graph = None
|
||||||
|
self.tail = None
|
||||||
|
|
||||||
|
def visitFunction(self, node):
|
||||||
|
|
||||||
|
if self.classname:
|
||||||
|
entity = '%s%s' % (self.classname, node.name)
|
||||||
|
else:
|
||||||
|
entity = node.name
|
||||||
|
|
||||||
|
name = '%d:1: %r' % (node.lineno, entity)
|
||||||
|
|
||||||
|
if self.graph is not None:
|
||||||
|
# closure
|
||||||
|
pathnode = self.appendPathNode(name)
|
||||||
|
self.tail = pathnode
|
||||||
|
self.default(node)
|
||||||
|
bottom = PathNode("", look='point')
|
||||||
|
self.graph.connect(self.tail, bottom)
|
||||||
|
self.graph.connect(pathnode, bottom)
|
||||||
|
self.tail = bottom
|
||||||
|
else:
|
||||||
|
self.graph = PathGraph(name, entity, node.lineno)
|
||||||
|
pathnode = PathNode(name)
|
||||||
|
self.tail = pathnode
|
||||||
|
self.default(node)
|
||||||
|
self.graphs["%s%s" % (self.classname, node.name)] = self.graph
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
visitFunctionDef = visitFunction
|
||||||
|
|
||||||
|
def visitClass(self, node):
|
||||||
|
old_classname = self.classname
|
||||||
|
self.classname += node.name + "."
|
||||||
|
self.default(node)
|
||||||
|
self.classname = old_classname
|
||||||
|
|
||||||
|
def appendPathNode(self, name):
|
||||||
|
if not self.tail:
|
||||||
|
return
|
||||||
|
pathnode = PathNode(name)
|
||||||
|
self.graph.connect(self.tail, pathnode)
|
||||||
|
self.tail = pathnode
|
||||||
|
return pathnode
|
||||||
|
|
||||||
|
def visitSimpleStatement(self, node):
|
||||||
|
if node.lineno is None:
|
||||||
|
lineno = 0
|
||||||
|
else:
|
||||||
|
lineno = node.lineno
|
||||||
|
name = "Stmt %d" % lineno
|
||||||
|
self.appendPathNode(name)
|
||||||
|
|
||||||
|
visitAssert = visitAssign = visitAssTuple = visitPrint = \
|
||||||
|
visitPrintnl = visitRaise = visitSubscript = visitDecorators = \
|
||||||
|
visitPass = visitDiscard = visitGlobal = visitReturn = \
|
||||||
|
visitSimpleStatement
|
||||||
|
|
||||||
|
def visitLoop(self, node):
|
||||||
|
name = "Loop %d" % node.lineno
|
||||||
|
|
||||||
|
if self.graph is None:
|
||||||
|
# global loop
|
||||||
|
self.graph = PathGraph(name, name, node.lineno)
|
||||||
|
pathnode = PathNode(name)
|
||||||
|
self.tail = pathnode
|
||||||
|
self.default(node)
|
||||||
|
self.graphs["%s%s" % (self.classname, name)] = self.graph
|
||||||
|
self.reset()
|
||||||
|
else:
|
||||||
|
pathnode = self.appendPathNode(name)
|
||||||
|
self.tail = pathnode
|
||||||
|
self.default(node.body)
|
||||||
|
bottom = PathNode("", look='point')
|
||||||
|
self.graph.connect(self.tail, bottom)
|
||||||
|
self.graph.connect(pathnode, bottom)
|
||||||
|
self.tail = bottom
|
||||||
|
|
||||||
|
# TODO: else clause in node.else_
|
||||||
|
|
||||||
|
visitFor = visitWhile = visitLoop
|
||||||
|
|
||||||
|
def visitIf(self, node):
|
||||||
|
name = "If %d" % node.lineno
|
||||||
|
pathnode = self.appendPathNode(name)
|
||||||
|
if not pathnode:
|
||||||
|
return # TODO: figure out what to do with if's outside def's.
|
||||||
|
loose_ends = []
|
||||||
|
for t, n in node.tests:
|
||||||
|
self.tail = pathnode
|
||||||
|
self.default(n)
|
||||||
|
loose_ends.append(self.tail)
|
||||||
|
if node.else_:
|
||||||
|
self.tail = pathnode
|
||||||
|
self.default(node.else_)
|
||||||
|
loose_ends.append(self.tail)
|
||||||
|
else:
|
||||||
|
loose_ends.append(pathnode)
|
||||||
|
bottom = PathNode("", look='point')
|
||||||
|
for le in loose_ends:
|
||||||
|
self.graph.connect(le, bottom)
|
||||||
|
self.tail = bottom
|
||||||
|
|
||||||
|
# TODO: visitTryExcept
|
||||||
|
# TODO: visitTryFinally
|
||||||
|
# TODO: visitWith
|
||||||
|
|
||||||
|
# XXX todo: determine which ones can add to the complexity
|
||||||
|
# py2
|
||||||
|
# TODO: visitStmt
|
||||||
|
# TODO: visitAssName
|
||||||
|
# TODO: visitCallFunc
|
||||||
|
# TODO: visitConst
|
||||||
|
|
||||||
|
# py3
|
||||||
|
# TODO: visitStore
|
||||||
|
# TODO: visitCall
|
||||||
|
# TODO: visitLoad
|
||||||
|
# TODO: visitNum
|
||||||
|
# TODO: visitarguments
|
||||||
|
# TODO: visitExpr
|
||||||
|
|
||||||
|
|
||||||
|
def get_code_complexity(code, min=7, filename='stdin'):
|
||||||
|
complex = []
|
||||||
|
try:
|
||||||
|
ast = parse(code)
|
||||||
|
except AttributeError:
|
||||||
|
e = sys.exc_info()[1]
|
||||||
|
sys.stderr.write("Unable to parse %s: %s\n" % (filename, e))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
visitor = PathGraphingAstVisitor()
|
||||||
|
visitor.preorder(ast, visitor)
|
||||||
|
for graph in visitor.graphs.values():
|
||||||
|
if graph is None:
|
||||||
|
# ?
|
||||||
|
continue
|
||||||
|
if graph.complexity() >= min:
|
||||||
|
complex.append(dict(
|
||||||
|
type = 'W',
|
||||||
|
lnum = graph.lineno,
|
||||||
|
text = '%s %r is too complex (%d)' % (
|
||||||
|
WARNING_CODE,
|
||||||
|
graph.entity,
|
||||||
|
graph.complexity(),
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
return complex
|
||||||
|
|
||||||
|
|
||||||
|
def get_module_complexity(module_path, min=7):
|
||||||
|
"""Returns the complexity of a module"""
|
||||||
|
code = open(module_path, "rU").read() + '\n\n'
|
||||||
|
return get_code_complexity(code, min, filename=module_path)
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
opar = optparse.OptionParser()
|
||||||
|
opar.add_option("-d", "--dot", dest="dot",
|
||||||
|
help="output a graphviz dot file", action="store_true")
|
||||||
|
opar.add_option("-m", "--min", dest="min",
|
||||||
|
help="minimum complexity for output", type="int",
|
||||||
|
default=2)
|
||||||
|
|
||||||
|
options, args = opar.parse_args(argv)
|
||||||
|
|
||||||
|
text = open(args[0], "rU").read() + '\n\n'
|
||||||
|
ast = parse(text)
|
||||||
|
visitor = PathGraphingAstVisitor()
|
||||||
|
visitor.preorder(ast, visitor)
|
||||||
|
|
||||||
|
if options.dot:
|
||||||
|
print('graph {')
|
||||||
|
for graph in visitor.graphs.values():
|
||||||
|
if graph.complexity() >= options.min:
|
||||||
|
graph.to_dot()
|
||||||
|
print('}')
|
||||||
|
else:
|
||||||
|
for graph in visitor.graphs.values():
|
||||||
|
if graph.complexity() >= options.min:
|
||||||
|
print(graph.name, graph.complexity())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main(sys.argv[1:])
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
__version__ = '0.6.1'
|
|
@ -0,0 +1,130 @@
|
||||||
|
"""
|
||||||
|
API for the command-line I{pyflakes} tool.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import _ast
|
||||||
|
|
||||||
|
from pyflakes import checker
|
||||||
|
from pyflakes import reporter as modReporter
|
||||||
|
|
||||||
|
__all__ = ['check', 'checkPath', 'checkRecursive', 'iterSourceCode', 'main']
|
||||||
|
|
||||||
|
|
||||||
|
def check(codeString, filename, reporter=None):
|
||||||
|
"""
|
||||||
|
Check the Python source given by C{codeString} for flakes.
|
||||||
|
|
||||||
|
@param codeString: The Python source to check.
|
||||||
|
@type codeString: C{str}
|
||||||
|
|
||||||
|
@param filename: The name of the file the source came from, used to report
|
||||||
|
errors.
|
||||||
|
@type filename: C{str}
|
||||||
|
|
||||||
|
@param reporter: A L{Reporter} instance, where errors and warnings will be
|
||||||
|
reported.
|
||||||
|
|
||||||
|
@return: The number of warnings emitted.
|
||||||
|
@rtype: C{int}
|
||||||
|
"""
|
||||||
|
if reporter is None:
|
||||||
|
reporter = modReporter._makeDefaultReporter()
|
||||||
|
# First, compile into an AST and handle syntax errors.
|
||||||
|
try:
|
||||||
|
tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST)
|
||||||
|
except SyntaxError:
|
||||||
|
value = sys.exc_info()[1]
|
||||||
|
msg = value.args[0]
|
||||||
|
|
||||||
|
(lineno, offset, text) = value.lineno, value.offset, value.text
|
||||||
|
|
||||||
|
# If there's an encoding problem with the file, the text is None.
|
||||||
|
if text is None:
|
||||||
|
# Avoid using msg, since for the only known case, it contains a
|
||||||
|
# bogus message that claims the encoding the file declared was
|
||||||
|
# unknown.
|
||||||
|
reporter.unexpectedError(filename, 'problem decoding source')
|
||||||
|
else:
|
||||||
|
reporter.syntaxError(filename, msg, lineno, offset, text)
|
||||||
|
return 1
|
||||||
|
except Exception:
|
||||||
|
reporter.unexpectedError(filename, 'problem decoding source')
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
# Okay, it's syntactically valid. Now check it.
|
||||||
|
w = checker.Checker(tree, filename)
|
||||||
|
w.messages.sort(key=lambda m: m.lineno)
|
||||||
|
for warning in w.messages:
|
||||||
|
reporter.flake(warning)
|
||||||
|
return len(w.messages)
|
||||||
|
|
||||||
|
|
||||||
|
def checkPath(filename, reporter=None):
|
||||||
|
"""
|
||||||
|
Check the given path, printing out any warnings detected.
|
||||||
|
|
||||||
|
@param reporter: A L{Reporter} instance, where errors and warnings will be
|
||||||
|
reported.
|
||||||
|
|
||||||
|
@return: the number of warnings printed
|
||||||
|
"""
|
||||||
|
if reporter is None:
|
||||||
|
reporter = modReporter._makeDefaultReporter()
|
||||||
|
try:
|
||||||
|
f = open(filename, 'U')
|
||||||
|
try:
|
||||||
|
return check(f.read() + '\n', filename, reporter)
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
except UnicodeError:
|
||||||
|
reporter.unexpectedError(filename, 'problem decoding source')
|
||||||
|
except IOError:
|
||||||
|
msg = sys.exc_info()[1]
|
||||||
|
reporter.unexpectedError(filename, msg.args[1])
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def iterSourceCode(paths):
|
||||||
|
"""
|
||||||
|
Iterate over all Python source files in C{paths}.
|
||||||
|
|
||||||
|
@param paths: A list of paths. Directories will be recursed into and
|
||||||
|
any .py files found will be yielded. Any non-directories will be
|
||||||
|
yielded as-is.
|
||||||
|
"""
|
||||||
|
for path in paths:
|
||||||
|
if os.path.isdir(path):
|
||||||
|
for dirpath, dirnames, filenames in os.walk(path):
|
||||||
|
for filename in filenames:
|
||||||
|
if filename.endswith('.py'):
|
||||||
|
yield os.path.join(dirpath, filename)
|
||||||
|
else:
|
||||||
|
yield path
|
||||||
|
|
||||||
|
|
||||||
|
def checkRecursive(paths, reporter):
|
||||||
|
"""
|
||||||
|
Recursively check all source files in C{paths}.
|
||||||
|
|
||||||
|
@param paths: A list of paths to Python source files and directories
|
||||||
|
containing Python source files.
|
||||||
|
@param reporter: A L{Reporter} where all of the warnings and errors
|
||||||
|
will be reported to.
|
||||||
|
@return: The number of warnings found.
|
||||||
|
"""
|
||||||
|
warnings = 0
|
||||||
|
for sourcePath in iterSourceCode(paths):
|
||||||
|
warnings += checkPath(sourcePath, reporter)
|
||||||
|
return warnings
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = sys.argv[1:]
|
||||||
|
reporter = modReporter._makeDefaultReporter()
|
||||||
|
if args:
|
||||||
|
warnings = checkRecursive(args, reporter)
|
||||||
|
else:
|
||||||
|
warnings = check(sys.stdin.read(), '<stdin>', reporter)
|
||||||
|
raise SystemExit(warnings > 0)
|
|
@ -0,0 +1,723 @@
|
||||||
|
# -*- test-case-name: pyflakes -*-
|
||||||
|
# (c) 2005-2010 Divmod, Inc.
|
||||||
|
# See LICENSE file for details
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
try:
|
||||||
|
import builtins
|
||||||
|
PY2 = False
|
||||||
|
except ImportError:
|
||||||
|
import __builtin__ as builtins
|
||||||
|
PY2 = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
import ast
|
||||||
|
iter_child_nodes = ast.iter_child_nodes
|
||||||
|
except (ImportError, AttributeError): # Python 2.5
|
||||||
|
import _ast as ast
|
||||||
|
|
||||||
|
def iter_child_nodes(node, astcls=ast.AST):
|
||||||
|
"""
|
||||||
|
Yield all direct child nodes of *node*, that is, all fields that are nodes
|
||||||
|
and all items of fields that are lists of nodes.
|
||||||
|
"""
|
||||||
|
for name in node._fields:
|
||||||
|
field = getattr(node, name, None)
|
||||||
|
if isinstance(field, astcls):
|
||||||
|
yield field
|
||||||
|
elif isinstance(field, list):
|
||||||
|
for item in field:
|
||||||
|
yield item
|
||||||
|
# Python >= 3.3 uses ast.Try instead of (ast.TryExcept + ast.TryFinally)
|
||||||
|
if hasattr(ast, 'Try'):
|
||||||
|
ast_TryExcept = ast.Try
|
||||||
|
ast_TryFinally = ()
|
||||||
|
else:
|
||||||
|
ast_TryExcept = ast.TryExcept
|
||||||
|
ast_TryFinally = ast.TryFinally
|
||||||
|
|
||||||
|
from pyflakes import messages
|
||||||
|
|
||||||
|
|
||||||
|
class Binding(object):
|
||||||
|
"""
|
||||||
|
Represents the binding of a value to a name.
|
||||||
|
|
||||||
|
The checker uses this to keep track of which names have been bound and
|
||||||
|
which names have not. See L{Assignment} for a special type of binding that
|
||||||
|
is checked with stricter rules.
|
||||||
|
|
||||||
|
@ivar used: pair of (L{Scope}, line-number) indicating the scope and
|
||||||
|
line number that this binding was last used
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, source):
|
||||||
|
self.name = name
|
||||||
|
self.source = source
|
||||||
|
self.used = False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__,
|
||||||
|
self.name,
|
||||||
|
self.source.lineno,
|
||||||
|
id(self))
|
||||||
|
|
||||||
|
|
||||||
|
class UnBinding(Binding):
|
||||||
|
"""Created by the 'del' operator."""
|
||||||
|
|
||||||
|
|
||||||
|
class Importation(Binding):
|
||||||
|
"""
|
||||||
|
A binding created by an import statement.
|
||||||
|
|
||||||
|
@ivar fullName: The complete name given to the import statement,
|
||||||
|
possibly including multiple dotted components.
|
||||||
|
@type fullName: C{str}
|
||||||
|
"""
|
||||||
|
def __init__(self, name, source):
|
||||||
|
self.fullName = name
|
||||||
|
name = name.split('.')[0]
|
||||||
|
super(Importation, self).__init__(name, source)
|
||||||
|
|
||||||
|
|
||||||
|
class Argument(Binding):
|
||||||
|
"""
|
||||||
|
Represents binding a name as an argument.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Definition(Binding):
|
||||||
|
"""
|
||||||
|
A binding that defines a function or a class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Assignment(Binding):
|
||||||
|
"""
|
||||||
|
Represents binding a name with an explicit assignment.
|
||||||
|
|
||||||
|
The checker will raise warnings for any Assignment that isn't used. Also,
|
||||||
|
the checker does not consider assignments in tuple/list unpacking to be
|
||||||
|
Assignments, rather it treats them as simple Bindings.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionDefinition(Definition):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ClassDefinition(Definition):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ExportBinding(Binding):
|
||||||
|
"""
|
||||||
|
A binding created by an C{__all__} assignment. If the names in the list
|
||||||
|
can be determined statically, they will be treated as names for export and
|
||||||
|
additional checking applied to them.
|
||||||
|
|
||||||
|
The only C{__all__} assignment that can be recognized is one which takes
|
||||||
|
the value of a literal list containing literal strings. For example::
|
||||||
|
|
||||||
|
__all__ = ["foo", "bar"]
|
||||||
|
|
||||||
|
Names which are imported and not otherwise used but appear in the value of
|
||||||
|
C{__all__} will not have an unused import warning reported for them.
|
||||||
|
"""
|
||||||
|
def names(self):
|
||||||
|
"""
|
||||||
|
Return a list of the names referenced by this binding.
|
||||||
|
"""
|
||||||
|
names = []
|
||||||
|
if isinstance(self.source, ast.List):
|
||||||
|
for node in self.source.elts:
|
||||||
|
if isinstance(node, ast.Str):
|
||||||
|
names.append(node.s)
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
class Scope(dict):
|
||||||
|
importStarred = False # set to True when import * is found
|
||||||
|
usesLocals = False
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self))
|
||||||
|
|
||||||
|
|
||||||
|
class ClassScope(Scope):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionScope(Scope):
|
||||||
|
"""
|
||||||
|
I represent a name scope for a function.
|
||||||
|
|
||||||
|
@ivar globals: Names declared 'global' in this function.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
super(FunctionScope, self).__init__()
|
||||||
|
self.globals = {}
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleScope(Scope):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Globally defined names which are not attributes of the builtins module, or
|
||||||
|
# are only present on some platforms.
|
||||||
|
_MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError']
|
||||||
|
|
||||||
|
|
||||||
|
def getNodeName(node):
|
||||||
|
# Returns node.id, or node.name, or None
|
||||||
|
if hasattr(node, 'id'): # One of the many nodes with an id
|
||||||
|
return node.id
|
||||||
|
if hasattr(node, 'name'): # a ExceptHandler node
|
||||||
|
return node.name
|
||||||
|
|
||||||
|
|
||||||
|
class Checker(object):
|
||||||
|
"""
|
||||||
|
I check the cleanliness and sanity of Python code.
|
||||||
|
|
||||||
|
@ivar _deferredFunctions: Tracking list used by L{deferFunction}. Elements
|
||||||
|
of the list are two-tuples. The first element is the callable passed
|
||||||
|
to L{deferFunction}. The second element is a copy of the scope stack
|
||||||
|
at the time L{deferFunction} was called.
|
||||||
|
|
||||||
|
@ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for
|
||||||
|
callables which are deferred assignment checks.
|
||||||
|
"""
|
||||||
|
|
||||||
|
nodeDepth = 0
|
||||||
|
traceTree = False
|
||||||
|
builtIns = set(dir(builtins)) | set(_MAGIC_GLOBALS)
|
||||||
|
|
||||||
|
def __init__(self, tree, filename='(none)', builtins=None):
|
||||||
|
self._deferredFunctions = []
|
||||||
|
self._deferredAssignments = []
|
||||||
|
self.deadScopes = []
|
||||||
|
self.messages = []
|
||||||
|
self.filename = filename
|
||||||
|
if builtins:
|
||||||
|
self.builtIns = self.builtIns.union(builtins)
|
||||||
|
self.scopeStack = [ModuleScope()]
|
||||||
|
self.futuresAllowed = True
|
||||||
|
self.root = tree
|
||||||
|
self.handleChildren(tree)
|
||||||
|
self.runDeferred(self._deferredFunctions)
|
||||||
|
# Set _deferredFunctions to None so that deferFunction will fail
|
||||||
|
# noisily if called after we've run through the deferred functions.
|
||||||
|
self._deferredFunctions = None
|
||||||
|
self.runDeferred(self._deferredAssignments)
|
||||||
|
# Set _deferredAssignments to None so that deferAssignment will fail
|
||||||
|
# noisily if called after we've run through the deferred assignments.
|
||||||
|
self._deferredAssignments = None
|
||||||
|
del self.scopeStack[1:]
|
||||||
|
self.popScope()
|
||||||
|
self.checkDeadScopes()
|
||||||
|
|
||||||
|
def deferFunction(self, callable):
|
||||||
|
"""
|
||||||
|
Schedule a function handler to be called just before completion.
|
||||||
|
|
||||||
|
This is used for handling function bodies, which must be deferred
|
||||||
|
because code later in the file might modify the global scope. When
|
||||||
|
`callable` is called, the scope at the time this is called will be
|
||||||
|
restored, however it will contain any new bindings added to it.
|
||||||
|
"""
|
||||||
|
self._deferredFunctions.append((callable, self.scopeStack[:]))
|
||||||
|
|
||||||
|
def deferAssignment(self, callable):
|
||||||
|
"""
|
||||||
|
Schedule an assignment handler to be called just after deferred
|
||||||
|
function handlers.
|
||||||
|
"""
|
||||||
|
self._deferredAssignments.append((callable, self.scopeStack[:]))
|
||||||
|
|
||||||
|
def runDeferred(self, deferred):
|
||||||
|
"""
|
||||||
|
Run the callables in C{deferred} using their associated scope stack.
|
||||||
|
"""
|
||||||
|
for handler, scope in deferred:
|
||||||
|
self.scopeStack = scope
|
||||||
|
handler()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scope(self):
|
||||||
|
return self.scopeStack[-1]
|
||||||
|
|
||||||
|
def popScope(self):
|
||||||
|
self.deadScopes.append(self.scopeStack.pop())
|
||||||
|
|
||||||
|
def checkDeadScopes(self):
|
||||||
|
"""
|
||||||
|
Look at scopes which have been fully examined and report names in them
|
||||||
|
which were imported but unused.
|
||||||
|
"""
|
||||||
|
for scope in self.deadScopes:
|
||||||
|
export = isinstance(scope.get('__all__'), ExportBinding)
|
||||||
|
if export:
|
||||||
|
all = scope['__all__'].names()
|
||||||
|
if not scope.importStarred and os.path.basename(self.filename) != '__init__.py':
|
||||||
|
# Look for possible mistakes in the export list
|
||||||
|
undefined = set(all) - set(scope)
|
||||||
|
for name in undefined:
|
||||||
|
self.report(messages.UndefinedExport,
|
||||||
|
scope['__all__'].source.lineno, name)
|
||||||
|
else:
|
||||||
|
all = []
|
||||||
|
|
||||||
|
# Look for imported names that aren't used.
|
||||||
|
for importation in scope.values():
|
||||||
|
if isinstance(importation, Importation):
|
||||||
|
if not importation.used and importation.name not in all:
|
||||||
|
self.report(messages.UnusedImport,
|
||||||
|
importation.source.lineno, importation.name)
|
||||||
|
|
||||||
|
def pushFunctionScope(self):
|
||||||
|
self.scopeStack.append(FunctionScope())
|
||||||
|
|
||||||
|
def pushClassScope(self):
|
||||||
|
self.scopeStack.append(ClassScope())
|
||||||
|
|
||||||
|
def report(self, messageClass, *args, **kwargs):
|
||||||
|
self.messages.append(messageClass(self.filename, *args, **kwargs))
|
||||||
|
|
||||||
|
def hasParent(self, node, kind):
|
||||||
|
while hasattr(node, 'parent'):
|
||||||
|
node = node.parent
|
||||||
|
if isinstance(node, kind):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getCommonAncestor(self, lnode, rnode, stop=None):
|
||||||
|
if not stop:
|
||||||
|
stop = self.root
|
||||||
|
if lnode is rnode:
|
||||||
|
return lnode
|
||||||
|
if stop in (lnode, rnode):
|
||||||
|
return stop
|
||||||
|
|
||||||
|
if not hasattr(lnode, 'parent') or not hasattr(rnode, 'parent'):
|
||||||
|
return
|
||||||
|
if (lnode.level > rnode.level):
|
||||||
|
return self.getCommonAncestor(lnode.parent, rnode, stop)
|
||||||
|
if (rnode.level > lnode.level):
|
||||||
|
return self.getCommonAncestor(lnode, rnode.parent, stop)
|
||||||
|
return self.getCommonAncestor(lnode.parent, rnode.parent, stop)
|
||||||
|
|
||||||
|
def descendantOf(self, node, ancestors, stop=None):
|
||||||
|
for a in ancestors:
|
||||||
|
if self.getCommonAncestor(node, a, stop) not in (stop, None):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onFork(self, parent, lnode, rnode, items):
|
||||||
|
return (self.descendantOf(lnode, items, parent) ^
|
||||||
|
self.descendantOf(rnode, items, parent))
|
||||||
|
|
||||||
|
def differentForks(self, lnode, rnode):
|
||||||
|
"""True, if lnode and rnode are located on different forks of IF/TRY"""
|
||||||
|
ancestor = self.getCommonAncestor(lnode, rnode)
|
||||||
|
if isinstance(ancestor, ast.If):
|
||||||
|
for fork in (ancestor.body, ancestor.orelse):
|
||||||
|
if self.onFork(ancestor, lnode, rnode, fork):
|
||||||
|
return True
|
||||||
|
elif isinstance(ancestor, ast_TryExcept):
|
||||||
|
body = ancestor.body + ancestor.orelse
|
||||||
|
for fork in [body] + [[hdl] for hdl in ancestor.handlers]:
|
||||||
|
if self.onFork(ancestor, lnode, rnode, fork):
|
||||||
|
return True
|
||||||
|
elif isinstance(ancestor, ast_TryFinally):
|
||||||
|
if self.onFork(ancestor, lnode, rnode, ancestor.body):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def addBinding(self, node, value, reportRedef=True):
|
||||||
|
"""
|
||||||
|
Called when a binding is altered.
|
||||||
|
|
||||||
|
- `node` is the statement responsible for the change
|
||||||
|
- `value` is the optional new value, a Binding instance, associated
|
||||||
|
with the binding; if None, the binding is deleted if it exists.
|
||||||
|
- if `reportRedef` is True (default), rebinding while unused will be
|
||||||
|
reported.
|
||||||
|
"""
|
||||||
|
redefinedWhileUnused = False
|
||||||
|
if not isinstance(self.scope, ClassScope):
|
||||||
|
for scope in self.scopeStack[::-1]:
|
||||||
|
existing = scope.get(value.name)
|
||||||
|
if (isinstance(existing, Importation)
|
||||||
|
and not existing.used
|
||||||
|
and (not isinstance(value, Importation) or value.fullName == existing.fullName)
|
||||||
|
and reportRedef
|
||||||
|
and not self.differentForks(node, existing.source)):
|
||||||
|
redefinedWhileUnused = True
|
||||||
|
self.report(messages.RedefinedWhileUnused,
|
||||||
|
node.lineno, value.name, existing.source.lineno)
|
||||||
|
|
||||||
|
existing = self.scope.get(value.name)
|
||||||
|
if not redefinedWhileUnused and self.hasParent(value.source, ast.ListComp):
|
||||||
|
if (existing and reportRedef
|
||||||
|
and not self.hasParent(existing.source, (ast.For, ast.ListComp))):
|
||||||
|
self.report(messages.RedefinedInListComp,
|
||||||
|
node.lineno, value.name, existing.source.lineno)
|
||||||
|
|
||||||
|
if isinstance(value, UnBinding):
|
||||||
|
try:
|
||||||
|
del self.scope[value.name]
|
||||||
|
except KeyError:
|
||||||
|
self.report(messages.UndefinedName, node.lineno, value.name)
|
||||||
|
elif (isinstance(existing, Definition)
|
||||||
|
and not existing.used
|
||||||
|
and not self.differentForks(node, existing.source)):
|
||||||
|
self.report(messages.RedefinedWhileUnused,
|
||||||
|
node.lineno, value.name, existing.source.lineno)
|
||||||
|
else:
|
||||||
|
self.scope[value.name] = value
|
||||||
|
|
||||||
|
def handleNodeLoad(self, node):
|
||||||
|
name = getNodeName(node)
|
||||||
|
if not name:
|
||||||
|
return
|
||||||
|
# try local scope
|
||||||
|
importStarred = self.scope.importStarred
|
||||||
|
try:
|
||||||
|
self.scope[name].used = (self.scope, node.lineno)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
# try enclosing function scopes
|
||||||
|
for scope in self.scopeStack[-2:0:-1]:
|
||||||
|
importStarred = importStarred or scope.importStarred
|
||||||
|
if not isinstance(scope, FunctionScope):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
scope[name].used = (self.scope, node.lineno)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
# try global scope
|
||||||
|
importStarred = importStarred or self.scopeStack[0].importStarred
|
||||||
|
try:
|
||||||
|
self.scopeStack[0][name].used = (self.scope, node.lineno)
|
||||||
|
except KeyError:
|
||||||
|
if not importStarred and name not in self.builtIns:
|
||||||
|
if (os.path.basename(self.filename) == '__init__.py' and name == '__path__'):
|
||||||
|
# the special name __path__ is valid only in packages
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.report(messages.UndefinedName, node.lineno, name)
|
||||||
|
|
||||||
|
def handleNodeStore(self, node):
|
||||||
|
name = getNodeName(node)
|
||||||
|
if not name:
|
||||||
|
return
|
||||||
|
# if the name hasn't already been defined in the current scope
|
||||||
|
if isinstance(self.scope, FunctionScope) and name not in self.scope:
|
||||||
|
# for each function or module scope above us
|
||||||
|
for scope in self.scopeStack[:-1]:
|
||||||
|
if not isinstance(scope, (FunctionScope, ModuleScope)):
|
||||||
|
continue
|
||||||
|
# if the name was defined in that scope, and the name has
|
||||||
|
# been accessed already in the current scope, and hasn't
|
||||||
|
# been declared global
|
||||||
|
if (name in scope and scope[name].used and scope[name].used[0] is self.scope
|
||||||
|
and name not in self.scope.globals):
|
||||||
|
# then it's probably a mistake
|
||||||
|
self.report(messages.UndefinedLocal,
|
||||||
|
scope[name].used[1], name, scope[name].source.lineno)
|
||||||
|
break
|
||||||
|
|
||||||
|
parent = getattr(node, 'parent', None)
|
||||||
|
if isinstance(parent, (ast.For, ast.comprehension, ast.Tuple, ast.List)):
|
||||||
|
binding = Binding(name, node)
|
||||||
|
elif parent is not None and name == '__all__' and isinstance(self.scope, ModuleScope):
|
||||||
|
binding = ExportBinding(name, parent.value)
|
||||||
|
else:
|
||||||
|
binding = Assignment(name, node)
|
||||||
|
if name in self.scope:
|
||||||
|
binding.used = self.scope[name].used
|
||||||
|
self.addBinding(node, binding)
|
||||||
|
|
||||||
|
def handleNodeDelete(self, node):
|
||||||
|
name = getNodeName(node)
|
||||||
|
if not name:
|
||||||
|
return
|
||||||
|
if isinstance(self.scope, FunctionScope) and name in self.scope.globals:
|
||||||
|
del self.scope.globals[name]
|
||||||
|
else:
|
||||||
|
self.addBinding(node, UnBinding(name, node))
|
||||||
|
|
||||||
|
def handleChildren(self, tree):
|
||||||
|
for node in iter_child_nodes(tree):
|
||||||
|
self.handleNode(node, tree)
|
||||||
|
|
||||||
|
def isDocstring(self, node):
|
||||||
|
"""
|
||||||
|
Determine if the given node is a docstring, as long as it is at the
|
||||||
|
correct place in the node tree.
|
||||||
|
"""
|
||||||
|
return isinstance(node, ast.Str) or (isinstance(node, ast.Expr) and
|
||||||
|
isinstance(node.value, ast.Str))
|
||||||
|
|
||||||
|
def handleNode(self, node, parent):
|
||||||
|
if node is None:
|
||||||
|
return
|
||||||
|
node.parent = parent
|
||||||
|
if self.traceTree:
|
||||||
|
print(' ' * self.nodeDepth + node.__class__.__name__)
|
||||||
|
self.nodeDepth += 1
|
||||||
|
if self.futuresAllowed and not (isinstance(node, ast.ImportFrom) or
|
||||||
|
self.isDocstring(node)):
|
||||||
|
self.futuresAllowed = False
|
||||||
|
nodeType = node.__class__.__name__.upper()
|
||||||
|
node.level = self.nodeDepth
|
||||||
|
try:
|
||||||
|
handler = getattr(self, nodeType)
|
||||||
|
handler(node)
|
||||||
|
finally:
|
||||||
|
self.nodeDepth -= 1
|
||||||
|
if self.traceTree:
|
||||||
|
print(' ' * self.nodeDepth + 'end ' + node.__class__.__name__)
|
||||||
|
|
||||||
|
def ignore(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# "stmt" type nodes
|
||||||
|
RETURN = DELETE = PRINT = WHILE = IF = WITH = WITHITEM = RAISE = \
|
||||||
|
TRYEXCEPT = TRYFINALLY = TRY = ASSERT = EXEC = EXPR = handleChildren
|
||||||
|
|
||||||
|
CONTINUE = BREAK = PASS = ignore
|
||||||
|
|
||||||
|
# "expr" type nodes
|
||||||
|
BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = YIELDFROM = \
|
||||||
|
COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = \
|
||||||
|
STARRED = handleChildren
|
||||||
|
|
||||||
|
NUM = STR = BYTES = ELLIPSIS = ignore
|
||||||
|
|
||||||
|
# "slice" type nodes
|
||||||
|
SLICE = EXTSLICE = INDEX = handleChildren
|
||||||
|
|
||||||
|
# expression contexts are node instances too, though being constants
|
||||||
|
LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
|
||||||
|
|
||||||
|
# same for operators
|
||||||
|
AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \
|
||||||
|
BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \
|
||||||
|
EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
|
||||||
|
|
||||||
|
# additional node types
|
||||||
|
COMPREHENSION = KEYWORD = handleChildren
|
||||||
|
|
||||||
|
def GLOBAL(self, node):
|
||||||
|
"""
|
||||||
|
Keep track of globals declarations.
|
||||||
|
"""
|
||||||
|
if isinstance(self.scope, FunctionScope):
|
||||||
|
self.scope.globals.update(dict.fromkeys(node.names))
|
||||||
|
|
||||||
|
NONLOCAL = GLOBAL
|
||||||
|
|
||||||
|
def LISTCOMP(self, node):
|
||||||
|
# handle generators before element
|
||||||
|
for gen in node.generators:
|
||||||
|
self.handleNode(gen, node)
|
||||||
|
self.handleNode(node.elt, node)
|
||||||
|
|
||||||
|
GENERATOREXP = SETCOMP = LISTCOMP
|
||||||
|
|
||||||
|
def DICTCOMP(self, node):
|
||||||
|
for gen in node.generators:
|
||||||
|
self.handleNode(gen, node)
|
||||||
|
self.handleNode(node.key, node)
|
||||||
|
self.handleNode(node.value, node)
|
||||||
|
|
||||||
|
def FOR(self, node):
|
||||||
|
"""
|
||||||
|
Process bindings for loop variables.
|
||||||
|
"""
|
||||||
|
vars = []
|
||||||
|
|
||||||
|
def collectLoopVars(n):
|
||||||
|
if isinstance(n, ast.Name):
|
||||||
|
vars.append(n.id)
|
||||||
|
elif isinstance(n, ast.expr_context):
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
for c in iter_child_nodes(n):
|
||||||
|
collectLoopVars(c)
|
||||||
|
|
||||||
|
collectLoopVars(node.target)
|
||||||
|
for varn in vars:
|
||||||
|
if (isinstance(self.scope.get(varn), Importation)
|
||||||
|
# unused ones will get an unused import warning
|
||||||
|
and self.scope[varn].used):
|
||||||
|
self.report(messages.ImportShadowedByLoopVar,
|
||||||
|
node.lineno, varn, self.scope[varn].source.lineno)
|
||||||
|
|
||||||
|
self.handleChildren(node)
|
||||||
|
|
||||||
|
def NAME(self, node):
|
||||||
|
"""
|
||||||
|
Handle occurrence of Name (which can be a load/store/delete access.)
|
||||||
|
"""
|
||||||
|
if node.id == 'locals' and isinstance(node.parent, ast.Call):
|
||||||
|
# we are doing locals() call in current scope
|
||||||
|
self.scope.usesLocals = True
|
||||||
|
# Locate the name in locals / function / globals scopes.
|
||||||
|
if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
|
||||||
|
self.handleNodeLoad(node)
|
||||||
|
elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
|
||||||
|
self.handleNodeStore(node)
|
||||||
|
elif isinstance(node.ctx, ast.Del):
|
||||||
|
self.handleNodeDelete(node)
|
||||||
|
else:
|
||||||
|
# must be a Param context -- this only happens for names in function
|
||||||
|
# arguments, but these aren't dispatched through here
|
||||||
|
raise RuntimeError("Got impossible expression context: %r" % (node.ctx,))
|
||||||
|
|
||||||
|
def FUNCTIONDEF(self, node):
|
||||||
|
if not hasattr(node, 'decorator_list'): # Python 2.5
|
||||||
|
node.decorator_list = node.decorators
|
||||||
|
for deco in node.decorator_list:
|
||||||
|
self.handleNode(deco, node)
|
||||||
|
self.addBinding(node, FunctionDefinition(node.name, node))
|
||||||
|
self.LAMBDA(node)
|
||||||
|
|
||||||
|
def LAMBDA(self, node):
|
||||||
|
args = []
|
||||||
|
|
||||||
|
if PY2:
|
||||||
|
def addArgs(arglist):
|
||||||
|
for arg in arglist:
|
||||||
|
if isinstance(arg, ast.Tuple):
|
||||||
|
addArgs(arg.elts)
|
||||||
|
else:
|
||||||
|
if arg.id in args:
|
||||||
|
self.report(messages.DuplicateArgument,
|
||||||
|
node.lineno, arg.id)
|
||||||
|
args.append(arg.id)
|
||||||
|
addArgs(node.args.args)
|
||||||
|
defaults = node.args.defaults
|
||||||
|
else:
|
||||||
|
for arg in node.args.args + node.args.kwonlyargs:
|
||||||
|
if arg.arg in args:
|
||||||
|
self.report(messages.DuplicateArgument,
|
||||||
|
node.lineno, arg.arg)
|
||||||
|
args.append(arg.arg)
|
||||||
|
self.handleNode(arg.annotation, node)
|
||||||
|
if hasattr(node, 'returns'): # Only for FunctionDefs
|
||||||
|
for annotation in (node.args.varargannotation,
|
||||||
|
node.args.kwargannotation, node.returns):
|
||||||
|
self.handleNode(annotation, node)
|
||||||
|
defaults = node.args.defaults + node.args.kw_defaults
|
||||||
|
|
||||||
|
# vararg/kwarg identifiers are not Name nodes
|
||||||
|
for wildcard in (node.args.vararg, node.args.kwarg):
|
||||||
|
if not wildcard:
|
||||||
|
continue
|
||||||
|
if wildcard in args:
|
||||||
|
self.report(messages.DuplicateArgument, node.lineno, wildcard)
|
||||||
|
args.append(wildcard)
|
||||||
|
for default in defaults:
|
||||||
|
self.handleNode(default, node)
|
||||||
|
|
||||||
|
def runFunction():
|
||||||
|
|
||||||
|
self.pushFunctionScope()
|
||||||
|
for name in args:
|
||||||
|
self.addBinding(node, Argument(name, node), reportRedef=False)
|
||||||
|
if isinstance(node.body, list):
|
||||||
|
# case for FunctionDefs
|
||||||
|
for stmt in node.body:
|
||||||
|
self.handleNode(stmt, node)
|
||||||
|
else:
|
||||||
|
# case for Lambdas
|
||||||
|
self.handleNode(node.body, node)
|
||||||
|
|
||||||
|
def checkUnusedAssignments():
|
||||||
|
"""
|
||||||
|
Check to see if any assignments have not been used.
|
||||||
|
"""
|
||||||
|
for name, binding in self.scope.items():
|
||||||
|
if (not binding.used and name not in self.scope.globals
|
||||||
|
and not self.scope.usesLocals
|
||||||
|
and isinstance(binding, Assignment)):
|
||||||
|
self.report(messages.UnusedVariable,
|
||||||
|
binding.source.lineno, name)
|
||||||
|
self.deferAssignment(checkUnusedAssignments)
|
||||||
|
self.popScope()
|
||||||
|
|
||||||
|
self.deferFunction(runFunction)
|
||||||
|
|
||||||
|
def CLASSDEF(self, node):
|
||||||
|
"""
|
||||||
|
Check names used in a class definition, including its decorators, base
|
||||||
|
classes, and the body of its definition. Additionally, add its name to
|
||||||
|
the current scope.
|
||||||
|
"""
|
||||||
|
# no class decorator in Python 2.5
|
||||||
|
for deco in getattr(node, 'decorator_list', ''):
|
||||||
|
self.handleNode(deco, node)
|
||||||
|
for baseNode in node.bases:
|
||||||
|
self.handleNode(baseNode, node)
|
||||||
|
if not PY2:
|
||||||
|
for keywordNode in node.keywords:
|
||||||
|
self.handleNode(keywordNode, node)
|
||||||
|
self.pushClassScope()
|
||||||
|
for stmt in node.body:
|
||||||
|
self.handleNode(stmt, node)
|
||||||
|
self.popScope()
|
||||||
|
self.addBinding(node, ClassDefinition(node.name, node))
|
||||||
|
|
||||||
|
def ASSIGN(self, node):
|
||||||
|
self.handleNode(node.value, node)
|
||||||
|
for target in node.targets:
|
||||||
|
self.handleNode(target, node)
|
||||||
|
|
||||||
|
def AUGASSIGN(self, node):
|
||||||
|
self.handleNodeLoad(node.target)
|
||||||
|
self.handleNode(node.value, node)
|
||||||
|
self.handleNode(node.target, node)
|
||||||
|
|
||||||
|
def IMPORT(self, node):
|
||||||
|
for alias in node.names:
|
||||||
|
name = alias.asname or alias.name
|
||||||
|
importation = Importation(name, node)
|
||||||
|
self.addBinding(node, importation)
|
||||||
|
|
||||||
|
def IMPORTFROM(self, node):
|
||||||
|
if node.module == '__future__':
|
||||||
|
if not self.futuresAllowed:
|
||||||
|
self.report(messages.LateFutureImport,
|
||||||
|
node.lineno, [n.name for n in node.names])
|
||||||
|
else:
|
||||||
|
self.futuresAllowed = False
|
||||||
|
|
||||||
|
for alias in node.names:
|
||||||
|
if alias.name == '*':
|
||||||
|
self.scope.importStarred = True
|
||||||
|
self.report(messages.ImportStarUsed, node.lineno, node.module)
|
||||||
|
continue
|
||||||
|
name = alias.asname or alias.name
|
||||||
|
importation = Importation(name, node)
|
||||||
|
if node.module == '__future__':
|
||||||
|
importation.used = (self.scope, node.lineno)
|
||||||
|
self.addBinding(node, importation)
|
||||||
|
|
||||||
|
def EXCEPTHANDLER(self, node):
|
||||||
|
# 3.x: in addition to handling children, we must handle the name of
|
||||||
|
# the exception, which is not a Name node, but a simple string.
|
||||||
|
if isinstance(node.name, str):
|
||||||
|
self.handleNodeStore(node)
|
||||||
|
self.handleChildren(node)
|
|
@ -0,0 +1,113 @@
|
||||||
|
# (c) 2005 Divmod, Inc. See LICENSE file for details
|
||||||
|
|
||||||
|
|
||||||
|
class Message(object):
|
||||||
|
message = ''
|
||||||
|
message_args = ()
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno):
|
||||||
|
self.filename = filename
|
||||||
|
self.lineno = lineno
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '%s:%s: %s' % (self.filename, self.lineno, self.message % self.message_args)
|
||||||
|
|
||||||
|
|
||||||
|
class UnusedImport(Message):
|
||||||
|
message = '%r imported but unused'
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, name):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (name,)
|
||||||
|
|
||||||
|
|
||||||
|
class RedefinedWhileUnused(Message):
|
||||||
|
message = 'redefinition of unused %r from line %r'
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, name, orig_lineno):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (name, orig_lineno)
|
||||||
|
|
||||||
|
|
||||||
|
class RedefinedInListComp(Message):
|
||||||
|
message = 'list comprehension redefines %r from line %r'
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, name, orig_lineno):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (name, orig_lineno)
|
||||||
|
|
||||||
|
|
||||||
|
class ImportShadowedByLoopVar(Message):
|
||||||
|
message = 'import %r from line %r shadowed by loop variable'
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, name, orig_lineno):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (name, orig_lineno)
|
||||||
|
|
||||||
|
|
||||||
|
class ImportStarUsed(Message):
|
||||||
|
message = "'from %s import *' used; unable to detect undefined names"
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, modname):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (modname,)
|
||||||
|
|
||||||
|
|
||||||
|
class UndefinedName(Message):
|
||||||
|
message = 'undefined name %r'
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, name):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (name,)
|
||||||
|
|
||||||
|
|
||||||
|
class UndefinedExport(Message):
|
||||||
|
message = 'undefined name %r in __all__'
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, name):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (name,)
|
||||||
|
|
||||||
|
|
||||||
|
class UndefinedLocal(Message):
|
||||||
|
message = "local variable %r (defined in enclosing scope on line %r) referenced before assignment"
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, name, orig_lineno):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (name, orig_lineno)
|
||||||
|
|
||||||
|
|
||||||
|
class DuplicateArgument(Message):
|
||||||
|
message = 'duplicate argument %r in function definition'
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, name):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (name,)
|
||||||
|
|
||||||
|
|
||||||
|
class Redefined(Message):
|
||||||
|
message = 'redefinition of %r from line %r'
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, name, orig_lineno):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (name, orig_lineno)
|
||||||
|
|
||||||
|
|
||||||
|
class LateFutureImport(Message):
|
||||||
|
message = 'future import(s) %r after other statements'
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, names):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (names,)
|
||||||
|
|
||||||
|
|
||||||
|
class UnusedVariable(Message):
|
||||||
|
"""
|
||||||
|
Indicates that a variable has been explicity assigned to but not actually
|
||||||
|
used.
|
||||||
|
"""
|
||||||
|
message = 'local variable %r is assigned to but never used'
|
||||||
|
|
||||||
|
def __init__(self, filename, lineno, names):
|
||||||
|
Message.__init__(self, filename, lineno)
|
||||||
|
self.message_args = (names,)
|
|
@ -0,0 +1,79 @@
|
||||||
|
# (c) 2005-2012 Divmod, Inc.
|
||||||
|
# See LICENSE file for details
|
||||||
|
|
||||||
|
import sys
|
||||||
|
try:
|
||||||
|
u = unicode
|
||||||
|
except NameError:
|
||||||
|
u = str
|
||||||
|
|
||||||
|
|
||||||
|
class Reporter(object):
|
||||||
|
"""
|
||||||
|
Formats the results of pyflakes checks to users.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, warningStream, errorStream):
|
||||||
|
"""
|
||||||
|
Construct a L{Reporter}.
|
||||||
|
|
||||||
|
@param warningStream: A file-like object where warnings will be
|
||||||
|
written to. The stream's C{write} method must accept unicode.
|
||||||
|
C{sys.stdout} is a good value.
|
||||||
|
@param errorStream: A file-like object where error output will be
|
||||||
|
written to. The stream's C{write} method must accept unicode.
|
||||||
|
C{sys.stderr} is a good value.
|
||||||
|
"""
|
||||||
|
self._stdout = warningStream
|
||||||
|
self._stderr = errorStream
|
||||||
|
|
||||||
|
def unexpectedError(self, filename, msg):
|
||||||
|
"""
|
||||||
|
An unexpected error occurred trying to process C{filename}.
|
||||||
|
|
||||||
|
@param filename: The path to a file that we could not process.
|
||||||
|
@ptype filename: C{unicode}
|
||||||
|
@param msg: A message explaining the problem.
|
||||||
|
@ptype msg: C{unicode}
|
||||||
|
"""
|
||||||
|
self._stderr.write(u("%s: %s\n") % (filename, msg))
|
||||||
|
|
||||||
|
def syntaxError(self, filename, msg, lineno, offset, text):
|
||||||
|
"""
|
||||||
|
There was a syntax errror in C{filename}.
|
||||||
|
|
||||||
|
@param filename: The path to the file with the syntax error.
|
||||||
|
@ptype filename: C{unicode}
|
||||||
|
@param msg: An explanation of the syntax error.
|
||||||
|
@ptype msg: C{unicode}
|
||||||
|
@param lineno: The line number where the syntax error occurred.
|
||||||
|
@ptype lineno: C{int}
|
||||||
|
@param offset: The column on which the syntax error occurred.
|
||||||
|
@ptype offset: C{int}
|
||||||
|
@param text: The source code containing the syntax error.
|
||||||
|
@ptype text: C{unicode}
|
||||||
|
"""
|
||||||
|
line = text.splitlines()[-1]
|
||||||
|
if offset is not None:
|
||||||
|
offset = offset - (len(text) - len(line))
|
||||||
|
self._stderr.write(u('%s:%d: %s\n') % (filename, lineno, msg))
|
||||||
|
self._stderr.write(u(line))
|
||||||
|
self._stderr.write(u('\n'))
|
||||||
|
if offset is not None:
|
||||||
|
self._stderr.write(u(" " * (offset + 1) + "^\n"))
|
||||||
|
|
||||||
|
def flake(self, message):
|
||||||
|
"""
|
||||||
|
pyflakes found something wrong with the code.
|
||||||
|
|
||||||
|
@param: A L{pyflakes.messages.Message}.
|
||||||
|
"""
|
||||||
|
self._stdout.write(u(message))
|
||||||
|
self._stdout.write(u('\n'))
|
||||||
|
|
||||||
|
|
||||||
|
def _makeDefaultReporter():
|
||||||
|
"""
|
||||||
|
Make a reporter that can be used when no reporter is specified.
|
||||||
|
"""
|
||||||
|
return Reporter(sys.stdout, sys.stderr)
|
|
@ -65,7 +65,7 @@ let s:bzrFunctions = {}
|
||||||
" Returns the executable used to invoke bzr suitable for use in a shell
|
" Returns the executable used to invoke bzr suitable for use in a shell
|
||||||
" command.
|
" command.
|
||||||
function! s:Executable()
|
function! s:Executable()
|
||||||
return VCSCommandGetOption('VCSCommandBZRExec', 'bzr')
|
return shellescape(VCSCommandGetOption('VCSCommandBZRExec', 'bzr'))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Function: s:DoCommand(cmd, cmdName, statusText) {{{2
|
" Function: s:DoCommand(cmd, cmdName, statusText) {{{2
|
||||||
|
|
|
@ -378,11 +378,6 @@ endfunction
|
||||||
" command line on Windows systems.
|
" command line on Windows systems.
|
||||||
|
|
||||||
function! s:VCSCommandUtility.system(...)
|
function! s:VCSCommandUtility.system(...)
|
||||||
if (has("win32") || has("win64")) && &sxq !~ '"'
|
|
||||||
let save_sxq = &sxq
|
|
||||||
set sxq=\"
|
|
||||||
endif
|
|
||||||
try
|
|
||||||
let output = call('system', a:000)
|
let output = call('system', a:000)
|
||||||
if exists('*iconv') && has('multi_byte')
|
if exists('*iconv') && has('multi_byte')
|
||||||
if(strlen(&tenc) && &tenc != &enc)
|
if(strlen(&tenc) && &tenc != &enc)
|
||||||
|
@ -398,11 +393,6 @@ function! s:VCSCommandUtility.system(...)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
endif
|
endif
|
||||||
finally
|
|
||||||
if exists("save_sxq")
|
|
||||||
let &sxq = save_sxq
|
|
||||||
endif
|
|
||||||
endtry
|
|
||||||
return output
|
return output
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
@ -1033,8 +1023,7 @@ function! s:VCSVimDiff(...)
|
||||||
let b:VCSCommandCommand = 'vimdiff'
|
let b:VCSCommandCommand = 'vimdiff'
|
||||||
diffthis
|
diffthis
|
||||||
let t:vcsCommandVimDiffScratchList = [resultBuffer]
|
let t:vcsCommandVimDiffScratchList = [resultBuffer]
|
||||||
" If no split method is defined, cheat, and set it to vertical.
|
call s:VCSCommandUtility.pushContext({'VCSCommandEdit': 'split', 'VCSCommandSplit': orientation})
|
||||||
call s:VCSCommandUtility.pushContext({'VCSCommandSplit': orientation})
|
|
||||||
try
|
try
|
||||||
let resultBuffer = s:VCSReview(a:2)
|
let resultBuffer = s:VCSReview(a:2)
|
||||||
finally
|
finally
|
||||||
|
@ -1048,7 +1037,6 @@ function! s:VCSVimDiff(...)
|
||||||
diffthis
|
diffthis
|
||||||
let t:vcsCommandVimDiffScratchList += [resultBuffer]
|
let t:vcsCommandVimDiffScratchList += [resultBuffer]
|
||||||
else
|
else
|
||||||
" Add new buffer. Force splitting behavior, otherwise why use vimdiff?
|
|
||||||
call s:VCSCommandUtility.pushContext({'VCSCommandEdit': 'split', 'VCSCommandSplit': orientation})
|
call s:VCSCommandUtility.pushContext({'VCSCommandEdit': 'split', 'VCSCommandSplit': orientation})
|
||||||
try
|
try
|
||||||
if(a:0 == 0)
|
if(a:0 == 0)
|
||||||
|
|
|
@ -111,7 +111,7 @@ let s:cvsFunctions = {}
|
||||||
" Returns the executable used to invoke cvs suitable for use in a shell
|
" Returns the executable used to invoke cvs suitable for use in a shell
|
||||||
" command.
|
" command.
|
||||||
function! s:Executable()
|
function! s:Executable()
|
||||||
return VCSCommandGetOption('VCSCommandCVSExec', 'cvs')
|
return shellescape(VCSCommandGetOption('VCSCommandCVSExec', 'cvs'))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
|
" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
|
||||||
|
|
|
@ -70,7 +70,7 @@ let s:gitFunctions = {}
|
||||||
" Returns the executable used to invoke git suitable for use in a shell
|
" Returns the executable used to invoke git suitable for use in a shell
|
||||||
" command.
|
" command.
|
||||||
function! s:Executable()
|
function! s:Executable()
|
||||||
return VCSCommandGetOption('VCSCommandGitExec', 'git')
|
return shellescape(VCSCommandGetOption('VCSCommandGitExec', 'git'))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
|
" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
|
||||||
|
|
|
@ -72,7 +72,7 @@ let s:hgFunctions = {}
|
||||||
" Returns the executable used to invoke hg suitable for use in a shell
|
" Returns the executable used to invoke hg suitable for use in a shell
|
||||||
" command.
|
" command.
|
||||||
function! s:Executable()
|
function! s:Executable()
|
||||||
return VCSCommandGetOption('VCSCommandHGExec', 'hg')
|
return shellescape(VCSCommandGetOption('VCSCommandHGExec', 'hg'))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
|
" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
|
||||||
|
|
|
@ -65,7 +65,7 @@ let s:svkFunctions = {}
|
||||||
" Returns the executable used to invoke SVK suitable for use in a shell
|
" Returns the executable used to invoke SVK suitable for use in a shell
|
||||||
" command.
|
" command.
|
||||||
function! s:Executable()
|
function! s:Executable()
|
||||||
return VCSCommandGetOption('VCSCommandSVKExec', 'svk')
|
return shellescape(VCSCommandGetOption('VCSCommandSVKExec', 'svk'))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
|
" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
|
||||||
|
|
|
@ -72,7 +72,7 @@ let s:svnFunctions = {}
|
||||||
" Returns the executable used to invoke git suitable for use in a shell
|
" Returns the executable used to invoke git suitable for use in a shell
|
||||||
" command.
|
" command.
|
||||||
function! s:Executable()
|
function! s:Executable()
|
||||||
return VCSCommandGetOption('VCSCommandSVNExec', 'svn')
|
return shellescape(VCSCommandGetOption('VCSCommandSVNExec', 'svn'))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
|
" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
|
||||||
|
@ -193,6 +193,10 @@ function! s:svnFunctions.GetBufferInfo()
|
||||||
if statusText =~ '^?'
|
if statusText =~ '^?'
|
||||||
return ['Unknown']
|
return ['Unknown']
|
||||||
endif
|
endif
|
||||||
|
" File explicitly ignored by SVN.
|
||||||
|
if statusText =~ '^I'
|
||||||
|
return ['Ignored']
|
||||||
|
endif
|
||||||
|
|
||||||
let [flags, revision, repository] = matchlist(statusText, '^\(.\{9}\)\s*\(\d\+\)\s\+\(\d\+\)')[1:3]
|
let [flags, revision, repository] = matchlist(statusText, '^\(.\{9}\)\s*\(\d\+\)\s\+\(\d\+\)')[1:3]
|
||||||
if revision == ''
|
if revision == ''
|
||||||
|
|
Loading…
Reference in New Issue