1
0
mirror of https://github.com/gryf/.vim.git synced 2025-12-17 11:30:29 +01:00

Moja prawie współczesna konfiguracja. Dużo rzeczy :)

This commit is contained in:
2010-04-11 20:41:45 +02:00
parent 74c5c4085f
commit 63717266b9
173 changed files with 61061 additions and 1933 deletions

View File

@@ -0,0 +1,28 @@
" ftplugin for python.
" See: eclim_py plugin in plugins dir
" Global Variables {{{
if !exists("g:EclimPythonValidate")
let g:EclimPythonValidate = 1
endif
" }}}
" Autocmds {{{
if g:EclimPythonValidate
augroup eclim_python_validate
autocmd! BufWritePost <buffer>
autocmd BufWritePost <buffer> call Validate(1)
augroup END
endif
" }}}
if !exists(":Validate")
command -nargs=0 -buffer Validate :call Validate(0)
endif
if !exists(":PyLint")
command -nargs=0 -buffer PyLint :call PyLint()
endif

View File

@@ -0,0 +1,226 @@
" pyflakes.vim - A script to highlight Python code on the fly with warnings
" from Pyflakes, a Python lint tool.
"
" Place this script and the accompanying pyflakes directory in
" .vim/ftplugin/python.
"
" See README for additional installation and information.
"
" Thanks to matlib.vim for ideas/code on interactive linting.
"
" Maintainer: Kevin Watters <kevin.watters@gmail.com>
" Version: 0.1
if exists("b:did_pyflakes_plugin")
finish " only load once
else
let b:did_pyflakes_plugin = 1
endif
if !exists('g:pyflakes_builtins')
let g:pyflakes_builtins = []
endif
if !exists("b:did_python_init")
let b:did_python_init = 0
if !has('python')
echoerr "Error: the pyflakes.vim plugin requires Vim to be compiled with +python"
finish
endif
python << EOF
import vim
import os.path
import sys
if sys.version_info[:2] < (2, 5):
raise AssertionError('Vim must be compiled with Python 2.5 or higher; you have ' + sys.version)
# get the directory this script is in: the pyflakes python module should be installed there.
scriptdir = os.path.join(os.path.dirname(vim.eval('expand("<sfile>")')), 'pyflakes')
sys.path.insert(0, scriptdir)
from pyflakes import checker, ast, messages
from operator import attrgetter
class SyntaxError(messages.Message):
message = 'could not compile: %s'
def __init__(self, filename, lineno, col, message):
messages.Message.__init__(self, filename, lineno, col)
self.message_args = (message,)
class blackhole(object):
write = flush = lambda *a, **k: None
def check(buffer):
filename = buffer.name
contents = '\n'.join(buffer[:]) + '\n'
vimenc = vim.eval('&encoding')
if vimenc:
contents = contents.decode(vimenc)
builtins = []
try:
builtins = eval(vim.eval('string(g:pyflakes_builtins)'))
except Exception:
pass
try:
# TODO: use warnings filters instead of ignoring stderr
old_stderr, sys.stderr = sys.stderr, blackhole()
try:
tree = ast.parse(contents, filename)
finally:
sys.stderr = old_stderr
except:
try:
value = sys.exc_info()[1]
lineno, offset, line = value[1][1:]
except IndexError:
lineno, offset, line = 1, 0, ''
if line and line.endswith("\n"):
line = line[:-1]
return [SyntaxError(filename, lineno, offset, str(value))]
else:
w = checker.Checker(tree, filename, builtins = builtins)
w.messages.sort(key = attrgetter('lineno'))
return w.messages
def vim_quote(s):
return s.replace("'", "''")
EOF
let b:did_python_init = 1
endif
if !b:did_python_init
finish
endif
au BufLeave <buffer> call s:ClearPyflakes()
au BufEnter <buffer> call s:RunPyflakes()
au InsertLeave <buffer> call s:RunPyflakes()
au InsertEnter <buffer> call s:RunPyflakes()
au BufWritePost <buffer> call s:RunPyflakes()
au CursorHold <buffer> call s:RunPyflakes()
au CursorHoldI <buffer> call s:RunPyflakes()
au CursorHold <buffer> call s:GetPyflakesMessage()
au CursorMoved <buffer> call s:GetPyflakesMessage()
if !exists("*s:PyflakesUpdate")
function s:PyflakesUpdate()
silent call s:RunPyflakes()
call s:GetPyflakesMessage()
endfunction
endif
" Call this function in your .vimrc to update PyFlakes
if !exists(":PyflakesUpdate")
command PyflakesUpdate :call s:PyflakesUpdate()
endif
" Hook common text manipulation commands to update PyFlakes
" TODO: is there a more general "text op" autocommand we could register
" for here?
noremap <buffer><silent> dd dd:PyflakesUpdate<CR>
noremap <buffer><silent> dw dw:PyflakesUpdate<CR>
noremap <buffer><silent> u u:PyflakesUpdate<CR>
noremap <buffer><silent> <C-R> <C-R>:PyflakesUpdate<CR>
" WideMsg() prints [long] message up to (&columns-1) length
" guaranteed without "Press Enter" prompt.
if !exists("*s:WideMsg")
function s:WideMsg(msg)
let x=&ruler | let y=&showcmd
set noruler noshowcmd
redraw
echo a:msg
let &ruler=x | let &showcmd=y
endfun
endif
if !exists("*s:RunPyflakes")
function s:RunPyflakes()
highlight link PyFlakes SpellBad
if exists("b:cleared")
if b:cleared == 0
silent call s:ClearPyflakes()
let b:cleared = 1
endif
else
let b:cleared = 1
endif
let b:matched = []
let b:matchedlines = {}
python << EOF
for w in check(vim.current.buffer):
vim.command('let s:matchDict = {}')
vim.command("let s:matchDict['lineNum'] = " + str(w.lineno))
vim.command("let s:matchDict['message'] = '%s'" % vim_quote(w.message % w.message_args))
vim.command("let b:matchedlines[" + str(w.lineno) + "] = s:matchDict")
if w.col is None or isinstance(w, SyntaxError):
# without column information, just highlight the whole line
# (minus the newline)
vim.command(r"let s:mID = matchadd('PyFlakes', '\%" + str(w.lineno) + r"l\n\@!')")
else:
# with a column number, highlight the first keyword there
vim.command(r"let s:mID = matchadd('PyFlakes', '^\%" + str(w.lineno) + r"l\_.\{-}\zs\k\+\k\@!\%>" + str(w.col) + r"c')")
vim.command("call add(b:matched, s:matchDict)")
EOF
let b:cleared = 0
endfunction
end
" keep track of whether or not we are showing a message
let b:showing_message = 0
if !exists("*s:GetPyflakesMessage")
function s:GetPyflakesMessage()
let s:cursorPos = getpos(".")
" Bail if RunPyflakes hasn't been called yet.
if !exists('b:matchedlines')
return
endif
" if there's a message for the line the cursor is currently on, echo
" it to the console
if has_key(b:matchedlines, s:cursorPos[1])
let s:pyflakesMatch = get(b:matchedlines, s:cursorPos[1])
call s:WideMsg(s:pyflakesMatch['message'])
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
endif
if !exists('*s:ClearPyflakes')
function s:ClearPyflakes()
let s:matches = getmatches()
for s:matchId in s:matches
if s:matchId['group'] == 'PyFlakes'
call matchdelete(s:matchId['id'])
endif
endfor
let b:matched = []
let b:matchedlines = {}
let b:cleared = 1
endfunction
endif

View File

@@ -0,0 +1,21 @@
Copyright (c) 2005 Divmod, Inc., http://www.divmod.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,36 @@
pyflakes
========
This version of PyFlakes_ has been improved to use Python's newer ``ast``
module, instead of ``compiler``. So code checking happens faster, and will stay
up to date with new language changes.
.. _PyFlakes: http://http://www.divmod.org/trac/wiki/DivmodPyflakes
TODO
----
Importing several modules from the same package results in unnecessary warnings:
::
import a.b
import a.c # Redefinition of unused "a" from line 1
The following construct for defining a function differently depending on some
condition results in a redefinition warning:
::
if some_condition:
def foo(): do_foo()
else:
def foo(): do_bar() # redefinition of function 'foo' from line 2
IDE Integration
---------------
* vim: pyflakes-vim_
.. _pyflakes-vim: http://github.com/kevinw/pyflakes-vim

View File

@@ -0,0 +1,11 @@
- Check for methods that override other methods except that they vary by case.
- assign/increment + unbound local error not caught
def foo():
bar = 5
def meep():
bar += 2
meep()
print bar
print foo()

View File

@@ -0,0 +1,4 @@
#!/usr/bin/python
from pyflakes.scripts.pyflakes import main
main()

Binary file not shown.

View File

