# HG changeset patch # User Steve Losh # Date 1274468342 14400 # Node ID d6e9fd358013a4c113259e9a2a0c3e6c66dbcc29 # Parent feaa7c9c1efff45d7019d78bb2222b6deabf465b vim: add pyflakes diff -r feaa7c9c1eff -r d6e9fd358013 vim/.vimrc --- a/vim/.vimrc Thu May 20 17:58:29 2010 -0400 +++ b/vim/.vimrc Fri May 21 14:59:02 2010 -0400 @@ -118,3 +118,9 @@ " Ack map a :Ack + +" Spellbad Coloring +if has("gui_running") + highlight SpellBad term=underline gui=undercurl guisp=Orange +endif + diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/README.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/README.rst Fri May 21 14:59:02 2010 -0400 @@ -0,0 +1,70 @@ +pyflakes-vim +============ + +A Vim plugin for checking Python code on the fly. + +PyFlakes catches common Python errors like mistyping a variable name or +accessing a local before it is bound, and also gives warnings for things like +unused imports. + +pyflakes-vim uses the output from PyFlakes to highlight errors in your code. + +Make sure to check vim.org_ for the latest updates. + +.. _pyflakes.vim: http://www.vim.org/scripts/script.php?script_id=2441 +.. _vim.org: http://www.vim.org/scripts/script.php?script_id=2441 + +Quick Installation +------------------ + +1. Make sure your ``.vimrc`` has:: + + filetype on " enables filetype detection + filetype plugin on " enables filetype specific plugins + +2. Download the latest release_. + +3. Unzip ``pyflakes.vim`` and the ``pyflakes`` directory into + ``~/.vim/ftplugin/python`` (or somewhere similar on your + `runtime path`_ that will be sourced for Python files). + +.. _release: http://www.vim.org/scripts/script.php?script_id=2441 +.. _runtime path: http://vimdoc.sourceforge.net/htmldoc/options.html#'runtimepath' + +Installation +------------ + +If you downloaded this from vim.org_, then just drop the contents of the zip +file into ``~/.vim/ftplugin/python``. + +Otherwise, you'll need PyFlakes on your PYTHONPATH somewhere. I recommend +getting my PyFlakes_ fork, which uses the ``_ast`` module new to Python 2.5, +and is faster and more current than PyFlakes' old usage of the deprecated +``compiler`` module. + +.. _vim.org: http://www.vim.org/scripts/script.php?script_id=2441 +.. _PyFlakes: http://github.com/kevinw/pyflakes + +Hacking +------- + +:: + + git clone git://github.com/kevinw/pyflakes-vim.git + cd pyflakes-vim + git clone git://github.com/kevinw/pyflakes.git + +TODO +---- + * signs_ support (show warning and error icons to left of the buffer area) + * configuration variables + * parse or intercept useful output from the warnings module + +.. _signs: http://www.vim.org/htmldoc/sign.html + +Changelog +--------- + +Please see http://www.vim.org/scripts/script.php?script_id=2441 for a history of +all changes. + diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes.vim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes.vim Fri May 21 14:59:02 2010 -0400 @@ -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 +" 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("")')), '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 call s:ClearPyflakes() + +au BufEnter call s:RunPyflakes() +au InsertLeave call s:RunPyflakes() +au InsertEnter call s:RunPyflakes() +au BufWritePost call s:RunPyflakes() + +au CursorHold call s:RunPyflakes() +au CursorHoldI call s:RunPyflakes() + +au CursorHold call s:GetPyflakesMessage() +au CursorMoved 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 dd dd:PyflakesUpdate +noremap dw dw:PyflakesUpdate +noremap u u:PyflakesUpdate +noremap :PyflakesUpdate + +" 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 + diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/LICENSE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/LICENSE Fri May 21 14:59:02 2010 -0400 @@ -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. diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/README.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/README.rst Fri May 21 14:59:02 2010 -0400 @@ -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 + diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/TODO --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/TODO Fri May 21 14:59:02 2010 -0400 @@ -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() + diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/bin/pyflakes --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/bin/pyflakes Fri May 21 14:59:02 2010 -0400 @@ -0,0 +1,4 @@ +#!/usr/bin/python + +from pyflakes.scripts.pyflakes import main +main() diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/__init__.py diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/ast.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/pyflakes/ast.py Fri May 21 14:59:02 2010 -0400 @@ -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='', 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 diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/checker.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/pyflakes/checker.py Fri May 21 14:59:02 2010 -0400 @@ -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 + diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/messages.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/pyflakes/messages.py Fri May 21 14:59:02 2010 -0400 @@ -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,) diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/scripts/__init__.py diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/scripts/pyflakes.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/pyflakes/scripts/pyflakes.py Fri May 21 14:59:02 2010 -0400 @@ -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(), '') + + raise SystemExit(warnings > 0) diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/test/__init__.py diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/test/harness.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/pyflakes/test/harness.py Fri May 21 14:59:02 2010 -0400 @@ -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 diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/test/test_imports.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/pyflakes/test/test_imports.py Fri May 21 14:59:02 2010 -0400 @@ -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) diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/test/test_other.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/pyflakes/test/test_other.py Fri May 21 14:59:02 2010 -0400 @@ -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] + ''') + diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/test/test_script.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/pyflakes/test/test_script.py Fri May 21 14:59:02 2010 -0400 @@ -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) diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/pyflakes/test/test_undefined_names.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/pyflakes/test/test_undefined_names.py Fri May 21 14:59:02 2010 -0400 @@ -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)') diff -r feaa7c9c1eff -r d6e9fd358013 vim/ftplugin/python/pyflakes/setup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vim/ftplugin/python/pyflakes/setup.py Fri May 21 14:59:02 2010 -0400 @@ -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.""")