Add pyflakes (0.7.3)
authorGregory Szorc <gps@mozilla.com>
Mon, 22 Jul 2013 19:32:35 -0700
changeset 19 26fbd682dfce1d932628d32296712bd624ff9c4c
parent 18 b5059675e80e142e982cafdf601682429865c849
child 20 3f6f66219a7232c94d9dbc88e9570e19d59d0476
push id9
push usergszorc@mozilla.com
push dateTue, 23 Jul 2013 03:56:04 +0000
Add pyflakes (0.7.3)
pyflakes/AUTHORS
pyflakes/LICENSE
pyflakes/MANIFEST.in
pyflakes/NEWS.txt
pyflakes/PKG-INFO
pyflakes/README.rst
pyflakes/bin/pyflakes
pyflakes/pyflakes.egg-info/PKG-INFO
pyflakes/pyflakes.egg-info/SOURCES.txt
pyflakes/pyflakes.egg-info/dependency_links.txt
pyflakes/pyflakes.egg-info/entry_points.txt
pyflakes/pyflakes.egg-info/top_level.txt
pyflakes/pyflakes/__init__.py
pyflakes/pyflakes/__main__.py
pyflakes/pyflakes/api.py
pyflakes/pyflakes/checker.py
pyflakes/pyflakes/messages.py
pyflakes/pyflakes/reporter.py
pyflakes/pyflakes/scripts/__init__.py
pyflakes/pyflakes/scripts/pyflakes.py
pyflakes/pyflakes/test/__init__.py
pyflakes/pyflakes/test/harness.py
pyflakes/pyflakes/test/test_api.py
pyflakes/pyflakes/test/test_doctests.py
pyflakes/pyflakes/test/test_imports.py
pyflakes/pyflakes/test/test_other.py
pyflakes/pyflakes/test/test_undefined_names.py
pyflakes/setup.cfg
pyflakes/setup.py
new file mode 100644
--- /dev/null
+++ b/pyflakes/AUTHORS
@@ -0,0 +1,19 @@
+
+Contributors
+------------
+
+* Moe Aboulkheir - Former Divmod Team
+* Jean-Paul Calderone - Former Divmod Team
+* Glyph Lefkowitz - Former Divmod Team
+* Tristan Seligmann
+* Jonathan Lange
+* Georg Brandl
+* Ronny Pfannschmidt
+* Virgil Dupras
+* Kevin Watters
+* Ian Cordasco
+* Florent Xicluna
+* Domen Kožar
+* Marcin Cieślak
+* Steven Myint
+* Ignas Mikalajūnas
new file mode 100644
--- /dev/null
+++ b/pyflakes/LICENSE
@@ -0,0 +1,21 @@
+Copyright 2005-2011 Divmod, Inc.
+Copyright 2013 Florent Xicluna
+
+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.
new file mode 100644
--- /dev/null
+++ b/pyflakes/MANIFEST.in
@@ -0,0 +1,3 @@
+include README.rst NEWS.txt
+include AUTHORS LICENSE
+include bin/pyflakes
new file mode 100644
--- /dev/null
+++ b/pyflakes/NEWS.txt
@@ -0,0 +1,89 @@
+0.7.3 (2013-07-02):
+  - Do not report undefined name for generator expression and dict or
+    set comprehension at class level.
+  - Deprecate `Checker.pushFunctionScope` and `Checker.pushClassScope`:
+    use `Checker.pushScope` instead.
+  - Remove dependency on Unittest2 for the tests.
+
+0.7.2 (2013-04-24):
+  - Fix computation of `DoctestSyntaxError.lineno` and `col`.
+  - Add boolean attribute `Checker.withDoctest` to ignore doctests.
+  - If environment variable `PYFLAKES_NODOCTEST` is set, skip doctests.
+  - Environment variable `PYFLAKES_BUILTINS` accepts a comma-separated
+    list of additional built-in names.
+
+0.7.1 (2013-04-23):
+  - File `bin/pyflakes` was missing in tarball generated with distribute.
+  - Fix reporting errors in non-ASCII filenames (Python 2.x).
+
+0.7.0 (2013-04-17):
+  - Add --version and --help options.
+  - Support `python -m pyflakes` (Python 2.7 and Python 3.x).
+  - Add attribute `Message.col` to report column offset.
+  - Do not report redefinition of variable for a variable used in a list
+    comprehension in a conditional.
+  - Do not report redefinition of variable for generator expressions and
+    set or dict comprehensions.
+  - Do not report undefined name when the code is protected with a
+    `NameError` exception handler.
+  - Do not report redefinition of variable when unassigning a module imported
+    for its side-effect.
+  - Support special locals like `__tracebackhide__` for py.test.
+  - Support checking doctests.
+  - Fix issue with Turkish locale where `'i'.upper() == 'i'` in Python 2.
+
+0.6.1 (2013-01-29):
+  - Fix detection of variables in augmented assignments.
+
+0.6.0 (2013-01-29):
+  - Support Python 3 up to 3.3, based on the pyflakes3k project.
+  - Preserve compatibility with Python 2.5 and all recent versions of Python.
+  - Support custom reporters in addition to the default Reporter.
+  - Allow function redefinition for modern property construction via
+    property.setter/deleter.
+  - Fix spurious redefinition warnings in conditionals.
+  - Do not report undefined name in __all__ if import * is used.
+  - Add WindowsError as a known built-in name on all platforms.
+  - Support specifying additional built-ins in the `Checker` constructor.
+  - Don't issue Unused Variable warning when using locals() in current scope.
+  - Handle problems with the encoding of source files.
+  - Remove dependency on Twisted for the tests.
+  - Support `python setup.py test` and `python setup.py develop`.
+  - Create script using setuptools `entry_points` to support all platforms,
+    including Windows.
+
+0.5.0 (2011-09-02):
+  - Convert pyflakes to use newer _ast infrastructure rather than compiler.
+  - Support for new syntax in 2.7 (including set literals, set comprehensions,
+    and dictionary comprehensions).
+  - Make sure class names don't get bound until after class definition.
+
+0.4.0 (2009-11-25):
+  - Fix reporting for certain SyntaxErrors which lack line number
+    information.
+  - Check for syntax errors more rigorously.
+  - Support checking names used with the class decorator syntax in versions
+    of Python which have it.
+  - Detect local variables which are bound but never used.
+  - Handle permission errors when trying to read source files.
+  - Handle problems with the encoding of source files.
+  - Support importing dotted names so as not to incorrectly report them as
+    redefined unused names.
+  - Support all forms of the with statement.
+  - Consider static `__all__` definitions and avoid reporting unused names
+    if the names are listed there.
+  - Fix incorrect checking of class names with respect to the names of their
+    bases in the class statement.
+  - Support the `__path__` global in `__init__.py`.
+
+0.3.0 (2009-01-30):
+  - Display more informative SyntaxError messages.
+  - Don't hang flymake with unmatched triple quotes (only report a single
+    line of source for a multiline syntax error).
+  - Recognize __builtins__ as a defined name.
+  - Improve pyflakes support for python versions 2.3-2.5
+  - Support for if-else expressions and with statements.
+  - Warn instead of error on non-existant file paths.
+  - Check for __future__ imports after other statements.
+  - Add reporting for some types of import shadowing.
+  - Improve reporting of unbound locals
new file mode 100644
--- /dev/null
+++ b/pyflakes/PKG-INFO
@@ -0,0 +1,51 @@
+Metadata-Version: 1.1
+Name: pyflakes
+Version: 0.7.3
+Summary: passive checker of Python programs
+Home-page: https://launchpad.net/pyflakes
+Author: Florent Xicluna
+Author-email: pyflakes-dev@lists.launchpad.net
+License: MIT
+Description: ========
+        Pyflakes
+        ========
+        
+        A simple program which checks Python source files for errors.
+        
+        Pyflakes analyzes programs and detects 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.
+        
+        It is `available on PyPI <http://pypi.python.org/pypi/pyflakes>`_
+        and it supports all active versions of Python from 2.5 to 3.3.
+        
+        
+        Installation
+        ------------
+        
+        It can be installed with::
+        
+          $ pip install --upgrade pyflakes
+        
+        
+        Mailing-list
+        ------------
+        
+        Share your feedback and ideas: `subscribe to the mailing-list
+        <http://mail.python.org/mailman/listinfo/code-quality>`_
+        
+        
+        .. image:: https://api.travis-ci.org/pyflakes/pyflakes.png
+           :target: https://travis-ci.org/pyflakes/pyflakes
+           :alt: Build status
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 6 - Mature
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Software Development
+Classifier: Topic :: Utilities
new file mode 100644
--- /dev/null
+++ b/pyflakes/README.rst
@@ -0,0 +1,32 @@
+========
+Pyflakes
+========
+
+A simple program which checks Python source files for errors.
+
+Pyflakes analyzes programs and detects 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.
+
+It is `available on PyPI <http://pypi.python.org/pypi/pyflakes>`_
+and it supports all active versions of Python from 2.5 to 3.3.
+
+
+Installation
+------------
+
+It can be installed with::
+
+  $ pip install --upgrade pyflakes
+
+
+Mailing-list
+------------
+
+Share your feedback and ideas: `subscribe to the mailing-list
+<http://mail.python.org/mailman/listinfo/code-quality>`_
+
+
+.. image:: https://api.travis-ci.org/pyflakes/pyflakes.png
+   :target: https://travis-ci.org/pyflakes/pyflakes
+   :alt: Build status
new file mode 100755
--- /dev/null
+++ b/pyflakes/bin/pyflakes
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+from pyflakes.scripts.pyflakes import main
+main()
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes.egg-info/PKG-INFO
@@ -0,0 +1,51 @@
+Metadata-Version: 1.1
+Name: pyflakes
+Version: 0.7.3
+Summary: passive checker of Python programs
+Home-page: https://launchpad.net/pyflakes
+Author: Florent Xicluna
+Author-email: pyflakes-dev@lists.launchpad.net
+License: MIT
+Description: ========
+        Pyflakes
+        ========
+        
+        A simple program which checks Python source files for errors.
+        
+        Pyflakes analyzes programs and detects 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.
+        
+        It is `available on PyPI <http://pypi.python.org/pypi/pyflakes>`_
+        and it supports all active versions of Python from 2.5 to 3.3.
+        
+        
+        Installation
+        ------------
+        
+        It can be installed with::
+        
+          $ pip install --upgrade pyflakes
+        
+        
+        Mailing-list
+        ------------
+        
+        Share your feedback and ideas: `subscribe to the mailing-list
+        <http://mail.python.org/mailman/listinfo/code-quality>`_
+        
+        
+        .. image:: https://api.travis-ci.org/pyflakes/pyflakes.png
+           :target: https://travis-ci.org/pyflakes/pyflakes
+           :alt: Build status
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 6 - Mature
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Software Development
+Classifier: Topic :: Utilities
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes.egg-info/SOURCES.txt
@@ -0,0 +1,27 @@
+AUTHORS
+LICENSE
+MANIFEST.in
+NEWS.txt
+README.rst
+setup.py
+bin/pyflakes
+pyflakes/__init__.py
+pyflakes/__main__.py
+pyflakes/api.py
+pyflakes/checker.py
+pyflakes/messages.py
+pyflakes/reporter.py
+pyflakes.egg-info/PKG-INFO
+pyflakes.egg-info/SOURCES.txt
+pyflakes.egg-info/dependency_links.txt
+pyflakes.egg-info/entry_points.txt
+pyflakes.egg-info/top_level.txt
+pyflakes/scripts/__init__.py
+pyflakes/scripts/pyflakes.py
+pyflakes/test/__init__.py
+pyflakes/test/harness.py
+pyflakes/test/test_api.py
+pyflakes/test/test_doctests.py
+pyflakes/test/test_imports.py
+pyflakes/test/test_other.py
+pyflakes/test/test_undefined_names.py
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes.egg-info/dependency_links.txt
@@ -0,0 +1,1 @@
+
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes.egg-info/entry_points.txt
@@ -0,0 +1,3 @@
+[console_scripts]
+pyflakes = pyflakes.api:main
+
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes.egg-info/top_level.txt
@@ -0,0 +1,1 @@
+pyflakes
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/__init__.py
@@ -0,0 +1,2 @@
+
+__version__ = '0.7.3'
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/__main__.py
@@ -0,0 +1,5 @@
+from pyflakes.api import main
+
+# python -m pyflakes (with Python >= 2.7)
+if __name__ == '__main__':
+    main(prog='pyflakes')
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/api.py
@@ -0,0 +1,131 @@
+"""
+API for the command-line I{pyflakes} tool.
+"""
+from __future__ import with_statement
+
+import sys
+import os
+import _ast
+from optparse import OptionParser
+
+from pyflakes import checker, __version__
+from pyflakes import reporter as modReporter
+
+__all__ = ['check', 'checkPath', 'checkRecursive', 'iterSourceCode', 'main']
+
+
+def check(codeString, filename, reporter=None):
+    """
+    Check the Python source given by C{codeString} for flakes.
+
+    @param codeString: The Python source to check.
+    @type codeString: C{str}
+
+    @param filename: The name of the file the source came from, used to report
+        errors.
+    @type filename: C{str}
+
+    @param reporter: A L{Reporter} instance, where errors and warnings will be
+        reported.
+
+    @return: The number of warnings emitted.
+    @rtype: C{int}
+    """
+    if reporter is None:
+        reporter = modReporter._makeDefaultReporter()
+    # First, compile into an AST and handle syntax errors.
+    try:
+        tree = compile(codeString, filename, "exec", _ast.PyCF_ONLY_AST)
+    except SyntaxError:
+        value = sys.exc_info()[1]
+        msg = value.args[0]
+
+        (lineno, offset, text) = value.lineno, value.offset, value.text
+
+        # If there's an encoding problem with the file, the text is None.
+        if text is None:
+            # Avoid using msg, since for the only known case, it contains a
+            # bogus message that claims the encoding the file declared was
+            # unknown.
+            reporter.unexpectedError(filename, 'problem decoding source')
+        else:
+            reporter.syntaxError(filename, msg, lineno, offset, text)
+        return 1
+    except Exception:
+        reporter.unexpectedError(filename, 'problem decoding source')
+        return 1
+    # Okay, it's syntactically valid.  Now check it.
+    w = checker.Checker(tree, filename)
+    w.messages.sort(key=lambda m: m.lineno)
+    for warning in w.messages:
+        reporter.flake(warning)
+    return len(w.messages)
+
+
+def checkPath(filename, reporter=None):
+    """
+    Check the given path, printing out any warnings detected.
+
+    @param reporter: A L{Reporter} instance, where errors and warnings will be
+        reported.
+
+    @return: the number of warnings printed
+    """
+    if reporter is None:
+        reporter = modReporter._makeDefaultReporter()
+    try:
+        with open(filename, 'U') as f:
+            codestr = f.read() + '\n'
+    except UnicodeError:
+        reporter.unexpectedError(filename, 'problem decoding source')
+        return 1
+    except IOError:
+        msg = sys.exc_info()[1]
+        reporter.unexpectedError(filename, msg.args[1])
+        return 1
+    return check(codestr, filename, reporter)
+
+
+def iterSourceCode(paths):
+    """
+    Iterate over all Python source files in C{paths}.
+
+    @param paths: A list of paths.  Directories will be recursed into and
+        any .py files found will be yielded.  Any non-directories will be
+        yielded as-is.
+    """
+    for path in paths:
+        if os.path.isdir(path):
+            for dirpath, dirnames, filenames in os.walk(path):
+                for filename in filenames:
+                    if filename.endswith('.py'):
+                        yield os.path.join(dirpath, filename)
+        else:
+            yield path
+
+
+def checkRecursive(paths, reporter):
+    """
+    Recursively check all source files in C{paths}.
+
+    @param paths: A list of paths to Python source files and directories
+        containing Python source files.
+    @param reporter: A L{Reporter} where all of the warnings and errors
+        will be reported to.
+    @return: The number of warnings found.
+    """
+    warnings = 0
+    for sourcePath in iterSourceCode(paths):
+        warnings += checkPath(sourcePath, reporter)
+    return warnings
+
+
+def main(prog=None):
+    parser = OptionParser(prog=prog, version=__version__)
+    __, args = parser.parse_args()
+    reporter = modReporter._makeDefaultReporter()
+    if args:
+        warnings = checkRecursive(args, reporter)
+    else:
+        warnings = check(sys.stdin.read(), '<stdin>', reporter)
+    raise SystemExit(warnings > 0)
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/checker.py
@@ -0,0 +1,850 @@
+"""
+Main module.
+
+Implement the central Checker class.
+Also, it models the Bindings and Scopes.
+"""
+import doctest
+import os
+import sys
+try:
+    builtin_vars = dir(__import__('builtins'))
+    PY2 = False
+except ImportError:
+    builtin_vars = dir(__import__('__builtin__'))
+    PY2 = True
+
+try:
+    import ast
+    iter_child_nodes = ast.iter_child_nodes
+except ImportError:     # Python 2.5
+    import _ast as ast
+
+    if 'decorator_list' not in ast.ClassDef._fields:
+        # Patch the missing attribute 'decorator_list'
+        ast.ClassDef.decorator_list = ()
+        ast.FunctionDef.decorator_list = property(lambda s: s.decorators)
+
+    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 in node._fields:
+            field = getattr(node, name, None)
+            if isinstance(field, ast.AST):
+                yield field
+            elif isinstance(field, list):
+                for item in field:
+                    yield item
+# Python >= 3.3 uses ast.Try instead of (ast.TryExcept + ast.TryFinally)
+if hasattr(ast, 'Try'):
+    ast_TryExcept = ast.Try
+    ast_TryFinally = ()
+else:
+    ast_TryExcept = ast.TryExcept
+    ast_TryFinally = ast.TryFinally
+
+from pyflakes import messages
+
+
+if PY2:
+    def getNodeType(node_class):
+        # workaround str.upper() which is locale-dependent
+        return str(unicode(node_class.__name__).upper())
+else:
+    def getNodeType(node_class):
+        return node_class.__name__.upper()
+
+
+class Binding(object):
+    """
+    Represents the binding of a value to a name.
+
+    The checker uses this to keep track of which names have been bound and
+    which names have not. See L{Assignment} for a special type of binding that
+    is checked with stricter rules.
+
+    @ivar used: pair of (L{Scope}, line-number) indicating the scope and
+                line number that this binding was last used
+    """
+
+    def __init__(self, name, source):
+        self.name = name
+        self.source = source
+        self.used = False
+
+    def __str__(self):
+        return self.name
+
+    def __repr__(self):
+        return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__,
+                                                        self.name,
+                                                        self.source.lineno,
+                                                        id(self))
+
+
+class Importation(Binding):
+    """
+    A binding created by an import statement.
+
+    @ivar fullName: The complete name given to the import statement,
+        possibly including multiple dotted components.
+    @type fullName: C{str}
+    """
+    def __init__(self, name, source):
+        self.fullName = name
+        name = name.split('.')[0]
+        super(Importation, self).__init__(name, source)
+
+
+class Argument(Binding):
+    """
+    Represents binding a name as an argument.
+    """
+
+
+class Definition(Binding):
+    """
+    A binding that defines a function or a class.
+    """
+
+
+class Assignment(Binding):
+    """
+    Represents binding a name with an explicit assignment.
+
+    The checker will raise warnings for any Assignment that isn't used. Also,
+    the checker does not consider assignments in tuple/list unpacking to be
+    Assignments, rather it treats them as simple Bindings.
+    """
+
+
+class FunctionDefinition(Definition):
+    pass
+
+
+class ClassDefinition(Definition):
+    pass
+
+
+class ExportBinding(Binding):
+    """
+    A binding created by an C{__all__} assignment.  If the names in the list
+    can be determined statically, they will be treated as names for export and
+    additional checking applied to them.
+
+    The only C{__all__} assignment that can be recognized is one which takes
+    the value of a literal list containing literal strings.  For example::
+
+        __all__ = ["foo", "bar"]
+
+    Names which are imported and not otherwise used but appear in the value of
+    C{__all__} will not have an unused import warning reported for them.
+    """
+    def names(self):
+        """
+        Return a list of the names referenced by this binding.
+        """
+        names = []
+        if isinstance(self.source, ast.List):
+            for node in self.source.elts:
+                if isinstance(node, ast.Str):
+                    names.append(node.s)
+        return names
+
+
+class Scope(dict):
+    importStarred = False       # set to True when import * is found
+
+    def __repr__(self):
+        scope_cls = self.__class__.__name__
+        return '<%s at 0x%x %s>' % (scope_cls, id(self), dict.__repr__(self))
+
+
+class ClassScope(Scope):
+    pass
+
+
+class FunctionScope(Scope):
+    """
+    I represent a name scope for a function.
+
+    @ivar globals: Names declared 'global' in this function.
+    """
+    usesLocals = False
+    alwaysUsed = set(['__tracebackhide__',
+                      '__traceback_info__', '__traceback_supplement__'])
+
+    def __init__(self):
+        super(FunctionScope, self).__init__()
+        # Simplify: manage the special locals as globals
+        self.globals = self.alwaysUsed.copy()
+
+    def unusedAssignments(self):
+        """
+        Return a generator for the assignments which have not been used.
+        """
+        for name, binding in self.items():
+            if (not binding.used and name not in self.globals
+                    and not self.usesLocals
+                    and isinstance(binding, Assignment)):
+                yield name, binding
+
+
+class GeneratorScope(Scope):
+    pass
+
+
+class ModuleScope(Scope):
+    pass
+
+
+# Globally defined names which are not attributes of the builtins module, or
+# are only present on some platforms.
+_MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError']
+
+
+def getNodeName(node):
+    # Returns node.id, or node.name, or None
+    if hasattr(node, 'id'):     # One of the many nodes with an id
+        return node.id
+    if hasattr(node, 'name'):   # a ExceptHandler node
+        return node.name
+
+
+class Checker(object):
+    """
+    I check the cleanliness and sanity of Python code.
+
+    @ivar _deferredFunctions: Tracking list used by L{deferFunction}.  Elements
+        of the list are two-tuples.  The first element is the callable passed
+        to L{deferFunction}.  The second element is a copy of the scope stack
+        at the time L{deferFunction} was called.
+
+    @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for
+        callables which are deferred assignment checks.
+    """
+
+    nodeDepth = 0
+    offset = None
+    traceTree = False
+    withDoctest = ('PYFLAKES_NODOCTEST' not in os.environ)
+
+    builtIns = set(builtin_vars).union(_MAGIC_GLOBALS)
+    _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS')
+    if _customBuiltIns:
+        builtIns.update(_customBuiltIns.split(','))
+    del _customBuiltIns
+
+    def __init__(self, tree, filename='(none)', builtins=None):
+        self._nodeHandlers = {}
+        self._deferredFunctions = []
+        self._deferredAssignments = []
+        self.deadScopes = []
+        self.messages = []
+        self.filename = filename
+        if builtins:
+            self.builtIns = self.builtIns.union(builtins)
+        self.scopeStack = [ModuleScope()]
+        self.exceptHandlers = [()]
+        self.futuresAllowed = True
+        self.root = tree
+        self.handleChildren(tree)
+        self.runDeferred(self._deferredFunctions)
+        # Set _deferredFunctions to None so that deferFunction will fail
+        # noisily if called after we've run through the deferred functions.
+        self._deferredFunctions = None
+        self.runDeferred(self._deferredAssignments)
+        # Set _deferredAssignments to None so that deferAssignment will fail
+        # noisily if called after we've run through the deferred assignments.
+        self._deferredAssignments = None
+        del self.scopeStack[1:]
+        self.popScope()
+        self.checkDeadScopes()
+
+    def deferFunction(self, callable):
+        """
+        Schedule a function handler to be called just before completion.
+
+        This is used for handling function bodies, which must be deferred
+        because code later in the file might modify the global scope. When
+        `callable` is called, the scope at the time this is called will be
+        restored, however it will contain any new bindings added to it.
+        """
+        self._deferredFunctions.append((callable, self.scopeStack[:], self.offset))
+
+    def deferAssignment(self, callable):
+        """
+        Schedule an assignment handler to be called just after deferred
+        function handlers.
+        """
+        self._deferredAssignments.append((callable, self.scopeStack[:], self.offset))
+
+    def runDeferred(self, deferred):
+        """
+        Run the callables in C{deferred} using their associated scope stack.
+        """
+        for handler, scope, offset in deferred:
+            self.scopeStack = scope
+            self.offset = offset
+            handler()
+
+    @property
+    def scope(self):
+        return self.scopeStack[-1]
+
+    def popScope(self):
+        self.deadScopes.append(self.scopeStack.pop())
+
+    def checkDeadScopes(self):
+        """
+        Look at scopes which have been fully examined and report names in them
+        which were imported but unused.
+        """
+        for scope in self.deadScopes:
+            export = isinstance(scope.get('__all__'), ExportBinding)
+            if export:
+                all = scope['__all__'].names()
+                if not scope.importStarred and \
+                   os.path.basename(self.filename) != '__init__.py':
+                    # Look for possible mistakes in the export list
+                    undefined = set(all) - set(scope)
+                    for name in undefined:
+                        self.report(messages.UndefinedExport,
+                                    scope['__all__'].source, name)
+            else:
+                all = []
+
+            # Look for imported names that aren't used.
+            for importation in scope.values():
+                if isinstance(importation, Importation):
+                    if not importation.used and importation.name not in all:
+                        self.report(messages.UnusedImport,
+                                    importation.source, importation.name)
+
+    def pushScope(self, scopeClass=FunctionScope):
+        self.scopeStack.append(scopeClass())
+
+    def pushFunctionScope(self):    # XXX Deprecated
+        self.pushScope(FunctionScope)
+
+    def pushClassScope(self):       # XXX Deprecated
+        self.pushScope(ClassScope)
+
+    def report(self, messageClass, *args, **kwargs):
+        self.messages.append(messageClass(self.filename, *args, **kwargs))
+
+    def hasParent(self, node, kind):
+        while hasattr(node, 'parent'):
+            node = node.parent
+            if isinstance(node, kind):
+                return True
+
+    def getCommonAncestor(self, lnode, rnode, stop=None):
+        if not stop:
+            stop = self.root
+        if lnode is rnode:
+            return lnode
+        if stop in (lnode, rnode):
+            return stop
+
+        if not hasattr(lnode, 'parent') or not hasattr(rnode, 'parent'):
+            return
+        if (lnode.level > rnode.level):
+            return self.getCommonAncestor(lnode.parent, rnode, stop)
+        if (rnode.level > lnode.level):
+            return self.getCommonAncestor(lnode, rnode.parent, stop)
+        return self.getCommonAncestor(lnode.parent, rnode.parent, stop)
+
+    def descendantOf(self, node, ancestors, stop=None):
+        for a in ancestors:
+            if self.getCommonAncestor(node, a, stop) not in (stop, None):
+                return True
+        return False
+
+    def onFork(self, parent, lnode, rnode, items):
+        return (self.descendantOf(lnode, items, parent) ^
+                self.descendantOf(rnode, items, parent))
+
+    def differentForks(self, lnode, rnode):
+        """True, if lnode and rnode are located on different forks of IF/TRY"""
+        ancestor = self.getCommonAncestor(lnode, rnode)
+        if isinstance(ancestor, ast.If):
+            for fork in (ancestor.body, ancestor.orelse):
+                if self.onFork(ancestor, lnode, rnode, fork):
+                    return True
+        elif isinstance(ancestor, ast_TryExcept):
+            body = ancestor.body + ancestor.orelse
+            for fork in [body] + [[hdl] for hdl in ancestor.handlers]:
+                if self.onFork(ancestor, lnode, rnode, fork):
+                    return True
+        elif isinstance(ancestor, ast_TryFinally):
+            if self.onFork(ancestor, lnode, rnode, ancestor.body):
+                return True
+        return False
+
+    def addBinding(self, node, value, reportRedef=True):
+        """
+        Called when a binding is altered.
+
+        - `node` is the statement responsible for the change
+        - `value` is the optional new value, a Binding instance, associated
+          with the binding; if None, the binding is deleted if it exists.
+        - if `reportRedef` is True (default), rebinding while unused will be
+          reported.
+        """
+        redefinedWhileUnused = False
+        if not isinstance(self.scope, ClassScope):
+            for scope in self.scopeStack[::-1]:
+                existing = scope.get(value.name)
+                if (isinstance(existing, Importation)
+                        and not existing.used
+                        and (not isinstance(value, Importation) or
+                             value.fullName == existing.fullName)
+                        and reportRedef
+                        and not self.differentForks(node, existing.source)):
+                    redefinedWhileUnused = True
+                    self.report(messages.RedefinedWhileUnused,
+                                node, value.name, existing.source)
+
+        existing = self.scope.get(value.name)
+        if not redefinedWhileUnused and self.hasParent(value.source, ast.ListComp):
+            if (existing and reportRedef
+                    and not self.hasParent(existing.source, (ast.For, ast.ListComp))
+                    and not self.differentForks(node, existing.source)):
+                self.report(messages.RedefinedInListComp,
+                            node, value.name, existing.source)
+
+        if (isinstance(existing, Definition)
+                and not existing.used
+                and not self.differentForks(node, existing.source)):
+            self.report(messages.RedefinedWhileUnused,
+                        node, value.name, existing.source)
+        else:
+            self.scope[value.name] = value
+
+    def getNodeHandler(self, node_class):
+        try:
+            return self._nodeHandlers[node_class]
+        except KeyError:
+            nodeType = getNodeType(node_class)
+        self._nodeHandlers[node_class] = handler = getattr(self, nodeType)
+        return handler
+
+    def handleNodeLoad(self, node):
+        name = getNodeName(node)
+        if not name:
+            return
+        # try local scope
+        try:
+            self.scope[name].used = (self.scope, node)
+        except KeyError:
+            pass
+        else:
+            return
+
+        scopes = [scope for scope in self.scopeStack[:-1]
+                  if isinstance(scope, (FunctionScope, ModuleScope))]
+        if isinstance(self.scope, GeneratorScope) and scopes[-1] != self.scopeStack[-2]:
+            scopes.append(self.scopeStack[-2])
+
+        # try enclosing function scopes and global scope
+        importStarred = self.scope.importStarred
+        for scope in reversed(scopes):
+            importStarred = importStarred or scope.importStarred
+            try:
+                scope[name].used = (self.scope, node)
+            except KeyError:
+                pass
+            else:
+                return
+
+        # look in the built-ins
+        if importStarred or name in self.builtIns:
+            return
+        if name == '__path__' and os.path.basename(self.filename) == '__init__.py':
+            # the special name __path__ is valid only in packages
+            return
+
+        # protected with a NameError handler?
+        if 'NameError' not in self.exceptHandlers[-1]:
+            self.report(messages.UndefinedName, node, name)
+
+    def handleNodeStore(self, node):
+        name = getNodeName(node)
+        if not name:
+            return
+        # if the name hasn't already been defined in the current scope
+        if isinstance(self.scope, FunctionScope) and name not in self.scope:
+            # for each function or module scope above us
+            for scope in self.scopeStack[:-1]:
+                if not isinstance(scope, (FunctionScope, ModuleScope)):
+                    continue
+                # if the name was defined in that scope, and the name has
+                # been accessed already in the current scope, and hasn't
+                # been declared global
+                used = name in scope and scope[name].used
+                if used and used[0] is self.scope and name not in self.scope.globals:
+                    # then it's probably a mistake
+                    self.report(messages.UndefinedLocal,
+                                scope[name].used[1], name, scope[name].source)
+                    break
+
+        parent = getattr(node, 'parent', None)
+        if isinstance(parent, (ast.For, ast.comprehension, ast.Tuple, ast.List)):
+            binding = Binding(name, node)
+        elif (parent is not None and name == '__all__' and
+              isinstance(self.scope, ModuleScope)):
+            binding = ExportBinding(name, parent.value)
+        else:
+            binding = Assignment(name, node)
+        if name in self.scope:
+            binding.used = self.scope[name].used
+        self.addBinding(node, binding)
+
+    def handleNodeDelete(self, node):
+        name = getNodeName(node)
+        if not name:
+            return
+        if isinstance(self.scope, FunctionScope) and name in self.scope.globals:
+            self.scope.globals.remove(name)
+        else:
+            try:
+                del self.scope[name]
+            except KeyError:
+                self.report(messages.UndefinedName, node, name)
+
+    def handleChildren(self, tree):
+        for node in iter_child_nodes(tree):
+            self.handleNode(node, tree)
+
+    def isDocstring(self, node):
+        """
+        Determine if the given node is a docstring, as long as it is at the
+        correct place in the node tree.
+        """
+        return isinstance(node, ast.Str) or (isinstance(node, ast.Expr) and
+                                             isinstance(node.value, ast.Str))
+
+    def getDocstring(self, node):
+        if isinstance(node, ast.Expr):
+            node = node.value
+        if not isinstance(node, ast.Str):
+            return (None, None)
+        # Computed incorrectly if the docstring has backslash
+        doctest_lineno = node.lineno - node.s.count('\n') - 1
+        return (node.s, doctest_lineno)
+
+    def handleNode(self, node, parent):
+        if node is None:
+            return
+        if self.offset and getattr(node, 'lineno', None) is not None:
+            node.lineno += self.offset[0]
+            node.col_offset += self.offset[1]
+        if self.traceTree:
+            print('  ' * self.nodeDepth + node.__class__.__name__)
+        if self.futuresAllowed and not (isinstance(node, ast.ImportFrom) or
+                                        self.isDocstring(node)):
+            self.futuresAllowed = False
+        self.nodeDepth += 1
+        node.level = self.nodeDepth
+        node.parent = parent
+        try:
+            handler = self.getNodeHandler(node.__class__)
+            handler(node)
+        finally:
+            self.nodeDepth -= 1
+        if self.traceTree:
+            print('  ' * self.nodeDepth + 'end ' + node.__class__.__name__)
+
+    _getDoctestExamples = doctest.DocTestParser().get_examples
+
+    def handleDoctests(self, node):
+        try:
+            docstring, node_lineno = self.getDocstring(node.body[0])
+            if not docstring:
+                return
+            examples = self._getDoctestExamples(docstring)
+        except (ValueError, IndexError):
+            # e.g. line 6 of the docstring for <string> has inconsistent
+            # leading whitespace: ...
+            return
+        node_offset = self.offset or (0, 0)
+        self.pushScope()
+        for example in examples:
+            try:
+                tree = compile(example.source, "<doctest>", "exec", ast.PyCF_ONLY_AST)
+            except SyntaxError:
+                e = sys.exc_info()[1]
+                position = (node_lineno + example.lineno + e.lineno,
+                            example.indent + 4 + e.offset)
+                self.report(messages.DoctestSyntaxError, node, position)
+            else:
+                self.offset = (node_offset[0] + node_lineno + example.lineno,
+                               node_offset[1] + example.indent + 4)
+                self.handleChildren(tree)
+                self.offset = node_offset
+        self.popScope()
+
+    def ignore(self, node):
+        pass
+
+    # "stmt" type nodes
+    RETURN = DELETE = PRINT = WHILE = IF = WITH = WITHITEM = RAISE = \
+        TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren
+
+    CONTINUE = BREAK = PASS = ignore
+
+    # "expr" type nodes
+    BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = YIELDFROM = \
+        COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = \
+        STARRED = handleChildren
+
+    NUM = STR = BYTES = ELLIPSIS = ignore
+
+    # "slice" type nodes
+    SLICE = EXTSLICE = INDEX = handleChildren
+
+    # expression contexts are node instances too, though being constants
+    LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
+
+    # same for operators
+    AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \
+        BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \
+        EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
+
+    # additional node types
+    COMPREHENSION = KEYWORD = handleChildren
+
+    def GLOBAL(self, node):
+        """
+        Keep track of globals declarations.
+        """
+        if isinstance(self.scope, FunctionScope):
+            self.scope.globals.update(node.names)
+
+    NONLOCAL = GLOBAL
+
+    def LISTCOMP(self, node):
+        # handle generators before element
+        for gen in node.generators:
+            self.handleNode(gen, node)
+        self.handleNode(node.elt, node)
+
+    def GENERATOREXP(self, node):
+        self.pushScope(GeneratorScope)
+        # handle generators before element
+        for gen in node.generators:
+            self.handleNode(gen, node)
+        self.handleNode(node.elt, node)
+        self.popScope()
+
+    SETCOMP = GENERATOREXP
+
+    def DICTCOMP(self, node):
+        self.pushScope(GeneratorScope)
+        for gen in node.generators:
+            self.handleNode(gen, node)
+        self.handleNode(node.key, node)
+        self.handleNode(node.value, node)
+        self.popScope()
+
+    def FOR(self, node):
+        """
+        Process bindings for loop variables.
+        """
+        vars = []
+
+        def collectLoopVars(n):
+            if isinstance(n, ast.Name):
+                vars.append(n.id)
+            elif isinstance(n, ast.expr_context):
+                return
+            else:
+                for c in iter_child_nodes(n):
+                    collectLoopVars(c)
+
+        collectLoopVars(node.target)
+        for varn in vars:
+            if (isinstance(self.scope.get(varn), Importation)
+                    # unused ones will get an unused import warning
+                    and self.scope[varn].used):
+                self.report(messages.ImportShadowedByLoopVar,
+                            node, varn, self.scope[varn].source)
+
+        self.handleChildren(node)
+
+    def NAME(self, node):
+        """
+        Handle occurrence of Name (which can be a load/store/delete access.)
+        """
+        # Locate the name in locals / function / globals scopes.
+        if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
+            self.handleNodeLoad(node)
+            if (node.id == 'locals' and isinstance(self.scope, FunctionScope)
+                    and isinstance(node.parent, ast.Call)):
+                # we are doing locals() call in current scope
+                self.scope.usesLocals = True
+        elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
+            self.handleNodeStore(node)
+        elif isinstance(node.ctx, ast.Del):
+            self.handleNodeDelete(node)
+        else:
+            # must be a Param context -- this only happens for names in function
+            # arguments, but these aren't dispatched through here
+            raise RuntimeError("Got impossible expression context: %r" % (node.ctx,))
+
+    def FUNCTIONDEF(self, node):
+        for deco in node.decorator_list:
+            self.handleNode(deco, node)
+        self.addBinding(node, FunctionDefinition(node.name, node))
+        self.LAMBDA(node)
+        if self.withDoctest:
+            self.deferFunction(lambda: self.handleDoctests(node))
+
+    def LAMBDA(self, node):
+        args = []
+
+        if PY2:
+            def addArgs(arglist):
+                for arg in arglist:
+                    if isinstance(arg, ast.Tuple):
+                        addArgs(arg.elts)
+                    else:
+                        if arg.id in args:
+                            self.report(messages.DuplicateArgument,
+                                        node, arg.id)
+                        args.append(arg.id)
+            addArgs(node.args.args)
+            defaults = node.args.defaults
+        else:
+            for arg in node.args.args + node.args.kwonlyargs:
+                if arg.arg in args:
+                    self.report(messages.DuplicateArgument,
+                                node, arg.arg)
+                args.append(arg.arg)
+                self.handleNode(arg.annotation, node)
+            if hasattr(node, 'returns'):    # Only for FunctionDefs
+                for annotation in (node.args.varargannotation,
+                                   node.args.kwargannotation, node.returns):
+                    self.handleNode(annotation, node)
+            defaults = node.args.defaults + node.args.kw_defaults
+
+        # vararg/kwarg identifiers are not Name nodes
+        for wildcard in (node.args.vararg, node.args.kwarg):
+            if not wildcard:
+                continue
+            if wildcard in args:
+                self.report(messages.DuplicateArgument, node, wildcard)
+            args.append(wildcard)
+        for default in defaults:
+            self.handleNode(default, node)
+
+        def runFunction():
+
+            self.pushScope()
+            for name in args:
+                self.addBinding(node, Argument(name, node), reportRedef=False)
+            if isinstance(node.body, list):
+                # case for FunctionDefs
+                for stmt in node.body:
+                    self.handleNode(stmt, node)
+            else:
+                # case for Lambdas
+                self.handleNode(node.body, node)
+
+            def checkUnusedAssignments():
+                """
+                Check to see if any assignments have not been used.
+                """
+                for name, binding in self.scope.unusedAssignments():
+                    self.report(messages.UnusedVariable, binding.source, name)
+            self.deferAssignment(checkUnusedAssignments)
+            self.popScope()
+
+        self.deferFunction(runFunction)
+
+    def CLASSDEF(self, node):
+        """
+        Check names used in a class definition, including its decorators, base
+        classes, and the body of its definition.  Additionally, add its name to
+        the current scope.
+        """
+        for deco in node.decorator_list:
+            self.handleNode(deco, node)
+        for baseNode in node.bases:
+            self.handleNode(baseNode, node)
+        if not PY2:
+            for keywordNode in node.keywords:
+                self.handleNode(keywordNode, node)
+        self.pushScope(ClassScope)
+        if self.withDoctest:
+            self.deferFunction(lambda: self.handleDoctests(node))
+        for stmt in node.body:
+            self.handleNode(stmt, node)
+        self.popScope()
+        self.addBinding(node, ClassDefinition(node.name, node))
+
+    def ASSIGN(self, node):
+        self.handleNode(node.value, node)
+        for target in node.targets:
+            self.handleNode(target, node)
+
+    def AUGASSIGN(self, node):
+        self.handleNodeLoad(node.target)
+        self.handleNode(node.value, node)
+        self.handleNode(node.target, node)
+
+    def IMPORT(self, node):
+        for alias in node.names:
+            name = alias.asname or alias.name
+            importation = Importation(name, node)
+            self.addBinding(node, importation)
+
+    def IMPORTFROM(self, node):
+        if node.module == '__future__':
+            if not self.futuresAllowed:
+                self.report(messages.LateFutureImport,
+                            node, [n.name for n in node.names])
+        else:
+            self.futuresAllowed = False
+
+        for alias in node.names:
+            if alias.name == '*':
+                self.scope.importStarred = True
+                self.report(messages.ImportStarUsed, node, node.module)
+                continue
+            name = alias.asname or alias.name
+            importation = Importation(name, node)
+            if node.module == '__future__':
+                importation.used = (self.scope, node)
+            self.addBinding(node, importation)
+
+    def TRY(self, node):
+        handler_names = []
+        # List the exception handlers
+        for handler in node.handlers:
+            if isinstance(handler.type, ast.Tuple):
+                for exc_type in handler.type.elts:
+                    handler_names.append(getNodeName(exc_type))
+            elif handler.type:
+                handler_names.append(getNodeName(handler.type))
+        # Memorize the except handlers and process the body
+        self.exceptHandlers.append(handler_names)
+        for child in node.body:
+            self.handleNode(child, node)
+        self.exceptHandlers.pop()
+        # Process the other nodes: "except:", "else:", "finally:"
+        for child in iter_child_nodes(node):
+            if child not in node.body:
+                self.handleNode(child, node)
+
+    TRYEXCEPT = TRY
+
+    def EXCEPTHANDLER(self, node):
+        # 3.x: in addition to handling children, we must handle the name of
+        # the exception, which is not a Name node, but a simple string.
+        if isinstance(node.name, str):
+            self.handleNodeStore(node)
+        self.handleChildren(node)
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/messages.py
@@ -0,0 +1,128 @@
+"""
+Provide the class Message and its subclasses.
+"""
+
+
+class Message(object):
+    message = ''
+    message_args = ()
+
+    def __init__(self, filename, loc):
+        self.filename = filename
+        self.lineno = loc.lineno
+        self.col = getattr(loc, 'col_offset', 0)
+
+    def __str__(self):
+        return '%s:%s: %s' % (self.filename, self.lineno,
+                              self.message % self.message_args)
+
+
+class UnusedImport(Message):
+    message = '%r imported but unused'
+
+    def __init__(self, filename, loc, name):
+        Message.__init__(self, filename, loc)
+        self.message_args = (name,)
+
+
+class RedefinedWhileUnused(Message):
+    message = 'redefinition of unused %r from line %r'
+
+    def __init__(self, filename, loc, name, orig_loc):
+        Message.__init__(self, filename, loc)
+        self.message_args = (name, orig_loc.lineno)
+
+
+class RedefinedInListComp(Message):
+    message = 'list comprehension redefines %r from line %r'
+
+    def __init__(self, filename, loc, name, orig_loc):
+        Message.__init__(self, filename, loc)
+        self.message_args = (name, orig_loc.lineno)
+
+
+class ImportShadowedByLoopVar(Message):
+    message = 'import %r from line %r shadowed by loop variable'
+
+    def __init__(self, filename, loc, name, orig_loc):
+        Message.__init__(self, filename, loc)
+        self.message_args = (name, orig_loc.lineno)
+
+
+class ImportStarUsed(Message):
+    message = "'from %s import *' used; unable to detect undefined names"
+
+    def __init__(self, filename, loc, modname):
+        Message.__init__(self, filename, loc)
+        self.message_args = (modname,)
+
+
+class UndefinedName(Message):
+    message = 'undefined name %r'
+
+    def __init__(self, filename, loc, name):
+        Message.__init__(self, filename, loc)
+        self.message_args = (name,)
+
+
+class DoctestSyntaxError(Message):
+    message = 'syntax error in doctest'
+
+    def __init__(self, filename, loc, position=None):
+        Message.__init__(self, filename, loc)
+        if position:
+            (self.lineno, self.col) = position
+        self.message_args = ()
+
+
+class UndefinedExport(Message):
+    message = 'undefined name %r in __all__'
+
+    def __init__(self, filename, loc, name):
+        Message.__init__(self, filename, loc)
+        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, loc, name, orig_loc):
+        Message.__init__(self, filename, loc)
+        self.message_args = (name, orig_loc.lineno)
+
+
+class DuplicateArgument(Message):
+    message = 'duplicate argument %r in function definition'
+
+    def __init__(self, filename, loc, name):
+        Message.__init__(self, filename, loc)
+        self.message_args = (name,)
+
+
+class Redefined(Message):
+    message = 'redefinition of %r from line %r'
+
+    def __init__(self, filename, loc, name, orig_loc):
+        Message.__init__(self, filename, loc)
+        self.message_args = (name, orig_loc.lineno)
+
+
+class LateFutureImport(Message):
+    message = 'future import(s) %r after other statements'
+
+    def __init__(self, filename, loc, names):
+        Message.__init__(self, filename, loc)
+        self.message_args = (names,)
+
+
+class UnusedVariable(Message):
+    """
+    Indicates that a variable has been explicity assigned to but not actually
+    used.
+    """
+    message = 'local variable %r is assigned to but never used'
+
+    def __init__(self, filename, loc, names):
+        Message.__init__(self, filename, loc)
+        self.message_args = (names,)
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/reporter.py
@@ -0,0 +1,76 @@
+"""
+Provide the Reporter class.
+"""
+
+import sys
+
+
+class Reporter(object):
+    """
+    Formats the results of pyflakes checks to users.
+    """
+
+    def __init__(self, warningStream, errorStream):
+        """
+        Construct a L{Reporter}.
+
+        @param warningStream: A file-like object where warnings will be
+            written to.  The stream's C{write} method must accept unicode.
+            C{sys.stdout} is a good value.
+        @param errorStream: A file-like object where error output will be
+            written to.  The stream's C{write} method must accept unicode.
+            C{sys.stderr} is a good value.
+        """
+        self._stdout = warningStream
+        self._stderr = errorStream
+
+    def unexpectedError(self, filename, msg):
+        """
+        An unexpected error occurred trying to process C{filename}.
+
+        @param filename: The path to a file that we could not process.
+        @ptype filename: C{unicode}
+        @param msg: A message explaining the problem.
+        @ptype msg: C{unicode}
+        """
+        self._stderr.write("%s: %s\n" % (filename, msg))
+
+    def syntaxError(self, filename, msg, lineno, offset, text):
+        """
+        There was a syntax errror in C{filename}.
+
+        @param filename: The path to the file with the syntax error.
+        @ptype filename: C{unicode}
+        @param msg: An explanation of the syntax error.
+        @ptype msg: C{unicode}
+        @param lineno: The line number where the syntax error occurred.
+        @ptype lineno: C{int}
+        @param offset: The column on which the syntax error occurred.
+        @ptype offset: C{int}
+        @param text: The source code containing the syntax error.
+        @ptype text: C{unicode}
+        """
+        line = text.splitlines()[-1]
+        if offset is not None:
+            offset = offset - (len(text) - len(line))
+        self._stderr.write('%s:%d: %s\n' % (filename, lineno, msg))
+        self._stderr.write(line)
+        self._stderr.write('\n')
+        if offset is not None:
+            self._stderr.write(" " * (offset + 1) + "^\n")
+
+    def flake(self, message):
+        """
+        pyflakes found something wrong with the code.
+
+        @param: A L{pyflakes.messages.Message}.
+        """
+        self._stdout.write(str(message))
+        self._stdout.write('\n')
+
+
+def _makeDefaultReporter():
+    """
+    Make a reporter that can be used when no reporter is specified.
+    """
+    return Reporter(sys.stdout, sys.stderr)
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/scripts/pyflakes.py
@@ -0,0 +1,7 @@
+"""
+Implementation of the command-line I{pyflakes} tool.
+"""
+from __future__ import absolute_import
+
+# For backward compatibility
+from pyflakes.api import check, checkPath, checkRecursive, iterSourceCode, main
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/test/harness.py
@@ -0,0 +1,41 @@
+
+import sys
+import textwrap
+import unittest
+
+from pyflakes import checker
+
+__all__ = ['TestCase', 'skip', 'skipIf']
+
+if sys.version_info < (2, 7):
+    skip = lambda why: (lambda func: 'skip')  # not callable
+    skipIf = lambda cond, why: (skip(why) if cond else lambda func: func)
+else:
+    skip = unittest.skip
+    skipIf = unittest.skipIf
+PyCF_ONLY_AST = 1024
+
+
+class TestCase(unittest.TestCase):
+
+    def flakes(self, input, *expectedOutputs, **kw):
+        tree = compile(textwrap.dedent(input), "<test>", "exec", PyCF_ONLY_AST)
+        w = checker.Checker(tree, **kw)
+        outputs = [type(o) for o in w.messages]
+        expectedOutputs = list(expectedOutputs)
+        outputs.sort(key=lambda t: t.__name__)
+        expectedOutputs.sort(key=lambda t: t.__name__)
+        self.assertEqual(outputs, expectedOutputs, '''\
+for input:
+%s
+expected outputs:
+%r
+but got:
+%s''' % (input, expectedOutputs, '\n'.join([str(o) for o in w.messages])))
+        return w
+
+    if sys.version_info < (2, 7):
+
+        def assertIs(self, expr1, expr2, msg=None):
+            if expr1 is not expr2:
+                self.fail(msg or '%r is not %r' % (expr1, expr2))
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/test/test_api.py
@@ -0,0 +1,564 @@
+"""
+Tests for L{pyflakes.scripts.pyflakes}.
+"""
+
+import os
+import sys
+import shutil
+import subprocess
+import tempfile
+
+from pyflakes.messages import UnusedImport
+from pyflakes.reporter import Reporter
+from pyflakes.api import (
+    checkPath,
+    checkRecursive,
+    iterSourceCode,
+)
+from pyflakes.test.harness import TestCase, skipIf
+
+if sys.version_info < (3,):
+    from cStringIO import StringIO
+else:
+    from io import StringIO
+    unichr = chr
+
+
+def withStderrTo(stderr, f, *args, **kwargs):
+    """
+    Call C{f} with C{sys.stderr} redirected to C{stderr}.
+    """
+    (outer, sys.stderr) = (sys.stderr, stderr)
+    try:
+        return f(*args, **kwargs)
+    finally:
+        sys.stderr = outer
+
+
+class Node(object):
+    """
+    Mock an AST node.
+    """
+    def __init__(self, lineno, col_offset=0):
+        self.lineno = lineno
+        self.col_offset = col_offset
+
+
+class LoggingReporter(object):
+    """
+    Implementation of Reporter that just appends any error to a list.
+    """
+
+    def __init__(self, log):
+        """
+        Construct a C{LoggingReporter}.
+
+        @param log: A list to append log messages to.
+        """
+        self.log = log
+
+    def flake(self, message):
+        self.log.append(('flake', str(message)))
+
+    def unexpectedError(self, filename, message):
+        self.log.append(('unexpectedError', filename, message))
+
+    def syntaxError(self, filename, msg, lineno, offset, line):
+        self.log.append(('syntaxError', filename, msg, lineno, offset, line))
+
+
+class TestIterSourceCode(TestCase):
+    """
+    Tests for L{iterSourceCode}.
+    """
+
+    def setUp(self):
+        self.tempdir = tempfile.mkdtemp()
+
+    def tearDown(self):
+        shutil.rmtree(self.tempdir)
+
+    def makeEmptyFile(self, *parts):
+        assert parts
+        fpath = os.path.join(self.tempdir, *parts)
+        fd = open(fpath, 'a')
+        fd.close()
+        return fpath
+
+    def test_emptyDirectory(self):
+        """
+        There are no Python files in an empty directory.
+        """
+        self.assertEqual(list(iterSourceCode([self.tempdir])), [])
+
+    def test_singleFile(self):
+        """
+        If the directory contains one Python file, C{iterSourceCode} will find
+        it.
+        """
+        childpath = self.makeEmptyFile('foo.py')
+        self.assertEqual(list(iterSourceCode([self.tempdir])), [childpath])
+
+    def test_onlyPythonSource(self):
+        """
+        Files that are not Python source files are not included.
+        """
+        self.makeEmptyFile('foo.pyc')
+        self.assertEqual(list(iterSourceCode([self.tempdir])), [])
+
+    def test_recurses(self):
+        """
+        If the Python files are hidden deep down in child directories, we will
+        find them.
+        """
+        os.mkdir(os.path.join(self.tempdir, 'foo'))
+        apath = self.makeEmptyFile('foo', 'a.py')
+        os.mkdir(os.path.join(self.tempdir, 'bar'))
+        bpath = self.makeEmptyFile('bar', 'b.py')
+        cpath = self.makeEmptyFile('c.py')
+        self.assertEqual(
+            sorted(iterSourceCode([self.tempdir])),
+            sorted([apath, bpath, cpath]))
+
+    def test_multipleDirectories(self):
+        """
+        L{iterSourceCode} can be given multiple directories.  It will recurse
+        into each of them.
+        """
+        foopath = os.path.join(self.tempdir, 'foo')
+        barpath = os.path.join(self.tempdir, 'bar')
+        os.mkdir(foopath)
+        apath = self.makeEmptyFile('foo', 'a.py')
+        os.mkdir(barpath)
+        bpath = self.makeEmptyFile('bar', 'b.py')
+        self.assertEqual(
+            sorted(iterSourceCode([foopath, barpath])),
+            sorted([apath, bpath]))
+
+    def test_explicitFiles(self):
+        """
+        If one of the paths given to L{iterSourceCode} is not a directory but
+        a file, it will include that in its output.
+        """
+        epath = self.makeEmptyFile('e.py')
+        self.assertEqual(list(iterSourceCode([epath])),
+                         [epath])
+
+
+class TestReporter(TestCase):
+    """
+    Tests for L{Reporter}.
+    """
+
+    def test_syntaxError(self):
+        """
+        C{syntaxError} reports that there was a syntax error in the source
+        file.  It reports to the error stream and includes the filename, line
+        number, error message, actual line of source and a caret pointing to
+        where the error is.
+        """
+        err = StringIO()
+        reporter = Reporter(None, err)
+        reporter.syntaxError('foo.py', 'a problem', 3, 4, 'bad line of source')
+        self.assertEqual(
+            ("foo.py:3: a problem\n"
+             "bad line of source\n"
+             "     ^\n"),
+            err.getvalue())
+
+    def test_syntaxErrorNoOffset(self):
+        """
+        C{syntaxError} doesn't include a caret pointing to the error if
+        C{offset} is passed as C{None}.
+        """
+        err = StringIO()
+        reporter = Reporter(None, err)
+        reporter.syntaxError('foo.py', 'a problem', 3, None,
+                             'bad line of source')
+        self.assertEqual(
+            ("foo.py:3: a problem\n"
+             "bad line of source\n"),
+            err.getvalue())
+
+    def test_multiLineSyntaxError(self):
+        """
+        If there's a multi-line syntax error, then we only report the last
+        line.  The offset is adjusted so that it is relative to the start of
+        the last line.
+        """
+        err = StringIO()
+        lines = [
+            'bad line of source',
+            'more bad lines of source',
+        ]
+        reporter = Reporter(None, err)
+        reporter.syntaxError('foo.py', 'a problem', 3, len(lines[0]) + 5,
+                             '\n'.join(lines))
+        self.assertEqual(
+            ("foo.py:3: a problem\n" +
+             lines[-1] + "\n" +
+             "     ^\n"),
+            err.getvalue())
+
+    def test_unexpectedError(self):
+        """
+        C{unexpectedError} reports an error processing a source file.
+        """
+        err = StringIO()
+        reporter = Reporter(None, err)
+        reporter.unexpectedError('source.py', 'error message')
+        self.assertEqual('source.py: error message\n', err.getvalue())
+
+    def test_flake(self):
+        """
+        C{flake} reports a code warning from Pyflakes.  It is exactly the
+        str() of a L{pyflakes.messages.Message}.
+        """
+        out = StringIO()
+        reporter = Reporter(out, None)
+        message = UnusedImport('foo.py', Node(42), 'bar')
+        reporter.flake(message)
+        self.assertEqual(out.getvalue(), "%s\n" % (message,))
+
+
+class CheckTests(TestCase):
+    """
+    Tests for L{check} and L{checkPath} which check a file for flakes.
+    """
+
+    def makeTempFile(self, content):
+        """
+        Make a temporary file containing C{content} and return a path to it.
+        """
+        _, fpath = tempfile.mkstemp()
+        if not hasattr(content, 'decode'):
+            content = content.encode('ascii')
+        fd = open(fpath, 'wb')
+        fd.write(content)
+        fd.close()
+        return fpath
+
+    def assertHasErrors(self, path, errorList):
+        """
+        Assert that C{path} causes errors.
+
+        @param path: A path to a file to check.
+        @param errorList: A list of errors expected to be printed to stderr.
+        """
+        err = StringIO()
+        count = withStderrTo(err, checkPath, path)
+        self.assertEqual(
+            (count, err.getvalue()), (len(errorList), ''.join(errorList)))
+
+    def getErrors(self, path):
+        """
+        Get any warnings or errors reported by pyflakes for the file at C{path}.
+
+        @param path: The path to a Python file on disk that pyflakes will check.
+        @return: C{(count, log)}, where C{count} is the number of warnings or
+            errors generated, and log is a list of those warnings, presented
+            as structured data.  See L{LoggingReporter} for more details.
+        """
+        log = []
+        reporter = LoggingReporter(log)
+        count = checkPath(path, reporter)
+        return count, log
+
+    def test_legacyScript(self):
+        from pyflakes.scripts import pyflakes as script_pyflakes
+        self.assertIs(script_pyflakes.checkPath, checkPath)
+
+    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.makeTempFile("def foo():\n\tpass\n\t")
+        self.assertHasErrors(fName, [])
+
+    def test_checkPathNonExisting(self):
+        """
+        L{checkPath} handles non-existing files.
+        """
+        count, errors = self.getErrors('extremo')
+        self.assertEqual(count, 1)
+        self.assertEqual(
+            errors,
+            [('unexpectedError', 'extremo', 'No such file or directory')])
+
+    def test_multilineSyntaxError(self):
+        """
+        Source which includes a syntax error which results in the raised
+        L{SyntaxError.text} containing multiple lines of source are reported
+        with only the last line of that source.
+        """
+        source = """\
+def foo():
+    '''
+
+def bar():
+    pass
+
+def baz():
+    '''quux'''
+"""
+
+        # Sanity check - SyntaxError.text should be multiple lines, if it
+        # isn't, something this test was unprepared for has happened.
+        def evaluate(source):
+            exec(source)
+        try:
+            evaluate(source)
+        except SyntaxError:
+            e = sys.exc_info()[1]
+            self.assertTrue(e.text.count('\n') > 1)
+        else:
+            self.fail()
+
+        sourcePath = self.makeTempFile(source)
+        self.assertHasErrors(
+            sourcePath,
+            ["""\
+%s:8: invalid syntax
+    '''quux'''
+           ^
+""" % (sourcePath,)])
+
+    def test_eofSyntaxError(self):
+        """
+        The error reported for source files which end prematurely causing a
+        syntax error reflects the cause for the syntax error.
+        """
+        sourcePath = self.makeTempFile("def foo(")
+        self.assertHasErrors(
+            sourcePath,
+            ["""\
+%s:1: unexpected EOF while parsing
+def foo(
+         ^
+""" % (sourcePath,)])
+
+    def test_nonDefaultFollowsDefaultSyntaxError(self):
+        """
+        Source which has a non-default argument following a default argument
+        should include the line number of the syntax error.  However these
+        exceptions do not include an offset.
+        """
+        source = """\
+def foo(bar=baz, bax):
+    pass
+"""
+        sourcePath = self.makeTempFile(source)
+        last_line = '        ^\n' if sys.version_info >= (3, 2) else ''
+        self.assertHasErrors(
+            sourcePath,
+            ["""\
+%s:1: non-default argument follows default argument
+def foo(bar=baz, bax):
+%s""" % (sourcePath, last_line)])
+
+    def test_nonKeywordAfterKeywordSyntaxError(self):
+        """
+        Source which has a non-keyword argument after a keyword argument should
+        include the line number of the syntax error.  However these exceptions
+        do not include an offset.
+        """
+        source = """\
+foo(bar=baz, bax)
+"""
+        sourcePath = self.makeTempFile(source)
+        last_line = '             ^\n' if sys.version_info >= (3, 2) else ''
+        self.assertHasErrors(
+            sourcePath,
+            ["""\
+%s:1: non-keyword arg after keyword arg
+foo(bar=baz, bax)
+%s""" % (sourcePath, last_line)])
+
+    def test_invalidEscape(self):
+        """
+        The invalid escape syntax raises ValueError in Python 2
+        """
+        ver = sys.version_info
+        # ValueError: invalid \x escape
+        sourcePath = self.makeTempFile(r"foo = '\xyz'")
+        if ver < (3,):
+            decoding_error = "%s: problem decoding source\n" % (sourcePath,)
+        else:
+            last_line = '       ^\n' if ver >= (3, 2) else ''
+            # Column has been "fixed" since 3.2.4 and 3.3.1
+            col = 1 if ver >= (3, 3, 1) or ((3, 2, 4) <= ver < (3, 3)) else 2
+            decoding_error = """\
+%s:1: (unicode error) 'unicodeescape' codec can't decode bytes \
+in position 0-%d: truncated \\xXX escape
+foo = '\\xyz'
+%s""" % (sourcePath, col, last_line)
+        self.assertHasErrors(
+            sourcePath, [decoding_error])
+
+    def test_permissionDenied(self):
+        """
+        If the source file is not readable, this is reported on standard
+        error.
+        """
+        sourcePath = self.makeTempFile('')
+        os.chmod(sourcePath, 0)
+        count, errors = self.getErrors(sourcePath)
+        self.assertEqual(count, 1)
+        self.assertEqual(
+            errors,
+            [('unexpectedError', sourcePath, "Permission denied")])
+
+    def test_pyflakesWarning(self):
+        """
+        If the source file has a pyflakes warning, this is reported as a
+        'flake'.
+        """
+        sourcePath = self.makeTempFile("import foo")
+        count, errors = self.getErrors(sourcePath)
+        self.assertEqual(count, 1)
+        self.assertEqual(
+            errors, [('flake', str(UnusedImport(sourcePath, Node(1), 'foo')))])
+
+    @skipIf(sys.version_info >= (3,), "not relevant")
+    def test_misencodedFileUTF8(self):
+        """
+        If a source file contains bytes which cannot be decoded, this is
+        reported on stderr.
+        """
+        SNOWMAN = unichr(0x2603)
+        source = ("""\
+# coding: ascii
+x = "%s"
+""" % SNOWMAN).encode('utf-8')
+        sourcePath = self.makeTempFile(source)
+        self.assertHasErrors(
+            sourcePath, ["%s: problem decoding source\n" % (sourcePath,)])
+
+    def test_misencodedFileUTF16(self):
+        """
+        If a source file contains bytes which cannot be decoded, this is
+        reported on stderr.
+        """
+        SNOWMAN = unichr(0x2603)
+        source = ("""\
+# coding: ascii
+x = "%s"
+""" % SNOWMAN).encode('utf-16')
+        sourcePath = self.makeTempFile(source)
+        self.assertHasErrors(
+            sourcePath, ["%s: problem decoding source\n" % (sourcePath,)])
+
+    def test_checkRecursive(self):
+        """
+        L{checkRecursive} descends into each directory, finding Python files
+        and reporting problems.
+        """
+        tempdir = tempfile.mkdtemp()
+        os.mkdir(os.path.join(tempdir, 'foo'))
+        file1 = os.path.join(tempdir, 'foo', 'bar.py')
+        fd = open(file1, 'wb')
+        fd.write("import baz\n".encode('ascii'))
+        fd.close()
+        file2 = os.path.join(tempdir, 'baz.py')
+        fd = open(file2, 'wb')
+        fd.write("import contraband".encode('ascii'))
+        fd.close()
+        log = []
+        reporter = LoggingReporter(log)
+        warnings = checkRecursive([tempdir], reporter)
+        self.assertEqual(warnings, 2)
+        self.assertEqual(
+            sorted(log),
+            sorted([('flake', str(UnusedImport(file1, Node(1), 'baz'))),
+                    ('flake',
+                     str(UnusedImport(file2, Node(1), 'contraband')))]))
+
+
+class IntegrationTests(TestCase):
+    """
+    Tests of the pyflakes script that actually spawn the script.
+    """
+
+    def setUp(self):
+        self.tempdir = tempfile.mkdtemp()
+        self.tempfilepath = os.path.join(self.tempdir, 'temp')
+
+    def tearDown(self):
+        shutil.rmtree(self.tempdir)
+
+    def getPyflakesBinary(self):
+        """
+        Return the path to the pyflakes binary.
+        """
+        import pyflakes
+        package_dir = os.path.dirname(pyflakes.__file__)
+        return os.path.join(package_dir, '..', 'bin', 'pyflakes')
+
+    def runPyflakes(self, paths, stdin=None):
+        """
+        Launch a subprocess running C{pyflakes}.
+
+        @param args: Command-line arguments to pass to pyflakes.
+        @param kwargs: Options passed on to C{subprocess.Popen}.
+        @return: C{(returncode, stdout, stderr)} of the completed pyflakes
+            process.
+        """
+        env = dict(os.environ)
+        env['PYTHONPATH'] = os.pathsep.join(sys.path)
+        command = [sys.executable, self.getPyflakesBinary()]
+        command.extend(paths)
+        if stdin:
+            p = subprocess.Popen(command, env=env, stdin=subprocess.PIPE,
+                                 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            (stdout, stderr) = p.communicate(stdin)
+        else:
+            p = subprocess.Popen(command, env=env,
+                                 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            (stdout, stderr) = p.communicate()
+        rv = p.wait()
+        if sys.version_info >= (3,):
+            stdout = stdout.decode('utf-8')
+            stderr = stderr.decode('utf-8')
+        return (stdout, stderr, rv)
+
+    def test_goodFile(self):
+        """
+        When a Python source file is all good, the return code is zero and no
+        messages are printed to either stdout or stderr.
+        """
+        fd = open(self.tempfilepath, 'a')
+        fd.close()
+        d = self.runPyflakes([self.tempfilepath])
+        self.assertEqual(d, ('', '', 0))
+
+    def test_fileWithFlakes(self):
+        """
+        When a Python source file has warnings, the return code is non-zero
+        and the warnings are printed to stdout.
+        """
+        fd = open(self.tempfilepath, 'wb')
+        fd.write("import contraband\n".encode('ascii'))
+        fd.close()
+        d = self.runPyflakes([self.tempfilepath])
+        expected = UnusedImport(self.tempfilepath, Node(1), 'contraband')
+        self.assertEqual(d, ("%s\n" % expected, '', 1))
+
+    def test_errors(self):
+        """
+        When pyflakes finds errors with the files it's given, (if they don't
+        exist, say), then the return code is non-zero and the errors are
+        printed to stderr.
+        """
+        d = self.runPyflakes([self.tempfilepath])
+        error_msg = '%s: No such file or directory\n' % (self.tempfilepath,)
+        self.assertEqual(d, ('', error_msg, 1))
+
+    def test_readFromStdin(self):
+        """
+        If no arguments are passed to C{pyflakes} then it reads from stdin.
+        """
+        d = self.runPyflakes([], stdin='import contraband'.encode('ascii'))
+        expected = UnusedImport('<stdin>', Node(1), 'contraband')
+        self.assertEqual(d, ("%s\n" % expected, '', 1))
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/test/test_doctests.py
@@ -0,0 +1,209 @@
+import textwrap
+
+from pyflakes import messages as m
+from pyflakes.test.test_other import Test as TestOther
+from pyflakes.test.test_imports import Test as TestImports
+from pyflakes.test.test_undefined_names import Test as TestUndefinedNames
+from pyflakes.test.harness import skip
+
+
+class Test(TestOther, TestImports, TestUndefinedNames):
+
+    def doctestify(self, input):
+        lines = []
+        for line in textwrap.dedent(input).splitlines():
+            if line.strip() == '':
+                pass
+            elif (line.startswith(' ') or
+                  line.startswith('except:') or
+                  line.startswith('except ') or
+                  line.startswith('finally:') or
+                  line.startswith('else:') or
+                  line.startswith('elif ')):
+                line = "... %s" % line
+            else:
+                line = ">>> %s" % line
+            lines.append(line)
+        doctestificator = textwrap.dedent('''\
+            def doctest_something():
+                """
+                   %s
+                """
+            ''')
+        return doctestificator % "\n       ".join(lines)
+
+    def flakes(self, input, *args, **kw):
+        return super(Test, self).flakes(self.doctestify(input),
+                                        *args, **kw)
+
+    def test_doubleNestingReportsClosestName(self):
+        """
+        Lines in doctest are a bit different so we can't use the test
+        from TestUndefinedNames
+        """
+        exc = super(Test, self).flakes('''
+        def doctest_stuff():
+            """
+                >>> def a():
+                ...     x = 1
+                ...     def b():
+                ...         x = 2 # line 7 in the file
+                ...         def c():
+                ...             x
+                ...             x = 3
+                ...             return x
+                ...         return x
+                ...     return x
+
+            """
+        ''', m.UndefinedLocal).messages[0]
+        self.assertEqual(exc.message_args, ('x', 7))
+
+    def test_futureImport(self):
+        """XXX This test can't work in a doctest"""
+
+    def test_importBeforeDoctest(self):
+        super(Test, self).flakes("""
+        import foo
+
+        def doctest_stuff():
+            '''
+                >>> foo
+            '''
+        """)
+
+    @skip("todo")
+    def test_importBeforeAndInDoctest(self):
+        super(Test, self).flakes('''
+        import foo
+
+        def doctest_stuff():
+            """
+                >>> import foo
+                >>> foo
+            """
+
+        foo
+        ''', m.Redefined)
+
+    def test_importInDoctestAndAfter(self):
+        super(Test, self).flakes('''
+        def doctest_stuff():
+            """
+                >>> import foo
+                >>> foo
+            """
+
+        import foo
+        foo()
+        ''')
+
+    def test_offsetInDoctests(self):
+        exc = super(Test, self).flakes('''
+
+        def doctest_stuff():
+            """
+                >>> x # line 5
+            """
+
+        ''', m.UndefinedName).messages[0]
+        self.assertEqual(exc.lineno, 5)
+        self.assertEqual(exc.col, 12)
+
+    def test_offsetInLambdasInDoctests(self):
+        exc = super(Test, self).flakes('''
+
+        def doctest_stuff():
+            """
+                >>> lambda: x # line 5
+            """
+
+        ''', m.UndefinedName).messages[0]
+        self.assertEqual(exc.lineno, 5)
+        self.assertEqual(exc.col, 20)
+
+    def test_offsetAfterDoctests(self):
+        exc = super(Test, self).flakes('''
+
+        def doctest_stuff():
+            """
+                >>> x = 5
+            """
+
+        x
+
+        ''', m.UndefinedName).messages[0]
+        self.assertEqual(exc.lineno, 8)
+        self.assertEqual(exc.col, 0)
+
+    def test_syntaxErrorInDoctest(self):
+        exceptions = super(Test, self).flakes(
+            '''
+            def doctest_stuff():
+                """
+                    >>> from # line 4
+                    >>>     fortytwo = 42
+                    >>> except Exception:
+                """
+            ''',
+            m.DoctestSyntaxError,
+            m.DoctestSyntaxError,
+            m.DoctestSyntaxError).messages
+        exc = exceptions[0]
+        self.assertEqual(exc.lineno, 4)
+        self.assertEqual(exc.col, 26)
+        exc = exceptions[1]
+        self.assertEqual(exc.lineno, 5)
+        self.assertEqual(exc.col, 16)
+        exc = exceptions[2]
+        self.assertEqual(exc.lineno, 6)
+        self.assertEqual(exc.col, 18)
+
+    def test_indentationErrorInDoctest(self):
+        exc = super(Test, self).flakes('''
+        def doctest_stuff():
+            """
+                >>> if True:
+                ... pass
+            """
+        ''', m.DoctestSyntaxError).messages[0]
+        self.assertEqual(exc.lineno, 5)
+        self.assertEqual(exc.col, 16)
+
+    def test_offsetWithMultiLineArgs(self):
+        (exc1, exc2) = super(Test, self).flakes(
+            '''
+            def doctest_stuff(arg1,
+                              arg2,
+                              arg3):
+                """
+                    >>> assert
+                    >>> this
+                """
+            ''',
+            m.DoctestSyntaxError,
+            m.UndefinedName).messages
+        self.assertEqual(exc1.lineno, 6)
+        self.assertEqual(exc1.col, 19)
+        self.assertEqual(exc2.lineno, 7)
+        self.assertEqual(exc2.col, 12)
+
+    def test_doctestCanReferToFunction(self):
+        super(Test, self).flakes("""
+        def foo():
+            '''
+                >>> foo
+            '''
+        """)
+
+    def test_doctestCanReferToClass(self):
+        super(Test, self).flakes("""
+        class Foo():
+            '''
+                >>> Foo
+            '''
+            def bar(self):
+                '''
+                    >>> Foo
+                '''
+        """)
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/test/test_imports.py
@@ -0,0 +1,800 @@
+
+from sys import version_info
+
+from pyflakes import messages as m
+from pyflakes.test.harness import TestCase, skip, skipIf
+
+
+class Test(TestCase):
+
+    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)')
+        self.flakes('import fu; del fu')
+
+    def test_redefinedWhileUnused(self):
+        self.flakes('import fu; fu = 3', m.RedefinedWhileUnused)
+        self.flakes('import fu; fu, bar = 3', m.RedefinedWhileUnused)
+        self.flakes('import fu; [fu, bar] = 3', m.RedefinedWhileUnused)
+
+    def test_redefinedIf(self):
+        """
+        Test that importing a module twice within an if
+        block does raise a warning.
+        """
+        self.flakes('''
+        i = 2
+        if i==1:
+            import os
+            import os
+        os.path''', m.RedefinedWhileUnused)
+
+    def test_redefinedIfElse(self):
+        """
+        Test that importing a module twice in if
+        and else blocks does not raise a warning.
+        """
+        self.flakes('''
+        i = 2
+        if i==1:
+            import os
+        else:
+            import os
+        os.path''')
+
+    def test_redefinedTry(self):
+        """
+        Test that importing a module twice in an try block
+        does raise a warning.
+        """
+        self.flakes('''
+        try:
+            import os
+            import os
+        except:
+            pass
+        os.path''', m.RedefinedWhileUnused)
+
+    def test_redefinedTryExcept(self):
+        """
+        Test that importing a module twice in an try
+        and except block does not raise a warning.
+        """
+        self.flakes('''
+        try:
+            import os
+        except:
+            import os
+        os.path''')
+
+    def test_redefinedTryNested(self):
+        """
+        Test that importing a module twice using a nested
+        try/except and if blocks does not issue a warning.
+        """
+        self.flakes('''
+        try:
+            if True:
+                if True:
+                    import os
+        except:
+            import os
+        os.path''')
+
+    def test_redefinedTryExceptMulti(self):
+        self.flakes("""
+        try:
+            from aa import mixer
+        except AttributeError:
+            from bb import mixer
+        except RuntimeError:
+            from cc import mixer
+        except:
+            from dd import mixer
+        mixer(123)
+        """)
+
+    def test_redefinedTryElse(self):
+        self.flakes("""
+        try:
+            from aa import mixer
+        except ImportError:
+            pass
+        else:
+            from bb import mixer
+        mixer(123)
+        """, m.RedefinedWhileUnused)
+
+    def test_redefinedTryExceptElse(self):
+        self.flakes("""
+        try:
+            import funca
+        except ImportError:
+            from bb import funca
+            from bb import funcb
+        else:
+            from bbb import funcb
+        print(funca, funcb)
+        """)
+
+    def test_redefinedTryExceptFinally(self):
+        self.flakes("""
+        try:
+            from aa import a
+        except ImportError:
+            from bb import a
+        finally:
+            a = 42
+        print(a)
+        """)
+
+    def test_redefinedTryExceptElseFinally(self):
+        self.flakes("""
+        try:
+            import b
+        except ImportError:
+            b = Ellipsis
+            from bb import a
+        else:
+            from aa import a
+        finally:
+            a = 42
+        print(a, b)
+        """)
+
+    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_redefinedBySubclass(self):
+        """
+        If an imported name is redefined by a class statement which also uses
+        that name in the bases list, no warning is emitted.
+        """
+        self.flakes('''
+        from fu import bar
+        class bar(bar):
+            pass
+        ''')
+
+    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):
+        as_exc = ', ' if version_info < (2, 6) else ' as '
+        self.flakes('''
+        import fu
+        try: pass
+        except Exception%sfu: pass
+        ''' % as_exc, 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)
+
+    @skipIf(version_info >= (3,), 'deprecated syntax')
+    def test_usedInBackquote(self):
+        self.flakes('import fu; `fu`')
+
+    def test_usedInExec(self):
+        if version_info < (3,):
+            exec_stmt = 'exec "print 1" in fu.bar'
+        else:
+            exec_stmt = 'exec("print(1)", fu.bar)'
+        self.flakes('import fu; %s' % exec_stmt)
+
+    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):
+        """
+        If a dotted name is imported and used, no warning is reported.
+        """
+        self.flakes('''
+        import fu.bar
+        fu.bar
+        ''')
+
+    def test_unusedPackageImport(self):
+        """
+        If a dotted name is imported and not used, an unused import warning is
+        reported.
+        """
+        self.flakes('import fu.bar', m.UnusedImport)
+
+    def test_duplicateSubmoduleImport(self):
+        """
+        If a submodule of a package is imported twice, an unused import warning
+        and a redefined while unused warning are reported.
+        """
+        self.flakes('''
+        import fu.bar, fu.bar
+        fu.bar
+        ''', m.RedefinedWhileUnused)
+        self.flakes('''
+        import fu.bar
+        import fu.bar
+        fu.bar
+        ''', m.RedefinedWhileUnused)
+
+    def test_differentSubmoduleImport(self):
+        """
+        If two different submodules of a package are imported, no duplicate
+        import warning is reported for the package.
+        """
+        self.flakes('''
+        import fu.bar, fu.baz
+        fu.bar, fu.baz
+        ''')
+        self.flakes('''
+        import fu.bar
+        import fu.baz
+        fu.bar, fu.baz
+        ''')
+
+    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
+        fu
+        ''')
+
+    def test_nonGlobalDoesNotRedefine(self):
+        self.flakes('''
+        import fu
+        def a():
+            fu = 3
+            return fu
+        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')
+
+    @skip("todo")
+    def test_importingForImportError(self):
+        self.flakes('''
+        try:
+            import fu
+        except ImportError:
+            pass
+        ''')
+
+    @skip("todo: requires evaluating attribute access")
+    def test_importedInClass(self):
+        """Imports in class scope can be used through self."""
+        self.flakes('''
+        class c:
+            import i
+            def __init__(self):
+                self.i
+        ''')
+
+    def test_futureImport(self):
+        """__future__ is special."""
+        self.flakes('from __future__ import division')
+        self.flakes('''
+        "docstring is allowed before future import"
+        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)
+        self.flakes('''
+        from foo import bar
+        from __future__ import division
+        bar
+        ''', m.LateFutureImport)
+
+
+class TestSpecialAll(TestCase):
+    """
+    Tests for suppression of unused import warnings by C{__all__}.
+    """
+    def test_ignoredInFunction(self):
+        """
+        An C{__all__} definition does not suppress unused import warnings in a
+        function scope.
+        """
+        self.flakes('''
+        def foo():
+            import bar
+            __all__ = ["bar"]
+        ''', m.UnusedImport, m.UnusedVariable)
+
+    def test_ignoredInClass(self):
+        """
+        An C{__all__} definition does not suppress unused import warnings in a
+        class scope.
+        """
+        self.flakes('''
+        class foo:
+            import bar
+            __all__ = ["bar"]
+        ''', m.UnusedImport)
+
+    def test_warningSuppressed(self):
+        """
+        If a name is imported and unused but is named in C{__all__}, no warning
+        is reported.
+        """
+        self.flakes('''
+        import foo
+        __all__ = ["foo"]
+        ''')
+
+    def test_unrecognizable(self):
+        """
+        If C{__all__} is defined in a way that can't be recognized statically,
+        it is ignored.
+        """
+        self.flakes('''
+        import foo
+        __all__ = ["f" + "oo"]
+        ''', m.UnusedImport)
+        self.flakes('''
+        import foo
+        __all__ = [] + ["foo"]
+        ''', m.UnusedImport)
+
+    def test_unboundExported(self):
+        """
+        If C{__all__} includes a name which is not bound, a warning is emitted.
+        """
+        self.flakes('''
+        __all__ = ["foo"]
+        ''', m.UndefinedExport)
+
+        # Skip this in __init__.py though, since the rules there are a little
+        # different.
+        for filename in ["foo/__init__.py", "__init__.py"]:
+            self.flakes('''
+            __all__ = ["foo"]
+            ''', filename=filename)
+
+    def test_importStarExported(self):
+        """
+        Do not report undefined if import * is used
+        """
+        self.flakes('''
+        from foolib import *
+        __all__ = ["foo"]
+        ''', m.ImportStarUsed)
+
+    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, m.UnusedImport)
+
+    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)
+
+
+class Python26Tests(TestCase):
+    """
+    Tests for checking of syntax which is valid in PYthon 2.6 and newer.
+    """
+
+    @skipIf(version_info < (2, 6), "Python >= 2.6 only")
+    def test_usedAsClassDecorator(self):
+        """
+        Using an imported name as a class decorator results in no warnings,
+        but using an undefined name as a class decorator results in an
+        undefined name warning.
+        """
+        self.flakes('''
+        from interior import decorate
+        @decorate
+        class foo:
+            pass
+        ''')
+
+        self.flakes('''
+        from interior import decorate
+        @decorate("foo")
+        class bar:
+            pass
+        ''')
+
+        self.flakes('''
+        @decorate
+        class foo:
+            pass
+        ''', m.UndefinedName)
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/test/test_other.py
@@ -0,0 +1,919 @@
+"""
+Tests for various Pyflakes behavior.
+"""
+
+from sys import version_info
+
+from pyflakes import messages as m
+from pyflakes.test.harness import TestCase, skip, skipIf
+
+
+class Test(TestCase):
+
+    def test_duplicateArgs(self):
+        self.flakes('def fu(bar, bar): pass', m.DuplicateArgument)
+
+    @skip("todo: this requires finding all assignments in the function body first")
+    def test_localReferencedBeforeAssignment(self):
+        self.flakes('''
+        a = 1
+        def f():
+            a; a=1
+        f()
+        ''', m.UndefinedName)
+
+    def test_redefinedInListComp(self):
+        """
+        Test that shadowing a variable in a list comprehension raises
+        a warning.
+        """
+        self.flakes('''
+        a = 1
+        [1 for a, b in [(1, 2)]]
+        ''', m.RedefinedInListComp)
+        self.flakes('''
+        class A:
+            a = 1
+            [1 for a, b in [(1, 2)]]
+        ''', m.RedefinedInListComp)
+        self.flakes('''
+        def f():
+            a = 1
+            [1 for a, b in [(1, 2)]]
+        ''', m.RedefinedInListComp)
+        self.flakes('''
+        [1 for a, b in [(1, 2)]]
+        [1 for a, b in [(1, 2)]]
+        ''')
+        self.flakes('''
+        for a, b in [(1, 2)]:
+            pass
+        [1 for a, b in [(1, 2)]]
+        ''')
+
+    def test_redefinedInGenerator(self):
+        """
+        Test that reusing a variable in a generator does not raise
+        a warning.
+        """
+        self.flakes('''
+        a = 1
+        (1 for a, b in [(1, 2)])
+        ''')
+        self.flakes('''
+        class A:
+            a = 1
+            list(1 for a, b in [(1, 2)])
+        ''')
+        self.flakes('''
+        def f():
+            a = 1
+            (1 for a, b in [(1, 2)])
+        ''', m.UnusedVariable)
+        self.flakes('''
+        (1 for a, b in [(1, 2)])
+        (1 for a, b in [(1, 2)])
+        ''')
+        self.flakes('''
+        for a, b in [(1, 2)]:
+            pass
+        (1 for a, b in [(1, 2)])
+        ''')
+
+    @skipIf(version_info < (2, 7), "Python >= 2.7 only")
+    def test_redefinedInSetComprehension(self):
+        """
+        Test that reusing a variable in a set comprehension does not raise
+        a warning.
+        """
+        self.flakes('''
+        a = 1
+        {1 for a, b in [(1, 2)]}
+        ''')
+        self.flakes('''
+        class A:
+            a = 1
+            {1 for a, b in [(1, 2)]}
+        ''')
+        self.flakes('''
+        def f():
+            a = 1
+            {1 for a, b in [(1, 2)]}
+        ''', m.UnusedVariable)
+        self.flakes('''
+        {1 for a, b in [(1, 2)]}
+        {1 for a, b in [(1, 2)]}
+        ''')
+        self.flakes('''
+        for a, b in [(1, 2)]:
+            pass
+        {1 for a, b in [(1, 2)]}
+        ''')
+
+    @skipIf(version_info < (2, 7), "Python >= 2.7 only")
+    def test_redefinedInDictComprehension(self):
+        """
+        Test that reusing a variable in a dict comprehension does not raise
+        a warning.
+        """
+        self.flakes('''
+        a = 1
+        {1: 42 for a, b in [(1, 2)]}
+        ''')
+        self.flakes('''
+        class A:
+            a = 1
+            {1: 42 for a, b in [(1, 2)]}
+        ''')
+        self.flakes('''
+        def f():
+            a = 1
+            {1: 42 for a, b in [(1, 2)]}
+        ''', m.UnusedVariable)
+        self.flakes('''
+        {1: 42 for a, b in [(1, 2)]}
+        {1: 42 for a, b in [(1, 2)]}
+        ''')
+        self.flakes('''
+        for a, b in [(1, 2)]:
+            pass
+        {1: 42 for a, b in [(1, 2)]}
+        ''')
+
+    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.RedefinedWhileUnused)
+
+    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.RedefinedWhileUnused)
+
+    def test_redefinedIfElseFunction(self):
+        """
+        Test that shadowing a function definition twice in an if
+        and else block does not raise a warning.
+        """
+        self.flakes('''
+        if True:
+            def a(): pass
+        else:
+            def a(): pass
+        ''')
+
+    def test_redefinedIfFunction(self):
+        """
+        Test that shadowing a function definition within an if block
+        raises a warning.
+        """
+        self.flakes('''
+        if True:
+            def a(): pass
+            def a(): pass
+        ''', m.RedefinedWhileUnused)
+
+    def test_redefinedTryExceptFunction(self):
+        """
+        Test that shadowing a function definition twice in try
+        and except block does not raise a warning.
+        """
+        self.flakes('''
+        try:
+            def a(): pass
+        except:
+            def a(): pass
+        ''')
+
+    def test_redefinedTryFunction(self):
+        """
+        Test that shadowing a function definition within a try block
+        raises a warning.
+        """
+        self.flakes('''
+        try:
+            def a(): pass
+            def a(): pass
+        except:
+            pass
+        ''', m.RedefinedWhileUnused)
+
+    def test_redefinedIfElseInListComp(self):
+        """
+        Test that shadowing a variable in a list comprehension in
+        an if and else block does not raise a warning.
+        """
+        self.flakes('''
+        if False:
+            a = 1
+        else:
+            [a for a in '12']
+        ''')
+
+    def test_redefinedElseInListComp(self):
+        """
+        Test that shadowing a variable in a list comprehension in
+        an else (or if) block raises a warning.
+        """
+        self.flakes('''
+        if False:
+            pass
+        else:
+            a = 1
+            [a for a in '12']
+        ''', m.RedefinedInListComp)
+
+    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)
+        ''')
+
+    @skipIf(version_info < (2, 6), "Python >= 2.6 only")
+    def test_modernProperty(self):
+        self.flakes("""
+        class A:
+            @property
+            def t(self):
+                pass
+            @t.setter
+            def t(self, value):
+                pass
+            @t.deleter
+            def t(self):
+                pass
+        """)
+
+    def test_unaryPlus(self):
+        """Don't die on unary +."""
+        self.flakes('+1')
+
+    def test_undefinedBaseClass(self):
+        """
+        If a name in the base list of a class definition is undefined, a
+        warning is emitted.
+        """
+        self.flakes('''
+        class foo(foo):
+            pass
+        ''', m.UndefinedName)
+
+    def test_classNameUndefinedInClassBody(self):
+        """
+        If a class name is used in the body of that class's definition and
+        the name is not already defined, a warning is emitted.
+        """
+        self.flakes('''
+        class foo:
+            foo
+        ''', m.UndefinedName)
+
+    def test_classNameDefinedPreviously(self):
+        """
+        If a class name is used in the body of that class's definition and
+        the name was previously defined in some other way, no warning is
+        emitted.
+        """
+        self.flakes('''
+        foo = None
+        class foo:
+            foo
+        ''')
+
+    def test_classRedefinition(self):
+        """
+        If a class is defined twice in the same module, a warning is emitted.
+        """
+        self.flakes('''
+        class Foo:
+            pass
+        class Foo:
+            pass
+        ''', m.RedefinedWhileUnused)
+
+    def test_functionRedefinedAsClass(self):
+        """
+        If a function is redefined as a class, a warning is emitted.
+        """
+        self.flakes('''
+        def Foo():
+            pass
+        class Foo:
+            pass
+        ''', m.RedefinedWhileUnused)
+
+    def test_classRedefinedAsFunction(self):
+        """
+        If a class is redefined as a function, a warning is emitted.
+        """
+        self.flakes('''
+        class Foo:
+            pass
+        def Foo():
+            pass
+        ''', m.RedefinedWhileUnused)
+
+    @skip("todo: Too hard to make this warn but other cases stay silent")
+    def test_doubleAssignment(self):
+        """
+        If a variable is re-assigned to without being used, no warning is
+        emitted.
+        """
+        self.flakes('''
+        x = 10
+        x = 20
+        ''', m.RedefinedWhileUnused)
+
+    def test_doubleAssignmentConditionally(self):
+        """
+        If a variable is re-assigned within a conditional, no warning is
+        emitted.
+        """
+        self.flakes('''
+        x = 10
+        if True:
+            x = 20
+        ''')
+
+    def test_doubleAssignmentWithUse(self):
+        """
+        If a variable is re-assigned to after being used, no warning is
+        emitted.
+        """
+        self.flakes('''
+        x = 10
+        y = x * 2
+        x = 20
+        ''')
+
+    def test_comparison(self):
+        """
+        If a defined name is used on either side of any of the six comparison
+        operators, no warning is emitted.
+        """
+        self.flakes('''
+        x = 10
+        y = 20
+        x < y
+        x <= y
+        x == y
+        x != y
+        x >= y
+        x > y
+        ''')
+
+    def test_identity(self):
+        """
+        If a defined name is used on either side of an identity test, no
+        warning is emitted.
+        """
+        self.flakes('''
+        x = 10
+        y = 20
+        x is y
+        x is not y
+        ''')
+
+    def test_containment(self):
+        """
+        If a defined name is used on either side of a containment test, no
+        warning is emitted.
+        """
+        self.flakes('''
+        x = 10
+        y = 20
+        x in y
+        x not in y
+        ''')
+
+    def test_loopControl(self):
+        """
+        break and continue statements are supported.
+        """
+        self.flakes('''
+        for x in [1, 2]:
+            break
+        ''')
+        self.flakes('''
+        for x in [1, 2]:
+            continue
+        ''')
+
+    def test_ellipsis(self):
+        """
+        Ellipsis in a slice is supported.
+        """
+        self.flakes('''
+        [1, 2][...]
+        ''')
+
+    def test_extendedSlice(self):
+        """
+        Extended slices are supported.
+        """
+        self.flakes('''
+        x = 3
+        [1, 2][x,:]
+        ''')
+
+    def test_varAugmentedAssignment(self):
+        """
+        Augmented assignment of a variable is supported.
+        We don't care about var refs.
+        """
+        self.flakes('''
+        foo = 0
+        foo += 1
+        ''')
+
+    def test_attrAugmentedAssignment(self):
+        """
+        Augmented assignment of attributes is supported.
+        We don't care about attr refs.
+        """
+        self.flakes('''
+        foo = None
+        foo.bar += foo.baz
+        ''')
+
+
+class TestUnusedAssignment(TestCase):
+    """
+    Tests for warning about unused assignments.
+    """
+
+    def test_unusedVariable(self):
+        """
+        Warn when a variable in a function is assigned a value that's never
+        used.
+        """
+        self.flakes('''
+        def a():
+            b = 1
+        ''', m.UnusedVariable)
+
+    def test_unusedVariableAsLocals(self):
+        """
+        Using locals() it is perfectly valid to have unused variables
+        """
+        self.flakes('''
+        def a():
+            b = 1
+            return locals()
+        ''')
+
+    def test_unusedVariableNoLocals(self):
+        """
+        Using locals() in wrong scope should not matter
+        """
+        self.flakes('''
+        def a():
+            locals()
+            def a():
+                b = 1
+                return
+        ''', m.UnusedVariable)
+
+    def test_assignToGlobal(self):
+        """
+        Assigning to a global and then not using that global is perfectly
+        acceptable. Do not mistake it for an unused local variable.
+        """
+        self.flakes('''
+        b = 0
+        def a():
+            global b
+            b = 1
+        ''')
+
+    @skipIf(version_info < (3,), 'new in Python 3')
+    def test_assignToNonlocal(self):
+        """
+        Assigning to a nonlocal and then not using that binding is perfectly
+        acceptable. Do not mistake it for an unused local variable.
+        """
+        self.flakes('''
+        b = b'0'
+        def a():
+            nonlocal b
+            b = b'1'
+        ''')
+
+    def test_assignToMember(self):
+        """
+        Assigning to a member of another object and then not using that member
+        variable is perfectly acceptable. Do not mistake it for an unused
+        local variable.
+        """
+        # XXX: Adding this test didn't generate a failure. Maybe not
+        # necessary?
+        self.flakes('''
+        class b:
+            pass
+        def a():
+            b.foo = 1
+        ''')
+
+    def test_assignInForLoop(self):
+        """
+        Don't warn when a variable in a for loop is assigned to but not used.
+        """
+        self.flakes('''
+        def f():
+            for i in range(10):
+                pass
+        ''')
+
+    def test_assignInListComprehension(self):
+        """
+        Don't warn when a variable in a list comprehension is
+        assigned to but not used.
+        """
+        self.flakes('''
+        def f():
+            [None for i in range(10)]
+        ''')
+
+    def test_generatorExpression(self):
+        """
+        Don't warn when a variable in a generator expression is
+        assigned to but not used.
+        """
+        self.flakes('''
+        def f():
+            (None for i in range(10))
+        ''')
+
+    def test_assignmentInsideLoop(self):
+        """
+        Don't warn when a variable assignment occurs lexically after its use.
+        """
+        self.flakes('''
+        def f():
+            x = None
+            for i in range(10):
+                if i > 2:
+                    return x
+                x = i * 2
+        ''')
+
+    def test_tupleUnpacking(self):
+        """
+        Don't warn when a variable included in tuple unpacking is unused. It's
+        very common for variables in a tuple unpacking assignment to be unused
+        in good Python code, so warning will only create false positives.
+        """
+        self.flakes('''
+        def f():
+            (x, y) = 1, 2
+        ''')
+
+    def test_listUnpacking(self):
+        """
+        Don't warn when a variable included in list unpacking is unused.
+        """
+        self.flakes('''
+        def f():
+            [x, y] = [1, 2]
+        ''')
+
+    def test_closedOver(self):
+        """
+        Don't warn when the assignment is used in an inner function.
+        """
+        self.flakes('''
+        def barMaker():
+            foo = 5
+            def bar():
+                return foo
+            return bar
+        ''')
+
+    def test_doubleClosedOver(self):
+        """
+        Don't warn when the assignment is used in an inner function, even if
+        that inner function itself is in an inner function.
+        """
+        self.flakes('''
+        def barMaker():
+            foo = 5
+            def bar():
+                def baz():
+                    return foo
+            return bar
+        ''')
+
+    def test_tracebackhideSpecialVariable(self):
+        """
+        Do not warn about unused local variable __tracebackhide__, which is
+        a special variable for py.test.
+        """
+        self.flakes("""
+            def helper():
+                __tracebackhide__ = True
+        """)
+
+    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_withStatementAttributeName(self):
+        """
+        No warnings are emitted for using an attribute as the target of a
+        C{with} statement.
+        """
+        self.flakes('''
+        from __future__ import with_statement
+        import foo
+        with open('foo') as foo.bar:
+            pass
+        ''')
+
+    def test_withStatementSubscript(self):
+        """
+        No warnings are emitted for using a subscript as the target of a
+        C{with} statement.
+        """
+        self.flakes('''
+        from __future__ import with_statement
+        import foo
+        with open('foo') as foo[0]:
+            pass
+        ''')
+
+    def test_withStatementSubscriptUndefined(self):
+        """
+        An undefined name warning is emitted if the subscript used as the
+        target of a C{with} statement is not defined.
+        """
+        self.flakes('''
+        from __future__ import with_statement
+        import foo
+        with open('foo') as foo[bar]:
+            pass
+        ''', m.UndefinedName)
+
+    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_withStatementListNames(self):
+        """
+        No warnings are emitted for using any of the list 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_withStatementComplicatedTarget(self):
+        """
+        If the target of a C{with} statement uses any or all of the valid forms
+        for that part of the grammar (See
+        U{http://docs.python.org/reference/compound_stmts.html#the-with-statement}),
+        the names involved are checked both for definedness and any bindings
+        created are respected in the suite of the statement and afterwards.
+        """
+        self.flakes('''
+        from __future__ import with_statement
+        c = d = e = g = h = i = None
+        with open('foo') as [(a, b), c[d], e.f, g[h:i]]:
+            a, b, c, d, e, g, h, i
+        a, b, c, d, e, g, h, i
+        ''')
+
+    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)
+
+    @skipIf(version_info < (2, 7), "Python >= 2.7 only")
+    def test_dictComprehension(self):
+        """
+        Dict comprehensions are properly handled.
+        """
+        self.flakes('''
+        a = {1: x for x in range(10)}
+        ''')
+
+    @skipIf(version_info < (2, 7), "Python >= 2.7 only")
+    def test_setComprehensionAndLiteral(self):
+        """
+        Set comprehensions are properly handled.
+        """
+        self.flakes('''
+        a = {1, 2, 3}
+        b = {x for x in range(10)}
+        ''')
+
+    def test_exceptionUsedInExcept(self):
+        as_exc = ', ' if version_info < (2, 6) else ' as '
+        self.flakes('''
+        try: pass
+        except Exception%se: e
+        ''' % as_exc)
+
+        self.flakes('''
+        def download_review():
+            try: pass
+            except Exception%se: e
+        ''' % as_exc)
+
+    def test_exceptWithoutNameInFunction(self):
+        """
+        Don't issue false warning when an unnamed exception is used.
+        Previously, there would be a false warning, but only when the
+        try..except was in a function
+        """
+        self.flakes('''
+        import tokenize
+        def foo():
+            try: pass
+            except tokenize.TokenError: pass
+        ''')
+
+    def test_exceptWithoutNameInFunctionTuple(self):
+        """
+        Don't issue false warning when an unnamed exception is used.
+        This example catches a tuple of exception types.
+        """
+        self.flakes('''
+        import tokenize
+        def foo():
+            try: pass
+            except (tokenize.TokenError, IndentationError): pass
+        ''')
+
+    def test_augmentedAssignmentImportedFunctionCall(self):
+        """
+        Consider a function that is called on the right part of an
+        augassign operation to be used.
+        """
+        self.flakes('''
+        from foo import bar
+        baz = 0
+        baz += bar()
+        ''')
+
+    @skipIf(version_info < (3, 3), 'new in Python 3.3')
+    def test_yieldFromUndefined(self):
+        """
+        Test C{yield from} statement
+        """
+        self.flakes('''
+        def bar():
+            yield from foo()
+        ''', m.UndefinedName)
new file mode 100644
--- /dev/null
+++ b/pyflakes/pyflakes/test/test_undefined_names.py
@@ -0,0 +1,400 @@
+
+from _ast import PyCF_ONLY_AST
+from sys import version_info
+
+from pyflakes import messages as m, checker
+from pyflakes.test.harness import TestCase, skip, skipIf
+
+
+class Test(TestCase):
+    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_builtinWindowsError(self):
+        """
+        C{WindowsError} is sometimes a builtin name, so no warning is emitted
+        for using it.
+        """
+        self.flakes('WindowsError')
+
+    def test_magicGlobalsFile(self):
+        """
+        Use of the C{__file__} magic global should not emit an undefined name
+        warning.
+        """
+        self.flakes('__file__')
+
+    def test_magicGlobalsBuiltins(self):
+        """
+        Use of the C{__builtins__} magic global should not emit an undefined
+        name warning.
+        """
+        self.flakes('__builtins__')
+
+    def test_magicGlobalsName(self):
+        """
+        Use of the C{__name__} magic global should not emit an undefined name
+        warning.
+        """
+        self.flakes('__name__')
+
+    def test_magicGlobalsPath(self):
+        """
+        Use of the C{__path__} magic global should not emit an undefined name
+        warning, if you refer to it from a file called __init__.py.
+        """
+        self.flakes('__path__', m.UndefinedName)
+        self.flakes('__path__', filename='package/__init__.py')
+
+    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)
+
+    @skipIf(version_info >= (3,), 'obsolete syntax')
+    def test_unpackedParameter(self):
+        """Unpacked function parameters create bindings."""
+        self.flakes('''
+        def a((bar, baz)):
+            bar; baz
+        ''')
+
+    @skip("todo")
+    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
+        ''')
+
+    def test_globalInGlobalScope(self):
+        """
+        A global statement in the global scope is ignored.
+        """
+        self.flakes('''
+        global x
+        def foo():
+            print(x)
+        ''', m.UndefinedName)
+
+    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
+            return a
+        ''', 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
+                    return a
+        ''', m.UndefinedLocal)
+
+    def test_intermediateClassScopeIgnored(self):
+        """
+        If a name defined in an enclosing scope is shadowed by a local variable
+        and the name is used locally before it is bound, an unbound local
+        warning is emitted, even if there is a class scope between the enclosing
+        scope and the local scope.
+        """
+        self.flakes('''
+        def f():
+            x = 1
+            class g:
+                def h(self):
+                    a = x
+                    x = None
+                    print(x, a)
+            print(x)
+        ''', 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
+                        return x
+                    return x
+                return x
+        ''', 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
+                    return a
+                return a
+        ''', m.UndefinedLocal)
+
+    def test_undefinedAugmentedAssignment(self):
+        self.flakes(
+            '''
+            def f(seq):
+                a = 0
+                seq[a] += 1
+                seq[b] /= 2
+                c[0] *= 2
+                a -= 3
+                d += 4
+                e[any] = 5
+            ''',
+            m.UndefinedName,    # b
+            m.UndefinedName,    # c
+            m.UndefinedName, m.UnusedVariable,  # d
+            m.UndefinedName,    # e
+        )
+
+    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
+            return foo
+        f()
+        ''', m.UndefinedName)
+
+    def test_definedAsStarArgs(self):
+        """Star and double-star arg names are defined."""
+        self.flakes('''
+        def f(a, *b, **c):
+            print(a, b, c)
+        ''')
+
+    @skipIf(version_info < (3,), 'new in Python 3')
+    def test_definedAsStarUnpack(self):
+        """Star names in unpack are defined."""
+        self.flakes('''
+        a, *b = range(10)
+        print(a, b)
+        ''')
+        self.flakes('''
+        *a, b = range(10)
+        print(a, b)
+        ''')
+        self.flakes('''
+        a, *b, c = range(10)
+        print(a, b, c)
+        ''')
+
+    @skipIf(version_info < (3,), 'new in Python 3')
+    def test_keywordOnlyArgs(self):
+        """Keyword-only arg names are defined."""
+        self.flakes('''
+        def f(*, a, b=None):
+            print(a, b)
+        ''')
+
+        self.flakes('''
+        import default_b
+        def f(*, a, b=default_b):
+            print(a, b)
+        ''')
+
+    @skipIf(version_info < (3,), 'new in Python 3')
+    def test_keywordOnlyArgsUndefined(self):
+        """Typo in kwonly name."""
+        self.flakes('''
+        def f(*, a, b=default_c):
+            print(a, b)
+        ''', m.UndefinedName)
+
+    @skipIf(version_info < (3,), 'new in Python 3')
+    def test_annotationUndefined(self):
+        """Undefined annotations."""
+        self.flakes('''
+        from abc import note1, note2, note3, note4, note5
+        def func(a: note1, *args: note2,
+                 b: note3=12, **kw: note4) -> note5: pass
+        ''')
+
+        self.flakes('''
+        def func():
+            d = e = 42
+            def func(a: {1, d}) -> (lambda c: e): pass
+        ''')
+
+    @skipIf(version_info < (3,), 'new in Python 3')
+    def test_metaClassUndefined(self):
+        self.flakes('''
+        from abc import ABCMeta
+        class A(metaclass=ABCMeta): pass
+        ''')
+
+    def test_definedInGenExp(self):
+        """
+        Using the loop variable of a generator expression results in no
+        warnings.
+        """
+        self.flakes('(a for a in %srange(10) if a)' %
+                    ('x' if version_info < (3,) else ''))
+
+    def test_undefinedWithErrorHandler(self):
+        """
+        Some compatibility code checks explicitly for NameError.
+        It should not trigger warnings.
+        """
+        self.flakes('''
+        try:
+            socket_map
+        except NameError:
+            socket_map = {}
+        ''')
+        self.flakes('''
+        try:
+            _memoryview.contiguous
+        except (NameError, AttributeError):
+            raise RuntimeError("Python >= 3.3 is required")
+        ''')
+        # If NameError is not explicitly handled, generate a warning
+        self.flakes('''
+        try:
+            socket_map
+        except:
+            socket_map = {}
+        ''', m.UndefinedName)
+        self.flakes('''
+        try:
+            socket_map
+        except Exception:
+            socket_map = {}
+        ''', m.UndefinedName)
+
+    def test_definedInClass(self):
+        """
+        Defined name for generator expressions and dict/set comprehension.
+        """
+        self.flakes('''
+        class A:
+            T = range(10)
+
+            Z = (x for x in T)
+            L = [x for x in T]
+            B = dict((i, str(i)) for i in T)
+        ''')
+
+        if version_info >= (2, 7):
+            self.flakes('''
+            class A:
+                T = range(10)
+
+                X = {x for x in T}
+                Y = {x:x for x in T}
+            ''')
+
+
+class NameTests(TestCase):
+    """
+    Tests for some extra cases of name handling.
+    """
+    def test_impossibleContext(self):
+        """
+        A Name node with an unrecognized context results in a RuntimeError being
+        raised.
+        """
+        tree = compile("x = 10", "<test>", "exec", PyCF_ONLY_AST)
+        # Make it into something unrecognizable.
+        tree.body[0].targets[0].ctx = object()
+        self.assertRaises(RuntimeError, checker.Checker, tree)
new file mode 100644
--- /dev/null
+++ b/pyflakes/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
new file mode 100755
--- /dev/null
+++ b/pyflakes/setup.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+# Copyright 2005-2011 Divmod, Inc.
+# Copyright 2013 Florent Xicluna.  See LICENSE file for details
+from __future__ import with_statement
+
+import os.path
+
+try:
+    from setuptools import setup
+except ImportError:
+    from distutils.core import setup
+    extra = {'scripts': ["bin/pyflakes"]}
+else:
+    extra = {
+        'test_suite': 'pyflakes.test',
+        'entry_points': {
+            'console_scripts': ['pyflakes = pyflakes.api:main'],
+        },
+    }
+
+
+def get_version(fname=os.path.join('pyflakes', '__init__.py')):
+    with open(fname) as f:
+        for line in f:
+            if line.startswith('__version__'):
+                return eval(line.split('=')[-1])
+
+
+def get_long_description():
+    descr = []
+    for fname in ('README.rst',):
+        with open(fname) as f:
+            descr.append(f.read())
+    return '\n\n'.join(descr)
+
+
+setup(
+    name="pyflakes",
+    license="MIT",
+    version=get_version(),
+    description="passive checker of Python programs",
+    long_description=get_long_description(),
+    author="Phil Frost",
+    author_email="indigo@bitglue.com",
+    maintainer="Florent Xicluna",
+    maintainer_email="pyflakes-dev@lists.launchpad.net",
+    url="https://launchpad.net/pyflakes",
+    packages=["pyflakes", "pyflakes.scripts", "pyflakes.test"],
+    classifiers=[
+        "Development Status :: 6 - Mature",
+        "Environment :: Console",
+        "Intended Audience :: Developers",
+        "License :: OSI Approved :: MIT License",
+        "Programming Language :: Python",
+        "Programming Language :: Python :: 2",
+        "Programming Language :: Python :: 3",
+        "Topic :: Software Development",
+        "Topic :: Utilities",
+    ],
+    **extra)