@@ -0,0 +1,311 @@
# -*- coding: utf-8 -*-
"""
ast
~~~
The `ast` module helps Python applications to process trees of the Python
abstract syntax grammar. The abstract syntax itself might change with
each Python release; this module helps to find out programmatically what
the current grammar looks like and allows modifications of it.
An abstract syntax tree can be generated by passing `ast.PyCF_ONLY_AST` as
a flag to the `compile()` builtin function or by using the `parse()`
function from this module. The result will be a tree of objects whose
classes all inherit from `ast.AST`.
A modified abstract syntax tree can be compiled into a Python code object
using the built-in `compile()` function.
Additionally various helper functions are provided that make working with
the trees simpler. The main intention of the helper functions and this
module in general is to provide an easy to use interface for libraries
that work tightly with the python syntax (template engines for example).
:copyright: Copyright 2008 by Armin Ronacher.
:license: Python License.
"""
from _ast import *
from _ast import __version__
def parse(expr, filename='<unknown>', mode='exec'):
"""
Parse an expression into an AST node.
Equivalent to compile(expr, filename, mode, PyCF_ONLY_AST).
"""
return compile(expr, filename, mode, PyCF_ONLY_AST)
def literal_eval(node_or_string):
"""
Safely evaluate an expression node or a string containing a Python
expression. The string or node provided may only consist of the following
Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
and None.
"""
_safe_names = {'None': None, 'True': True, 'False': False}
if isinstance(node_or_string, basestring):
node_or_string = parse(node_or_string, mode='eval')
if isinstance(node_or_string, Expression):
node_or_string = node_or_string.body
def _convert(node):
if isinstance(node, Str):
return node.s
elif isinstance(node, Num):
return node.n
elif isinstance(node, Tuple):
return tuple(map(_convert, node.elts))
elif isinstance(node, List):
return list(map(_convert, node.elts))
elif isinstance(node, Dict):
return dict((_convert(k), _convert(v)) for k, v
in zip(node.keys, node.values))
elif isinstance(node, Name):
if node.id in _safe_names:
return _safe_names[node.id]
raise ValueError('malformed string')
return _convert(node_or_string)
def dump(node, annotate_fields=True, include_attributes=False):
"""
Return a formatted dump of the tree in *node*. This is mainly useful for
debugging purposes. The returned string will show the names and the values
for fields. This makes the code impossible to evaluate, so if evaluation is
wanted *annotate_fields* must be set to False. Attributes such as line
numbers and column offsets are not dumped by default. If this is wanted,
*include_attributes* can be set to True.
"""
def _format(node):
if isinstance(node, AST):
fields = [(a, _format(b)) for a, b in iter_fields(node)]
rv = '%s(%s' % (node.__class__.__name__, ', '.join(
('%s=%s' % field for field in fields)
if annotate_fields else
(b for a, b in fields)
))
if include_attributes and node._attributes:
rv += fields and ', ' or ' '
rv += ', '.join('%s=%s' % (a, _format(getattr(node, a)))
for a in node._attributes)
return rv + ')'
elif isinstance(node, list):
return '[%s]' % ', '.join(_format(x) for x in node)
return repr(node)
if not isinstance(node, AST):
raise TypeError('expected AST, got %r' % node.__class__.__name__)
return _format(node)
def copy_location(new_node, old_node):
"""
Copy source location (`lineno` and `col_offset` attributes) from
*old_node* to *new_node* if possible, and return *new_node*.
"""
for attr in 'lineno', 'col_offset':
if attr in old_node._attributes and attr in new_node._attributes \
and hasattr(old_node, attr):
setattr(new_node, attr, getattr(old_node, attr))
return new_node
def fix_missing_locations(node):
"""
When you compile a node tree with compile(), the compiler expects lineno and
col_offset attributes for every node that supports them. This is rather
tedious to fill in for generated nodes, so this helper adds these attributes
recursively where not already set, by setting them to the values of the
parent node. It works recursively starting at *node*.
"""
def _fix(node, lineno, col_offset):
if 'lineno' in node._attributes:
if not hasattr(node, 'lineno'):
node.lineno = lineno
else:
lineno = node.lineno
if 'col_offset' in node._attributes:
if not hasattr(node, 'col_offset'):
node.col_offset = col_offset
else:
col_offset = node.col_offset
for child in iter_child_nodes(node):
_fix(child, lineno, col_offset)
_fix(node, 1, 0)
return node
def add_col_end(node):
def _fix(node, next):
children = list(iter_child_nodes(node))
for i, child in enumerate(children):
next_offset = children[i+1].col_offset if i < len(children) else next.col_offset
child.col_end = next_offset
def increment_lineno(node, n=1):
"""
Increment the line number of each node in the tree starting at *node* by *n*.
This is useful to "move code" to a different location in a file.
"""
if 'lineno' in node._attributes:
node.lineno = getattr(node, 'lineno', 0) + n
for child in walk(node):
if 'lineno' in child._attributes:
child.lineno = getattr(child, 'lineno', 0) + n
return node
def iter_fields(node):
"""
Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
that is present on *node*.
"""
if node._fields is None:
return
for field in node._fields:
try:
yield field, getattr(node, field)
except AttributeError:
pass
def iter_child_nodes(node):
"""
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, field in iter_fields(node):
if isinstance(field, AST):
yield field
elif isinstance(field, list):
for item in field:
if isinstance(item, AST):
yield item
def get_docstring(node, clean=True):
"""
Return the docstring for the given node or None if no docstring can
be found. If the node provided does not have docstrings a TypeError
will be raised.
"""
if not isinstance(node, (FunctionDef, ClassDef, Module)):
raise TypeError("%r can't have docstrings" % node.__class__.__name__)
if node.body and isinstance(node.body[0], Expr) and \
isinstance(node.body[0].value, Str):
if clean:
import inspect
return inspect.cleandoc(node.body[0].value.s)
return node.body[0].value.s
def walk(node):
"""
Recursively yield all child nodes of *node*, in no specified order. This is
useful if you only want to modify nodes in place and don't care about the
context.
"""
from collections import deque
todo = deque([node])
while todo:
node = todo.popleft()
todo.extend(iter_child_nodes(node))
yield node
class NodeVisitor(object):
"""
A node visitor base class that walks the abstract syntax tree and calls a
visitor function for every node found. This function may return a value
which is forwarded by the `visit` method.
This class is meant to be subclassed, with the subclass adding visitor
methods.
Per default the visitor functions for the nodes are ``'visit_'`` +
class name of the node. So a `TryFinally` node visit function would
be `visit_TryFinally`. This behavior can be changed by overriding
the `visit` method. If no visitor function exists for a node
(return value `None`) the `generic_visit` visitor is used instead.
Don't use the `NodeVisitor` if you want to apply changes to nodes during
traversing. For this a special visitor exists (`NodeTransformer`) that
allows modifications.
"""
def visit(self, node):
"""Visit a node."""
method = 'visit_' + node.__class__.__name__
visitor = getattr(self, method, self.generic_visit)
return visitor(node)
def generic_visit(self, node):
"""Called if no explicit visitor function exists for a node."""
for field, value in iter_fields(node):
if isinstance(value, list):
for item in value:
if isinstance(item, AST):
self.visit(item)
elif isinstance(value, AST):
self.visit(value)
class NodeTransformer(NodeVisitor):
"""
A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
allows modification of nodes.
The `NodeTransformer` will walk the AST and use the return value of the
visitor methods to replace or remove the old node. If the return value of
the visitor method is ``None``, the node will be removed from its location,
otherwise it is replaced with the return value. The return value may be the
original node in which case no replacement takes place.
Here is an example transformer that rewrites all occurrences of name lookups
(``foo``) to ``data['foo']``::
class RewriteName(NodeTransformer):
def visit_Name(self, node):
return copy_location(Subscript(
value=Name(id='data', ctx=Load()),
slice=Index(value=Str(s=node.id)),
ctx=node.ctx
), node)
Keep in mind that if the node you're operating on has child nodes you must
either transform the child nodes yourself or call the :meth:`generic_visit`
method for the node first.
For nodes that were part of a collection of statements (that applies to all
statement nodes), the visitor may also return a list of nodes rather than
just a single node.
Usually you use the transformer like this::
node = YourTransformer().visit(node)
"""
def generic_visit(self, node):
for field, old_value in iter_fields(node):
old_value = getattr(node, field, None)
if isinstance(old_value, list):
new_values = []
for value in old_value:
if isinstance(value, AST):
value = self.visit(value)
if value is None:
continue
elif not isinstance(value, AST):
new_values.extend(value)
continue
new_values.append(value)
old_value[:] = new_values
elif isinstance(old_value, AST):
new_node = self.visit(old_value)
if new_node is None:
delattr(node, field)
else:
setattr(node, field, new_node)
return node

Binary file not shown.

View File

@@ -0,0 +1,389 @@
import ast
from pyflakes import messages
import __builtin__
allowed_before_future = (ast.Module, ast.ImportFrom, ast.Expr, ast.Str)
defined_names = set(('__file__', '__builtins__'))
class Binding(object):
"""
@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):
def __init__(self, name, source):
name = name.split('.')[0]
super(Importation, self).__init__(name, source)
class Assignment(Binding):
pass
class FunctionDefinition(Binding):
pass
class Scope(dict):
import_starred = False # set to True when import * is found
def __repr__(self):
return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self))
def __init__(self):
super(Scope, self).__init__()
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
class Checker(ast.NodeVisitor):
def __init__(self, tree, filename='(none)', builtins = None):
ast.NodeVisitor.__init__(self)
self.deferred = []
self.dead_scopes = []
self.messages = []
self.filename = filename
self.scope_stack = [ModuleScope()]
self.futures_allowed = True
self.builtins = frozenset(builtins or [])
self.visit(tree)
for handler, scope in self.deferred:
self.scope_stack = scope
handler()
del self.scope_stack[1:]
self.pop_scope()
self.check_dead_scopes()
def defer(self, callable):
'''Schedule something to be called after 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.deferred.append( (callable, self.scope_stack[:]) )
def check_dead_scopes(self):
# Check for modules that were imported but unused
for scope in self.dead_scopes:
for importation in scope.itervalues():
if isinstance(importation, Importation) and not importation.used:
self.report(messages.UnusedImport, importation.source.lineno, importation.name)
def push_function_scope(self):
self.scope_stack.append(FunctionScope())
def push_class_scope(self):
self.scope_stack.append(ClassScope())
def pop_scope(self):
scope = self.scope_stack.pop()
self.dead_scopes.append(scope)
@property
def scope(self):
return self.scope_stack[-1]
def report(self, message_class, *args, **kwargs):
self.messages.append(message_class(self.filename, *args, **kwargs))
def visit_Import(self, node):
for name_node in node.names:
# "import bar as foo" -> name=bar, asname=foo
name = name_node.asname or name_node.name
self.add_binding(node, Importation(name, node))
def visit_GeneratorExp(self, node):
for generator in node.generators:
self.visit(generator.iter)
self.assign_vars(generator.target)
for generator in node.generators:
if hasattr(node, 'elt'):
self.visit(node.elt)
self.visit_nodes(generator.ifs)
visit_ListComp = visit_GeneratorExp
def visit_For(self, node):
'''
Process bindings for loop variables.
'''
self.visit_nodes(node.iter)
for var in self.flatten(node.target):
upval = self.scope.get(var.id)
if isinstance(upval, Importation) and upval.used:
self.report(messages.ImportShadowedByLoopVar,
node.lineno, node.col_offset, var.id, upval.source.lineno)
self.add_binding(var, Assignment(var.id, var))
self.visit_nodes(node.body + node.orelse)
def visit_FunctionDef(self, node):
try:
decorators = node.decorator_list
except AttributeError:
# Use .decorators for Python 2.5 compatibility
decorators = node.decorators
self.visit_nodes(decorators)
self.add_binding(node, FunctionDefinition(node.name, node))
self.visit_Lambda(node)
def visit_Lambda(self, node):
self.visit_nodes(node.args.defaults)
def run_function():
self.push_function_scope()
# Check for duplicate arguments
argnames = set()
for arg in self.flatten(node.args.args):
if arg.id in argnames:
self.report(messages.DuplicateArgument, arg.lineno, arg.col_offset, arg.id)
argnames.add(arg.id)
self.assign_vars(node.args.args, report_redef=False)
if node.args.vararg is not None:
self.add_binding(node, Assignment(node.args.vararg, node), False)
if node.args.kwarg is not None:
self.add_binding(node, Assignment(node.args.kwarg, node), False)
self.visit_nodes(node.body)
self.pop_scope()
self.defer(run_function)
def visit_Name(self, node):
'''
Locate names in locals / function / globals scopes.
'''
scope, name = self.scope, node.id
# try local scope
import_starred = scope.import_starred
try:
scope[name].used = (scope, node.lineno, node.col_offset)
except KeyError:
pass
else:
return
# try enclosing function scopes
for func_scope in self.scope_stack[-2:0:-1]:
import_starred = import_starred or func_scope.import_starred
if not isinstance(func_scope, FunctionScope):
continue
try:
func_scope[name].used = (scope, node.lineno, node.col_offset)
except KeyError:
pass
else:
return
# try global scope
import_starred = import_starred or self.scope_stack[0].import_starred
try:
self.scope_stack[0][node.id].used = (scope, node.lineno, node.col_offset)
except KeyError:
if not import_starred and not self.is_builtin(name):
self.report(messages.UndefinedName, node.lineno, node.col_offset, name)
def assign_vars(self, targets, report_redef=True):
scope = self.scope
for target in self.flatten(targets):
name = target.id
# if the name hasn't already been defined in the current scope
if isinstance(scope, FunctionScope) and name not in scope:
# for each function or module scope above us
for upscope in self.scope_stack[:-1]:
if not isinstance(upscope, (FunctionScope, ModuleScope)):
continue
upval = upscope.get(name)
# 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 upval is not None:
if upval.used and upval.used[0] is scope and name not in scope.globals:
# then it's probably a mistake
self.report(messages.UndefinedLocal,
upval.used[1], upval.used[2], name, upval.source.lineno, upval.source.col_offset)
self.add_binding(target, Assignment(name, target), report_redef)
def visit_Assign(self, node):
for target in node.targets:
self.visit_nodes(node.value)
self.assign_vars(node.targets)
def visit_Delete(self, node):
for target in self.flatten(node.targets):
if isinstance(self.scope, FunctionScope) and target.id in self.scope.globals:
del self.scope.globals[target.id]
else:
self.add_binding(target, UnBinding(target.id, target))
def visit_With(self, node):
self.visit(node.context_expr)
# handle new bindings made by optional "as" part
if node.optional_vars is not None:
self.assign_vars(node.optional_vars)
self.visit_nodes(node.body)
def visit_ImportFrom(self, node):
if node.module == '__future__':
if not self.futures_allowed:
self.report(messages.LateFutureImport, node.lineno, node.col_offset, [alias.name for alias in node.names])
else:
self.futures_allowed = False
for alias in node.names:
if alias.name == '*':
self.scope.import_starred = True
self.report(messages.ImportStarUsed, node.lineno, node.col_offset, node.module)
continue
name = alias.asname or alias.name
importation = Importation(name, node)
if node.module == '__future__':
importation.used = (self.scope, node.lineno, node.col_offset)
self.add_binding(node, importation)
def visit_Global(self, node):
'''
Keep track of global declarations.
'''
scope = self.scope
if isinstance(scope, FunctionScope):
scope.globals.update(dict.fromkeys(node.names))
def visit_ClassDef(self, node):
self.add_binding(node, Assignment(node.name, node))
self.visit_nodes(node.bases)
self.push_class_scope()
self.visit_nodes(node.body)
self.pop_scope()
def visit_excepthandler(self, node):
if node.type is not None:
self.visit(node.type)
if node.name is not None:
self.assign_vars(node.name)
self.visit_nodes(node.body)
visit_ExceptHandler = visit_excepthandler # in 2.6, this was CamelCased
def flatten(self, nodes):
if isinstance(nodes, ast.Attribute):
self.visit(nodes)
return []
elif isinstance(nodes, ast.Subscript):
self.visit(nodes.value)
self.visit(nodes.slice)
return []
elif isinstance(nodes, ast.Name):
return [nodes]
elif isinstance(nodes, (ast.Tuple, ast.List)):
return self.flatten(nodes.elts)
flattened_nodes = []
for node in nodes:
if hasattr(node, 'elts'):
flattened_nodes += self.flatten(node.elts)
elif node is not None:
flattened_nodes += self.flatten(node)
return flattened_nodes
def add_binding(self, node, value, report_redef=True):
line, col, scope, name = node.lineno, node.col_offset, self.scope, value.name
# Check for a redefined function
func = scope.get(name)
if (isinstance(func, FunctionDefinition) and isinstance(value, FunctionDefinition)):
self.report(messages.RedefinedFunction, line, name, func.source.lineno)
# Check for redefining an unused import
if report_redef and not isinstance(scope, ClassScope):
for up_scope in self.scope_stack[::-1]:
upval = up_scope.get(name)
if isinstance(upval, Importation) and not upval.used:
self.report(messages.RedefinedWhileUnused, line, col, name, upval.source.lineno)
# Check for "del undefined_name"
if isinstance(value, UnBinding):
try:
del scope[name]
except KeyError:
self.report(messages.UndefinedName, line, col, name)
else:
scope[name] = value
def visit(self, node):
if not isinstance(node, allowed_before_future):
self.futures_allowed = False
return super(Checker, self).visit(node)
def visit_nodes(self, nodes):
try:
nodes = list(getattr(nodes, 'elts', nodes))
except TypeError:
nodes = [nodes]
for node in nodes:
self.visit(node)
def is_builtin(self, name):
if hasattr(__builtin__, name):
return True
if name in defined_names:
return True
if name in self.builtins:
return True
return False

Binary file not shown.

View File

@@ -0,0 +1,77 @@
# (c) 2005 Divmod, Inc. See LICENSE file for details
class Message(object):
message = ''
message_args = ()
def __init__(self, filename, lineno, col = None):
self.filename = filename
self.lineno = lineno
self.col = col
def __str__(self):
if self.col is not None:
return '%s:%s(%d): %s' % (self.filename, self.lineno, self.col, self.message % self.message_args)
else:
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, col, 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, col, name, orig_lineno):
Message.__init__(self, filename, lineno, col)
self.message_args = (name, orig_lineno)
class ImportStarUsed(Message):
message = "'from %s import *' used; unable to detect undefined names"
def __init__(self, filename, lineno, col, modname):
Message.__init__(self, filename, lineno, col)
self.message_args = (modname,)
class UndefinedName(Message):
message = 'undefined name %r'
def __init__(self, filename, lineno, col, name):
Message.__init__(self, filename, lineno, col)
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, col, name, orig_lineno, orig_col):
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, col, name):
Message.__init__(self, filename, lineno, col)
self.message_args = (name,)
class RedefinedFunction(Message):
message = 'redefinition of function %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, col, names):
Message.__init__(self, filename, lineno)
self.message_args = (names,)

Binary file not shown.

View File

@@ -0,0 +1,63 @@
"""
Implementation of the command-line I{pyflakes} tool.
"""
import _ast
import sys
import os
checker = __import__('pyflakes.checker').checker
def check(codeString, filename):
try:
tree = compile(codeString, filename, 'exec', _ast.PyCF_ONLY_AST)
except (SyntaxError, IndentationError):
value = sys.exc_info()[1]
try:
(lineno, offset, line) = value[1][1:]
except IndexError:
print >> sys.stderr, 'could not compile %r' % (filename,)
return 1
if line.endswith("\n"):
line = line[:-1]
print >> sys.stderr, '%s:%d: could not compile' % (filename, lineno)
print >> sys.stderr, line
print >> sys.stderr, " " * (offset-2), "^"
return 1
else:
w = checker.Checker(tree, filename)
w.messages.sort(lambda a, b: cmp(a.lineno, b.lineno))
for warning in w.messages:
print warning
return len(w.messages)
def checkPath(filename):
"""
Check the given path, printing out any warnings detected.
@return: the number of warnings printed
"""
if os.path.exists(filename):
return check(file(filename, 'U').read() + '\n', filename)
else:
print >> sys.stderr, '%s: no such file' % (filename,)
return 1
def main():
warnings = 0
args = sys.argv[1:]
if args:
for arg in args:
if os.path.isdir(arg):
for dirpath, dirnames, filenames in os.walk(arg):
for filename in filenames:
if filename.endswith('.py'):
warnings += checkPath(os.path.join(dirpath, filename))
else:
warnings += checkPath(arg)
else:
warnings += check(sys.stdin.read(), '<stdin>')
raise SystemExit(warnings > 0)

View File

@@ -0,0 +1,24 @@
import textwrap
from twisted.trial import unittest
from pyflakes import checker, ast
class Test(unittest.TestCase):
def flakes(self, input, *expectedOutputs):
w = checker.Checker(ast.parse(textwrap.dedent(input)))
outputs = [type(o) for o in w.messages]
expectedOutputs = list(expectedOutputs)
outputs.sort()
expectedOutputs.sort()
self.assert_(outputs == expectedOutputs, '''\
for input:
%s
expected outputs:
%s
but got:
%s''' % (input, repr(expectedOutputs), '\n'.join([str(o) for o in w.messages])))
return w

View File

@@ -0,0 +1,512 @@
from sys import version_info
from pyflakes import messages as m
from pyflakes.test import harness
class Test(harness.Test):
def test_unusedImport(self):
self.flakes('import fu, bar', m.UnusedImport, m.UnusedImport)
self.flakes('from baz import fu, bar', m.UnusedImport, m.UnusedImport)
def test_aliasedImport(self):
self.flakes('import fu as FU, bar as FU', m.RedefinedWhileUnused, m.UnusedImport)
self.flakes('from moo import fu as FU, bar as FU', m.RedefinedWhileUnused, m.UnusedImport)
def test_usedImport(self):
self.flakes('import fu; print fu')
self.flakes('from baz import fu; print fu')
def test_redefinedWhileUnused(self):
self.flakes('import fu; fu = 3', m.RedefinedWhileUnused)
self.flakes('import fu; del fu', m.RedefinedWhileUnused)
self.flakes('import fu; fu, bar = 3', m.RedefinedWhileUnused)
self.flakes('import fu; [fu, bar] = 3', m.RedefinedWhileUnused)
def test_redefinedByFunction(self):
self.flakes('''
import fu
def fu():
pass
''', m.RedefinedWhileUnused)
def test_redefinedInNestedFunction(self):
"""
Test that shadowing a global name with a nested function definition
generates a warning.
"""
self.flakes('''
import fu
def bar():
def baz():
def fu():
pass
''', m.RedefinedWhileUnused, m.UnusedImport)
def test_redefinedByClass(self):
self.flakes('''
import fu
class fu:
pass
''', m.RedefinedWhileUnused)
def test_redefinedInClass(self):
"""
Test that shadowing a global with a class attribute does not produce a
warning.
"""
self.flakes('''
import fu
class bar:
fu = 1
print fu
''')
def test_usedInFunction(self):
self.flakes('''
import fu
def fun():
print fu
''')
def test_shadowedByParameter(self):
self.flakes('''
import fu
def fun(fu):
print fu
''', m.UnusedImport)
self.flakes('''
import fu
def fun(fu):
print fu
print fu
''')
def test_newAssignment(self):
self.flakes('fu = None')
def test_usedInGetattr(self):
self.flakes('import fu; fu.bar.baz')
self.flakes('import fu; "bar".fu.baz', m.UnusedImport)
def test_usedInSlice(self):
self.flakes('import fu; print fu.bar[1:]')
def test_usedInIfBody(self):
self.flakes('''
import fu
if True: print fu
''')
def test_usedInIfConditional(self):
self.flakes('''
import fu
if fu: pass
''')
def test_usedInElifConditional(self):
self.flakes('''
import fu
if False: pass
elif fu: pass
''')
def test_usedInElse(self):
self.flakes('''
import fu
if False: pass
else: print fu
''')
def test_usedInCall(self):
self.flakes('import fu; fu.bar()')
def test_usedInClass(self):
self.flakes('''
import fu
class bar:
bar = fu
''')
def test_usedInClassBase(self):
self.flakes('''
import fu
class bar(object, fu.baz):
pass
''')
def test_notUsedInNestedScope(self):
self.flakes('''
import fu
def bleh():
pass
print fu
''')
def test_usedInFor(self):
self.flakes('''
import fu
for bar in range(9):
print fu
''')
def test_usedInForElse(self):
self.flakes('''
import fu
for bar in range(10):
pass
else:
print fu
''')
def test_redefinedByFor(self):
self.flakes('''
import fu
for fu in range(2):
pass
''', m.RedefinedWhileUnused)
def test_shadowedByFor(self):
"""
Test that shadowing a global name with a for loop variable generates a
warning.
"""
self.flakes('''
import fu
fu.bar()
for fu in ():
pass
''', m.ImportShadowedByLoopVar)
def test_shadowedByForDeep(self):
"""
Test that shadowing a global name with a for loop variable nested in a
tuple unpack generates a warning.
"""
self.flakes('''
import fu
fu.bar()
for (x, y, z, (a, b, c, (fu,))) in ():
pass
''', m.ImportShadowedByLoopVar)
def test_usedInReturn(self):
self.flakes('''
import fu
def fun():
return fu
''')
def test_usedInOperators(self):
self.flakes('import fu; 3 + fu.bar')
self.flakes('import fu; 3 % fu.bar')
self.flakes('import fu; 3 - fu.bar')
self.flakes('import fu; 3 * fu.bar')
self.flakes('import fu; 3 ** fu.bar')
self.flakes('import fu; 3 / fu.bar')
self.flakes('import fu; 3 // fu.bar')
self.flakes('import fu; -fu.bar')
self.flakes('import fu; ~fu.bar')
self.flakes('import fu; 1 == fu.bar')
self.flakes('import fu; 1 | fu.bar')
self.flakes('import fu; 1 & fu.bar')
self.flakes('import fu; 1 ^ fu.bar')
self.flakes('import fu; 1 >> fu.bar')
self.flakes('import fu; 1 << fu.bar')
def test_usedInAssert(self):
self.flakes('import fu; assert fu.bar')
def test_usedInSubscript(self):
self.flakes('import fu; fu.bar[1]')
def test_usedInLogic(self):
self.flakes('import fu; fu and False')
self.flakes('import fu; fu or False')
self.flakes('import fu; not fu.bar')
def test_usedInList(self):
self.flakes('import fu; [fu]')
def test_usedInTuple(self):
self.flakes('import fu; (fu,)')
def test_usedInTry(self):
self.flakes('''
import fu
try: fu
except: pass
''')
def test_usedInExcept(self):
self.flakes('''
import fu
try: fu
except: pass
''')
def test_redefinedByExcept(self):
self.flakes('''
import fu
try: pass
except Exception, fu: pass
''', m.RedefinedWhileUnused)
def test_usedInRaise(self):
self.flakes('''
import fu
raise fu.bar
''')
def test_usedInYield(self):
self.flakes('''
import fu
def gen():
yield fu
''')
def test_usedInDict(self):
self.flakes('import fu; {fu:None}')
self.flakes('import fu; {1:fu}')
def test_usedInParameterDefault(self):
self.flakes('''
import fu
def f(bar=fu):
pass
''')
def test_usedInAttributeAssign(self):
self.flakes('import fu; fu.bar = 1')
def test_usedInKeywordArg(self):
self.flakes('import fu; fu.bar(stuff=fu)')
def test_usedInAssignment(self):
self.flakes('import fu; bar=fu')
self.flakes('import fu; n=0; n+=fu')
def test_usedInListComp(self):
self.flakes('import fu; [fu for _ in range(1)]')
self.flakes('import fu; [1 for _ in range(1) if fu]')
def test_redefinedByListComp(self):
self.flakes('import fu; [1 for fu in range(1)]', m.RedefinedWhileUnused)
def test_usedInTryFinally(self):
self.flakes('''
import fu
try: pass
finally: fu
''')
self.flakes('''
import fu
try: fu
finally: pass
''')
def test_usedInWhile(self):
self.flakes('''
import fu
while 0:
fu
''')
self.flakes('''
import fu
while fu: pass
''')
def test_usedInGlobal(self):
self.flakes('''
import fu
def f(): global fu
''', m.UnusedImport)
def test_usedInBackquote(self):
self.flakes('import fu; `fu`')
def test_usedInExec(self):
self.flakes('import fu; exec "print 1" in fu.bar')
def test_usedInLambda(self):
self.flakes('import fu; lambda: fu')
def test_shadowedByLambda(self):
self.flakes('import fu; lambda fu: fu', m.UnusedImport)
def test_usedInSliceObj(self):
self.flakes('import fu; "meow"[::fu]')
def test_unusedInNestedScope(self):
self.flakes('''
def bar():
import fu
fu
''', m.UnusedImport, m.UndefinedName)
def test_methodsDontUseClassScope(self):
self.flakes('''
class bar:
import fu
def fun(self):
fu
''', m.UnusedImport, m.UndefinedName)
def test_nestedFunctionsNestScope(self):
self.flakes('''
def a():
def b():
fu
import fu
''')
def test_nestedClassAndFunctionScope(self):
self.flakes('''
def a():
import fu
class b:
def c(self):
print fu
''')
def test_importStar(self):
self.flakes('from fu import *', m.ImportStarUsed)
def test_packageImport(self):
self.flakes('import fu.bar; fu.bar')
test_packageImport.todo = "this has been hacked to treat 'import fu.bar' as just 'import fu'"
def test_assignRHSFirst(self):
self.flakes('import fu; fu = fu')
self.flakes('import fu; fu, bar = fu')
self.flakes('import fu; [fu, bar] = fu')
self.flakes('import fu; fu += fu')
def test_tryingMultipleImports(self):
self.flakes('''
try:
import fu
except ImportError:
import bar as fu
''')
test_tryingMultipleImports.todo = ''
def test_nonGlobalDoesNotRedefine(self):
self.flakes('''
import fu
def a():
fu = 3
fu
''')
def test_functionsRunLater(self):
self.flakes('''
def a():
fu
import fu
''')
def test_functionNamesAreBoundNow(self):
self.flakes('''
import fu
def fu():
fu
fu
''', m.RedefinedWhileUnused)
def test_ignoreNonImportRedefinitions(self):
self.flakes('a = 1; a = 2')
def test_importingForImportError(self):
self.flakes('''
try:
import fu
except ImportError:
pass
''')
test_importingForImportError.todo = ''
def test_explicitlyPublic(self):
'''imports mentioned in __all__ are not unused'''
self.flakes('import fu; __all__ = ["fu"]')
test_explicitlyPublic.todo = "this would require importing the module or doing smarter parsing"
def test_importedInClass(self):
'''Imports in class scope can be used through self'''
self.flakes('''
class c:
import i
def __init__(self):
self.i
''')
test_importedInClass.todo = 'requires evaluating attribute access'
def test_futureImport(self):
'''__future__ is special'''
self.flakes('from __future__ import division')
def test_futureImportFirst(self):
"""
__future__ imports must come before anything else.
"""
self.flakes('''
x = 5
from __future__ import division
''', m.LateFutureImport)
class Python24Tests(harness.Test):
"""
Tests for checking of syntax which is valid in Python 2.4 and newer.
"""
if version_info < (2, 4):
skip = "Python 2.4 required for generator expression and decorator tests."
def test_usedInGenExp(self):
"""
Using a global in a generator expression results in no warnings.
"""
self.flakes('import fu; (fu for _ in range(1))')
self.flakes('import fu; (1 for _ in range(1) if fu)')
def test_redefinedByGenExp(self):
"""
Re-using a global name as the loop variable for a generator
expression results in a redefinition warning.
"""
self.flakes('import fu; (1 for fu in range(1))', m.RedefinedWhileUnused)
def test_usedAsDecorator(self):
"""
Using a global name in a decorator statement results in no warnings,
but using an undefined name in a decorator statement results in an
undefined name warning.
"""
self.flakes('''
from interior import decorate
@decorate
def f():
return "hello"
''')
self.flakes('''
from interior import decorate
@decorate('value')
def f():
return "hello"
''')
self.flakes('''
@decorate
def f():
return "hello"
''', m.UndefinedName)

View File

@@ -0,0 +1,234 @@
# (c) 2005-2008 Divmod, Inc.
# See LICENSE file for details
"""
Tests for various Pyflakes behavior.
"""
from sys import version_info
from pyflakes import messages as m
from pyflakes.test import harness
class Test(harness.Test):
def test_duplicateArgs(self):
self.flakes('def fu(bar, bar): pass', m.DuplicateArgument)
def test_localReferencedBeforeAssignment(self):
self.flakes('''
a = 1
def f():
a; a=1
f()
''', m.UndefinedName)
test_localReferencedBeforeAssignment.todo = 'this requires finding all assignments in the function body first'
def test_redefinedFunction(self):
"""
Test that shadowing a function definition with another one raises a
warning.
"""
self.flakes('''
def a(): pass
def a(): pass
''', m.RedefinedFunction)
def test_redefinedClassFunction(self):
"""
Test that shadowing a function definition in a class suite with another
one raises a warning.
"""
self.flakes('''
class A:
def a(): pass
def a(): pass
''', m.RedefinedFunction)
def test_functionDecorator(self):
"""
Test that shadowing a function definition with a decorated version of
that function does not raise a warning.
"""
self.flakes('''
from somewhere import somedecorator
def a(): pass
a = somedecorator(a)
''')
def test_classFunctionDecorator(self):
"""
Test that shadowing a function definition in a class suite with a
decorated version of that function does not raise a warning.
"""
self.flakes('''
class A:
def a(): pass
a = classmethod(a)
''')
def test_unaryPlus(self):
'''Don't die on unary +'''
self.flakes('+1')
class Python25Test(harness.Test):
"""
Tests for checking of syntax only available in Python 2.5 and newer.
"""
if version_info < (2, 5):
skip = "Python 2.5 required for if-else and with tests"
def test_ifexp(self):
"""
Test C{foo if bar else baz} statements.
"""
self.flakes("a = 'moo' if True else 'oink'")
self.flakes("a = foo if True else 'oink'", m.UndefinedName)
self.flakes("a = 'moo' if True else bar", m.UndefinedName)
def test_withStatementNoNames(self):
"""
No warnings are emitted for using inside or after a nameless C{with}
statement a name defined beforehand.
"""
self.flakes('''
from __future__ import with_statement
bar = None
with open("foo"):
bar
bar
''')
def test_withStatementSingleName(self):
"""
No warnings are emitted for using a name defined by a C{with} statement
within the suite or afterwards.
"""
self.flakes('''
from __future__ import with_statement
with open('foo') as bar:
bar
bar
''')
def test_withStatementTupleNames(self):
"""
No warnings are emitted for using any of the tuple of names defined by
a C{with} statement within the suite or afterwards.
"""
self.flakes('''
from __future__ import with_statement
with open('foo') as (bar, baz):
bar, baz
bar, baz
''')
def test_withStatementSingleNameUndefined(self):
"""
An undefined name warning is emitted if the name first defined by a
C{with} statement is used before the C{with} statement.
"""
self.flakes('''
from __future__ import with_statement
bar
with open('foo') as bar:
pass
''', m.UndefinedName)
def test_withStatementTupleNamesUndefined(self):
"""
An undefined name warning is emitted if a name first defined by a the
tuple-unpacking form of the C{with} statement is used before the
C{with} statement.
"""
self.flakes('''
from __future__ import with_statement
baz
with open('foo') as (bar, baz):
pass
''', m.UndefinedName)
def test_withStatementSingleNameRedefined(self):
"""
A redefined name warning is emitted if a name bound by an import is
rebound by the name defined by a C{with} statement.
"""
self.flakes('''
from __future__ import with_statement
import bar
with open('foo') as bar:
pass
''', m.RedefinedWhileUnused)
def test_withStatementTupleNamesRedefined(self):
"""
A redefined name warning is emitted if a name bound by an import is
rebound by one of the names defined by the tuple-unpacking form of a
C{with} statement.
"""
self.flakes('''
from __future__ import with_statement
import bar
with open('foo') as (bar, baz):
pass
''', m.RedefinedWhileUnused)
def test_withStatementUndefinedInside(self):
"""
An undefined name warning is emitted if a name is used inside the
body of a C{with} statement without first being bound.
"""
self.flakes('''
from __future__ import with_statement
with open('foo') as bar:
baz
''', m.UndefinedName)
def test_withStatementNameDefinedInBody(self):
"""
A name defined in the body of a C{with} statement can be used after
the body ends without warning.
"""
self.flakes('''
from __future__ import with_statement
with open('foo') as bar:
baz = 10
baz
''')
def test_withStatementUndefinedInExpression(self):
"""
An undefined name warning is emitted if a name in the I{test}
expression of a C{with} statement is undefined.
"""
self.flakes('''
from __future__ import with_statement
with bar as baz:
pass
''', m.UndefinedName)
self.flakes('''
from __future__ import with_statement
with bar as bar:
pass
''', m.UndefinedName)
def test_listNestedListComprehension(self):
self.flakes('''
root = [['213', '123'], ['4354']]
foo = [int(c) for group in root for c in group]
''')

View File

@@ -0,0 +1,48 @@
"""
Tests for L{pyflakes.scripts.pyflakes}.
"""
import sys
from StringIO import StringIO
from twisted.python.filepath import FilePath
from twisted.trial.unittest import TestCase
from pyflakes.scripts.pyflakes import checkPath
def withStderrTo(stderr, f):
"""
Call C{f} with C{sys.stderr} redirected to C{stderr}.
"""
(outer, sys.stderr) = (sys.stderr, stderr)
try:
return f()
finally:
sys.stderr = outer
class CheckTests(TestCase):
"""
Tests for L{check} and L{checkPath} which check a file for flakes.
"""
def test_missingTrailingNewline(self):
"""
Source which doesn't end with a newline shouldn't cause any
exception to be raised nor an error indicator to be returned by
L{check}.
"""
fName = self.mktemp()
FilePath(fName).setContent("def foo():\n\tpass\n\t")
self.assertFalse(checkPath(fName))
def test_checkPathNonExisting(self):
"""
L{checkPath} handles non-existing files.
"""
err = StringIO()
count = withStderrTo(err, lambda: checkPath('extremo'))
self.assertEquals(err.getvalue(), 'extremo: no such file\n')
self.assertEquals(count, 1)

View File

@@ -0,0 +1,182 @@
from sys import version_info
from pyflakes import messages as m
from pyflakes.test import harness
class Test(harness.Test):
def test_undefined(self):
self.flakes('bar', m.UndefinedName)
def test_definedInListComp(self):
self.flakes('[a for a in range(10) if a]')
def test_functionsNeedGlobalScope(self):
self.flakes('''
class a:
def b():
fu
fu = 1
''')
def test_builtins(self):
self.flakes('range(10)')
def test_magic_globals(self):
self.flakes('__file__')
def test_globalImportStar(self):
'''Can't find undefined names with import *'''
self.flakes('from fu import *; bar', m.ImportStarUsed)
def test_localImportStar(self):
'''A local import * still allows undefined names to be found in upper scopes'''
self.flakes('''
def a():
from fu import *
bar
''', m.ImportStarUsed, m.UndefinedName)
def test_unpackedParameter(self):
'''Unpacked function parameters create bindings'''
self.flakes('''
def a((bar, baz)):
bar; baz
''')
def test_definedByGlobal(self):
'''"global" can make an otherwise undefined name in another function defined'''
self.flakes('''
def a(): global fu; fu = 1
def b(): fu
''')
test_definedByGlobal.todo = ''
def test_del(self):
'''del deletes bindings'''
self.flakes('a = 1; del a; a', m.UndefinedName)
def test_delGlobal(self):
'''del a global binding from a function'''
self.flakes('''
a = 1
def f():
global a
del a
a
''')
def test_delUndefined(self):
'''del an undefined name'''
self.flakes('del a', m.UndefinedName)
def test_globalFromNestedScope(self):
'''global names are available from nested scopes'''
self.flakes('''
a = 1
def b():
def c():
a
''')
def test_laterRedefinedGlobalFromNestedScope(self):
"""
Test that referencing a local name that shadows a global, before it is
defined, generates a warning.
"""
self.flakes('''
a = 1
def fun():
a
a = 2
''', m.UndefinedLocal)
def test_laterRedefinedGlobalFromNestedScope2(self):
"""
Test that referencing a local name in a nested scope that shadows a
global declared in an enclosing scope, before it is defined, generates
a warning.
"""
self.flakes('''
a = 1
def fun():
global a
def fun2():
a
a = 2
''', m.UndefinedLocal)
def test_doubleNestingReportsClosestName(self):
"""
Test that referencing a local name in a nested scope that shadows a
variable declared in two different outer scopes before it is defined
in the innermost scope generates an UnboundLocal warning which
refers to the nearest shadowed name.
"""
exc = self.flakes('''
def a():
x = 1
def b():
x = 2 # line 5
def c():
x
x = 3
''', m.UndefinedLocal).messages[0]
self.assertEqual(exc.message_args, ('x', 5))
def test_laterRedefinedGlobalFromNestedScope3(self):
"""
Test that referencing a local name in a nested scope that shadows a
global, before it is defined, generates a warning.
"""
self.flakes('''
def fun():
a = 1
def fun2():
a
a = 1
''', m.UndefinedLocal)
def test_nestedClass(self):
'''nested classes can access enclosing scope'''
self.flakes('''
def f(foo):
class C:
bar = foo
def f(self):
return foo
return C()
f(123).f()
''')
def test_badNestedClass(self):
'''free variables in nested classes must bind at class creation'''
self.flakes('''
def f():
class C:
bar = foo
foo = 456
f()
''', m.UndefinedName)
class Python24Test(harness.Test):
"""
Tests for checking of syntax which is valid in Python 2.4 and newer.
"""
if version_info < (2, 4):
skip = "Python 2.4 required for generator expression tests."
def test_definedInGenExp(self):
"""
Using the loop variable of a generator expression results in no
warnings.
"""
self.flakes('(a for a in xrange(10) if a)')

View File

@@ -0,0 +1,19 @@
#!/usr/bin/python
# (c) 2005 Divmod, Inc. See LICENSE file for details
from distutils.core import setup
setup(
name="pyflakes",
license="MIT",
version="0.2.1",
description="passive checker of Python programs",
author="Phil Frost",
maintainer="Moe Aboulkheir",
maintainer_email="moe@divmod.com",
url="http://www.divmod.org/projects/pyflakes",
packages=["pyflakes", "pyflakes.scripts"],
scripts=["bin/pyflakes"],
long_description="""Pyflakes is program to analyze Python programs and detect various errors. It
works by parsing the source file, not importing it, so it is safe to use on
modules with side effects. It's also much faster.""")

View File

@@ -0,0 +1,446 @@
" -*- vim -*-
" FILE: python_fn.vim
" LAST MODIFICATION: 2008-08-28 8:19pm
" (C) Copyright 2001-2005 Mikael Berthe <bmikael@lists.lilotux.net>
" Maintained by Jon Franklin <jvfranklin@gmail.com>
" Version: 1.13
" USAGE:
"
" Save this file to $VIMFILES/ftplugin/python.vim. You can have multiple
" python ftplugins by creating $VIMFILES/ftplugin/python and saving your
" ftplugins in that directory. If saving this to the global ftplugin
" directory, this is the recommended method, since vim ships with an
" ftplugin/python.vim file already.
" You can set the global variable "g:py_select_leading_comments" to 0
" if you don't want to select comments preceding a declaration (these
" are usually the description of the function/class).
" You can set the global variable "g:py_select_trailing_comments" to 0
" if you don't want to select comments at the end of a function/class.
" If these variables are not defined, both leading and trailing comments
" are selected.
" Example: (in your .vimrc) "let g:py_select_leading_comments = 0"
" You may want to take a look at the 'shiftwidth' option for the
" shift commands...
"
" REQUIREMENTS:
" vim (>= 7)
"
" Shortcuts:
" ]t -- Jump to beginning of block
" ]e -- Jump to end of block
" ]v -- Select (Visual Line Mode) block
" ]< -- Shift block to left
" ]> -- Shift block to right
" ]# -- Comment selection
" ]u -- Uncomment selection
" ]c -- Select current/previous class
" ]d -- Select current/previous function
" ]<up> -- Jump to previous line with the same/lower indentation
" ]<down> -- Jump to next line with the same/lower indentation
" Only do this when not done yet for this buffer
if exists("b:loaded_py_ftplugin")
finish
endif
let b:loaded_py_ftplugin = 1
map ]t :PBoB<CR>
vmap ]t :<C-U>PBOB<CR>m'gv``
map ]e :PEoB<CR>
vmap ]e :<C-U>PEoB<CR>m'gv``
map ]v ]tV]e
map ]< ]tV]e<
vmap ]< <
map ]> ]tV]e>
vmap ]> >
map ]# :call PythonCommentSelection()<CR>
vmap ]# :call PythonCommentSelection()<CR>
map ]u :call PythonUncommentSelection()<CR>
vmap ]u :call PythonUncommentSelection()<CR>
map ]c :call PythonSelectObject("class")<CR>
map ]d :call PythonSelectObject("function")<CR>
map ]<up> :call PythonNextLine(-1)<CR>
map ]<down> :call PythonNextLine(1)<CR>
" You may prefer use <s-up> and <s-down>... :-)
" jump to previous class
map ]J :call PythonDec("class", -1)<CR>
vmap ]J :call PythonDec("class", -1)<CR>
" jump to next class
map ]j :call PythonDec("class", 1)<CR>
vmap ]j :call PythonDec("class", 1)<CR>
" jump to previous function
map ]F :call PythonDec("function", -1)<CR>
vmap ]F :call PythonDec("function", -1)<CR>
" jump to next function
map ]f :call PythonDec("function", 1)<CR>
vmap ]f :call PythonDec("function", 1)<CR>
" Menu entries
nmenu <silent> &Python.Update\ IM-Python\ Menu
\:call UpdateMenu()<CR>
nmenu &Python.-Sep1- :
nmenu <silent> &Python.Beginning\ of\ Block<Tab>[t
\]t
nmenu <silent> &Python.End\ of\ Block<Tab>]e
\]e
nmenu &Python.-Sep2- :
nmenu <silent> &Python.Shift\ Block\ Left<Tab>]<
\]<
vmenu <silent> &Python.Shift\ Block\ Left<Tab>]<
\]<
nmenu <silent> &Python.Shift\ Block\ Right<Tab>]>
\]>
vmenu <silent> &Python.Shift\ Block\ Right<Tab>]>
\]>
nmenu &Python.-Sep3- :
vmenu <silent> &Python.Comment\ Selection<Tab>]#
\]#
nmenu <silent> &Python.Comment\ Selection<Tab>]#
\]#
vmenu <silent> &Python.Uncomment\ Selection<Tab>]u
\]u
nmenu <silent> &Python.Uncomment\ Selection<Tab>]u
\]u
nmenu &Python.-Sep4- :
nmenu <silent> &Python.Previous\ Class<Tab>]J
\]J
nmenu <silent> &Python.Next\ Class<Tab>]j
\]j
nmenu <silent> &Python.Previous\ Function<Tab>]F
\]F
nmenu <silent> &Python.Next\ Function<Tab>]f
\]f
nmenu &Python.-Sep5- :
nmenu <silent> &Python.Select\ Block<Tab>]v
\]v
nmenu <silent> &Python.Select\ Function<Tab>]d
\]d
nmenu <silent> &Python.Select\ Class<Tab>]c
\]c
nmenu &Python.-Sep6- :
nmenu <silent> &Python.Previous\ Line\ wrt\ indent<Tab>]<up>
\]<up>
nmenu <silent> &Python.Next\ Line\ wrt\ indent<Tab>]<down>
\]<down>
:com! PBoB execute "normal ".PythonBoB(line('.'), -1, 1)."G"
:com! PEoB execute "normal ".PythonBoB(line('.'), 1, 1)."G"
:com! UpdateMenu call UpdateMenu()
" Go to a block boundary (-1: previous, 1: next)
" If force_sel_comments is true, 'g:py_select_trailing_comments' is ignored
function! PythonBoB(line, direction, force_sel_comments)
let ln = a:line
let ind = indent(ln)
let mark = ln
let indent_valid = strlen(getline(ln))
let ln = ln + a:direction
if (a:direction == 1) && (!a:force_sel_comments) &&
\ exists("g:py_select_trailing_comments") &&
\ (!g:py_select_trailing_comments)
let sel_comments = 0
else
let sel_comments = 1
endif
while((ln >= 1) && (ln <= line('$')))
if (sel_comments) || (match(getline(ln), "^\\s*#") == -1)
if (!indent_valid)
let indent_valid = strlen(getline(ln))
let ind = indent(ln)
let mark = ln
else
if (strlen(getline(ln)))
if (indent(ln) < ind)
break
endif
let mark = ln
endif
endif
endif
let ln = ln + a:direction
endwhile
return mark
endfunction
" Go to previous (-1) or next (1) class/function definition
function! PythonDec(obj, direction)
if (a:obj == "class")
let objregexp = "^\\s*class\\s\\+[a-zA-Z0-9_]\\+"
\ . "\\s*\\((\\([a-zA-Z0-9_,. \\t\\n]\\)*)\\)\\=\\s*:"
else
let objregexp = "^\\s*def\\s\\+[a-zA-Z0-9_]\\+\\s*(\\_[^:#]*)\\s*:"
endif
let flag = "W"
if (a:direction == -1)
let flag = flag."b"
endif
let res = search(objregexp, flag)
endfunction
" Comment out selected lines
" commentString is inserted in non-empty lines, and should be aligned with
" the block
function! PythonCommentSelection() range
let commentString = "#"
let cl = a:firstline
let ind = 1000 " I hope nobody use so long lines! :)
" Look for smallest indent
while (cl <= a:lastline)
if strlen(getline(cl))
let cind = indent(cl)
let ind = ((ind < cind) ? ind : cind)
endif
let cl = cl + 1
endwhile
if (ind == 1000)
let ind = 1
else
let ind = ind + 1
endif
let cl = a:firstline
execute ":".cl
" Insert commentString in each non-empty line, in column ind
while (cl <= a:lastline)
if strlen(getline(cl))
execute "normal ".ind."|i".commentString
endif
execute "normal \<Down>"
let cl = cl + 1
endwhile
endfunction
" Uncomment selected lines
function! PythonUncommentSelection() range
" commentString could be different than the one from CommentSelection()
" For example, this could be "# \\="
let commentString = "#"
let cl = a:firstline
while (cl <= a:lastline)
let ul = substitute(getline(cl),
\"\\(\\s*\\)".commentString."\\(.*\\)$", "\\1\\2", "")
call setline(cl, ul)
let cl = cl + 1
endwhile
endfunction
" Select an object ("class"/"function")
function! PythonSelectObject(obj)
" Go to the object declaration
normal $
call PythonDec(a:obj, -1)
let beg = line('.')
if !exists("g:py_select_leading_comments") || (g:py_select_leading_comments)
let decind = indent(beg)
let cl = beg
while (cl>1)
let cl = cl - 1
if (indent(cl) == decind) && (getline(cl)[decind] == "#")
let beg = cl
else
break
endif
endwhile
endif
if (a:obj == "class")
let eod = "\\(^\\s*class\\s\\+[a-zA-Z0-9_]\\+\\s*"
\ . "\\((\\([a-zA-Z0-9_,. \\t\\n]\\)*)\\)\\=\\s*\\)\\@<=:"
else
let eod = "\\(^\\s*def\\s\\+[a-zA-Z0-9_]\\+\\s*(\\_[^:#]*)\\s*\\)\\@<=:"
endif
" Look for the end of the declaration (not always the same line!)
call search(eod, "")
" Is it a one-line definition?
if match(getline('.'), "^\\s*\\(#.*\\)\\=$", col('.')) == -1
let cl = line('.')
execute ":".beg
execute "normal V".cl."G"
else
" Select the whole block
execute "normal \<Down>"
let cl = line('.')
execute ":".beg
execute "normal V".PythonBoB(cl, 1, 0)."G"
endif
endfunction
" Jump to the next line with the same (or lower) indentation
" Useful for moving between "if" and "else", for example.
function! PythonNextLine(direction)
let ln = line('.')
let ind = indent(ln)
let indent_valid = strlen(getline(ln))
let ln = ln + a:direction
while((ln >= 1) && (ln <= line('$')))
if (!indent_valid) && strlen(getline(ln))
break
else
if (strlen(getline(ln)))
if (indent(ln) <= ind)
break
endif
endif
endif
let ln = ln + a:direction
endwhile
execute "normal ".ln."G"
endfunction
function! UpdateMenu()
" delete menu if it already exists, then rebuild it.
" this is necessary in case you've got multiple buffers open
" a future enhancement to this would be to make the menu aware of
" all buffers currently open, and group classes and functions by buffer
if exists("g:menuran")
aunmenu IM-Python
endif
let restore_fe = &foldenable
set nofoldenable
" preserve disposition of window and cursor
let cline=line('.')
let ccol=col('.') - 1
norm H
let hline=line('.')
" create the menu
call MenuBuilder()
" restore disposition of window and cursor
exe "norm ".hline."Gzt"
let dnscroll=cline-hline
exe "norm ".dnscroll."j".ccol."l"
let &foldenable = restore_fe
endfunction
function! MenuBuilder()
norm gg0
let currentclass = -1
let classlist = []
let parentclass = ""
while line(".") < line("$")
" search for a class or function
if match ( getline("."), '^\s*class\s\+[_a-zA-Z].*\|^\s*def\s\+[_a-zA-Z].*' ) != -1
norm ^
let linenum = line('.')
let indentcol = col('.')
norm "nye
let classordef=@n
norm w"nywge
let objname=@n
let parentclass = FindParentClass(classlist, indentcol)
if classordef == "class"
call AddClass(objname, linenum, parentclass)
else " this is a function
call AddFunction(objname, linenum, parentclass)
endif
" We actually created a menu, so lets set the global variable
let g:menuran=1
call RebuildClassList(classlist, [objname, indentcol], classordef)
endif " line matched
norm j
endwhile
endfunction
" classlist contains the list of nested classes we are in.
" in most cases it will be empty or contain a single class
" but where a class is nested within another, it will contain 2 or more
" this function adds or removes classes from the list based on indentation
function! RebuildClassList(classlist, newclass, classordef)
let i = len(a:classlist) - 1
while i > -1
if a:newclass[1] <= a:classlist[i][1]
call remove(a:classlist, i)
endif
let i = i - 1
endwhile
if a:classordef == "class"
call add(a:classlist, a:newclass)
endif
endfunction
" we found a class or function, determine its parent class based on
" indentation and what's contained in classlist
function! FindParentClass(classlist, indentcol)
let i = 0
let parentclass = ""
while i < len(a:classlist)
if a:indentcol <= a:classlist[i][1]
break
else
if len(parentclass) == 0
let parentclass = a:classlist[i][0]
else
let parentclass = parentclass.'\.'.a:classlist[i][0]
endif
endif
let i = i + 1
endwhile
return parentclass
endfunction
" add a class to the menu
function! AddClass(classname, lineno, parentclass)
if len(a:parentclass) > 0
let classstring = a:parentclass.'\.'.a:classname
else
let classstring = a:classname
endif
exe 'menu IM-Python.classes.'.classstring.' :call <SID>JumpToAndUnfold('.a:lineno.')<CR>'
endfunction
" add a function to the menu, grouped by member class
function! AddFunction(functionname, lineno, parentclass)
if len(a:parentclass) > 0
let funcstring = a:parentclass.'.'.a:functionname
else
let funcstring = a:functionname
endif
exe 'menu IM-Python.functions.'.funcstring.' :call <SID>JumpToAndUnfold('.a:lineno.')<CR>'
endfunction
function! s:JumpToAndUnfold(line)
" Go to the right line
execute 'normal '.a:line.'gg'
" Check to see if we are in a fold
let lvl = foldlevel(a:line)
if lvl != 0
" and if so, then expand the fold out, other wise, ignore this part.
execute 'normal 15zo'
endif
endfunction
"" This one will work only on vim 6.2 because of the try/catch expressions.
" function! s:JumpToAndUnfoldWithExceptions(line)
" try
" execute 'normal '.a:line.'gg15zo'
" catch /^Vim\((\a\+)\)\=:E490:/
" " Do nothing, just consume the error
" endtry
"endfunction
" vim:set et sts=2 sw=2:

View File

@@ -0,0 +1,625 @@
"pythoncomplete.vim - Omni Completion for python
" Maintainer: Aaron Griffin <aaronmgriffin@gmail.com>
" Version: 0.9
" Last Updated: 18 Jun 2009
"
" Changes
" TODO:
" 'info' item output can use some formatting work
" Add an "unsafe eval" mode, to allow for return type evaluation
" Complete basic syntax along with import statements
" i.e. "import url<c-x,c-o>"
" Continue parsing on invalid line??
"
" v 0.9
" * Fixed docstring parsing for classes and functions
" * Fixed parsing of *args and **kwargs type arguments
" * Better function param parsing to handle things like tuples and
" lambda defaults args
"
" v 0.8
" * Fixed an issue where the FIRST assignment was always used instead of
" using a subsequent assignment for a variable
" * Fixed a scoping issue when working inside a parameterless function
"
"
" v 0.7
" * Fixed function list sorting (_ and __ at the bottom)
" * Removed newline removal from docs. It appears vim handles these better in
" recent patches
"
" v 0.6:
" * Fixed argument completion
" * Removed the 'kind' completions, as they are better indicated
" with real syntax
" * Added tuple assignment parsing (whoops, that was forgotten)
" * Fixed import handling when flattening scope
"
" v 0.5:
" Yeah, I skipped a version number - 0.4 was never public.
" It was a bugfix version on top of 0.3. This is a complete
" rewrite.
"
if !has('python')
echo "Error: Required vim compiled with +python"
finish
endif
function! pythoncomplete#Complete(findstart, base)
"findstart = 1 when we need to get the text length
if a:findstart == 1
let line = getline('.')
let idx = col('.')
while idx > 0
let idx -= 1
let c = line[idx]
if c =~ '\w'
continue
elseif ! c =~ '\.'
let idx = -1
break
else
break
endif
endwhile
return idx
"findstart = 0 when we need to return the list of completions
else
"vim no longer moves the cursor upon completion... fix that
let line = getline('.')
let idx = col('.')
let cword = ''
while idx > 0
let idx -= 1
let c = line[idx]
if c =~ '\w' || c =~ '\.'
let cword = c . cword
continue
elseif strlen(cword) > 0 || idx == 0
break
endif
endwhile
execute "python vimcomplete('" . cword . "', '" . a:base . "')"
return g:pythoncomplete_completions
endif
endfunction
function! s:DefPython()
python << PYTHONEOF
import sys, tokenize, cStringIO, types
from token import NAME, DEDENT, NEWLINE, STRING
debugstmts=[]
def dbg(s): debugstmts.append(s)
def showdbg():
for d in debugstmts: print "DBG: %s " % d
def vimcomplete(context,match):
global debugstmts
debugstmts = []
try:
import vim
def complsort(x,y):
try:
xa = x['abbr']
ya = y['abbr']
if xa[0] == '_':
if xa[1] == '_' and ya[0:2] == '__':
return xa > ya
elif ya[0:2] == '__':
return -1
elif y[0] == '_':
return xa > ya
else:
return 1
elif ya[0] == '_':
return -1
else:
return xa > ya
except:
return 0
cmpl = Completer()
cmpl.evalsource('\n'.join(vim.current.buffer),vim.eval("line('.')"))
all = cmpl.get_completions(context,match)
all.sort(complsort)
dictstr = '['
# have to do this for double quoting
for cmpl in all:
dictstr += '{'
for x in cmpl: dictstr += '"%s":"%s",' % (x,cmpl[x])
dictstr += '"icase":0},'
if dictstr[-1] == ',': dictstr = dictstr[:-1]
dictstr += ']'
#dbg("dict: %s" % dictstr)
vim.command("silent let g:pythoncomplete_completions = %s" % dictstr)
#dbg("Completion dict:\n%s" % all)
except vim.error:
dbg("VIM Error: %s" % vim.error)
class Completer(object):
def __init__(self):
self.compldict = {}
self.parser = PyParser()
def evalsource(self,text,line=0):
sc = self.parser.parse(text,line)
src = sc.get_code()
dbg("source: %s" % src)
try: exec(src) in self.compldict
except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1]))
for l in sc.locals:
try: exec(l) in self.compldict
except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l))
def _cleanstr(self,doc):
return doc.replace('"',' ').replace("'",' ')
def get_arguments(self,func_obj):
def _ctor(obj):
try: return class_ob.__init__.im_func
except AttributeError:
for base in class_ob.__bases__:
rc = _find_constructor(base)
if rc is not None: return rc
return None
arg_offset = 1
if type(func_obj) == types.ClassType: func_obj = _ctor(func_obj)
elif type(func_obj) == types.MethodType: func_obj = func_obj.im_func
else: arg_offset = 0
arg_text=''
if type(func_obj) in [types.FunctionType, types.LambdaType]:
try:
cd = func_obj.func_code
real_args = cd.co_varnames[arg_offset:cd.co_argcount]
defaults = func_obj.func_defaults or ''
defaults = map(lambda name: "=%s" % name, defaults)
defaults = [""] * (len(real_args)-len(defaults)) + defaults
items = map(lambda a,d: a+d, real_args, defaults)
if func_obj.func_code.co_flags & 0x4:
items.append("...")
if func_obj.func_code.co_flags & 0x8:
items.append("***")
arg_text = (','.join(items)) + ')'
except:
dbg("arg completion: %s: %s" % (sys.exc_info()[0],sys.exc_info()[1]))
pass
if len(arg_text) == 0:
# The doc string sometimes contains the function signature
# this works for alot of C modules that are part of the
# standard library
doc = func_obj.__doc__
if doc:
doc = doc.lstrip()
pos = doc.find('\n')
if pos > 0:
sigline = doc[:pos]
lidx = sigline.find('(')
ridx = sigline.find(')')
if lidx > 0 and ridx > 0:
arg_text = sigline[lidx+1:ridx] + ')'
if len(arg_text) == 0: arg_text = ')'
return arg_text
def get_completions(self,context,match):
dbg("get_completions('%s','%s')" % (context,match))
stmt = ''
if context: stmt += str(context)
if match: stmt += str(match)
try:
result = None
all = {}
ridx = stmt.rfind('.')
if len(stmt) > 0 and stmt[-1] == '(':
result = eval(_sanitize(stmt[:-1]), self.compldict)
doc = result.__doc__
if doc is None: doc = ''
args = self.get_arguments(result)
return [{'word':self._cleanstr(args),'info':self._cleanstr(doc)}]
elif ridx == -1:
match = stmt
all = self.compldict
else:
match = stmt[ridx+1:]
stmt = _sanitize(stmt[:ridx])
result = eval(stmt, self.compldict)
all = dir(result)
dbg("completing: stmt:%s" % stmt)
completions = []
try: maindoc = result.__doc__
except: maindoc = ' '
if maindoc is None: maindoc = ' '
for m in all:
if m == "_PyCmplNoType": continue #this is internal
try:
dbg('possible completion: %s' % m)
if m.find(match) == 0:
if result is None: inst = all[m]
else: inst = getattr(result,m)
try: doc = inst.__doc__
except: doc = maindoc
typestr = str(inst)
if doc is None or doc == '': doc = maindoc
wrd = m[len(match):]
c = {'word':wrd, 'abbr':m, 'info':self._cleanstr(doc)}
if "function" in typestr:
c['word'] += '('
c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst))
elif "method" in typestr:
c['word'] += '('
c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst))
elif "module" in typestr:
c['word'] += '.'
elif "class" in typestr:
c['word'] += '('
c['abbr'] += '('
completions.append(c)
except:
i = sys.exc_info()
dbg("inner completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt))
return completions
except:
i = sys.exc_info()
dbg("completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt))
return []
class Scope(object):
def __init__(self,name,indent,docstr=''):
self.subscopes = []
self.docstr = docstr
self.locals = []
self.parent = None
self.name = name
self.indent = indent
def add(self,sub):
#print 'push scope: [%s@%s]' % (sub.name,sub.indent)
sub.parent = self
self.subscopes.append(sub)
return sub
def doc(self,str):
""" Clean up a docstring """
d = str.replace('\n',' ')
d = d.replace('\t',' ')
while d.find(' ') > -1: d = d.replace(' ',' ')
while d[0] in '"\'\t ': d = d[1:]
while d[-1] in '"\'\t ': d = d[:-1]
dbg("Scope(%s)::docstr = %s" % (self,d))
self.docstr = d
def local(self,loc):
self._checkexisting(loc)
self.locals.append(loc)
def copy_decl(self,indent=0):
""" Copy a scope's declaration only, at the specified indent level - not local variables """
return Scope(self.name,indent,self.docstr)
def _checkexisting(self,test):
"Convienance function... keep out duplicates"
if test.find('=') > -1:
var = test.split('=')[0].strip()
for l in self.locals:
if l.find('=') > -1 and var == l.split('=')[0].strip():
self.locals.remove(l)
def get_code(self):
str = ""
if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n'
for l in self.locals:
if l.startswith('import'): str += l+'\n'
str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n'
for sub in self.subscopes:
str += sub.get_code()
for l in self.locals:
if not l.startswith('import'): str += l+'\n'
return str
def pop(self,indent):
#print 'pop scope: [%s] to [%s]' % (self.indent,indent)
outer = self
while outer.parent != None and outer.indent >= indent:
outer = outer.parent
return outer
def currentindent(self):
#print 'parse current indent: %s' % self.indent
return ' '*self.indent
def childindent(self):
#print 'parse child indent: [%s]' % (self.indent+1)
return ' '*(self.indent+1)
class Class(Scope):
def __init__(self, name, supers, indent, docstr=''):
Scope.__init__(self,name,indent, docstr)
self.supers = supers
def copy_decl(self,indent=0):
c = Class(self.name,self.supers,indent, self.docstr)
for s in self.subscopes:
c.add(s.copy_decl(indent+1))
return c
def get_code(self):
str = '%sclass %s' % (self.currentindent(),self.name)
if len(self.supers) > 0: str += '(%s)' % ','.join(self.supers)
str += ':\n'
if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n'
if len(self.subscopes) > 0:
for s in self.subscopes: str += s.get_code()
else:
str += '%spass\n' % self.childindent()
return str
class Function(Scope):
def __init__(self, name, params, indent, docstr=''):
Scope.__init__(self,name,indent, docstr)
self.params = params
def copy_decl(self,indent=0):
return Function(self.name,self.params,indent, self.docstr)
def get_code(self):
str = "%sdef %s(%s):\n" % \
(self.currentindent(),self.name,','.join(self.params))
if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n'
str += "%spass\n" % self.childindent()
return str
class PyParser:
def __init__(self):
self.top = Scope('global',0)
self.scope = self.top
def _parsedotname(self,pre=None):
#returns (dottedname, nexttoken)
name = []
if pre is None:
tokentype, token, indent = self.next()
if tokentype != NAME and token != '*':
return ('', token)
else: token = pre
name.append(token)
while True:
tokentype, token, indent = self.next()
if token != '.': break
tokentype, token, indent = self.next()
if tokentype != NAME: break
name.append(token)
return (".".join(name), token)
def _parseimportlist(self):
imports = []
while True:
name, token = self._parsedotname()
if not name: break
name2 = ''
if token == 'as': name2, token = self._parsedotname()
imports.append((name, name2))
while token != "," and "\n" not in token:
tokentype, token, indent = self.next()
if token != ",": break
return imports
def _parenparse(self):
name = ''
names = []
level = 1
while True:
tokentype, token, indent = self.next()
if token in (')', ',') and level == 1:
if '=' not in name: name = name.replace(' ', '')
names.append(name.strip())
name = ''
if token == '(':
level += 1
name += "("
elif token == ')':
level -= 1
if level == 0: break
else: name += ")"
elif token == ',' and level == 1:
pass
else:
name += "%s " % str(token)
return names
def _parsefunction(self,indent):
self.scope=self.scope.pop(indent)
tokentype, fname, ind = self.next()
if tokentype != NAME: return None
tokentype, open, ind = self.next()
if open != '(': return None
params=self._parenparse()
tokentype, colon, ind = self.next()
if colon != ':': return None
return Function(fname,params,indent)
def _parseclass(self,indent):
self.scope=self.scope.pop(indent)
tokentype, cname, ind = self.next()
if tokentype != NAME: return None
super = []
tokentype, next, ind = self.next()
if next == '(':
super=self._parenparse()
elif next != ':': return None
return Class(cname,super,indent)
def _parseassignment(self):
assign=''
tokentype, token, indent = self.next()
if tokentype == tokenize.STRING or token == 'str':
return '""'
elif token == '(' or token == 'tuple':
return '()'
elif token == '[' or token == 'list':
return '[]'
elif token == '{' or token == 'dict':
return '{}'
elif tokentype == tokenize.NUMBER:
return '0'
elif token == 'open' or token == 'file':
return 'file'
elif token == 'None':
return '_PyCmplNoType()'
elif token == 'type':
return 'type(_PyCmplNoType)' #only for method resolution
else:
assign += token
level = 0
while True:
tokentype, token, indent = self.next()
if token in ('(','{','['):
level += 1
elif token in (']','}',')'):
level -= 1
if level == 0: break
elif level == 0:
if token in (';','\n'): break
assign += token
return "%s" % assign
def next(self):
type, token, (lineno, indent), end, self.parserline = self.gen.next()
if lineno == self.curline:
#print 'line found [%s] scope=%s' % (line.replace('\n',''),self.scope.name)
self.currentscope = self.scope
return (type, token, indent)
def _adjustvisibility(self):
newscope = Scope('result',0)
scp = self.currentscope
while scp != None:
if type(scp) == Function:
slice = 0
#Handle 'self' params
if scp.parent != None and type(scp.parent) == Class:
slice = 1
newscope.local('%s = %s' % (scp.params[0],scp.parent.name))
for p in scp.params[slice:]:
i = p.find('=')
if len(p) == 0: continue
pvar = ''
ptype = ''
if i == -1:
pvar = p
ptype = '_PyCmplNoType()'
else:
pvar = p[:i]
ptype = _sanitize(p[i+1:])
if pvar.startswith('**'):
pvar = pvar[2:]
ptype = '{}'
elif pvar.startswith('*'):
pvar = pvar[1:]
ptype = '[]'
newscope.local('%s = %s' % (pvar,ptype))
for s in scp.subscopes:
ns = s.copy_decl(0)
newscope.add(ns)
for l in scp.locals: newscope.local(l)
scp = scp.parent
self.currentscope = newscope
return self.currentscope
#p.parse(vim.current.buffer[:],vim.eval("line('.')"))
def parse(self,text,curline=0):
self.curline = int(curline)
buf = cStringIO.StringIO(''.join(text) + '\n')
self.gen = tokenize.generate_tokens(buf.readline)
self.currentscope = self.scope
try:
freshscope=True
while True:
tokentype, token, indent = self.next()
#dbg( 'main: token=[%s] indent=[%s]' % (token,indent))
if tokentype == DEDENT or token == "pass":
self.scope = self.scope.pop(indent)
elif token == 'def':
func = self._parsefunction(indent)
if func is None:
print "function: syntax error..."
continue
dbg("new scope: function")
freshscope = True
self.scope = self.scope.add(func)
elif token == 'class':
cls = self._parseclass(indent)
if cls is None:
print "class: syntax error..."
continue
freshscope = True
dbg("new scope: class")
self.scope = self.scope.add(cls)
elif token == 'import':
imports = self._parseimportlist()
for mod, alias in imports:
loc = "import %s" % mod
if len(alias) > 0: loc += " as %s" % alias
self.scope.local(loc)
freshscope = False
elif token == 'from':
mod, token = self._parsedotname()
if not mod or token != "import":
print "from: syntax error..."
continue
names = self._parseimportlist()
for name, alias in names:
loc = "from %s import %s" % (mod,name)
if len(alias) > 0: loc += " as %s" % alias
self.scope.local(loc)
freshscope = False
elif tokentype == STRING:
if freshscope: self.scope.doc(token)
elif tokentype == NAME:
name,token = self._parsedotname(token)
if token == '=':
stmt = self._parseassignment()
dbg("parseassignment: %s = %s" % (name, stmt))
if stmt != None:
self.scope.local("%s = %s" % (name,stmt))
freshscope = False
except StopIteration: #thrown on EOF
pass
except:
dbg("parse error: %s, %s @ %s" %
(sys.exc_info()[0], sys.exc_info()[1], self.parserline))
return self._adjustvisibility()
def _sanitize(str):
val = ''
level = 0
for c in str:
if c in ('(','{','['):
level += 1
elif c in (']','}',')'):
level -= 1
elif level == 0:
val += c
return val
sys.path.extend(['.','..'])
PYTHONEOF
endfunction
call s:DefPython()
" vim: set et ts=4: