Revert "Revert "pylint: upgrade to 1.3.1""

The reason cited for reverting was invalid, so reland the update.

BUG=chromium:431514

Review URL: https://codereview.chromium.org/739393004

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@293049 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/pylintrc b/pylintrc
index 98866d0..77b0063 100644
--- a/pylintrc
+++ b/pylintrc
@@ -90,7 +90,7 @@
 # unpacking-non-sequence
 # unused-import
 # useless-else-on-loop
-disable=C0103,C0111,C0302,I0010,I0011,R0801,R0901,R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0921,R0922,W0122,W0141,W0142,W0402,W0404,W0511,W0603,W0703,W1201
+disable=C0103,C0111,C0302,I0010,I0011,R0801,R0901,R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0921,R0922,W0122,W0141,W0142,W0402,W0404,W0511,W0603,W0703,W1201,bad-continuation,anomalous-backslash-in-string,bad-context-manager,bad-indentation,bad-str-strip-call,bad-whitespace,cell-var-from-loop,deprecated-lambda,eval-used,function-redefined,import-error,locally-enabled,missing-final-newline,no-init,no-name-in-module,no-self-use,not-callable,old-style-class,protected-access,superfluous-parens,super-on-old-class,too-many-function-args,trailing-whitespace,unnecessary-semicolon,unpacking-non-sequence,unused-import,useless-else-on-loop
 
 
 [REPORTS]
@@ -99,9 +99,6 @@
 # (visual studio) and html
 output-format=text
 
-# Include message's id in output
-include-ids=yes
-
 # Put messages in a separate file for each module / package specified on the
 # command line instead of printing them on stdout. Reports (if any) will be
 # written in a file name "pylint_global.[txt|html]".
diff --git a/third_party/logilab/astng/__init__.py b/third_party/logilab/astng/__init__.py
deleted file mode 100644
index 70b2f3e..0000000
--- a/third_party/logilab/astng/__init__.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
-#
-# This file is part of logilab-astng.
-#
-# logilab-astng is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by the
-# Free Software Foundation, either version 2.1 of the License, or (at your
-# option) any later version.
-#
-# logilab-astng is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
-# for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-"""Python Abstract Syntax Tree New Generation
-
-The aim of this module is to provide a common base representation of
-python source code for projects such as pychecker, pyreverse,
-pylint... Well, actually the development of this library is essentially
-governed by pylint's needs.
-
-It extends class defined in the python's _ast module with some
-additional methods and attributes. Instance attributes are added by a
-builder object, which can either generate extended ast (let's call
-them astng ;) by visiting an existent ast tree or by inspecting living
-object. Methods are added by monkey patching ast classes.
-
-Main modules are:
-
-* nodes and scoped_nodes for more information about methods and
-  attributes added to different node classes
-
-* the manager contains a high level object to get astng trees from
-  source files and living objects. It maintains a cache of previously
-  constructed tree for quick access
-
-* builder contains the class responsible to build astng trees
-"""
-__doctype__ = "restructuredtext en"
-
-import sys
-if sys.version_info >= (3, 0):
-    BUILTINS_MODULE = 'builtins'
-else:
-    BUILTINS_MODULE = '__builtin__'
-
-# WARNING: internal imports order matters !
-
-# make all exception classes accessible from astng package
-from logilab.astng.exceptions import *
-
-# make all node classes accessible from astng package
-from logilab.astng.nodes import *
-
-# trigger extra monkey-patching
-from logilab.astng import inference
-
-# more stuff available
-from logilab.astng import raw_building
-from logilab.astng.bases import YES, Instance, BoundMethod, UnboundMethod
-from logilab.astng.node_classes import are_exclusive, unpack_infer
-from logilab.astng.scoped_nodes import builtin_lookup
-
-# make a manager instance (borg) as well as Project and Package classes
-# accessible from astng package
-from logilab.astng.manager import ASTNGManager, Project
-MANAGER = ASTNGManager()
-del ASTNGManager
diff --git a/third_party/logilab/astng/__pkginfo__.py b/third_party/logilab/astng/__pkginfo__.py
deleted file mode 100644
index f671ac2..0000000
--- a/third_party/logilab/astng/__pkginfo__.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
-#
-# This file is part of logilab-astng.
-#
-# logilab-astng is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by the
-# Free Software Foundation, either version 2.1 of the License, or (at your
-# option) any later version.
-#
-# logilab-astng is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
-# for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-"""logilab.astng packaging information"""
-
-distname = 'logilab-astng'
-
-modname = 'astng'
-subpackage_of = 'logilab'
-
-numversion = (0, 23, 1)
-version = '.'.join([str(num) for num in numversion])
-
-install_requires = ['logilab-common >= 0.53.0']
-
-license = 'LGPL'
-
-author = 'Logilab'
-author_email = '[email protected]'
-mailinglist = "mailto://%s" % author_email
-web = "http://www.logilab.org/project/%s" % distname
-ftp = "ftp://ftp.logilab.org/pub/%s" % modname
-
-description = "rebuild a new abstract syntax tree from Python's ast"
-
-from os.path import join
-include_dirs = [join('test', 'regrtest_data'),
-                join('test', 'data'), join('test', 'data2')]
diff --git a/third_party/logilab/astng/as_string.py b/third_party/logilab/astng/as_string.py
deleted file mode 100644
index 0a42668..0000000
--- a/third_party/logilab/astng/as_string.py
+++ /dev/null
@@ -1,427 +0,0 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
-#
-# This file is part of logilab-astng.
-#
-# logilab-astng is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by the
-# Free Software Foundation, either version 2.1 of the License, or (at your
-# option) any later version.
-#
-# logilab-astng is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
-# for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-"""This module renders ASTNG nodes to string representation.
-
-It will probably not work on bare _ast trees.
-"""
-import sys
-
-
-INDENT = '    ' # 4 spaces ; keep indentation variable
-
-
-def _import_string(names):
-    """return a list of (name, asname) formatted as a string"""
-    _names = []
-    for name, asname in names:
-        if asname is not None:
-            _names.append('%s as %s' % (name, asname))
-        else:
-            _names.append(name)
-    return  ', '.join(_names)
-
-
-class AsStringVisitor(object):
-    """Visitor to render an ASTNG node as string """
-
-    def __call__(self, node):
-        """Makes this visitor behave as a simple function"""
-        return node.accept(self)
-
-    def _stmt_list(self, stmts):
-        """return a list of nodes to string"""
-        stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr])
-        return INDENT + stmts.replace('\n', '\n'+INDENT)
-
-
-    ## visit_<node> methods ###########################################
-
-    def visit_arguments(self, node):
-        """return an astng.Function node as string"""
-        return node.format_args()
-
-    def visit_assattr(self, node):
-        """return an astng.AssAttr node as string"""
-        return self.visit_getattr(node)
-
-    def visit_assert(self, node):
-        """return an astng.Assert node as string"""
-        if node.fail:
-            return 'assert %s, %s' % (node.test.accept(self),
-                                        node.fail.accept(self))
-        return 'assert %s' % node.test.accept(self)
-
-    def visit_assname(self, node):
-        """return an astng.AssName node as string"""
-        return node.name
-
-    def visit_assign(self, node):
-        """return an astng.Assign node as string"""
-        lhs = ' = '.join([n.accept(self) for n in node.targets])
-        return '%s = %s' % (lhs, node.value.accept(self))
-
-    def visit_augassign(self, node):
-        """return an astng.AugAssign node as string"""
-        return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self))
-
-    def visit_backquote(self, node):
-        """return an astng.Backquote node as string"""
-        return '`%s`' % node.value.accept(self)
-
-    def visit_binop(self, node):
-        """return an astng.BinOp node as string"""
-        return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self))
-
-    def visit_boolop(self, node):
-        """return an astng.BoolOp node as string"""
-        return (' %s ' % node.op).join(['(%s)' % n.accept(self)
-                                            for n in node.values])
-
-    def visit_break(self, node):
-        """return an astng.Break node as string"""
-        return 'break'
-
-    def visit_callfunc(self, node):
-        """return an astng.CallFunc node as string"""
-        expr_str = node.func.accept(self)
-        args = [arg.accept(self) for arg in node.args]
-        if node.starargs:
-            args.append( '*' + node.starargs.accept(self))
-        if node.kwargs:
-            args.append( '**' + node.kwargs.accept(self))
-        return '%s(%s)' % (expr_str, ', '.join(args))
-
-    def visit_class(self, node):
-        """return an astng.Class node as string"""
-        decorate = node.decorators and node.decorators.accept(self)  or ''
-        bases =  ', '.join([n.accept(self) for n in node.bases])
-        bases = bases and '(%s)' % bases or ''
-        docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
-        return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs,
-                                            self._stmt_list( node.body))
-
-    def visit_compare(self, node):
-        """return an astng.Compare node as string"""
-        rhs_str = ' '.join(['%s %s' % (op, expr.accept(self))
-                            for op, expr in node.ops])
-        return '%s %s' % (node.left.accept(self), rhs_str)
-
-    def visit_comprehension(self, node):
-        """return an astng.Comprehension node as string"""
-        ifs = ''.join([ ' if %s' % n.accept(self) for n in node.ifs])
-        return 'for %s in %s%s' % (node.target.accept(self),
-                                    node.iter.accept(self), ifs )
-
-    def visit_const(self, node):
-        """return an astng.Const node as string"""
-        return repr(node.value)
-
-    def visit_continue(self, node):
-        """return an astng.Continue node as string"""
-        return 'continue'
-
-    def visit_delete(self, node): # XXX check if correct
-        """return an astng.Delete node as string"""
-        return 'del %s' % ', '.join([child.accept(self)
-                                for child in node.targets])
-
-    def visit_delattr(self, node):
-        """return an astng.DelAttr node as string"""
-        return self.visit_getattr(node)
-
-    def visit_delname(self, node):
-        """return an astng.DelName node as string"""
-        return node.name
-
-    def visit_decorators(self, node):
-        """return an astng.Decorators node as string"""
-        return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes])
-
-    def visit_dict(self, node):
-        """return an astng.Dict node as string"""
-        return '{%s}' % ', '.join(['%s: %s' % (key.accept(self),
-                            value.accept(self)) for key, value in node.items])
-
-    def visit_dictcomp(self, node):
-        """return an astng.DictComp node as string"""
-        return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self),
-                ' '.join([n.accept(self) for n in node.generators]))
-
-    def visit_discard(self, node):
-        """return an astng.Discard node as string"""
-        return node.value.accept(self)
-
-    def visit_emptynode(self, node):
-        """dummy method for visiting an Empty node"""
-        return ''
-
-    def visit_excepthandler(self, node):
-        if node.type:
-            if node.name:
-                excs = 'except %s, %s' % (node.type.accept(self),
-                                        node.name.accept(self))
-            else:
-                excs = 'except %s' % node.type.accept(self)
-        else:
-            excs = 'except'
-        return '%s:\n%s' % (excs, self._stmt_list(node.body))
-
-    def visit_ellipsis(self, node):
-        """return an astng.Ellipsis node as string"""
-        return '...'
-
-    def visit_empty(self, node):
-        """return an Empty node as string"""
-        return ''
-
-    def visit_exec(self, node):
-        """return an astng.Exec node as string"""
-        if node.locals:
-            return 'exec %s in %s, %s' % (node.expr.accept(self),
-                                          node.locals.accept(self),
-                                          node.globals.accept(self))
-        if node.globals:
-            return 'exec %s in %s' % (node.expr.accept(self),
-                                      node.globals.accept(self))
-        return 'exec %s' % node.expr.accept(self)
-
-    def visit_extslice(self, node):
-        """return an astng.ExtSlice node as string"""
-        return ','.join( [dim.accept(self) for dim in node.dims] )
-
-    def visit_for(self, node):
-        """return an astng.For node as string"""
-        fors = 'for %s in %s:\n%s' % (node.target.accept(self),
-                                    node.iter.accept(self),
-                                    self._stmt_list( node.body))
-        if node.orelse:
-            fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse))
-        return fors
-
-    def visit_from(self, node):
-        """return an astng.From node as string"""
-        return 'from %s import %s' % ('.' * (node.level or 0) + node.modname,
-                                      _import_string(node.names))
-
-    def visit_function(self, node):
-        """return an astng.Function node as string"""
-        decorate = node.decorators and node.decorators.accept(self)  or ''
-        docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
-        return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self),
-                                        docs, self._stmt_list(node.body))
-
-    def visit_genexpr(self, node):
-        """return an astng.GenExpr node as string"""
-        return '(%s %s)' % (node.elt.accept(self), ' '.join([n.accept(self)
-                                                    for n in node.generators]))
-
-    def visit_getattr(self, node):
-        """return an astng.Getattr node as string"""
-        return '%s.%s' % (node.expr.accept(self), node.attrname)
-
-    def visit_global(self, node):
-        """return an astng.Global node as string"""
-        return 'global %s' % ', '.join(node.names)
-
-    def visit_if(self, node):
-        """return an astng.If node as string"""
-        ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))]
-        if node.orelse:# XXX use elif ???
-            ifs.append('else:\n%s' % self._stmt_list(node.orelse))
-        return '\n'.join(ifs)
-
-    def visit_ifexp(self, node):
-        """return an astng.IfExp node as string"""
-        return '%s if %s else %s' % (node.body.accept(self),
-                node.test.accept(self), node.orelse.accept(self))
-
-    def visit_import(self, node):
-        """return an astng.Import node as string"""
-        return 'import %s' % _import_string(node.names)
-
-    def visit_keyword(self, node):
-        """return an astng.Keyword node as string"""
-        return '%s=%s' % (node.arg, node.value.accept(self))
-
-    def visit_lambda(self, node):
-        """return an astng.Lambda node as string"""
-        return 'lambda %s: %s' % (node.args.accept(self), node.body.accept(self))
-
-    def visit_list(self, node):
-        """return an astng.List node as string"""
-        return '[%s]' % ', '.join([child.accept(self) for child in node.elts])
-
-    def visit_listcomp(self, node):
-        """return an astng.ListComp node as string"""
-        return '[%s %s]' % (node.elt.accept(self), ' '.join([n.accept(self)
-                                                for n in node.generators]))
-
-    def visit_module(self, node):
-        """return an astng.Module node as string"""
-        docs = node.doc and '"""%s"""\n\n' % node.doc or ''
-        return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n'
-
-    def visit_name(self, node):
-        """return an astng.Name node as string"""
-        return node.name
-
-    def visit_pass(self, node):
-        """return an astng.Pass node as string"""
-        return 'pass'
-
-    def visit_print(self, node):
-        """return an astng.Print node as string"""
-        nodes = ', '.join([n.accept(self) for n in node.values])
-        if not node.nl:
-            nodes = '%s,' % nodes
-        if node.dest:
-            return 'print >> %s, %s' % (node.dest.accept(self), nodes)
-        return 'print %s' % nodes
-
-    def visit_raise(self, node):
-        """return an astng.Raise node as string"""
-        if node.exc:
-            if node.inst:
-                if node.tback:
-                    return 'raise %s, %s, %s' % (node.exc.accept(self),
-                                                node.inst.accept(self),
-                                                node.tback.accept(self))
-                return 'raise %s, %s' % (node.exc.accept(self),
-                                        node.inst.accept(self))
-            return 'raise %s' % node.exc.accept(self)
-        return 'raise'
-
-    def visit_return(self, node):
-        """return an astng.Return node as string"""
-        if node.value:
-            return 'return %s' % node.value.accept(self)
-        else:
-            return 'return'
-
-    def visit_index(self, node):
-        """return a astng.Index node as string"""
-        return node.value.accept(self)
-
-    def visit_set(self, node):
-        """return an astng.Set node as string"""
-        return '{%s}' % ', '.join([child.accept(self) for child in node.elts])
-
-    def visit_setcomp(self, node):
-        """return an astng.SetComp node as string"""
-        return '{%s %s}' % (node.elt.accept(self), ' '.join([n.accept(self)
-                                                for n in node.generators]))
-
-    def visit_slice(self, node):
-        """return a astng.Slice node as string"""
-        lower = node.lower and node.lower.accept(self) or ''
-        upper = node.upper and node.upper.accept(self) or ''
-        step = node.step and node.step.accept(self) or ''
-        if step:
-            return '%s:%s:%s' % (lower, upper, step)
-        return  '%s:%s' % (lower, upper)
-
-    def visit_subscript(self, node):
-        """return an astng.Subscript node as string"""
-        return '%s[%s]' % (node.value.accept(self), node.slice.accept(self))
-
-    def visit_tryexcept(self, node):
-        """return an astng.TryExcept node as string"""
-        trys = ['try:\n%s' % self._stmt_list( node.body)]
-        for handler in node.handlers:
-            trys.append(handler.accept(self))
-        if node.orelse:
-            trys.append('else:\n%s' % self._stmt_list(node.orelse))
-        return '\n'.join(trys)
-
-    def visit_tryfinally(self, node):
-        """return an astng.TryFinally node as string"""
-        return 'try:\n%s\nfinally:\n%s' % (self._stmt_list( node.body),
-                                        self._stmt_list(node.finalbody))
-
-    def visit_tuple(self, node):
-        """return an astng.Tuple node as string"""
-        return '(%s)' % ', '.join([child.accept(self) for child in node.elts])
-
-    def visit_unaryop(self, node):
-        """return an astng.UnaryOp node as string"""
-        if node.op == 'not':
-            operator = 'not '
-        else:
-            operator = node.op
-        return '%s%s' % (operator, node.operand.accept(self))
-
-    def visit_while(self, node):
-        """return an astng.While node as string"""
-        whiles = 'while %s:\n%s' % (node.test.accept(self),
-                                    self._stmt_list(node.body))
-        if node.orelse:
-            whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse))
-        return whiles
-
-    def visit_with(self, node): # 'with' without 'as' is possible
-        """return an astng.With node as string"""
-        as_var = node.vars and " as (%s)" % (node.vars.accept(self)) or ""
-        withs = 'with (%s)%s:\n%s' % (node.expr.accept(self), as_var,
-                                        self._stmt_list( node.body))
-        return withs
-
-    def visit_yield(self, node):
-        """yield an ast.Yield node as string"""
-        yi_val = node.value and (" " + node.value.accept(self)) or ""
-        return 'yield' + yi_val
-
-
-class AsStringVisitor3k(AsStringVisitor):
-    """AsStringVisitor3k overwrites some AsStringVisitor methods"""
-
-    def visit_excepthandler(self, node):
-        if node.type:
-            if node.name:
-                excs = 'except %s as %s' % (node.type.accept(self),
-                                        node.name.accept(self))
-            else:
-                excs = 'except %s' % node.type.accept(self)
-        else:
-            excs = 'except'
-        return '%s:\n%s' % (excs, self._stmt_list(node.body))
-
-    def visit_nonlocal(self, node):
-        """return an astng.Nonlocal node as string"""
-        return 'nonlocal %s' % ', '.join(node.names)
-
-    def visit_raise(self, node):
-        """return an astng.Raise node as string"""
-        if node.exc:
-            if node.cause:
-                return 'raise %s from %s' % (node.exc.accept(self),
-                                             node.cause.accept(self))
-            return 'raise %s' % node.exc.accept(self)
-        return 'raise'
-
-    def visit_starred(self, node):
-        """return Starred node as string"""
-        return "*" + node.value.accept(self)
-
-if sys.version_info >= (3, 0):
-    AsStringVisitor = AsStringVisitor3k
-
-# this visitor is stateless, thus it can be reused
-as_string = AsStringVisitor()
-
diff --git a/third_party/logilab/astng/exceptions.py b/third_party/logilab/astng/exceptions.py
deleted file mode 100644
index 7dd6135..0000000
--- a/third_party/logilab/astng/exceptions.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
-#
-# This file is part of logilab-astng.
-#
-# logilab-astng is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by the
-# Free Software Foundation, either version 2.1 of the License, or (at your
-# option) any later version.
-#
-# logilab-astng is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
-# for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-"""this module contains exceptions used in the astng library
-
-"""
-
-__doctype__ = "restructuredtext en"
-
-class ASTNGError(Exception):
-    """base exception class for all astng related exceptions"""
-
-class ASTNGBuildingException(ASTNGError):
-    """exception class when we are unable to build an astng representation"""
-
-class ResolveError(ASTNGError):
-    """base class of astng resolution/inference error"""
-
-class NotFoundError(ResolveError):
-    """raised when we are unable to resolve a name"""
-
-class InferenceError(ResolveError):
-    """raised when we are unable to infer a node"""
-
-class UnresolvableName(InferenceError):
-    """raised when we are unable to resolve a name"""
-
-class NoDefault(ASTNGError):
-    """raised by function's `default_value` method when an argument has
-    no default value
-    """
-
diff --git a/third_party/logilab/astng/manager.py b/third_party/logilab/astng/manager.py
deleted file mode 100644
index 8a4f02b..0000000
--- a/third_party/logilab/astng/manager.py
+++ /dev/null
@@ -1,299 +0,0 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
-#
-# This file is part of logilab-astng.
-#
-# logilab-astng is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by the
-# Free Software Foundation, either version 2.1 of the License, or (at your
-# option) any later version.
-#
-# logilab-astng is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
-# for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-"""astng manager: avoid multiple astng build of a same module when
-possible by providing a class responsible to get astng representation
-from various source and using a cache of built modules)
-"""
-
-__docformat__ = "restructuredtext en"
-
-import sys
-import os
-from os.path import dirname, basename, abspath, join, isdir, exists
-
-from logilab.common.modutils import NoSourceFile, is_python_source, \
-     file_from_modpath, load_module_from_name, modpath_from_file, \
-     get_module_files, get_source_file, zipimport
-from logilab.common.configuration import OptionsProviderMixIn
-
-from logilab.astng.exceptions import ASTNGBuildingException
-
-def astng_wrapper(func, modname):
-    """wrapper to give to ASTNGManager.project_from_files"""
-    print 'parsing %s...' % modname
-    try:
-        return func(modname)
-    except ASTNGBuildingException, exc:
-        print exc
-    except Exception, exc:
-        import traceback
-        traceback.print_exc()
-
-def _silent_no_wrap(func, modname):
-    """silent wrapper that doesn't do anything; can be used for tests"""
-    return func(modname)
-
-def safe_repr(obj):
-    try:
-        return repr(obj)
-    except:
-        return '???'
-
-
-
-class ASTNGManager(OptionsProviderMixIn):
-    """the astng manager, responsible to build astng from files
-     or modules.
-
-    Use the Borg pattern.
-    """
-
-    name = 'astng loader'
-    options = (("ignore",
-                {'type' : "csv", 'metavar' : "<file>",
-                 'dest' : "black_list", "default" : ('CVS',),
-                 'help' : "add <file> (may be a directory) to the black list\
-. It should be a base name, not a path. You may set this option multiple times\
-."}),
-               ("project",
-                {'default': "No Name", 'type' : 'string', 'short': 'p',
-                 'metavar' : '<project name>',
-                 'help' : 'set the project name.'}),
-               )
-    brain = {}
-    def __init__(self):
-        self.__dict__ = ASTNGManager.brain
-        if not self.__dict__:
-            OptionsProviderMixIn.__init__(self)
-            self.load_defaults()
-            # NOTE: cache entries are added by the [re]builder
-            self.astng_cache = {}
-            self._mod_file_cache = {}
-            self.transformers = []
-
-    def astng_from_file(self, filepath, modname=None, fallback=True, source=False):
-        """given a module name, return the astng object"""
-        try:
-            filepath = get_source_file(filepath, include_no_ext=True)
-            source = True
-        except NoSourceFile:
-            pass
-        if modname is None:
-            try:
-                modname = '.'.join(modpath_from_file(filepath))
-            except ImportError:
-                modname = filepath
-        if modname in self.astng_cache:
-            return self.astng_cache[modname]
-        if source:
-            from logilab.astng.builder import ASTNGBuilder
-            return ASTNGBuilder(self).file_build(filepath, modname)
-        elif fallback and modname:
-            return self.astng_from_module_name(modname)
-        raise ASTNGBuildingException('unable to get astng for file %s' %
-                                     filepath)
-
-    def astng_from_module_name(self, modname, context_file=None):
-        """given a module name, return the astng object"""
-        if modname in self.astng_cache:
-            return self.astng_cache[modname]
-        if modname == '__main__':
-            from logilab.astng.builder import ASTNGBuilder
-            return ASTNGBuilder(self).string_build('', modname)
-        old_cwd = os.getcwd()
-        if context_file:
-            os.chdir(dirname(context_file))
-        try:
-            filepath = self.file_from_module_name(modname, context_file)
-            if filepath is not None and not is_python_source(filepath):
-                module = self.zip_import_data(filepath)
-                if module is not None:
-                    return module
-            if filepath is None or not is_python_source(filepath):
-                try:
-                    module = load_module_from_name(modname)
-                except Exception, ex:
-                    msg = 'Unable to load module %s (%s)' % (modname, ex)
-                    raise ASTNGBuildingException(msg)
-                return self.astng_from_module(module, modname)
-            return self.astng_from_file(filepath, modname, fallback=False)
-        finally:
-            os.chdir(old_cwd)
-
-    def zip_import_data(self, filepath):
-        if zipimport is None:
-            return None
-        from logilab.astng.builder import ASTNGBuilder
-        builder = ASTNGBuilder(self)
-        for ext in ('.zip', '.egg'):
-            try:
-                eggpath, resource = filepath.rsplit(ext + '/', 1)
-            except ValueError:
-                continue
-            try:
-                importer = zipimport.zipimporter(eggpath + ext)
-                zmodname = resource.replace('/', '.')
-                if importer.is_package(resource):
-                    zmodname =  zmodname + '.__init__'
-                module = builder.string_build(importer.get_source(resource),
-                                              zmodname, filepath)
-                return module
-            except:
-                continue
-        return None
-
-    def file_from_module_name(self, modname, contextfile):
-        try:
-            value = self._mod_file_cache[(modname, contextfile)]
-        except KeyError:
-            try:
-                value = file_from_modpath(modname.split('.'),
-                                          context_file=contextfile)
-            except ImportError, ex:
-                msg = 'Unable to load module %s (%s)' % (modname, ex)
-                value = ASTNGBuildingException(msg)
-            self._mod_file_cache[(modname, contextfile)] = value
-        if isinstance(value, ASTNGBuildingException):
-            raise value
-        return value
-
-    def astng_from_module(self, module, modname=None):
-        """given an imported module, return the astng object"""
-        modname = modname or module.__name__
-        if modname in self.astng_cache:
-            return self.astng_cache[modname]
-        try:
-            # some builtin modules don't have __file__ attribute
-            filepath = module.__file__
-            if is_python_source(filepath):
-                return self.astng_from_file(filepath, modname)
-        except AttributeError:
-            pass
-        from logilab.astng.builder import ASTNGBuilder
-        return ASTNGBuilder(self).module_build(module, modname)
-
-    def astng_from_class(self, klass, modname=None):
-        """get astng for the given class"""
-        if modname is None:
-            try:
-                modname = klass.__module__
-            except AttributeError:
-                raise ASTNGBuildingException(
-                    'Unable to get module for class %s' % safe_repr(klass))
-        modastng = self.astng_from_module_name(modname)
-        return modastng.getattr(klass.__name__)[0] # XXX
-
-
-    def infer_astng_from_something(self, obj, context=None):
-        """infer astng for the given class"""
-        if hasattr(obj, '__class__') and not isinstance(obj, type):
-            klass = obj.__class__
-        else:
-            klass = obj
-        try:
-            modname = klass.__module__
-        except AttributeError:
-            raise ASTNGBuildingException(
-                'Unable to get module for %s' % safe_repr(klass))
-        except Exception, ex:
-            raise ASTNGBuildingException(
-                'Unexpected error while retrieving module for %s: %s'
-                % (safe_repr(klass), ex))
-        try:
-            name = klass.__name__
-        except AttributeError:
-            raise ASTNGBuildingException(
-                'Unable to get name for %s' % safe_repr(klass))
-        except Exception, ex:
-            raise ASTNGBuildingException(
-                'Unexpected error while retrieving name for %s: %s'
-                % (safe_repr(klass), ex))
-        # take care, on living object __module__ is regularly wrong :(
-        modastng = self.astng_from_module_name(modname)
-        if klass is obj:
-            for  infered in modastng.igetattr(name, context):
-                yield infered
-        else:
-            for infered in modastng.igetattr(name, context):
-                yield infered.instanciate_class()
-
-    def project_from_files(self, files, func_wrapper=astng_wrapper,
-                           project_name=None, black_list=None):
-        """return a Project from a list of files or modules"""
-        # build the project representation
-        project_name = project_name or self.config.project
-        black_list = black_list or self.config.black_list
-        project = Project(project_name)
-        for something in files:
-            if not exists(something):
-                fpath = file_from_modpath(something.split('.'))
-            elif isdir(something):
-                fpath = join(something, '__init__.py')
-            else:
-                fpath = something
-            astng = func_wrapper(self.astng_from_file, fpath)
-            if astng is None:
-                continue
-            # XXX why is first file defining the project.path ?
-            project.path = project.path or astng.file
-            project.add_module(astng)
-            base_name = astng.name
-            # recurse in package except if __init__ was explicitly given
-            if astng.package and something.find('__init__') == -1:
-                # recurse on others packages / modules if this is a package
-                for fpath in get_module_files(dirname(astng.file),
-                                              black_list):
-                    astng = func_wrapper(self.astng_from_file, fpath)
-                    if astng is None or astng.name == base_name:
-                        continue
-                    project.add_module(astng)
-        return project
-
-    def register_transformer(self, transformer):
-        self.transformers.append(transformer)
-
-class Project:
-    """a project handle a set of modules / packages"""
-    def __init__(self, name=''):
-        self.name = name
-        self.path = None
-        self.modules = []
-        self.locals = {}
-        self.__getitem__ = self.locals.__getitem__
-        self.__iter__ = self.locals.__iter__
-        self.values = self.locals.values
-        self.keys = self.locals.keys
-        self.items = self.locals.items
-
-    def add_module(self, node):
-        self.locals[node.name] = node
-        self.modules.append(node)
-
-    def get_module(self, name):
-        return self.locals[name]
-
-    def get_children(self):
-        return self.modules
-
-    def __repr__(self):
-        return '<Project %r at %s (%s modules)>' % (self.name, id(self),
-                                                    len(self.modules))
-
-
diff --git a/third_party/logilab/astng/LICENSE.txt b/third_party/logilab/astroid/LICENSE.txt
similarity index 100%
rename from third_party/logilab/astng/LICENSE.txt
rename to third_party/logilab/astroid/LICENSE.txt
diff --git a/third_party/logilab/astng/README.chromium b/third_party/logilab/astroid/README.chromium
similarity index 92%
rename from third_party/logilab/astng/README.chromium
rename to third_party/logilab/astroid/README.chromium
index b78f091..4e460b9 100644
--- a/third_party/logilab/astng/README.chromium
+++ b/third_party/logilab/astroid/README.chromium
@@ -1,5 +1,5 @@
 URL: http://www.logilab.org/project/logilab-astng
-Version: 0.23.1
+Version: 1.2.1
 License: GPL
 License File: LICENSE.txt
 
diff --git a/third_party/logilab/astroid/__init__.py b/third_party/logilab/astroid/__init__.py
new file mode 100644
index 0000000..19c8090
--- /dev/null
+++ b/third_party/logilab/astroid/__init__.py
@@ -0,0 +1,118 @@
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:[email protected]
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# astroid is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+"""Python Abstract Syntax Tree New Generation
+
+The aim of this module is to provide a common base representation of
+python source code for projects such as pychecker, pyreverse,
+pylint... Well, actually the development of this library is essentially
+governed by pylint's needs.
+
+It extends class defined in the python's _ast module with some
+additional methods and attributes. Instance attributes are added by a
+builder object, which can either generate extended ast (let's call
+them astroid ;) by visiting an existent ast tree or by inspecting living
+object. Methods are added by monkey patching ast classes.
+
+Main modules are:
+
+* nodes and scoped_nodes for more information about methods and
+  attributes added to different node classes
+
+* the manager contains a high level object to get astroid trees from
+  source files and living objects. It maintains a cache of previously
+  constructed tree for quick access
+
+* builder contains the class responsible to build astroid trees
+"""
+__doctype__ = "restructuredtext en"
+
+import sys
+import re
+from operator import attrgetter
+
+# WARNING: internal imports order matters !
+
+# make all exception classes accessible from astroid package
+from astroid.exceptions import *
+
+# make all node classes accessible from astroid package
+from astroid.nodes import *
+
+# trigger extra monkey-patching
+from astroid import inference
+
+# more stuff available
+from astroid import raw_building
+from astroid.bases import YES, Instance, BoundMethod, UnboundMethod
+from astroid.node_classes import are_exclusive, unpack_infer
+from astroid.scoped_nodes import builtin_lookup
+
+# make a manager instance (borg) as well as Project and Package classes
+# accessible from astroid package
+from astroid.manager import AstroidManager, Project
+MANAGER = AstroidManager()
+del AstroidManager
+
+# transform utilities (filters and decorator)
+
+class AsStringRegexpPredicate(object):
+    """Class to be used as predicate that may be given to `register_transform`
+
+    First argument is a regular expression that will be searched against the `as_string`
+    representation of the node onto which it's applied.
+
+    If specified, the second argument is an `attrgetter` expression that will be
+    applied on the node first to get the actual node on which `as_string` should
+    be called.
+    """
+    def __init__(self, regexp, expression=None):
+        self.regexp = re.compile(regexp)
+        self.expression = expression
+
+    def __call__(self, node):
+        if self.expression is not None:
+            node = attrgetter(self.expression)(node)
+        return self.regexp.search(node.as_string())
+
+def inference_tip(infer_function):
+    """Given an instance specific inference function, return a function to be
+    given to MANAGER.register_transform to set this inference function.
+
+    Typical usage
+
+    .. sourcecode:: python
+
+       MANAGER.register_transform(CallFunc, inference_tip(infer_named_tuple),
+                                  AsStringRegexpPredicate('namedtuple', 'func'))
+    """
+    def transform(node, infer_function=infer_function):
+        node._explicit_inference = infer_function
+        return node
+    return transform
+
+# load brain plugins
+from os import listdir
+from os.path import join, dirname
+BRAIN_MODULES_DIR = join(dirname(__file__), 'brain')
+if BRAIN_MODULES_DIR not in sys.path:
+    # add it to the end of the list so user path take precedence
+    sys.path.append(BRAIN_MODULES_DIR)
+# load modules in this directory
+for module in listdir(BRAIN_MODULES_DIR):
+    if module.endswith('.py'):
+        __import__(module[:-3])
diff --git a/third_party/logilab/astroid/__pkginfo__.py b/third_party/logilab/astroid/__pkginfo__.py
new file mode 100644
index 0000000..b601f29
--- /dev/null
+++ b/third_party/logilab/astroid/__pkginfo__.py
@@ -0,0 +1,49 @@
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:[email protected]
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# astroid is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+"""astroid packaging information"""
+
+distname = 'astroid'
+
+modname = 'astroid'
+
+numversion = (1, 2, 1)
+version = '.'.join([str(num) for num in numversion])
+
+install_requires = ['logilab-common >= 0.60.0']
+
+license = 'LGPL'
+
+author = 'Logilab'
+author_email = '[email protected]'
+mailinglist = "mailto://%s" % author_email
+web = 'http://bitbucket.org/logilab/astroid'
+
+description = "rebuild a new abstract syntax tree from Python's ast"
+
+from os.path import join
+include_dirs = ['brain',
+                join('test', 'regrtest_data'),
+                join('test', 'data'), join('test', 'data2')
+               ]
+
+classifiers = ["Topic :: Software Development :: Libraries :: Python Modules",
+               "Topic :: Software Development :: Quality Assurance",
+               "Programming Language :: Python",
+               "Programming Language :: Python :: 2",
+               "Programming Language :: Python :: 3",
+              ]
diff --git a/third_party/logilab/astroid/as_string.py b/third_party/logilab/astroid/as_string.py
new file mode 100644
index 0000000..f19713d
--- /dev/null
+++ b/third_party/logilab/astroid/as_string.py
@@ -0,0 +1,499 @@
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:[email protected]
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# astroid is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+"""This module renders Astroid nodes as string:
+
+* :func:`to_code` function return equivalent (hopefuly valid) python string
+
+* :func:`dump` function return an internal representation of nodes found
+  in the tree, useful for debugging or understanding the tree structure
+"""
+
+import sys
+
+INDENT = '    ' # 4 spaces ; keep indentation variable
+
+
+def dump(node, ids=False):
+    """print a nice astroid tree representation.
+
+    :param ids: if true, we also print the ids (usefull for debugging)
+    """
+    result = []
+    _repr_tree(node, result, ids=ids)
+    return "\n".join(result)
+
+def _repr_tree(node, result, indent='', _done=None, ids=False):
+    """built a tree representation of a node as a list of lines"""
+    if _done is None:
+        _done = set()
+    if not hasattr(node, '_astroid_fields'): # not a astroid node
+        return
+    if node in _done:
+        result.append(indent + 'loop in tree: %s' % node)
+        return
+    _done.add(node)
+    node_str = str(node)
+    if ids:
+        node_str += '  . \t%x' % id(node)
+    result.append(indent + node_str)
+    indent += INDENT
+    for field in node._astroid_fields:
+        value = getattr(node, field)
+        if isinstance(value, (list, tuple)):
+            result.append(indent + field + " = [")
+            for child in value:
+                if isinstance(child, (list, tuple)):
+                    # special case for Dict # FIXME
+                    _repr_tree(child[0], result, indent, _done, ids)
+                    _repr_tree(child[1], result, indent, _done, ids)
+                    result.append(indent + ',')
+                else:
+                    _repr_tree(child, result, indent, _done, ids)
+            result.append(indent + "]")
+        else:
+            result.append(indent + field + " = ")
+            _repr_tree(value, result, indent, _done, ids)
+
+
+class AsStringVisitor(object):
+    """Visitor to render an Astroid node as a valid python code string"""
+
+    def __call__(self, node):
+        """Makes this visitor behave as a simple function"""
+        return node.accept(self)
+
+    def _stmt_list(self, stmts):
+        """return a list of nodes to string"""
+        stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr])
+        return INDENT + stmts.replace('\n', '\n'+INDENT)
+
+
+    ## visit_<node> methods ###########################################
+
+    def visit_arguments(self, node):
+        """return an astroid.Function node as string"""
+        return node.format_args()
+
+    def visit_assattr(self, node):
+        """return an astroid.AssAttr node as string"""
+        return self.visit_getattr(node)
+
+    def visit_assert(self, node):
+        """return an astroid.Assert node as string"""
+        if node.fail:
+            return 'assert %s, %s' % (node.test.accept(self),
+                                      node.fail.accept(self))
+        return 'assert %s' % node.test.accept(self)
+
+    def visit_assname(self, node):
+        """return an astroid.AssName node as string"""
+        return node.name
+
+    def visit_assign(self, node):
+        """return an astroid.Assign node as string"""
+        lhs = ' = '.join([n.accept(self) for n in node.targets])
+        return '%s = %s' % (lhs, node.value.accept(self))
+
+    def visit_augassign(self, node):
+        """return an astroid.AugAssign node as string"""
+        return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self))
+
+    def visit_backquote(self, node):
+        """return an astroid.Backquote node as string"""
+        return '`%s`' % node.value.accept(self)
+
+    def visit_binop(self, node):
+        """return an astroid.BinOp node as string"""
+        return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self))
+
+    def visit_boolop(self, node):
+        """return an astroid.BoolOp node as string"""
+        return (' %s ' % node.op).join(['(%s)' % n.accept(self)
+                                        for n in node.values])
+
+    def visit_break(self, node):
+        """return an astroid.Break node as string"""
+        return 'break'
+
+    def visit_callfunc(self, node):
+        """return an astroid.CallFunc node as string"""
+        expr_str = node.func.accept(self)
+        args = [arg.accept(self) for arg in node.args]
+        if node.starargs:
+            args.append('*' + node.starargs.accept(self))
+        if node.kwargs:
+            args.append('**' + node.kwargs.accept(self))
+        return '%s(%s)' % (expr_str, ', '.join(args))
+
+    def visit_class(self, node):
+        """return an astroid.Class node as string"""
+        decorate = node.decorators and node.decorators.accept(self)  or ''
+        bases = ', '.join([n.accept(self) for n in node.bases])
+        if sys.version_info[0] == 2:
+            bases = bases and '(%s)' % bases or ''
+        else:
+            metaclass = node.metaclass()
+            if metaclass:
+                if bases:
+                    bases = '(%s, metaclass=%s)' % (bases, metaclass.name)
+                else:
+                    bases = '(metaclass=%s)' % metaclass.name
+            else:
+                bases = bases and '(%s)' % bases or ''
+        docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
+        return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs,
+                                              self._stmt_list(node.body))
+
+    def visit_compare(self, node):
+        """return an astroid.Compare node as string"""
+        rhs_str = ' '.join(['%s %s' % (op, expr.accept(self))
+                            for op, expr in node.ops])
+        return '%s %s' % (node.left.accept(self), rhs_str)
+
+    def visit_comprehension(self, node):
+        """return an astroid.Comprehension node as string"""
+        ifs = ''.join([' if %s' % n.accept(self) for n in node.ifs])
+        return 'for %s in %s%s' % (node.target.accept(self),
+                                   node.iter.accept(self), ifs)
+
+    def visit_const(self, node):
+        """return an astroid.Const node as string"""
+        return repr(node.value)
+
+    def visit_continue(self, node):
+        """return an astroid.Continue node as string"""
+        return 'continue'
+
+    def visit_delete(self, node): # XXX check if correct
+        """return an astroid.Delete node as string"""
+        return 'del %s' % ', '.join([child.accept(self)
+                                     for child in node.targets])
+
+    def visit_delattr(self, node):
+        """return an astroid.DelAttr node as string"""
+        return self.visit_getattr(node)
+
+    def visit_delname(self, node):
+        """return an astroid.DelName node as string"""
+        return node.name
+
+    def visit_decorators(self, node):
+        """return an astroid.Decorators node as string"""
+        return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes])
+
+    def visit_dict(self, node):
+        """return an astroid.Dict node as string"""
+        return '{%s}' % ', '.join(['%s: %s' % (key.accept(self),
+                                               value.accept(self))
+                                   for key, value in node.items])
+
+    def visit_dictcomp(self, node):
+        """return an astroid.DictComp node as string"""
+        return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self),
+                                ' '.join([n.accept(self) for n in node.generators]))
+
+    def visit_discard(self, node):
+        """return an astroid.Discard node as string"""
+        return node.value.accept(self)
+
+    def visit_emptynode(self, node):
+        """dummy method for visiting an Empty node"""
+        return ''
+
+    def visit_excepthandler(self, node):
+        if node.type:
+            if node.name:
+                excs = 'except %s, %s' % (node.type.accept(self),
+                                          node.name.accept(self))
+            else:
+                excs = 'except %s' % node.type.accept(self)
+        else:
+            excs = 'except'
+        return '%s:\n%s' % (excs, self._stmt_list(node.body))
+
+    def visit_ellipsis(self, node):
+        """return an astroid.Ellipsis node as string"""
+        return '...'
+
+    def visit_empty(self, node):
+        """return an Empty node as string"""
+        return ''
+
+    def visit_exec(self, node):
+        """return an astroid.Exec node as string"""
+        if node.locals:
+            return 'exec %s in %s, %s' % (node.expr.accept(self),
+                                          node.locals.accept(self),
+                                          node.globals.accept(self))
+        if node.globals:
+            return 'exec %s in %s' % (node.expr.accept(self),
+                                      node.globals.accept(self))
+        return 'exec %s' % node.expr.accept(self)
+
+    def visit_extslice(self, node):
+        """return an astroid.ExtSlice node as string"""
+        return ','.join([dim.accept(self) for dim in node.dims])
+
+    def visit_for(self, node):
+        """return an astroid.For node as string"""
+        fors = 'for %s in %s:\n%s' % (node.target.accept(self),
+                                      node.iter.accept(self),
+                                      self._stmt_list(node.body))
+        if node.orelse:
+            fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse))
+        return fors
+
+    def visit_from(self, node):
+        """return an astroid.From node as string"""
+        return 'from %s import %s' % ('.' * (node.level or 0) + node.modname,
+                                      _import_string(node.names))
+
+    def visit_function(self, node):
+        """return an astroid.Function node as string"""
+        decorate = node.decorators and node.decorators.accept(self)  or ''
+        docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
+        return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self),
+                                          docs, self._stmt_list(node.body))
+
+    def visit_genexpr(self, node):
+        """return an astroid.GenExpr node as string"""
+        return '(%s %s)' % (node.elt.accept(self),
+                            ' '.join([n.accept(self) for n in node.generators]))
+
+    def visit_getattr(self, node):
+        """return an astroid.Getattr node as string"""
+        return '%s.%s' % (node.expr.accept(self), node.attrname)
+
+    def visit_global(self, node):
+        """return an astroid.Global node as string"""
+        return 'global %s' % ', '.join(node.names)
+
+    def visit_if(self, node):
+        """return an astroid.If node as string"""
+        ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))]
+        if node.orelse:# XXX use elif ???
+            ifs.append('else:\n%s' % self._stmt_list(node.orelse))
+        return '\n'.join(ifs)
+
+    def visit_ifexp(self, node):
+        """return an astroid.IfExp node as string"""
+        return '%s if %s else %s' % (node.body.accept(self),
+                                     node.test.accept(self),
+                                     node.orelse.accept(self))
+
+    def visit_import(self, node):
+        """return an astroid.Import node as string"""
+        return 'import %s' % _import_string(node.names)
+
+    def visit_keyword(self, node):
+        """return an astroid.Keyword node as string"""
+        return '%s=%s' % (node.arg, node.value.accept(self))
+
+    def visit_lambda(self, node):
+        """return an astroid.Lambda node as string"""
+        return 'lambda %s: %s' % (node.args.accept(self),
+                                  node.body.accept(self))
+
+    def visit_list(self, node):
+        """return an astroid.List node as string"""
+        return '[%s]' % ', '.join([child.accept(self) for child in node.elts])
+
+    def visit_listcomp(self, node):
+        """return an astroid.ListComp node as string"""
+        return '[%s %s]' % (node.elt.accept(self),
+                            ' '.join([n.accept(self) for n in node.generators]))
+
+    def visit_module(self, node):
+        """return an astroid.Module node as string"""
+        docs = node.doc and '"""%s"""\n\n' % node.doc or ''
+        return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n'
+
+    def visit_name(self, node):
+        """return an astroid.Name node as string"""
+        return node.name
+
+    def visit_pass(self, node):
+        """return an astroid.Pass node as string"""
+        return 'pass'
+
+    def visit_print(self, node):
+        """return an astroid.Print node as string"""
+        nodes = ', '.join([n.accept(self) for n in node.values])
+        if not node.nl:
+            nodes = '%s,' % nodes
+        if node.dest:
+            return 'print >> %s, %s' % (node.dest.accept(self), nodes)
+        return 'print %s' % nodes
+
+    def visit_raise(self, node):
+        """return an astroid.Raise node as string"""
+        if node.exc:
+            if node.inst:
+                if node.tback:
+                    return 'raise %s, %s, %s' % (node.exc.accept(self),
+                                                 node.inst.accept(self),
+                                                 node.tback.accept(self))
+                return 'raise %s, %s' % (node.exc.accept(self),
+                                         node.inst.accept(self))
+            return 'raise %s' % node.exc.accept(self)
+        return 'raise'
+
+    def visit_return(self, node):
+        """return an astroid.Return node as string"""
+        if node.value:
+            return 'return %s' % node.value.accept(self)
+        else:
+            return 'return'
+
+    def visit_index(self, node):
+        """return a astroid.Index node as string"""
+        return node.value.accept(self)
+
+    def visit_set(self, node):
+        """return an astroid.Set node as string"""
+        return '{%s}' % ', '.join([child.accept(self) for child in node.elts])
+
+    def visit_setcomp(self, node):
+        """return an astroid.SetComp node as string"""
+        return '{%s %s}' % (node.elt.accept(self),
+                            ' '.join([n.accept(self) for n in node.generators]))
+
+    def visit_slice(self, node):
+        """return a astroid.Slice node as string"""
+        lower = node.lower and node.lower.accept(self) or ''
+        upper = node.upper and node.upper.accept(self) or ''
+        step = node.step and node.step.accept(self) or ''
+        if step:
+            return '%s:%s:%s' % (lower, upper, step)
+        return  '%s:%s' % (lower, upper)
+
+    def visit_subscript(self, node):
+        """return an astroid.Subscript node as string"""
+        return '%s[%s]' % (node.value.accept(self), node.slice.accept(self))
+
+    def visit_tryexcept(self, node):
+        """return an astroid.TryExcept node as string"""
+        trys = ['try:\n%s' % self._stmt_list(node.body)]
+        for handler in node.handlers:
+            trys.append(handler.accept(self))
+        if node.orelse:
+            trys.append('else:\n%s' % self._stmt_list(node.orelse))
+        return '\n'.join(trys)
+
+    def visit_tryfinally(self, node):
+        """return an astroid.TryFinally node as string"""
+        return 'try:\n%s\nfinally:\n%s' % (self._stmt_list(node.body),
+                                           self._stmt_list(node.finalbody))
+
+    def visit_tuple(self, node):
+        """return an astroid.Tuple node as string"""
+        if len(node.elts) == 1:
+            return '(%s, )' % node.elts[0].accept(self)
+        return '(%s)' % ', '.join([child.accept(self) for child in node.elts])
+
+    def visit_unaryop(self, node):
+        """return an astroid.UnaryOp node as string"""
+        if node.op == 'not':
+            operator = 'not '
+        else:
+            operator = node.op
+        return '%s%s' % (operator, node.operand.accept(self))
+
+    def visit_while(self, node):
+        """return an astroid.While node as string"""
+        whiles = 'while %s:\n%s' % (node.test.accept(self),
+                                    self._stmt_list(node.body))
+        if node.orelse:
+            whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse))
+        return whiles
+
+    def visit_with(self, node): # 'with' without 'as' is possible
+        """return an astroid.With node as string"""
+        items = ', '.join(('(%s)' % expr.accept(self)) +
+                          (vars and ' as (%s)' % (vars.accept(self)) or '')
+                          for expr, vars in node.items)
+        return 'with %s:\n%s' % (items, self._stmt_list(node.body))
+
+    def visit_yield(self, node):
+        """yield an ast.Yield node as string"""
+        yi_val = node.value and (" " + node.value.accept(self)) or ""
+        expr = 'yield' + yi_val
+        if node.parent.is_statement:
+            return expr
+        else:
+            return "(%s)" % (expr,)
+
+
+class AsStringVisitor3k(AsStringVisitor):
+    """AsStringVisitor3k overwrites some AsStringVisitor methods"""
+
+    def visit_excepthandler(self, node):
+        if node.type:
+            if node.name:
+                excs = 'except %s as %s' % (node.type.accept(self),
+                                            node.name.accept(self))
+            else:
+                excs = 'except %s' % node.type.accept(self)
+        else:
+            excs = 'except'
+        return '%s:\n%s' % (excs, self._stmt_list(node.body))
+
+    def visit_nonlocal(self, node):
+        """return an astroid.Nonlocal node as string"""
+        return 'nonlocal %s' % ', '.join(node.names)
+
+    def visit_raise(self, node):
+        """return an astroid.Raise node as string"""
+        if node.exc:
+            if node.cause:
+                return 'raise %s from %s' % (node.exc.accept(self),
+                                             node.cause.accept(self))
+            return 'raise %s' % node.exc.accept(self)
+        return 'raise'
+
+    def visit_starred(self, node):
+        """return Starred node as string"""
+        return "*" + node.value.accept(self)
+
+    def visit_yieldfrom(self, node):
+        """ Return an astroid.YieldFrom node as string. """
+        yi_val = node.value and (" " + node.value.accept(self)) or ""
+        expr = 'yield from' + yi_val
+        if node.parent.is_statement:
+            return expr
+        else:
+            return "(%s)" % (expr,)
+
+
+def _import_string(names):
+    """return a list of (name, asname) formatted as a string"""
+    _names = []
+    for name, asname in names:
+        if asname is not None:
+            _names.append('%s as %s' % (name, asname))
+        else:
+            _names.append(name)
+    return  ', '.join(_names)
+
+
+if sys.version_info >= (3, 0):
+    AsStringVisitor = AsStringVisitor3k
+
+# this visitor is stateless, thus it can be reused
+to_code = AsStringVisitor()
+
diff --git a/third_party/logilab/astng/bases.py b/third_party/logilab/astroid/bases.py
similarity index 83%
rename from third_party/logilab/astng/bases.py
rename to third_party/logilab/astroid/bases.py
index 92f12aa..37e613b 100644
--- a/third_party/logilab/astng/bases.py
+++ b/third_party/logilab/astroid/bases.py
@@ -1,43 +1,43 @@
-# -*- coding: utf-8 -*-
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
 """This module contains base classes and functions for the nodes and some
 inference utils.
 """
 
 __docformat__ = "restructuredtext en"
 
+import sys
 from contextlib import contextmanager
 
-from logilab.common.compat import builtins
+from astroid.exceptions import (InferenceError, AstroidError, NotFoundError,
+                                UnresolvableName, UseInferenceDefault)
 
-from logilab.astng import BUILTINS_MODULE
-from logilab.astng.exceptions import InferenceError, ASTNGError, \
-                                       NotFoundError, UnresolvableName
-from logilab.astng.as_string import as_string
 
-BUILTINS_NAME = builtins.__name__
+if sys.version_info >= (3, 0):
+    BUILTINS = 'builtins'
+else:
+    BUILTINS = '__builtin__'
+
 
 class Proxy(object):
     """a simple proxy object"""
-    _proxied = None
+
+    _proxied = None # proxied object may be set by class or by instance
 
     def __init__(self, proxied=None):
         if proxied is not None:
@@ -72,7 +72,7 @@
         name = self.lookupname
         if (node, name) in self.path:
             raise StopIteration()
-        self.path.add( (node, name) )
+        self.path.add((node, name))
 
     def clone(self):
         # XXX copy lookupname/callcontext ?
@@ -131,6 +131,8 @@
     def __repr__(self):
         return 'YES'
     def __getattribute__(self, name):
+        if name == 'next':
+            raise AttributeError('next method should not be called')
         if name.startswith('__') and name.endswith('__'):
             # to avoid inspection pb
             return super(_Yes, self).__getattribute__(name)
@@ -169,6 +171,9 @@
     def igetattr(self, name, context=None):
         """inferred getattr"""
         try:
+            # avoid recursively inferring the same attr on the same class
+            if context:
+                context.push((self._proxied, name))
             # XXX frame should be self._proxied, or not ?
             get_attr = self.getattr(name, context, lookupclass=False)
             return _infer_stmts(self._wrap_attr(get_attr, context), context,
@@ -186,7 +191,7 @@
         """wrap bound methods of attrs in a InstanceMethod proxies"""
         for attr in attrs:
             if isinstance(attr, UnboundMethod):
-                if BUILTINS_NAME + '.property' in attr.decoratornames():
+                if BUILTINS + '.property' in attr.decoratornames():
                     for infered in attr.infer_call_result(self, context):
                         yield infered
                 else:
@@ -198,6 +203,8 @@
         """infer what a class instance is returning when called"""
         infered = False
         for node in self._proxied.igetattr('__call__', context):
+            if node is YES:
+                continue
             for res in node.infer_call_result(caller, context):
                 infered = True
                 yield res
@@ -251,14 +258,15 @@
         # If we're unbound method __new__ of builtin object, the result is an
         # instance of the class given as first argument.
         if (self._proxied.name == '__new__' and
-                self._proxied.parent.frame().qname() == '%s.object' % BUILTINS_MODULE):
-            return (x is YES and x or Instance(x) for x in caller.args[0].infer())
+                self._proxied.parent.frame().qname() == '%s.object' % BUILTINS):
+            infer = caller.args[0].infer() if caller.args else []
+            return ((x is YES and x or Instance(x)) for x in infer)
         return self._proxied.infer_call_result(caller, context)
 
 
 class BoundMethod(UnboundMethod):
     """a special node representing a method bound to an instance"""
-    def __init__(self,  proxy, bound):
+    def __init__(self, proxy, bound):
         UnboundMethod.__init__(self, proxy)
         self.bound = bound
 
@@ -272,12 +280,15 @@
 
 
 class Generator(Instance):
-    """a special node representing a generator"""
+    """a special node representing a generator.
+
+    Proxied class is set once for all in raw_building.
+    """
     def callable(self):
-        return True
+        return False
 
     def pytype(self):
-        return '%s.generator' % BUILTINS_MODULE
+        return '%s.generator' % BUILTINS
 
     def display_type(self):
         return 'Generator'
@@ -334,7 +345,7 @@
 # Node  ######################################################################
 
 class NodeNG(object):
-    """Base Class for all ASTNG node classes.
+    """Base Class for all Astroid node classes.
 
     It represents a node of the new abstract syntax tree.
     """
@@ -349,7 +360,24 @@
     # parent node in the tree
     parent = None
     # attributes containing child node(s) redefined in most concrete classes:
-    _astng_fields = ()
+    _astroid_fields = ()
+    # instance specific inference function infer(node, context)
+    _explicit_inference = None
+
+    def infer(self, context=None, **kwargs):
+        """main interface to the interface system, return a generator on infered
+        values.
+
+        If the instance has some explicit inference function set, it will be
+        called instead of the default interface.
+        """
+        if self._explicit_inference is not None:
+            # explicit_inference is not bound, give it self explicitly
+            try:
+                return self._explicit_inference(self, context, **kwargs)
+            except UseInferenceDefault:
+                pass
+        return self._infer(context, **kwargs)
 
     def _repr_name(self):
         """return self.name or self.attrname or '' for nice representation"""
@@ -359,20 +387,19 @@
         return '%s(%s)' % (self.__class__.__name__, self._repr_name())
 
     def __repr__(self):
-        return '<%s(%s) l.%s [%s] at Ox%x>' % (self.__class__.__name__,
-                                           self._repr_name(),
-                                           self.fromlineno,
-                                           self.root().name,
-                                           id(self))
+        return '<%s(%s) l.%s [%s] at 0x%x>' % (self.__class__.__name__,
+                                               self._repr_name(),
+                                               self.fromlineno,
+                                               self.root().name,
+                                               id(self))
 
 
     def accept(self, visitor):
-        klass = self.__class__.__name__
         func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
         return func(self)
 
     def get_children(self):
-        for field in self._astng_fields:
+        for field in self._astroid_fields:
             attr = getattr(self, field)
             if attr is None:
                 continue
@@ -384,7 +411,7 @@
 
     def last_child(self):
         """an optimized version of list(get_children())[-1]"""
-        for field in self._astng_fields[::-1]:
+        for field in self._astroid_fields[::-1]:
             attr = getattr(self, field)
             if not attr: # None or empty listy / tuple
                 continue
@@ -428,7 +455,7 @@
 
     def child_sequence(self, child):
         """search for the right sequence where the child lies in"""
-        for field in self._astng_fields:
+        for field in self._astroid_fields:
             node_or_sequence = getattr(self, field)
             if node_or_sequence is child:
                 return [node_or_sequence]
@@ -436,20 +463,20 @@
             if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
                 return node_or_sequence
         else:
-            msg = 'Could not found %s in %s\'s children'
-            raise ASTNGError(msg % (repr(child), repr(self)))
+            msg = 'Could not find %s in %s\'s children'
+            raise AstroidError(msg % (repr(child), repr(self)))
 
     def locate_child(self, child):
         """return a 2-uple (child attribute name, sequence or node)"""
-        for field in self._astng_fields:
+        for field in self._astroid_fields:
             node_or_sequence = getattr(self, field)
             # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
             if child is node_or_sequence:
                 return field, child
             if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
                 return field, node_or_sequence
-        msg = 'Could not found %s in %s\'s children'
-        raise ASTNGError(msg % (repr(child), repr(self)))
+        msg = 'Could not find %s in %s\'s children'
+        raise AstroidError(msg % (repr(child), repr(self)))
     # FIXME : should we merge child_sequence and locate_child ? locate_child
     # is only used in are_exclusive, child_sequence one time in pylint.
 
@@ -538,7 +565,7 @@
         # overridden for From, Import, Global, TryExcept and Arguments
         return None
 
-    def infer(self, context=None):
+    def _infer(self, context=None):
         """we don't know how to resolve a statement by default"""
         # this method is overridden by most concrete classes
         raise InferenceError(self.__class__.__name__)
@@ -561,15 +588,12 @@
         return False
 
     def as_string(self):
-        return as_string(self)
+        from astroid.as_string import to_code
+        return to_code(self)
 
     def repr_tree(self, ids=False):
-        """print a nice astng tree representation.
-
-        :param ids: if true, we also print the ids (usefull for debugging)"""
-        result = []
-        _repr_tree(self, result, ids=ids)
-        return "\n".join(result)
+        from astroid.as_string import dump
+        return dump(self)
 
 
 class Statement(NodeNG):
@@ -591,39 +615,3 @@
         index = stmts.index(self)
         if index >= 1:
             return stmts[index -1]
-
-INDENT = "    "
-
-def _repr_tree(node, result, indent='', _done=None, ids=False):
-    """built a tree representation of a node as a list of lines"""
-    if _done is None:
-        _done = set()
-    if not hasattr(node, '_astng_fields'): # not a astng node
-        return
-    if node in _done:
-        result.append( indent + 'loop in tree: %s' % node )
-        return
-    _done.add(node)
-    node_str = str(node)
-    if ids:
-        node_str += '  . \t%x' % id(node)
-    result.append( indent + node_str )
-    indent += INDENT
-    for field in node._astng_fields:
-        value = getattr(node, field)
-        if isinstance(value, (list, tuple) ):
-            result.append(  indent + field + " = [" )
-            for child in value:
-                if isinstance(child, (list, tuple) ):
-                    # special case for Dict # FIXME
-                    _repr_tree(child[0], result, indent, _done, ids)
-                    _repr_tree(child[1], result, indent, _done, ids)
-                    result.append(indent + ',')
-                else:
-                    _repr_tree(child, result, indent, _done, ids)
-            result.append(  indent + "]" )
-        else:
-            result.append(  indent + field + " = " )
-            _repr_tree(value, result, indent, _done, ids)
-
-
diff --git a/third_party/logilab/astroid/brain/py2gi.py b/third_party/logilab/astroid/brain/py2gi.py
new file mode 100644
index 0000000..dd9868d
--- /dev/null
+++ b/third_party/logilab/astroid/brain/py2gi.py
@@ -0,0 +1,159 @@
+"""Astroid hooks for the Python 2 GObject introspection bindings.
+
+Helps with understanding everything imported from 'gi.repository'
+"""
+
+import inspect
+import sys
+import re
+
+from astroid import MANAGER, AstroidBuildingException
+from astroid.builder import AstroidBuilder
+
+
+_inspected_modules = {}
+
+_identifier_re = r'^[A-Za-z_]\w*$'
+
+def _gi_build_stub(parent):
+    """
+    Inspect the passed module recursively and build stubs for functions,
+    classes, etc.
+    """
+    classes = {}
+    functions = {}
+    constants = {}
+    methods = {}
+    for name in dir(parent):
+        if name.startswith("__"):
+            continue
+
+        # Check if this is a valid name in python
+        if not re.match(_identifier_re, name):
+            continue
+
+        try:
+            obj = getattr(parent, name)
+        except:
+            continue
+
+        if inspect.isclass(obj):
+            classes[name] = obj
+        elif (inspect.isfunction(obj) or
+              inspect.isbuiltin(obj)):
+            functions[name] = obj
+        elif (inspect.ismethod(obj) or
+              inspect.ismethoddescriptor(obj)):
+            methods[name] = obj
+        elif type(obj) in [int, str]:
+            constants[name] = obj
+        elif (str(obj).startswith("<flags") or
+              str(obj).startswith("<enum ") or
+              str(obj).startswith("<GType ") or
+              inspect.isdatadescriptor(obj)):
+            constants[name] = 0
+        elif callable(obj):
+            # Fall back to a function for anything callable
+            functions[name] = obj
+        else:
+            # Assume everything else is some manner of constant
+            constants[name] = 0
+
+    ret = ""
+
+    if constants:
+        ret += "# %s contants\n\n" % parent.__name__
+    for name in sorted(constants):
+        if name[0].isdigit():
+            # GDK has some busted constant names like
+            # Gdk.EventType.2BUTTON_PRESS
+            continue
+
+        val = constants[name]
+
+        strval = str(val)
+        if type(val) is str:
+            strval = '"%s"' % str(val).replace("\\", "\\\\")
+        ret += "%s = %s\n" % (name, strval)
+
+    if ret:
+        ret += "\n\n"
+    if functions:
+        ret += "# %s functions\n\n" % parent.__name__
+    for name in sorted(functions):
+        func = functions[name]
+        ret += "def %s(*args, **kwargs):\n" % name
+        ret += "    pass\n"
+
+    if ret:
+        ret += "\n\n"
+    if methods:
+        ret += "# %s methods\n\n" % parent.__name__
+    for name in sorted(methods):
+        func = methods[name]
+        ret += "def %s(self, *args, **kwargs):\n" % name
+        ret += "    pass\n"
+
+    if ret:
+        ret += "\n\n"
+    if classes:
+        ret += "# %s classes\n\n" % parent.__name__
+    for name in sorted(classes):
+        ret += "class %s(object):\n" % name
+
+        classret = _gi_build_stub(classes[name])
+        if not classret:
+            classret = "pass\n"
+
+        for line in classret.splitlines():
+            ret += "    " + line + "\n"
+        ret += "\n"
+
+    return ret
+
+# Overwrite Module.module_import to _actually_ import the introspected module if
+# it's a gi module, then build stub code by examining its info and get an astng
+# from that
+
+from astroid.scoped_nodes import Module
+_orig_import_module = Module.import_module
+
+def _new_import_module(self, modname, relative_only=False, level=None):
+    # Could be a static piece of gi.repository or whatever unrelated module,
+    # let that fall through
+    try:
+        return _orig_import_module(self, modname, relative_only, level)
+    except AstroidBuildingException:
+        # we only consider gi.repository submodules
+        if not modname.startswith('gi.repository.'):
+            if relative_only and level is None:
+                level = 0
+            modname = self.relative_to_absolute_name(modname, level)
+        if not modname.startswith('gi.repository.'):
+            raise
+    # build astroid representation unless we already tried so
+    if modname not in _inspected_modules:
+        modnames = [modname]
+        # GLib and GObject have some special case handling
+        # in pygobject that we need to cope with
+        if modname == 'gi.repository.GLib':
+            modnames.append('gi._glib')
+        elif modname == 'gi.repository.GObject':
+            modnames.append('gi._gobject')
+        try:
+            modcode = ''
+            for m in modnames:
+                __import__(m)
+                modcode += _gi_build_stub(sys.modules[m])
+        except ImportError:
+            astng = _inspected_modules[modname] = None
+        else:
+            astng = AstroidBuilder(MANAGER).string_build(modcode, modname)
+            _inspected_modules[modname] = astng
+    else:
+        astng = _inspected_modules[modname]
+    if astng is None:
+        raise AstroidBuildingException('Failed to import module %r' % modname)
+    return astng
+
+Module.import_module = _new_import_module
diff --git a/third_party/logilab/astroid/brain/py2mechanize.py b/third_party/logilab/astroid/brain/py2mechanize.py
new file mode 100644
index 0000000..1e0b102
--- /dev/null
+++ b/third_party/logilab/astroid/brain/py2mechanize.py
@@ -0,0 +1,20 @@
+from astroid import MANAGER
+from astroid.builder import AstroidBuilder
+
+def mechanize_transform(module):
+    fake = AstroidBuilder(MANAGER).string_build('''
+
+class Browser(object):
+    def open(self, url, data=None, timeout=None):
+        return None
+    def open_novisit(self, url, data=None, timeout=None):
+        return None
+    def open_local_file(self, filename):
+        return None
+
+''')
+    module.locals['Browser'] = fake.locals['Browser']
+
+import py2stdlib
+py2stdlib.MODULE_TRANSFORMS['mechanize'] = mechanize_transform
+
diff --git a/third_party/logilab/astroid/brain/py2qt4.py b/third_party/logilab/astroid/brain/py2qt4.py
new file mode 100644
index 0000000..0ee0410
--- /dev/null
+++ b/third_party/logilab/astroid/brain/py2qt4.py
@@ -0,0 +1,25 @@
+"""Astroid hooks for the Python 2 qt4 module.
+
+Currently help understanding of :
+
+* PyQT4.QtCore
+"""
+
+from astroid import MANAGER
+from astroid.builder import AstroidBuilder
+
+
+def pyqt4_qtcore_transform(module):
+    fake = AstroidBuilder(MANAGER).string_build('''
+
+def SIGNAL(signal_name): pass
+
+class QObject(object):
+    def emit(self, signal): pass
+''')
+    for klass in ('QObject',):
+        module.locals[klass] = fake.locals[klass]
+
+
+import py2stdlib
+py2stdlib.MODULE_TRANSFORMS['PyQt4.QtCore'] = pyqt4_qtcore_transform
diff --git a/third_party/logilab/astroid/brain/py2stdlib.py b/third_party/logilab/astroid/brain/py2stdlib.py
new file mode 100644
index 0000000..2ead081
--- /dev/null
+++ b/third_party/logilab/astroid/brain/py2stdlib.py
@@ -0,0 +1,331 @@
+"""Astroid hooks for the Python 2 standard library.
+
+Currently help understanding of :
+
+* hashlib.md5 and hashlib.sha1
+"""
+
+import sys
+from textwrap import dedent
+
+from astroid import (
+    MANAGER, AsStringRegexpPredicate,
+    UseInferenceDefault, inference_tip,
+    YES, InferenceError)
+from astroid import exceptions
+from astroid import nodes
+from astroid.builder import AstroidBuilder
+
+MODULE_TRANSFORMS = {}
+PY3K = sys.version_info > (3, 0)
+PY33 = sys.version_info >= (3, 3)
+
+# general function
+
+def infer_func_form(node, base_type, context=None, enum=False):
+    """Specific inference function for namedtuple or Python 3 enum. """
+    def infer_first(node):
+        try:
+            value = node.infer(context=context).next()
+            if value is YES:
+                raise UseInferenceDefault()
+            else:
+                return value
+        except StopIteration:
+            raise InferenceError()
+
+    # node is a CallFunc node, class name as first argument and generated class
+    # attributes as second argument
+    if len(node.args) != 2:
+        # something weird here, go back to class implementation
+        raise UseInferenceDefault()
+    # namedtuple or enums list of attributes can be a list of strings or a
+    # whitespace-separate string
+    try:
+        name = infer_first(node.args[0]).value
+        names = infer_first(node.args[1])
+        try:
+            attributes = names.value.replace(',', ' ').split()
+        except AttributeError:
+            if not enum:
+                attributes = [infer_first(const).value for const in names.elts]
+            else:
+                # Enums supports either iterator of (name, value) pairs
+                # or mappings.
+                # TODO: support only list, tuples and mappings.
+                if hasattr(names, 'items') and isinstance(names.items, list):
+                    attributes = [infer_first(const[0]).value
+                                  for const in names.items
+                                  if isinstance(const[0], nodes.Const)]
+                elif hasattr(names, 'elts'):
+                    # Enums can support either ["a", "b", "c"]
+                    # or [("a", 1), ("b", 2), ...], but they can't
+                    # be mixed.
+                    if all(isinstance(const, nodes.Tuple)
+                           for const in names.elts):
+                        attributes = [infer_first(const.elts[0]).value
+                                      for const in names.elts
+                                      if isinstance(const, nodes.Tuple)]
+                    else:
+                        attributes = [infer_first(const).value
+                                      for const in names.elts]
+                else:
+                    raise AttributeError
+                if not attributes:
+                    raise AttributeError
+    except (AttributeError, exceptions.InferenceError) as exc:
+        raise UseInferenceDefault()
+    # we want to return a Class node instance with proper attributes set
+    class_node = nodes.Class(name, 'docstring')
+    class_node.parent = node.parent
+    # set base class=tuple
+    class_node.bases.append(base_type)
+    # XXX add __init__(*attributes) method
+    for attr in attributes:
+        fake_node = nodes.EmptyNode()
+        fake_node.parent = class_node
+        class_node.instance_attrs[attr] = [fake_node]
+    return class_node, name, attributes
+
+
+# module specific transformation functions #####################################
+
+def transform(module):
+    try:
+        tr = MODULE_TRANSFORMS[module.name]
+    except KeyError:
+        pass
+    else:
+        tr(module)
+MANAGER.register_transform(nodes.Module, transform)
+
+# module specific transformation functions #####################################
+
+def hashlib_transform(module):
+    template = '''
+
+class %s(object):
+  def __init__(self, value=''): pass
+  def digest(self):
+    return u''
+  def update(self, value): pass
+  def hexdigest(self):
+    return u''
+'''
+
+    algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
+    classes = "".join(template % hashfunc for hashfunc in algorithms)
+
+    fake = AstroidBuilder(MANAGER).string_build(classes)
+
+    for hashfunc in algorithms:
+        module.locals[hashfunc] = fake.locals[hashfunc]
+
+def collections_transform(module):
+    fake = AstroidBuilder(MANAGER).string_build('''
+
+class defaultdict(dict):
+    default_factory = None
+    def __missing__(self, key): pass
+
+class deque(object):
+    maxlen = 0
+    def __init__(self, iterable=None, maxlen=None): pass
+    def append(self, x): pass
+    def appendleft(self, x): pass
+    def clear(self): pass
+    def count(self, x): return 0
+    def extend(self, iterable): pass
+    def extendleft(self, iterable): pass
+    def pop(self): pass
+    def popleft(self): pass
+    def remove(self, value): pass
+    def reverse(self): pass
+    def rotate(self, n): pass
+    def __iter__(self): return self
+
+''')
+
+    for klass in ('deque', 'defaultdict'):
+        module.locals[klass] = fake.locals[klass]
+
+def pkg_resources_transform(module):
+    fake = AstroidBuilder(MANAGER).string_build('''
+
+def resource_exists(package_or_requirement, resource_name):
+    pass
+
+def resource_isdir(package_or_requirement, resource_name):
+    pass
+
+def resource_filename(package_or_requirement, resource_name):
+    pass
+
+def resource_stream(package_or_requirement, resource_name):
+    pass
+
+def resource_string(package_or_requirement, resource_name):
+    pass
+
+def resource_listdir(package_or_requirement, resource_name):
+    pass
+
+def extraction_error():
+    pass
+
+def get_cache_path(archive_name, names=()):
+    pass
+
+def postprocess(tempname, filename):
+    pass
+
+def set_extraction_path(path):
+    pass
+
+def cleanup_resources(force=False):
+    pass
+
+''')
+
+    for func_name, func in fake.locals.items():
+        module.locals[func_name] = func
+
+
+def subprocess_transform(module):
+    if PY3K:
+        communicate = (bytes('string', 'ascii'), bytes('string', 'ascii'))
+        init = """
+    def __init__(self, args, bufsize=0, executable=None,
+                 stdin=None, stdout=None, stderr=None,
+                 preexec_fn=None, close_fds=False, shell=False,
+                 cwd=None, env=None, universal_newlines=False,
+                 startupinfo=None, creationflags=0, restore_signals=True,
+                 start_new_session=False, pass_fds=()):
+        pass
+        """
+    else:
+        communicate = ('string', 'string')
+        init = """
+    def __init__(self, args, bufsize=0, executable=None,
+                 stdin=None, stdout=None, stderr=None,
+                 preexec_fn=None, close_fds=False, shell=False,
+                 cwd=None, env=None, universal_newlines=False,
+                 startupinfo=None, creationflags=0):
+        pass
+        """
+    if PY33:
+        wait_signature = 'def wait(self, timeout=None)'
+    else:
+        wait_signature = 'def wait(self)'
+    fake = AstroidBuilder(MANAGER).string_build('''
+
+class Popen(object):
+    returncode = pid = 0
+    stdin = stdout = stderr = file()
+
+    %(init)s
+
+    def communicate(self, input=None):
+        return %(communicate)r
+    %(wait_signature)s:
+        return self.returncode
+    def poll(self):
+        return self.returncode
+    def send_signal(self, signal):
+        pass
+    def terminate(self):
+        pass
+    def kill(self):
+        pass
+   ''' % {'init': init,
+          'communicate': communicate,
+          'wait_signature': wait_signature})
+
+    for func_name, func in fake.locals.items():
+        module.locals[func_name] = func
+
+
+
+MODULE_TRANSFORMS['hashlib'] = hashlib_transform
+MODULE_TRANSFORMS['collections'] = collections_transform
+MODULE_TRANSFORMS['pkg_resources'] = pkg_resources_transform
+MODULE_TRANSFORMS['subprocess'] = subprocess_transform
+
+# namedtuple support ###########################################################
+
+def infer_named_tuple(node, context=None):
+    """Specific inference function for namedtuple CallFunc node"""
+    class_node, name, attributes = infer_func_form(node, nodes.Tuple._proxied,
+                                                   context=context)
+    fake = AstroidBuilder(MANAGER).string_build('''
+class %(name)s(tuple):
+    _fields = %(fields)r
+    def _asdict(self):
+        return self.__dict__
+    @classmethod
+    def _make(cls, iterable, new=tuple.__new__, len=len):
+        return new(cls, iterable)
+    def _replace(_self, **kwds):
+        result = _self._make(map(kwds.pop, %(fields)r, _self))
+        if kwds:
+            raise ValueError('Got unexpected field names: %%r' %% list(kwds))
+        return result
+    ''' % {'name': name, 'fields': attributes})
+    class_node.locals['_asdict'] = fake.body[0].locals['_asdict']
+    class_node.locals['_make'] = fake.body[0].locals['_make']
+    class_node.locals['_replace'] = fake.body[0].locals['_replace']
+    class_node.locals['_fields'] = fake.body[0].locals['_fields']
+    # we use UseInferenceDefault, we can't be a generator so return an iterator
+    return iter([class_node])
+
+def infer_enum(node, context=None):
+    """ Specific inference function for enum CallFunc node. """
+    enum_meta = nodes.Class("EnumMeta", 'docstring')
+    class_node = infer_func_form(node, enum_meta,
+                                 context=context, enum=True)[0]
+    return iter([class_node.instanciate_class()])
+
+def infer_enum_class(node, context=None):
+    """ Specific inference for enums. """
+    names = set(('Enum', 'IntEnum', 'enum.Enum', 'enum.IntEnum'))
+    for basename in node.basenames:
+        # TODO: doesn't handle subclasses yet.
+        if basename not in names:
+            continue
+        if node.root().name == 'enum':
+            # Skip if the class is directly from enum module.
+            break
+        for local, values in node.locals.items():
+            if any(not isinstance(value, nodes.AssName)
+                   for value in values):
+                continue
+            parent = values[0].parent
+            real_value = parent.value
+            new_targets = []
+            for target in parent.targets:
+                # Replace all the assignments with our mocked class.
+                classdef = dedent('''
+                class %(name)s(object):
+                    @property
+                    def value(self):
+                        return %(value)s
+                    @property
+                    def name(self):
+                        return %(name)r
+                    %(name)s = %(value)s
+                ''' % {'name': target.name,
+                       'value': real_value.as_string()})
+                fake = AstroidBuilder(MANAGER).string_build(classdef)[target.name]
+                fake.parent = target.parent
+                for method in node.mymethods():
+                    fake.locals[method.name] = [method]
+                new_targets.append(fake.instanciate_class())
+            node.locals[local] = new_targets
+        break
+    return node
+
+MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_named_tuple),
+                           AsStringRegexpPredicate('namedtuple', 'func'))
+MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_enum),
+                           AsStringRegexpPredicate('Enum', 'func'))
+MANAGER.register_transform(nodes.Class, infer_enum_class)
diff --git a/third_party/logilab/astng/builder.py b/third_party/logilab/astroid/builder.py
similarity index 69%
rename from third_party/logilab/astng/builder.py
rename to third_party/logilab/astroid/builder.py
index 9309793..692016a 100644
--- a/third_party/logilab/astng/builder.py
+++ b/third_party/logilab/astroid/builder.py
@@ -1,40 +1,38 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-"""The ASTNGBuilder makes astng from living object and / or from _ast
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+"""The AstroidBuilder makes astroid from living object and / or from _ast
 
 The builder is not thread safe and can't be used to parse different sources
 at the same time.
 """
+from __future__ import with_statement
 
 __docformat__ = "restructuredtext en"
 
-import sys, re
-from os.path import splitext, basename, dirname, exists, abspath
+import sys
+from os.path import splitext, basename, exists, abspath
 
-from logilab.common.modutils import modpath_from_file
-
-from logilab.astng.exceptions import ASTNGBuildingException, InferenceError
-from logilab.astng.raw_building import InspectBuilder
-from logilab.astng.rebuilder import TreeRebuilder
-from logilab.astng.manager import ASTNGManager
-from logilab.astng.bases import YES, Instance
+from astroid.exceptions import AstroidBuildingException, InferenceError
+from astroid.raw_building import InspectBuilder
+from astroid.rebuilder import TreeRebuilder
+from astroid.manager import AstroidManager
+from astroid.bases import YES, Instance
+from astroid.modutils import modpath_from_file
 
 from _ast import PyCF_ONLY_AST
 def parse(string):
@@ -44,21 +42,21 @@
     from tokenize import detect_encoding
 
     def open_source_file(filename):
-        byte_stream = open(filename, 'bU')
-        encoding = detect_encoding(byte_stream.readline)[0]
-        stream = open(filename, 'U', encoding=encoding)
+        with open(filename, 'rb') as byte_stream:
+            encoding = detect_encoding(byte_stream.readline)[0]
+        stream = open(filename, 'rU', encoding=encoding)
         try:
             data = stream.read()
-        except UnicodeError, uex: # wrong encodingg
+        except UnicodeError: # wrong encodingg
             # detect_encoding returns utf-8 if no encoding specified
             msg = 'Wrong (%s) or no encoding specified' % encoding
-            raise ASTNGBuildingException(msg)
+            raise AstroidBuildingException(msg)
         return stream, encoding, data
 
 else:
     import re
 
-    _ENCODING_RGX = re.compile("\s*#+.*coding[:=]\s*([-\w.]+)")
+    _ENCODING_RGX = re.compile(r"\s*#+.*coding[:=]\s*([-\w.]+)")
 
     def _guess_encoding(string):
         """get encoding from a python file as string or return None if not found
@@ -81,17 +79,17 @@
 
 # ast NG builder ##############################################################
 
-MANAGER = ASTNGManager()
+MANAGER = AstroidManager()
 
-class ASTNGBuilder(InspectBuilder):
-    """provide astng building methods"""
-    rebuilder = TreeRebuilder()
+class AstroidBuilder(InspectBuilder):
+    """provide astroid building methods"""
 
     def __init__(self, manager=None):
+        InspectBuilder.__init__(self)
         self._manager = manager or MANAGER
 
     def module_build(self, module, modname=None):
-        """build an astng from a living module instance
+        """build an astroid from a living module instance
         """
         node = None
         path = getattr(module, '__file__', None)
@@ -103,46 +101,59 @@
             # this is a built-in module
             # get a partial representation by introspection
             node = self.inspect_build(module, modname=modname, path=path)
+            # we have to handle transformation by ourselves since the rebuilder
+            # isn't called for builtin nodes
+            #
+            # XXX it's then only called for Module nodes, not for underlying
+            # nodes
+            node = self._manager.transform(node)
         return node
 
     def file_build(self, path, modname=None):
-        """build astng from a source code file (i.e. from an ast)
+        """build astroid from a source code file (i.e. from an ast)
 
         path is expected to be a python source file
         """
         try:
-            stream, encoding, data = open_source_file(path)
+            _, encoding, data = open_source_file(path)
         except IOError, exc:
             msg = 'Unable to load file %r (%s)' % (path, exc)
-            raise ASTNGBuildingException(msg)
+            raise AstroidBuildingException(msg)
         except SyntaxError, exc: # py3k encoding specification error
-            raise ASTNGBuildingException(exc)
+            raise AstroidBuildingException(exc)
         except LookupError, exc: # unknown encoding
-            raise ASTNGBuildingException(exc)
+            raise AstroidBuildingException(exc)
         # get module name if necessary
         if modname is None:
             try:
                 modname = '.'.join(modpath_from_file(path))
             except ImportError:
                 modname = splitext(basename(path))[0]
-        # build astng representation
-        node = self.string_build(data, modname, path)
-        node.file_encoding = encoding
-        return node
+        # build astroid representation
+        module = self._data_build(data, modname, path)
+        return self._post_build(module, encoding)
 
     def string_build(self, data, modname='', path=None):
-        """build astng from source code string and return rebuilded astng"""
+        """build astroid from source code string and return rebuilded astroid"""
         module = self._data_build(data, modname, path)
-        self._manager.astng_cache[module.name] = module
+        module.file_bytes = data.encode('utf-8')
+        return self._post_build(module, 'utf-8')
+
+    def _post_build(self, module, encoding):
+        """handles encoding and delayed nodes
+        after a module has been built
+        """
+        module.file_encoding = encoding
+        self._manager.cache_module(module)
         # post tree building steps after we stored the module in the cache:
         for from_node in module._from_nodes:
+            if from_node.modname == '__future__':
+                for symbol, _ in from_node.names:
+                    module.future_imports.add(symbol)
             self.add_from_names_to_locals(from_node)
         # handle delayed assattr nodes
         for delayed in module._delayed_assattr:
             self.delayed_assattr(delayed)
-        if modname:
-            for transformer in self._manager.transformers:
-                transformer(module)
         return module
 
     def _data_build(self, data, modname, path):
@@ -158,11 +169,11 @@
             package = True
         else:
             package = path and path.find('__init__.py') > -1 or False
-        self.rebuilder.init()
-        module = self.rebuilder.visit_module(node, modname, package)
+        rebuilder = TreeRebuilder(self._manager)
+        module = rebuilder.visit_module(node, modname, package)
         module.file = module.path = node_file
-        module._from_nodes = self.rebuilder._from_nodes
-        module._delayed_assattr = self.rebuilder._delayed_assattr
+        module._from_nodes = rebuilder._from_nodes
+        module._delayed_assattr = rebuilder._delayed_assattr
         return module
 
     def add_from_names_to_locals(self, node):
@@ -176,8 +187,8 @@
         for (name, asname) in node.names:
             if name == '*':
                 try:
-                    imported = node.root().import_module(node.modname)
-                except ASTNGBuildingException:
+                    imported = node.do_import_module()
+                except InferenceError:
                     continue
                 for name in imported.wildcard_import_names():
                     node.parent.set_local(name, node)
diff --git a/third_party/logilab/astroid/exceptions.py b/third_party/logilab/astroid/exceptions.py
new file mode 100644
index 0000000..3889e2e
--- /dev/null
+++ b/third_party/logilab/astroid/exceptions.py
@@ -0,0 +1,51 @@
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:[email protected]
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# astroid is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+"""this module contains exceptions used in the astroid library
+
+"""
+
+__doctype__ = "restructuredtext en"
+
+class AstroidError(Exception):
+    """base exception class for all astroid related exceptions"""
+
+class AstroidBuildingException(AstroidError):
+    """exception class when we are unable to build an astroid representation"""
+
+class ResolveError(AstroidError):
+    """base class of astroid resolution/inference error"""
+
+class NotFoundError(ResolveError):
+    """raised when we are unable to resolve a name"""
+
+class InferenceError(ResolveError):
+    """raised when we are unable to infer a node"""
+
+class UseInferenceDefault(Exception):
+    """exception to be raised in custom inference function to indicate that it
+    should go back to the default behaviour
+    """
+
+class UnresolvableName(InferenceError):
+    """raised when we are unable to resolve a name"""
+
+class NoDefault(AstroidError):
+    """raised by function's `default_value` method when an argument has
+    no default value
+    """
+
diff --git a/third_party/logilab/astng/inference.py b/third_party/logilab/astroid/inference.py
similarity index 72%
rename from third_party/logilab/astng/inference.py
rename to third_party/logilab/astroid/inference.py
index 62bd7d9..4186307 100644
--- a/third_party/logilab/astng/inference.py
+++ b/third_party/logilab/astroid/inference.py
@@ -1,43 +1,43 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-"""this module contains a set of functions to handle inference on astng trees
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+"""this module contains a set of functions to handle inference on astroid trees
 """
 
 __doctype__ = "restructuredtext en"
 
 from itertools import chain
-import sys
 
-from logilab.astng import nodes
+from astroid import nodes
 
-from logilab.astng.manager import ASTNGManager
-from logilab.astng.exceptions import (ASTNGBuildingException, ASTNGError,
-    InferenceError, NoDefault, NotFoundError, UnresolvableName)
-from logilab.astng.bases import YES, Instance, InferenceContext, Generator, \
-     _infer_stmts, copy_context, path_wrapper, raise_if_nothing_infered
-from logilab.astng.protocols import _arguments_infer_argname
+from astroid.manager import AstroidManager
+from astroid.exceptions import (AstroidError, InferenceError, NoDefault,
+                                NotFoundError, UnresolvableName)
+from astroid.bases import (YES, Instance, InferenceContext,
+                           _infer_stmts, copy_context, path_wrapper,
+                           raise_if_nothing_infered)
+from astroid.protocols import (
+    _arguments_infer_argname,
+    BIN_OP_METHOD, UNARY_OP_METHOD)
 
-MANAGER = ASTNGManager()
+MANAGER = AstroidManager()
 
 
-class CallContext:
+class CallContext(object):
     """when inferring a function call, this class is used to remember values
     given as argument
     """
@@ -58,7 +58,7 @@
         try:
             return self.nargs[name].infer(context)
         except KeyError:
-            # Function.args.args can be None in astng (means that we don't have
+            # Function.args.args can be None in astroid (means that we don't have
             # information on argnames)
             argindex = funcnode.args.find_argname(name)[0]
             if argindex is not None:
@@ -75,6 +75,11 @@
                         return iter((boundnode,))
                     if funcnode.type == 'classmethod':
                         return iter((boundnode,))
+                # if we have a method, extract one position
+                # from the index, so we'll take in account
+                # the extra parameter represented by `self` or `cls`
+                if funcnode.type in ('method', 'classmethod'):
+                    argindex -= 1
                 # 2. search arg index
                 try:
                     return self.args[argindex].infer(context)
@@ -129,25 +134,51 @@
     """inference's end for node such as Module, Class, Function, Const...
     """
     yield self
-nodes.Module.infer = infer_end
-nodes.Class.infer = infer_end
-nodes.Function.infer = infer_end
-nodes.Lambda.infer = infer_end
-nodes.Const.infer = infer_end
-nodes.List.infer = infer_end
-nodes.Tuple.infer = infer_end
-nodes.Dict.infer = infer_end
+nodes.Module._infer = infer_end
+nodes.Class._infer = infer_end
+nodes.Function._infer = infer_end
+nodes.Lambda._infer = infer_end
+nodes.Const._infer = infer_end
+nodes.List._infer = infer_end
+nodes.Tuple._infer = infer_end
+nodes.Dict._infer = infer_end
+nodes.Set._infer = infer_end
 
+def _higher_function_scope(node):
+    """ Search for the first function which encloses the given
+    scope. This can be used for looking up in that function's
+    scope, in case looking up in a lower scope for a particular
+    name fails.
+
+    :param node: A scope node.
+    :returns:
+        ``None``, if no parent function scope was found,
+        otherwise an instance of :class:`astroid.scoped_nodes.Function`,
+        which encloses the given node.
+    """
+    current = node
+    while current.parent and not isinstance(current.parent, nodes.Function):
+        current = current.parent
+    if current and current.parent:
+        return current.parent
 
 def infer_name(self, context=None):
     """infer a Name: use name lookup rules"""
     frame, stmts = self.lookup(self.name)
     if not stmts:
-        raise UnresolvableName(self.name)
+        # Try to see if the name is enclosed in a nested function
+        # and use the higher (first function) scope for searching.
+        # TODO: should this be promoted to other nodes as well?
+        parent_function = _higher_function_scope(self.scope())
+        if parent_function:
+            _, stmts = parent_function.lookup(self.name)
+
+        if not stmts:
+            raise UnresolvableName(self.name)
     context = context.clone()
     context.lookupname = self.name
     return _infer_stmts(stmts, context, frame)
-nodes.Name.infer = path_wrapper(infer_name)
+nodes.Name._infer = path_wrapper(infer_name)
 nodes.AssName.infer_lhs = infer_name # won't work with a path wrapper
 
 
@@ -167,7 +198,7 @@
         except InferenceError:
             ## XXX log error ?
             continue
-nodes.CallFunc.infer = path_wrapper(raise_if_nothing_infered(infer_callfunc))
+nodes.CallFunc._infer = path_wrapper(raise_if_nothing_infered(infer_callfunc))
 
 
 def infer_import(self, context=None, asname=True):
@@ -179,7 +210,7 @@
         yield self.do_import_module(self.real_name(name))
     else:
         yield self.do_import_module(name)
-nodes.Import.infer = path_wrapper(infer_import)
+nodes.Import._infer = path_wrapper(infer_import)
 
 def infer_name_module(self, name):
     context = InferenceContext()
@@ -195,14 +226,14 @@
         raise InferenceError()
     if asname:
         name = self.real_name(name)
-    module = self.do_import_module(self.modname)
+    module = self.do_import_module()
     try:
         context = copy_context(context)
         context.lookupname = name
         return _infer_stmts(module.getattr(name, ignore_locals=module is self.root()), context)
     except NotFoundError:
         raise InferenceError(name)
-nodes.From.infer = path_wrapper(infer_from)
+nodes.From._infer = path_wrapper(infer_from)
 
 
 def infer_getattr(self, context=None):
@@ -222,7 +253,7 @@
         except AttributeError:
             # XXX method / function
             context.boundnode = None
-nodes.Getattr.infer = path_wrapper(raise_if_nothing_infered(infer_getattr))
+nodes.Getattr._infer = path_wrapper(raise_if_nothing_infered(infer_getattr))
 nodes.AssAttr.infer_lhs = raise_if_nothing_infered(infer_getattr) # # won't work with a path wrapper
 
 
@@ -233,19 +264,24 @@
         return _infer_stmts(self.root().getattr(context.lookupname), context)
     except NotFoundError:
         raise InferenceError()
-nodes.Global.infer = path_wrapper(infer_global)
+nodes.Global._infer = path_wrapper(infer_global)
 
 
 def infer_subscript(self, context=None):
     """infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]"""
-    if isinstance(self.slice, nodes.Index):
-        index = self.slice.value.infer(context).next()
-        if index is YES:
-            yield YES
-            return
+    value = self.value.infer(context).next()
+    if value is YES:
+        yield YES
+        return
+
+    index = self.slice.infer(context).next()
+    if index is YES:
+        yield YES
+        return
+
+    if isinstance(index, nodes.Const):
         try:
-            # suppose it's a Tuple/List node (attribute error else)
-            assigned = self.value.getitem(index.value, context)
+            assigned = value.getitem(index.value, context)
         except AttributeError:
             raise InferenceError()
         except (IndexError, TypeError):
@@ -255,16 +291,9 @@
             yield infered
     else:
         raise InferenceError()
-nodes.Subscript.infer = path_wrapper(infer_subscript)
+nodes.Subscript._infer = path_wrapper(infer_subscript)
 nodes.Subscript.infer_lhs = raise_if_nothing_infered(infer_subscript)
 
-
-UNARY_OP_METHOD = {'+': '__pos__',
-                   '-': '__neg__',
-                   '~': '__invert__',
-                   'not': None, # XXX not '__nonzero__'
-                  }
-
 def infer_unaryop(self, context=None):
     for operand in self.operand.infer(context):
         try:
@@ -285,22 +314,7 @@
                     raise
                 except:
                     yield YES
-nodes.UnaryOp.infer = path_wrapper(infer_unaryop)
-
-
-BIN_OP_METHOD = {'+':  '__add__',
-                 '-':  '__sub__',
-                 '/':  '__div__',
-                 '//': '__floordiv__',
-                 '*':  '__mul__',
-                 '**': '__power__',
-                 '%':  '__mod__',
-                 '&':  '__and__',
-                 '|':  '__or__',
-                 '^':  '__xor__',
-                 '<<': '__lshift__',
-                 '>>': '__rshift__',
-                 }
+nodes.UnaryOp._infer = path_wrapper(infer_unaryop)
 
 def _infer_binop(operator, operand1, operand2, context, failures=None):
     if operand1 is YES:
@@ -330,7 +344,7 @@
         for rhs in self.right.infer(context):
             for val in _infer_binop(self.op, rhs, lhs, context):
                 yield val
-nodes.BinOp.infer = path_wrapper(infer_binop)
+nodes.BinOp._infer = path_wrapper(infer_binop)
 
 
 def infer_arguments(self, context=None):
@@ -338,7 +352,7 @@
     if name is None:
         raise InferenceError()
     return _arguments_infer_argname(self, name, context)
-nodes.Arguments.infer = infer_arguments
+nodes.Arguments._infer = infer_arguments
 
 
 def infer_ass(self, context=None):
@@ -350,8 +364,8 @@
         return stmt.infer(context)
     stmts = list(self.assigned_stmts(context=context))
     return _infer_stmts(stmts, context)
-nodes.AssName.infer = path_wrapper(infer_ass)
-nodes.AssAttr.infer = path_wrapper(infer_ass)
+nodes.AssName._infer = path_wrapper(infer_ass)
+nodes.AssAttr._infer = path_wrapper(infer_ass)
 
 def infer_augassign(self, context=None):
     failures = []
@@ -362,7 +376,7 @@
         for rhs in self.value.infer(context):
             for val in _infer_binop(self.op, rhs, lhs, context):
                 yield val
-nodes.AugAssign.infer = path_wrapper(infer_augassign)
+nodes.AugAssign._infer = path_wrapper(infer_augassign)
 
 
 # no infer method on DelName and DelAttr (expected InferenceError)
@@ -373,10 +387,14 @@
         yield YES
     else:
         try:
-            for infered in MANAGER.infer_astng_from_something(self.object,
-                                                              context=context):
+            for infered in MANAGER.infer_ast_from_something(self.object,
+                                                            context=context):
                 yield infered
-        except ASTNGError:
+        except AstroidError:
             yield YES
-nodes.EmptyNode.infer = path_wrapper(infer_empty_node)
+nodes.EmptyNode._infer = path_wrapper(infer_empty_node)
 
+
+def infer_index(self, context=None):
+    return self.value.infer(context)
+nodes.Index._infer = infer_index
diff --git a/third_party/logilab/astng/inspector.py b/third_party/logilab/astroid/inspector.py
similarity index 75%
rename from third_party/logilab/astng/inspector.py
rename to third_party/logilab/astroid/inspector.py
index a4abd1f..1fc3192 100644
--- a/third_party/logilab/astng/inspector.py
+++ b/third_party/logilab/astroid/inspector.py
@@ -1,35 +1,21 @@
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-"""visitor doing some postprocessing on the astng tree.
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+"""visitor doing some postprocessing on the astroid tree.
 Try to resolve definitions (namespace) dictionary, relationship...
 
 This module has been imported from pyreverse
@@ -39,25 +25,23 @@
 
 from os.path import dirname
 
-from logilab.common.modutils import get_module_part, is_relative, \
-     is_standard_module
+import astroid
+from astroid.exceptions import InferenceError
+from astroid.utils import LocalsVisitor
+from astroid.modutils import get_module_part, is_relative, is_standard_module
 
-from logilab import astng
-from logilab.astng.exceptions import InferenceError
-from logilab.astng.utils import LocalsVisitor
-
-class IdGeneratorMixIn:
+class IdGeneratorMixIn(object):
     """
     Mixin adding the ability to generate integer uid
     """
     def __init__(self, start_value=0):
         self.id_count = start_value
-    
+
     def init_counter(self, start_value=0):
         """init the id counter
         """
         self.id_count = start_value
-        
+
     def generate_id(self):
         """generate a new identifier
         """
@@ -68,26 +52,26 @@
 class Linker(IdGeneratorMixIn, LocalsVisitor):
     """
     walk on the project tree and resolve relationships.
-    
+
     According to options the following attributes may be added to visited nodes:
-    
+
     * uid,
-      a unique identifier for the node (on astng.Project, astng.Module,
-      astng.Class and astng.locals_type). Only if the linker has been instantiated
+      a unique identifier for the node (on astroid.Project, astroid.Module,
+      astroid.Class and astroid.locals_type). Only if the linker has been instantiated
       with tag=True parameter (False by default).
-            
+
     * Function
       a mapping from locals names to their bounded value, which may be a
-      constant like a string or an integer, or an astng node (on astng.Module,
-      astng.Class and astng.Function).
+      constant like a string or an integer, or an astroid node (on astroid.Module,
+      astroid.Class and astroid.Function).
 
     * instance_attrs_type
-      as locals_type but for klass member attributes (only on astng.Class)
-      
+      as locals_type but for klass member attributes (only on astroid.Class)
+
     * implements,
-      list of implemented interface _objects_ (only on astng.Class nodes)
+      list of implemented interface _objects_ (only on astroid.Class nodes)
     """
-    
+
     def __init__(self, project, inherited_interfaces=0, tag=False):
         IdGeneratorMixIn.__init__(self)
         LocalsVisitor.__init__(self)
@@ -98,30 +82,30 @@
         # visited project
         self.project = project
 
-        
+
     def visit_project(self, node):
-        """visit an astng.Project node
-        
+        """visit an astroid.Project node
+
          * optionally tag the node with a unique id
         """
         if self.tag:
             node.uid = self.generate_id()
         for module in node.modules:
             self.visit(module)
-            
+
     def visit_package(self, node):
-        """visit an astng.Package node
-        
+        """visit an astroid.Package node
+
          * optionally tag the node with a unique id
         """
         if self.tag:
             node.uid = self.generate_id()
         for subelmt in node.values():
             self.visit(subelmt)
-            
+
     def visit_module(self, node):
-        """visit an astng.Module node
-        
+        """visit an astroid.Module node
+
          * set the locals_type mapping
          * set the depends mapping
          * optionally tag the node with a unique id
@@ -132,10 +116,10 @@
         node.depends = []
         if self.tag:
             node.uid = self.generate_id()
-    
+
     def visit_class(self, node):
-        """visit an astng.Class node
-        
+        """visit an astroid.Class node
+
          * set the locals_type and instance_attrs_type mappings
          * set the implements list and build it
          * optionally tag the node with a unique id
@@ -162,8 +146,8 @@
             node.implements = ()
 
     def visit_function(self, node):
-        """visit an astng.Function node
-        
+        """visit an astroid.Function node
+
          * set the locals_type mapping
          * optionally tag the node with a unique id
         """
@@ -172,14 +156,14 @@
         node.locals_type = {}
         if self.tag:
             node.uid = self.generate_id()
-            
+
     link_project = visit_project
     link_module = visit_module
     link_class = visit_class
     link_function = visit_function
-        
+
     def visit_assname(self, node):
-        """visit an astng.AssName node
+        """visit an astroid.AssName node
 
         handle locals_type
         """
@@ -192,7 +176,7 @@
             frame = node.frame()
         else:
             # the name has been defined as 'global' in the frame and belongs
-            # there. Btw the frame is not yet visited as the name is in the 
+            # there. Btw the frame is not yet visited as the name is in the
             # root locals; the frame hence has no locals_type attribute
             frame = node.root()
         try:
@@ -204,11 +188,11 @@
                         already_infered.append(valnode)
             except KeyError:
                 frame.locals_type[node.name] = values
-        except astng.InferenceError:
+        except astroid.InferenceError:
             pass
 
     def handle_assattr_type(self, node, parent):
-        """handle an astng.AssAttr node
+        """handle an astroid.AssAttr node
 
         handle instance_attrs_type
         """
@@ -221,23 +205,23 @@
                         already_infered.append(valnode)
             except KeyError:
                 parent.instance_attrs_type[node.attrname] = values
-        except astng.InferenceError:
+        except astroid.InferenceError:
             pass
-            
+
     def visit_import(self, node):
-        """visit an astng.Import node
-        
+        """visit an astroid.Import node
+
         resolve module dependencies
         """
         context_file = node.root().file
         for name in node.names:
             relative = is_relative(name[0], context_file)
             self._imported_module(node, name[0], relative)
-        
+
 
     def visit_from(self, node):
-        """visit an astng.From node
-        
+        """visit an astroid.From node
+
         resolve module dependencies
         """
         basename = node.modname
@@ -254,13 +238,13 @@
             if fullname.find('.') > -1:
                 try:
                     # XXX: don't use get_module_part, missing package precedence
-                    fullname = get_module_part(fullname)
+                    fullname = get_module_part(fullname, context_file)
                 except ImportError:
                     continue
             if fullname != basename:
                 self._imported_module(node, fullname, relative)
 
-        
+
     def compute_module(self, context_name, mod_path):
         """return true if the module should be added to dependencies"""
         package_dir = dirname(self.project.path)
@@ -269,7 +253,7 @@
         elif is_standard_module(mod_path, (package_dir,)):
             return 1
         return 0
-    
+
     # protected methods ########################################################
 
     def _imported_module(self, node, mod_path, relative):
diff --git a/third_party/logilab/astroid/manager.py b/third_party/logilab/astroid/manager.py
new file mode 100644
index 0000000..fde52e2
--- /dev/null
+++ b/third_party/logilab/astroid/manager.py
@@ -0,0 +1,347 @@
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:[email protected]
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# astroid is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+"""astroid manager: avoid multiple astroid build of a same module when
+possible by providing a class responsible to get astroid representation
+from various source and using a cache of built modules)
+"""
+
+__docformat__ = "restructuredtext en"
+
+import os
+from os.path import dirname, join, isdir, exists
+from warnings import warn
+
+from logilab.common.configuration import OptionsProviderMixIn
+
+from astroid.exceptions import AstroidBuildingException
+from astroid.modutils import NoSourceFile, is_python_source, \
+     file_from_modpath, load_module_from_name, modpath_from_file, \
+     get_module_files, get_source_file, zipimport
+
+
+def astroid_wrapper(func, modname):
+    """wrapper to give to AstroidManager.project_from_files"""
+    print 'parsing %s...' % modname
+    try:
+        return func(modname)
+    except AstroidBuildingException, exc:
+        print exc
+    except Exception, exc:
+        import traceback
+        traceback.print_exc()
+
+def _silent_no_wrap(func, modname):
+    """silent wrapper that doesn't do anything; can be used for tests"""
+    return func(modname)
+
+def safe_repr(obj):
+    try:
+        return repr(obj)
+    except:
+        return '???'
+
+
+
+class AstroidManager(OptionsProviderMixIn):
+    """the astroid manager, responsible to build astroid from files
+     or modules.
+
+    Use the Borg pattern.
+    """
+
+    name = 'astroid loader'
+    options = (("ignore",
+                {'type' : "csv", 'metavar' : "<file>",
+                 'dest' : "black_list", "default" : ('CVS',),
+                 'help' : "add <file> (may be a directory) to the black list\
+. It should be a base name, not a path. You may set this option multiple times\
+."}),
+               ("project",
+                {'default': "No Name", 'type' : 'string', 'short': 'p',
+                 'metavar' : '<project name>',
+                 'help' : 'set the project name.'}),
+              )
+    brain = {}
+    def __init__(self):
+        self.__dict__ = AstroidManager.brain
+        if not self.__dict__:
+            OptionsProviderMixIn.__init__(self)
+            self.load_defaults()
+            # NOTE: cache entries are added by the [re]builder
+            self.astroid_cache = {}
+            self._mod_file_cache = {}
+            self.transforms = {}
+
+    def ast_from_file(self, filepath, modname=None, fallback=True, source=False):
+        """given a module name, return the astroid object"""
+        try:
+            filepath = get_source_file(filepath, include_no_ext=True)
+            source = True
+        except NoSourceFile:
+            pass
+        if modname is None:
+            try:
+                modname = '.'.join(modpath_from_file(filepath))
+            except ImportError:
+                modname = filepath
+        if modname in self.astroid_cache and self.astroid_cache[modname].file == filepath:
+            return self.astroid_cache[modname]
+        if source:
+            from astroid.builder import AstroidBuilder
+            return AstroidBuilder(self).file_build(filepath, modname)
+        elif fallback and modname:
+            return self.ast_from_module_name(modname)
+        raise AstroidBuildingException('unable to get astroid for file %s' %
+                                       filepath)
+
+    def ast_from_module_name(self, modname, context_file=None):
+        """given a module name, return the astroid object"""
+        if modname in self.astroid_cache:
+            return self.astroid_cache[modname]
+        if modname == '__main__':
+            from astroid.builder import AstroidBuilder
+            return AstroidBuilder(self).string_build('', modname)
+        old_cwd = os.getcwd()
+        if context_file:
+            os.chdir(dirname(context_file))
+        try:
+            filepath = self.file_from_module_name(modname, context_file)
+            if filepath is not None and not is_python_source(filepath):
+                module = self.zip_import_data(filepath)
+                if module is not None:
+                    return module
+            if filepath is None or not is_python_source(filepath):
+                try:
+                    module = load_module_from_name(modname)
+                except Exception, ex:
+                    msg = 'Unable to load module %s (%s)' % (modname, ex)
+                    raise AstroidBuildingException(msg)
+                return self.ast_from_module(module, modname)
+            return self.ast_from_file(filepath, modname, fallback=False)
+        finally:
+            os.chdir(old_cwd)
+
+    def zip_import_data(self, filepath):
+        if zipimport is None:
+            return None
+        from astroid.builder import AstroidBuilder
+        builder = AstroidBuilder(self)
+        for ext in ('.zip', '.egg'):
+            try:
+                eggpath, resource = filepath.rsplit(ext + '/', 1)
+            except ValueError:
+                continue
+            try:
+                importer = zipimport.zipimporter(eggpath + ext)
+                zmodname = resource.replace('/', '.')
+                if importer.is_package(resource):
+                    zmodname = zmodname + '.__init__'
+                module = builder.string_build(importer.get_source(resource),
+                                              zmodname, filepath)
+                return module
+            except:
+                continue
+        return None
+
+    def file_from_module_name(self, modname, contextfile):
+        try:
+            value = self._mod_file_cache[(modname, contextfile)]
+        except KeyError:
+            try:
+                value = file_from_modpath(modname.split('.'),
+                                          context_file=contextfile)
+            except ImportError, ex:
+                msg = 'Unable to load module %s (%s)' % (modname, ex)
+                value = AstroidBuildingException(msg)
+            self._mod_file_cache[(modname, contextfile)] = value
+        if isinstance(value, AstroidBuildingException):
+            raise value
+        return value
+
+    def ast_from_module(self, module, modname=None):
+        """given an imported module, return the astroid object"""
+        modname = modname or module.__name__
+        if modname in self.astroid_cache:
+            return self.astroid_cache[modname]
+        try:
+            # some builtin modules don't have __file__ attribute
+            filepath = module.__file__
+            if is_python_source(filepath):
+                return self.ast_from_file(filepath, modname)
+        except AttributeError:
+            pass
+        from astroid.builder import AstroidBuilder
+        return AstroidBuilder(self).module_build(module, modname)
+
+    def ast_from_class(self, klass, modname=None):
+        """get astroid for the given class"""
+        if modname is None:
+            try:
+                modname = klass.__module__
+            except AttributeError:
+                raise AstroidBuildingException(
+                    'Unable to get module for class %s' % safe_repr(klass))
+        modastroid = self.ast_from_module_name(modname)
+        return modastroid.getattr(klass.__name__)[0] # XXX
+
+
+    def infer_ast_from_something(self, obj, context=None):
+        """infer astroid for the given class"""
+        if hasattr(obj, '__class__') and not isinstance(obj, type):
+            klass = obj.__class__
+        else:
+            klass = obj
+        try:
+            modname = klass.__module__
+        except AttributeError:
+            raise AstroidBuildingException(
+                'Unable to get module for %s' % safe_repr(klass))
+        except Exception, ex:
+            raise AstroidBuildingException(
+                'Unexpected error while retrieving module for %s: %s'
+                % (safe_repr(klass), ex))
+        try:
+            name = klass.__name__
+        except AttributeError:
+            raise AstroidBuildingException(
+                'Unable to get name for %s' % safe_repr(klass))
+        except Exception, ex:
+            raise AstroidBuildingException(
+                'Unexpected error while retrieving name for %s: %s'
+                % (safe_repr(klass), ex))
+        # take care, on living object __module__ is regularly wrong :(
+        modastroid = self.ast_from_module_name(modname)
+        if klass is obj:
+            for  infered in modastroid.igetattr(name, context):
+                yield infered
+        else:
+            for infered in modastroid.igetattr(name, context):
+                yield infered.instanciate_class()
+
+    def project_from_files(self, files, func_wrapper=astroid_wrapper,
+                           project_name=None, black_list=None):
+        """return a Project from a list of files or modules"""
+        # build the project representation
+        project_name = project_name or self.config.project
+        black_list = black_list or self.config.black_list
+        project = Project(project_name)
+        for something in files:
+            if not exists(something):
+                fpath = file_from_modpath(something.split('.'))
+            elif isdir(something):
+                fpath = join(something, '__init__.py')
+            else:
+                fpath = something
+            astroid = func_wrapper(self.ast_from_file, fpath)
+            if astroid is None:
+                continue
+            # XXX why is first file defining the project.path ?
+            project.path = project.path or astroid.file
+            project.add_module(astroid)
+            base_name = astroid.name
+            # recurse in package except if __init__ was explicitly given
+            if astroid.package and something.find('__init__') == -1:
+                # recurse on others packages / modules if this is a package
+                for fpath in get_module_files(dirname(astroid.file),
+                                              black_list):
+                    astroid = func_wrapper(self.ast_from_file, fpath)
+                    if astroid is None or astroid.name == base_name:
+                        continue
+                    project.add_module(astroid)
+        return project
+
+    def register_transform(self, node_class, transform, predicate=None):
+        """Register `transform(node)` function to be applied on the given
+        Astroid's `node_class` if `predicate` is None or return a true value
+        when called with the node as argument.
+
+        The transform function may return a value which is then used to
+        substitute the original node in the tree.
+        """
+        self.transforms.setdefault(node_class, []).append((transform, predicate))
+
+    def unregister_transform(self, node_class, transform, predicate=None):
+        """Unregister the given transform."""
+        self.transforms[node_class].remove((transform, predicate))
+
+    def transform(self, node):
+        """Call matching transforms for the given node if any and return the
+        transformed node.
+        """
+        cls = node.__class__
+        if cls not in self.transforms:
+            # no transform registered for this class of node
+            return node
+
+        transforms = self.transforms[cls]
+        orig_node = node  # copy the reference
+        for transform_func, predicate in transforms:
+            if predicate is None or predicate(node):
+                ret = transform_func(node)
+                # if the transformation function returns something, it's
+                # expected to be a replacement for the node
+                if ret is not None:
+                    if node is not orig_node:
+                        # node has already be modified by some previous
+                        # transformation, warn about it
+                        warn('node %s substituted multiple times' % node)
+                    node = ret
+        return node
+
+    def cache_module(self, module):
+        """Cache a module if no module with the same name is known yet."""
+        self.astroid_cache.setdefault(module.name, module)
+
+    def clear_cache(self):
+        self.astroid_cache.clear()
+        # force bootstrap again, else we may ends up with cache inconsistency
+        # between the manager and CONST_PROXY, making
+        # unittest_lookup.LookupTC.test_builtin_lookup fail depending on the
+        # test order
+        from astroid.raw_building import astroid_bootstrapping
+        astroid_bootstrapping()
+
+
+class Project(object):
+    """a project handle a set of modules / packages"""
+    def __init__(self, name=''):
+        self.name = name
+        self.path = None
+        self.modules = []
+        self.locals = {}
+        self.__getitem__ = self.locals.__getitem__
+        self.__iter__ = self.locals.__iter__
+        self.values = self.locals.values
+        self.keys = self.locals.keys
+        self.items = self.locals.items
+
+    def add_module(self, node):
+        self.locals[node.name] = node
+        self.modules.append(node)
+
+    def get_module(self, name):
+        return self.locals[name]
+
+    def get_children(self):
+        return self.modules
+
+    def __repr__(self):
+        return '<Project %r at %s (%s modules)>' % (self.name, id(self),
+                                                    len(self.modules))
+
+
diff --git a/third_party/logilab/astng/mixins.py b/third_party/logilab/astroid/mixins.py
similarity index 73%
rename from third_party/logilab/astng/mixins.py
rename to third_party/logilab/astroid/mixins.py
index 869a25a..f903724 100644
--- a/third_party/logilab/astng/mixins.py
+++ b/third_party/logilab/astroid/mixins.py
@@ -1,39 +1,25 @@
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
 """This module contains some mixins for the different nodes.
 """
 
-from logilab.astng.exceptions import (ASTNGBuildingException, InferenceError,
-                                      NotFoundError)
+from astroid.exceptions import (AstroidBuildingException, InferenceError,
+                                NotFoundError)
 
 
 class BlockRangeMixIn(object):
@@ -55,6 +41,7 @@
             return lineno, orelse[0].fromlineno - 1
         return lineno, last or self.tolineno
 
+
 class FilterStmtsMixin(object):
     """Mixin for statement filtering and assignment type"""
 
@@ -92,14 +79,13 @@
         return self.parent.ass_type()
 
 
-
 class FromImportMixIn(FilterStmtsMixin):
     """MixIn for From and Import Nodes"""
 
     def _infer_name(self, frame, name):
         return name
 
-    def do_import_module(self, modname):
+    def do_import_module(self, modname=None):
         """return the ast for a module whose name is <modname> imported by <self>
         """
         # handle special case where we are on a package node importing a module
@@ -108,6 +94,8 @@
         # XXX: no more needed ?
         mymodule = self.root()
         level = getattr(self, 'level', None) # Import as no level
+        if modname is None:
+            modname = self.modname
         # XXX we should investigate deeper if we really want to check
         # importing itself: modname and mymodule.name be relative or absolute
         if mymodule.relative_to_absolute_name(modname, level) == mymodule.name:
@@ -115,7 +103,7 @@
             return mymodule
         try:
             return mymodule.import_module(modname, level=level)
-        except ASTNGBuildingException:
+        except AstroidBuildingException:
             raise InferenceError(modname)
         except SyntaxError, ex:
             raise InferenceError(str(ex))
@@ -132,5 +120,3 @@
                 return name
         raise NotFoundError(asname)
 
-
-
diff --git a/third_party/logilab/astroid/modutils.py b/third_party/logilab/astroid/modutils.py
new file mode 100644
index 0000000..1f4a4fc
--- /dev/null
+++ b/third_party/logilab/astroid/modutils.py
@@ -0,0 +1,635 @@
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:[email protected]
+#
+# This file is part of astroid.
+#
+# astroid is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option) any
+# later version.
+#
+# astroid is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with astroid.  If not, see <http://www.gnu.org/licenses/>.
+"""Python modules manipulation utility functions.
+
+:type PY_SOURCE_EXTS: tuple(str)
+:var PY_SOURCE_EXTS: list of possible python source file extension
+
+:type STD_LIB_DIR: str
+:var STD_LIB_DIR: directory where standard modules are located
+
+:type BUILTIN_MODULES: dict
+:var BUILTIN_MODULES: dictionary with builtin module names has key
+"""
+from __future__ import with_statement
+
+__docformat__ = "restructuredtext en"
+
+import sys
+import os
+from os.path import splitext, join, abspath, isdir, dirname, exists
+from imp import find_module, load_module, C_BUILTIN, PY_COMPILED, PKG_DIRECTORY
+from distutils.sysconfig import get_python_lib
+from distutils.errors import DistutilsPlatformError
+
+try:
+    import zipimport
+except ImportError:
+    zipimport = None
+
+ZIPFILE = object()
+
+from logilab.common import _handle_blacklist
+
+# Notes about STD_LIB_DIR
+# Consider arch-specific installation for STD_LIB_DIR definition
+# :mod:`distutils.sysconfig` contains to much hardcoded values to rely on
+#
+# :see: `Problems with /usr/lib64 builds <http://bugs.python.org/issue1294959>`_
+# :see: `FHS <http://www.pathname.com/fhs/pub/fhs-2.3.html#LIBLTQUALGTALTERNATEFORMATESSENTIAL>`_
+if sys.platform.startswith('win'):
+    PY_SOURCE_EXTS = ('py', 'pyw')
+    PY_COMPILED_EXTS = ('dll', 'pyd')
+else:
+    PY_SOURCE_EXTS = ('py',)
+    PY_COMPILED_EXTS = ('so',)
+
+try:
+    STD_LIB_DIR = get_python_lib(standard_lib=1)
+# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to
+# non-valid path, see https://bugs.pypy.org/issue1164
+except DistutilsPlatformError:
+    STD_LIB_DIR = '//'
+
+EXT_LIB_DIR = get_python_lib()
+
+BUILTIN_MODULES = dict(zip(sys.builtin_module_names,
+                           [1]*len(sys.builtin_module_names)))
+
+
+class NoSourceFile(Exception):
+    """exception raised when we are not able to get a python
+    source file for a precompiled file
+    """
+
+
+def load_module_from_name(dotted_name, path=None, use_sys=1):
+    """Load a Python module from its name.
+
+    :type dotted_name: str
+    :param dotted_name: python name of a module or package
+
+    :type path: list or None
+    :param path:
+      optional list of path where the module or package should be
+      searched (use sys.path if nothing or None is given)
+
+    :type use_sys: bool
+    :param use_sys:
+      boolean indicating whether the sys.modules dictionary should be
+      used or not
+
+
+    :raise ImportError: if the module or package is not found
+
+    :rtype: module
+    :return: the loaded module
+    """
+    return load_module_from_modpath(dotted_name.split('.'), path, use_sys)
+
+
+def load_module_from_modpath(parts, path=None, use_sys=1):
+    """Load a python module from its splitted name.
+
+    :type parts: list(str) or tuple(str)
+    :param parts:
+      python name of a module or package splitted on '.'
+
+    :type path: list or None
+    :param path:
+      optional list of path where the module or package should be
+      searched (use sys.path if nothing or None is given)
+
+    :type use_sys: bool
+    :param use_sys:
+      boolean indicating whether the sys.modules dictionary should be used or not
+
+    :raise ImportError: if the module or package is not found
+
+    :rtype: module
+    :return: the loaded module
+    """
+    if use_sys:
+        try:
+            return sys.modules['.'.join(parts)]
+        except KeyError:
+            pass
+    modpath = []
+    prevmodule = None
+    for part in parts:
+        modpath.append(part)
+        curname = '.'.join(modpath)
+        module = None
+        if len(modpath) != len(parts):
+            # even with use_sys=False, should try to get outer packages from sys.modules
+            module = sys.modules.get(curname)
+        elif use_sys:
+            # because it may have been indirectly loaded through a parent
+            module = sys.modules.get(curname)
+        if module is None:
+            mp_file, mp_filename, mp_desc = find_module(part, path)
+            module = load_module(curname, mp_file, mp_filename, mp_desc)
+        if prevmodule:
+            setattr(prevmodule, part, module)
+        _file = getattr(module, '__file__', '')
+        if not _file and len(modpath) != len(parts):
+            raise ImportError('no module in %s' % '.'.join(parts[len(modpath):]))
+        path = [dirname(_file)]
+        prevmodule = module
+    return module
+
+
+def load_module_from_file(filepath, path=None, use_sys=1, extrapath=None):
+    """Load a Python module from it's path.
+
+    :type filepath: str
+    :param filepath: path to the python module or package
+
+    :type path: list or None
+    :param path:
+      optional list of path where the module or package should be
+      searched (use sys.path if nothing or None is given)
+
+    :type use_sys: bool
+    :param use_sys:
+      boolean indicating whether the sys.modules dictionary should be
+      used or not
+
+
+    :raise ImportError: if the module or package is not found
+
+    :rtype: module
+    :return: the loaded module
+    """
+    modpath = modpath_from_file(filepath, extrapath)
+    return load_module_from_modpath(modpath, path, use_sys)
+
+
+def _check_init(path, mod_path):
+    """check there are some __init__.py all along the way"""
+    for part in mod_path:
+        path = join(path, part)
+        if not _has_init(path):
+            return False
+    return True
+
+
+def modpath_from_file(filename, extrapath=None):
+    """given a file path return the corresponding splitted module's name
+    (i.e name of a module or package splitted on '.')
+
+    :type filename: str
+    :param filename: file's path for which we want the module's name
+
+    :type extrapath: dict
+    :param extrapath:
+      optional extra search path, with path as key and package name for the path
+      as value. This is usually useful to handle package splitted in multiple
+      directories using __path__ trick.
+
+
+    :raise ImportError:
+      if the corresponding module's name has not been found
+
+    :rtype: list(str)
+    :return: the corresponding splitted module's name
+    """
+    base = splitext(abspath(filename))[0]
+    if extrapath is not None:
+        for path_ in extrapath:
+            path = _abspath(path_)
+            if path and base[:len(path)] == path:
+                submodpath = [pkg for pkg in base[len(path):].split(os.sep)
+                              if pkg]
+                if _check_init(path, submodpath[:-1]):
+                    return extrapath[path_].split('.') + submodpath
+    for path in sys.path:
+        path = _abspath(path)
+        if path and base.startswith(path):
+            modpath = [pkg for pkg in base[len(path):].split(os.sep) if pkg]
+            if _check_init(path, modpath[:-1]):
+                return modpath
+    raise ImportError('Unable to find module for %s in %s' % (
+        filename, ', \n'.join(sys.path)))
+
+
+
+def file_from_modpath(modpath, path=None, context_file=None):
+    """given a mod path (i.e. splitted module / package name), return the
+    corresponding file, giving priority to source file over precompiled
+    file if it exists
+
+    :type modpath: list or tuple
+    :param modpath:
+      splitted module's name (i.e name of a module or package splitted
+      on '.')
+      (this means explicit relative imports that start with dots have
+      empty strings in this list!)
+
+    :type path: list or None
+    :param path:
+      optional list of path where the module or package should be
+      searched (use sys.path if nothing or None is given)
+
+    :type context_file: str or None
+    :param context_file:
+      context file to consider, necessary if the identifier has been
+      introduced using a relative import unresolvable in the actual
+      context (i.e. modutils)
+
+    :raise ImportError: if there is no such module in the directory
+
+    :rtype: str or None
+    :return:
+      the path to the module's file or None if it's an integrated
+      builtin module such as 'sys'
+    """
+    if context_file is not None:
+        context = dirname(context_file)
+    else:
+        context = context_file
+    if modpath[0] == 'xml':
+        # handle _xmlplus
+        try:
+            return _file_from_modpath(['_xmlplus'] + modpath[1:], path, context)
+        except ImportError:
+            return _file_from_modpath(modpath, path, context)
+    elif modpath == ['os', 'path']:
+        # FIXME: currently ignoring search_path...
+        return os.path.__file__
+    return _file_from_modpath(modpath, path, context)
+
+
+
+def get_module_part(dotted_name, context_file=None):
+    """given a dotted name return the module part of the name :
+
+    >>> get_module_part('logilab.common.modutils.get_module_part')
+    'logilab.common.modutils'
+
+    :type dotted_name: str
+    :param dotted_name: full name of the identifier we are interested in
+
+    :type context_file: str or None
+    :param context_file:
+      context file to consider, necessary if the identifier has been
+      introduced using a relative import unresolvable in the actual
+      context (i.e. modutils)
+
+
+    :raise ImportError: if there is no such module in the directory
+
+    :rtype: str or None
+    :return:
+      the module part of the name or None if we have not been able at
+      all to import the given name
+
+    XXX: deprecated, since it doesn't handle package precedence over module
+    (see #10066)
+    """
+    # os.path trick
+    if dotted_name.startswith('os.path'):
+        return 'os.path'
+    parts = dotted_name.split('.')
+    if context_file is not None:
+        # first check for builtin module which won't be considered latter
+        # in that case (path != None)
+        if parts[0] in BUILTIN_MODULES:
+            if len(parts) > 2:
+                raise ImportError(dotted_name)
+            return parts[0]
+        # don't use += or insert, we want a new list to be created !
+    path = None
+    starti = 0
+    if parts[0] == '':
+        assert context_file is not None, \
+                'explicit relative import, but no context_file?'
+        path = [] # prevent resolving the import non-relatively
+        starti = 1
+    while parts[starti] == '': # for all further dots: change context
+        starti += 1
+        context_file = dirname(context_file)
+    for i in range(starti, len(parts)):
+        try:
+            file_from_modpath(parts[starti:i+1], path=path,
+                              context_file=context_file)
+        except ImportError:
+            if not i >= max(1, len(parts) - 2):
+                raise
+            return '.'.join(parts[:i])
+    return dotted_name
+
+
+def get_module_files(src_directory, blacklist):
+    """given a package directory return a list of all available python
+    module's files in the package and its subpackages
+
+    :type src_directory: str
+    :param src_directory:
+      path of the directory corresponding to the package
+
+    :type blacklist: list or tuple
+    :param blacklist:
+      optional list of files or directory to ignore, default to the value of
+      `logilab.common.STD_BLACKLIST`
+
+    :rtype: list
+    :return:
+      the list of all available python module's files in the package and
+      its subpackages
+    """
+    files = []
+    for directory, dirnames, filenames in os.walk(src_directory):
+        _handle_blacklist(blacklist, dirnames, filenames)
+        # check for __init__.py
+        if not '__init__.py' in filenames:
+            dirnames[:] = ()
+            continue
+        for filename in filenames:
+            if _is_python_file(filename):
+                src = join(directory, filename)
+                files.append(src)
+    return files
+
+
+def get_source_file(filename, include_no_ext=False):
+    """given a python module's file name return the matching source file
+    name (the filename will be returned identically if it's a already an
+    absolute path to a python source file...)
+
+    :type filename: str
+    :param filename: python module's file name
+
+
+    :raise NoSourceFile: if no source file exists on the file system
+
+    :rtype: str
+    :return: the absolute path of the source file if it exists
+    """
+    base, orig_ext = splitext(abspath(filename))
+    for ext in PY_SOURCE_EXTS:
+        source_path = '%s.%s' % (base, ext)
+        if exists(source_path):
+            return source_path
+    if include_no_ext and not orig_ext and exists(base):
+        return base
+    raise NoSourceFile(filename)
+
+
+def is_python_source(filename):
+    """
+    rtype: bool
+    return: True if the filename is a python source file
+    """
+    return splitext(filename)[1][1:] in PY_SOURCE_EXTS
+
+
+def is_standard_module(modname, std_path=(STD_LIB_DIR,)):
+    """try to guess if a module is a standard python module (by default,
+    see `std_path` parameter's description)
+
+    :type modname: str
+    :param modname: name of the module we are interested in
+
+    :type std_path: list(str) or tuple(str)
+    :param std_path: list of path considered has standard
+
+
+    :rtype: bool
+    :return:
+      true if the module:
+      - is located on the path listed in one of the directory in `std_path`
+      - is a built-in module
+    """
+    modname = modname.split('.')[0]
+    try:
+        filename = file_from_modpath([modname])
+    except ImportError:
+        # import failed, i'm probably not so wrong by supposing it's
+        # not standard...
+        return False
+    # modules which are not living in a file are considered standard
+    # (sys and __builtin__ for instance)
+    if filename is None:
+        return True
+    filename = abspath(filename)
+    if filename.startswith(EXT_LIB_DIR):
+        return False
+    for path in std_path:
+        if filename.startswith(_abspath(path)):
+            return True
+    return False
+
+
+
+def is_relative(modname, from_file):
+    """return true if the given module name is relative to the given
+    file name
+
+    :type modname: str
+    :param modname: name of the module we are interested in
+
+    :type from_file: str
+    :param from_file:
+      path of the module from which modname has been imported
+
+    :rtype: bool
+    :return:
+      true if the module has been imported relatively to `from_file`
+    """
+    if not isdir(from_file):
+        from_file = dirname(from_file)
+    if from_file in sys.path:
+        return False
+    try:
+        find_module(modname.split('.')[0], [from_file])
+        return True
+    except ImportError:
+        return False
+
+
+# internal only functions #####################################################
+
+def _file_from_modpath(modpath, path=None, context=None):
+    """given a mod path (i.e. splitted module / package name), return the
+    corresponding file
+
+    this function is used internally, see `file_from_modpath`'s
+    documentation for more information
+    """
+    assert len(modpath) > 0
+    if context is not None:
+        try:
+            mtype, mp_filename = _module_file(modpath, [context])
+        except ImportError:
+            mtype, mp_filename = _module_file(modpath, path)
+    else:
+        mtype, mp_filename = _module_file(modpath, path)
+    if mtype == PY_COMPILED:
+        try:
+            return get_source_file(mp_filename)
+        except NoSourceFile:
+            return mp_filename
+    elif mtype == C_BUILTIN:
+        # integrated builtin module
+        return None
+    elif mtype == PKG_DIRECTORY:
+        mp_filename = _has_init(mp_filename)
+    return mp_filename
+
+def _search_zip(modpath, pic):
+    for filepath, importer in pic.items():
+        if importer is not None:
+            if importer.find_module(modpath[0]):
+                if not importer.find_module(os.path.sep.join(modpath)):
+                    raise ImportError('No module named %s in %s/%s' % (
+                        '.'.join(modpath[1:]), filepath, modpath))
+                return ZIPFILE, abspath(filepath) + os.path.sep + os.path.sep.join(modpath), filepath
+    raise ImportError('No module named %s' % '.'.join(modpath))
+
+
+def _abspath(path, _abspathcache={}): #pylint: disable=dangerous-default-value
+    """abspath with caching"""
+    # _module_file calls abspath on every path in sys.path every time it's
+    # called; on a larger codebase this easily adds up to half a second just
+    # assembling path components. This cache alleviates that.
+    try:
+        return _abspathcache[path]
+    except KeyError:
+        if not path: # don't cache result for ''
+            return abspath(path)
+        _abspathcache[path] = abspath(path)
+        return _abspathcache[path]
+
+try:
+    import pkg_resources
+except ImportError:
+    pkg_resources = None
+
+def _module_file(modpath, path=None):
+    """get a module type / file path
+
+    :type modpath: list or tuple
+    :param modpath:
+      splitted module's name (i.e name of a module or package splitted
+      on '.'), with leading empty strings for explicit relative import
+
+    :type path: list or None
+    :param path:
+      optional list of path where the module or package should be
+      searched (use sys.path if nothing or None is given)
+
+
+    :rtype: tuple(int, str)
+    :return: the module type flag and the file path for a module
+    """
+    # egg support compat
+    try:
+        pic = sys.path_importer_cache
+        _path = (path is None and sys.path or path)
+        for __path in _path:
+            if not __path in pic:
+                try:
+                    pic[__path] = zipimport.zipimporter(__path)
+                except zipimport.ZipImportError:
+                    pic[__path] = None
+        checkeggs = True
+    except AttributeError:
+        checkeggs = False
+    # pkg_resources support (aka setuptools namespace packages)
+    if pkg_resources is not None and modpath[0] in pkg_resources._namespace_packages and len(modpath) > 1:
+        # setuptools has added into sys.modules a module object with proper
+        # __path__, get back information from there
+        module = sys.modules[modpath.pop(0)]
+        path = module.__path__
+    imported = []
+    while modpath:
+        modname = modpath[0]
+        # take care to changes in find_module implementation wrt builtin modules
+        #
+        # Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23)
+        # >>> imp.find_module('posix')
+        # (None, 'posix', ('', '', 6))
+        #
+        # Python 3.3.1 (default, Apr 26 2013, 12:08:46)
+        # >>> imp.find_module('posix')
+        # (None, None, ('', '', 6))
+        try:
+            _, mp_filename, mp_desc = find_module(modname, path)
+        except ImportError:
+            if checkeggs:
+                return _search_zip(modpath, pic)[:2]
+            raise
+        else:
+            if checkeggs and mp_filename:
+                fullabspath = [_abspath(x) for x in _path]
+                try:
+                    pathindex = fullabspath.index(dirname(abspath(mp_filename)))
+                    emtype, emp_filename, zippath = _search_zip(modpath, pic)
+                    if pathindex > _path.index(zippath):
+                        # an egg takes priority
+                        return emtype, emp_filename
+                except ValueError:
+                    # XXX not in _path
+                    pass
+                except ImportError:
+                    pass
+                checkeggs = False
+        imported.append(modpath.pop(0))
+        mtype = mp_desc[2]
+        if modpath:
+            if mtype != PKG_DIRECTORY:
+                raise ImportError('No module %s in %s' % ('.'.join(modpath),
+                                                          '.'.join(imported)))
+            # XXX guess if package is using pkgutil.extend_path by looking for
+            # those keywords in the first four Kbytes
+            try:
+                with open(join(mp_filename, '__init__.py')) as stream:
+                    data = stream.read(4096)
+            except IOError:
+                path = [mp_filename]
+            else:
+                if 'pkgutil' in data and 'extend_path' in data:
+                    # extend_path is called, search sys.path for module/packages
+                    # of this name see pkgutil.extend_path documentation
+                    path = [join(p, *imported) for p in sys.path
+                            if isdir(join(p, *imported))]
+                else:
+                    path = [mp_filename]
+    return mtype, mp_filename
+
+def _is_python_file(filename):
+    """return true if the given filename should be considered as a python file
+
+    .pyc and .pyo are ignored
+    """
+    for ext in ('.py', '.so', '.pyd', '.pyw'):
+        if filename.endswith(ext):
+            return True
+    return False
+
+
+def _has_init(directory):
+    """if the given directory has a valid __init__ file, return its path,
+    else return None
+    """
+    mod_or_pack = join(directory, '__init__')
+    for ext in PY_SOURCE_EXTS + ('pyc', 'pyo'):
+        if exists(mod_or_pack + '.' + ext):
+            return mod_or_pack + '.' + ext
+    return None
diff --git a/third_party/logilab/astng/node_classes.py b/third_party/logilab/astroid/node_classes.py
similarity index 77%
rename from third_party/logilab/astng/node_classes.py
rename to third_party/logilab/astroid/node_classes.py
index 607ad90..6d59745 100644
--- a/third_party/logilab/astng/node_classes.py
+++ b/third_party/logilab/astroid/node_classes.py
@@ -1,33 +1,32 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
 """Module for some node classes. More nodes in scoped_nodes.py
 """
 
 import sys
 
-from logilab.astng import BUILTINS_MODULE
-from logilab.astng.exceptions import NoDefault
-from logilab.astng.bases import (NodeNG, Statement, Instance, InferenceContext,
-                                 _infer_stmts, YES)
-from logilab.astng.mixins import BlockRangeMixIn, AssignTypeMixin, \
-                                 ParentAssignTypeMixin, FromImportMixIn
+from astroid.exceptions import NoDefault
+from astroid.bases import (NodeNG, Statement, Instance, InferenceContext,
+                           _infer_stmts, YES, BUILTINS)
+from astroid.mixins import (BlockRangeMixIn, AssignTypeMixin,
+                            ParentAssignTypeMixin, FromImportMixIn)
+
+PY3K = sys.version_info >= (3, 0)
 
 
 def unpack_infer(stmt, context=None):
@@ -39,13 +38,18 @@
             for infered_elt in unpack_infer(elt, context):
                 yield infered_elt
         return
+    # if infered is a final node, return it and stop
     infered = stmt.infer(context).next()
-    if infered is stmt or infered is YES:
+    if infered is stmt:
         yield infered
         return
+    # else, infer recursivly, except YES object that should be returned as is
     for infered in stmt.infer(context):
-        for inf_inf in unpack_infer(infered, context):
-            yield inf_inf
+        if infered is YES:
+            yield infered
+        else:
+            for inf_inf in unpack_infer(infered, context):
+                yield inf_inf
 
 
 def are_exclusive(stmt1, stmt2, exceptions=None):
@@ -80,16 +84,16 @@
             # nodes are in exclusive branches
             if isinstance(node, If) and exceptions is None:
                 if (node.locate_child(previous)[1]
-                    is not node.locate_child(children[node])[1]):
+                        is not node.locate_child(children[node])[1]):
                     return True
             elif isinstance(node, TryExcept):
                 c2attr, c2node = node.locate_child(previous)
                 c1attr, c1node = node.locate_child(children[node])
                 if c1node is not c2node:
                     if ((c2attr == 'body' and c1attr == 'handlers' and children[node].catch(exceptions)) or
-                        (c2attr == 'handlers' and c1attr == 'body' and previous.catch(exceptions)) or
-                        (c2attr == 'handlers' and c1attr == 'orelse') or
-                        (c2attr == 'orelse' and c1attr == 'handlers')):
+                            (c2attr == 'handlers' and c1attr == 'body' and previous.catch(exceptions)) or
+                            (c2attr == 'handlers' and c1attr == 'orelse') or
+                            (c2attr == 'orelse' and c1attr == 'handlers')):
                         return True
                 elif c2attr == 'handlers' and c1attr == 'handlers':
                     return previous is not children[node]
@@ -106,13 +110,13 @@
     def lookup(self, name):
         """lookup a variable name
 
-        return the scope node and the list of assignments associated to the given
-        name according to the scope where it has been found (locals, globals or
-        builtin)
+        return the scope node and the list of assignments associated to the
+        given name according to the scope where it has been found (locals,
+        globals or builtin)
 
-        The lookup is starting from self's scope. If self is not a frame itself and
-        the name is found in the inner frame locals, statements will be filtered
-        to remove ignorable statements according to self's location
+        The lookup is starting from self's scope. If self is not a frame itself
+        and the name is found in the inner frame locals, statements will be
+        filtered to remove ignorable statements according to self's location
         """
         return self.scope().scope_lookup(self, name)
 
@@ -251,9 +255,30 @@
 
 class Arguments(NodeNG, AssignTypeMixin):
     """class representing an Arguments node"""
-    _astng_fields = ('args', 'defaults')
+    if PY3K:
+        # Python 3.4+ uses a different approach regarding annotations,
+        # each argument is a new class, _ast.arg, which exposes an
+        # 'annotation' attribute. In astroid though, arguments are exposed
+        # as is in the Arguments node and the only way to expose annotations
+        # is by using something similar with Python 3.3:
+        #  - we expose 'varargannotation' and 'kwargannotation' of annotations
+        #    of varargs and kwargs.
+        #  - we expose 'annotation', a list with annotations for
+        #    for each normal argument. If an argument doesn't have an
+        #    annotation, its value will be None.
+
+        _astroid_fields = ('args', 'defaults', 'kwonlyargs',
+                           'kw_defaults', 'annotations',
+                           'varargannotation', 'kwargannotation')
+        annotations = None
+        varargannotation = None
+        kwargannotation = None
+    else:
+        _astroid_fields = ('args', 'defaults', 'kwonlyargs', 'kw_defaults')
     args = None
     defaults = None
+    kwonlyargs = None
+    kw_defaults = None
 
     def __init__(self, vararg=None, kwarg=None):
         self.vararg = vararg
@@ -266,11 +291,17 @@
 
     def format_args(self):
         """return arguments formatted as string"""
-        result = [_format_args(self.args, self.defaults)]
+        result = []
+        if self.args:
+            result.append(_format_args(self.args, self.defaults))
         if self.vararg:
             result.append('*%s' % self.vararg)
         if self.kwarg:
             result.append('**%s' % self.kwarg)
+        if self.kwonlyargs:
+            if not self.vararg:
+                result.append('*')
+            result.append(_format_args(self.kwonlyargs, self.kw_defaults))
         return ', '.join(result)
 
     def default_value(self, argname):
@@ -283,6 +314,9 @@
             idx = i - (len(self.args) - len(self.defaults))
             if idx >= 0:
                 return self.defaults[idx]
+        i = _find_arg(argname, self.kwonlyargs)[0]
+        if i is not None and self.kw_defaults[i] is not None:
+            return self.kw_defaults[i]
         raise NoDefault()
 
     def is_argument(self, name):
@@ -299,6 +333,12 @@
             return _find_arg(argname, self.args, rec)
         return None, None
 
+    def get_children(self):
+        """override get_children to skip over None elements in kw_defaults"""
+        for child in super(Arguments, self).get_children():
+            if child is not None:
+                yield child
+
 
 def _find_arg(argname, args, rec=False):
     for i, arg in enumerate(args):
@@ -324,47 +364,48 @@
         else:
             values.append(arg.name)
             if defaults is not None and i >= default_offset:
-                values[-1] += '=' + defaults[i-default_offset].as_string()
+                if defaults[i-default_offset] is not None:
+                    values[-1] += '=' + defaults[i-default_offset].as_string()
     return ', '.join(values)
 
 
 class AssAttr(NodeNG, ParentAssignTypeMixin):
     """class representing an AssAttr node"""
-    _astng_fields = ('expr',)
+    _astroid_fields = ('expr',)
     expr = None
 
 class Assert(Statement):
     """class representing an Assert node"""
-    _astng_fields = ('test', 'fail',)
+    _astroid_fields = ('test', 'fail',)
     test = None
     fail = None
 
 class Assign(Statement, AssignTypeMixin):
     """class representing an Assign node"""
-    _astng_fields = ('targets', 'value',)
+    _astroid_fields = ('targets', 'value',)
     targets = None
     value = None
 
 class AugAssign(Statement, AssignTypeMixin):
     """class representing an AugAssign node"""
-    _astng_fields = ('target', 'value',)
+    _astroid_fields = ('target', 'value',)
     target = None
     value = None
 
 class Backquote(NodeNG):
     """class representing a Backquote node"""
-    _astng_fields = ('value',)
+    _astroid_fields = ('value',)
     value = None
 
 class BinOp(NodeNG):
     """class representing a BinOp node"""
-    _astng_fields = ('left', 'right',)
+    _astroid_fields = ('left', 'right',)
     left = None
     right = None
 
 class BoolOp(NodeNG):
     """class representing a BoolOp node"""
-    _astng_fields = ('values',)
+    _astroid_fields = ('values',)
     values = None
 
 class Break(Statement):
@@ -373,7 +414,7 @@
 
 class CallFunc(NodeNG):
     """class representing a CallFunc node"""
-    _astng_fields = ('func', 'args', 'starargs', 'kwargs')
+    _astroid_fields = ('func', 'args', 'starargs', 'kwargs')
     func = None
     args = None
     starargs = None
@@ -385,7 +426,7 @@
 
 class Compare(NodeNG):
     """class representing a Compare node"""
-    _astng_fields = ('left', 'ops',)
+    _astroid_fields = ('left', 'ops',)
     left = None
     ops = None
 
@@ -403,7 +444,7 @@
 
 class Comprehension(NodeNG):
     """class representing a Comprehension node"""
-    _astng_fields = ('target', 'iter' ,'ifs')
+    _astroid_fields = ('target', 'iter', 'ifs')
     target = None
     iter = None
     ifs = None
@@ -456,7 +497,7 @@
 
 class Decorators(NodeNG):
     """class representing a Decorators node"""
-    _astng_fields = ('nodes',)
+    _astroid_fields = ('nodes',)
     nodes = None
 
     def __init__(self, nodes=None):
@@ -468,29 +509,29 @@
 
 class DelAttr(NodeNG, ParentAssignTypeMixin):
     """class representing a DelAttr node"""
-    _astng_fields = ('expr',)
+    _astroid_fields = ('expr',)
     expr = None
 
 
 class Delete(Statement, AssignTypeMixin):
     """class representing a Delete node"""
-    _astng_fields = ('targets',)
+    _astroid_fields = ('targets',)
     targets = None
 
 
 class Dict(NodeNG, Instance):
     """class representing a Dict node"""
-    _astng_fields = ('items',)
+    _astroid_fields = ('items',)
 
     def __init__(self, items=None):
         if items is None:
             self.items = []
         else:
             self.items = [(const_factory(k), const_factory(v))
-                          for k,v in items.iteritems()]
+                          for k, v in items.iteritems()]
 
     def pytype(self):
-        return '%s.dict' % BUILTINS_MODULE
+        return '%s.dict' % BUILTINS
 
     def get_children(self):
         """get children of a Dict node"""
@@ -508,19 +549,22 @@
     def itered(self):
         return self.items[::2]
 
-    def getitem(self, key, context=None):
-        for i in xrange(0, len(self.items), 2):
-            for inferedkey in self.items[i].infer(context):
+    def getitem(self, lookup_key, context=None):
+        for key, value in self.items:
+            for inferedkey in key.infer(context):
                 if inferedkey is YES:
                     continue
-                if isinstance(inferedkey, Const) and inferedkey.value == key:
-                    return self.items[i+1]
-        raise IndexError(key)
+                if isinstance(inferedkey, Const) \
+                        and inferedkey.value == lookup_key:
+                    return value
+        # This should raise KeyError, but all call sites only catch
+        # IndexError. Let's leave it like that for now.
+        raise IndexError(lookup_key)
 
 
 class Discard(Statement):
     """class representing a Discard node"""
-    _astng_fields = ('value',)
+    _astroid_fields = ('value',)
     value = None
 
 
@@ -534,7 +578,7 @@
 
 class ExceptHandler(Statement, AssignTypeMixin):
     """class representing an ExceptHandler node"""
-    _astng_fields = ('type', 'name', 'body',)
+    _astroid_fields = ('type', 'name', 'body',)
     type = None
     name = None
     body = None
@@ -562,7 +606,7 @@
 
 class Exec(Statement):
     """class representing an Exec node"""
-    _astng_fields = ('expr', 'globals', 'locals',)
+    _astroid_fields = ('expr', 'globals', 'locals',)
     expr = None
     globals = None
     locals = None
@@ -570,12 +614,12 @@
 
 class ExtSlice(NodeNG):
     """class representing an ExtSlice node"""
-    _astng_fields = ('dims',)
+    _astroid_fields = ('dims',)
     dims = None
 
 class For(BlockRangeMixIn, AssignTypeMixin, Statement):
     """class representing a For node"""
-    _astng_fields = ('target', 'iter', 'body', 'orelse',)
+    _astroid_fields = ('target', 'iter', 'body', 'orelse',)
     target = None
     iter = None
     body = None
@@ -589,14 +633,14 @@
 class From(FromImportMixIn, Statement):
     """class representing a From node"""
 
-    def __init__(self,  fromname, names, level=0):
+    def __init__(self, fromname, names, level=0):
         self.modname = fromname
         self.names = names
         self.level = level
 
 class Getattr(NodeNG):
     """class representing a Getattr node"""
-    _astng_fields = ('expr',)
+    _astroid_fields = ('expr',)
     expr = None
 
 
@@ -612,7 +656,7 @@
 
 class If(BlockRangeMixIn, Statement):
     """class representing an If node"""
-    _astng_fields = ('test', 'body', 'orelse')
+    _astroid_fields = ('test', 'body', 'orelse')
     test = None
     body = None
     orelse = None
@@ -632,7 +676,7 @@
 
 class IfExp(NodeNG):
     """class representing an IfExp node"""
-    _astng_fields = ('test', 'body', 'orelse')
+    _astroid_fields = ('test', 'body', 'orelse')
     test = None
     body = None
     orelse = None
@@ -644,19 +688,19 @@
 
 class Index(NodeNG):
     """class representing an Index node"""
-    _astng_fields = ('value',)
+    _astroid_fields = ('value',)
     value = None
 
 
 class Keyword(NodeNG):
     """class representing a Keyword node"""
-    _astng_fields = ('value',)
+    _astroid_fields = ('value',)
     value = None
 
 
 class List(NodeNG, Instance, ParentAssignTypeMixin):
     """class representing a List node"""
-    _astng_fields = ('elts',)
+    _astroid_fields = ('elts',)
 
     def __init__(self, elts=None):
         if elts is None:
@@ -665,7 +709,7 @@
             self.elts = [const_factory(e) for e in elts]
 
     def pytype(self):
-        return '%s.list' % BUILTINS_MODULE
+        return '%s.list' % BUILTINS
 
     def getitem(self, index, context=None):
         return self.elts[index]
@@ -690,7 +734,7 @@
 
 class Print(Statement):
     """class representing a Print node"""
-    _astng_fields = ('dest', 'values',)
+    _astroid_fields = ('dest', 'values',)
     dest = None
     values = None
 
@@ -699,11 +743,11 @@
     """class representing a Raise node"""
     exc = None
     if sys.version_info < (3, 0):
-        _astng_fields = ('exc', 'inst', 'tback')
+        _astroid_fields = ('exc', 'inst', 'tback')
         inst = None
         tback = None
     else:
-        _astng_fields = ('exc', 'cause')
+        _astroid_fields = ('exc', 'cause')
         exc = None
         cause = None
 
@@ -717,13 +761,13 @@
 
 class Return(Statement):
     """class representing a Return node"""
-    _astng_fields = ('value',)
+    _astroid_fields = ('value',)
     value = None
 
 
 class Set(NodeNG, Instance, ParentAssignTypeMixin):
     """class representing a Set node"""
-    _astng_fields = ('elts',)
+    _astroid_fields = ('elts',)
 
     def __init__(self, elts=None):
         if elts is None:
@@ -732,7 +776,7 @@
             self.elts = [const_factory(e) for e in elts]
 
     def pytype(self):
-        return '%s.set' % BUILTINS_MODULE
+        return '%s.set' % BUILTINS
 
     def itered(self):
         return self.elts
@@ -740,27 +784,27 @@
 
 class Slice(NodeNG):
     """class representing a Slice node"""
-    _astng_fields = ('lower', 'upper', 'step')
+    _astroid_fields = ('lower', 'upper', 'step')
     lower = None
     upper = None
     step = None
 
-class Starred(NodeNG):
+class Starred(NodeNG, ParentAssignTypeMixin):
     """class representing a Starred node"""
-    _astng_fields = ('value',)
+    _astroid_fields = ('value',)
     value = None
 
 
 class Subscript(NodeNG):
     """class representing a Subscript node"""
-    _astng_fields = ('value', 'slice')
+    _astroid_fields = ('value', 'slice')
     value = None
     slice = None
 
 
 class TryExcept(BlockRangeMixIn, Statement):
     """class representing a TryExcept node"""
-    _astng_fields = ('body', 'handlers', 'orelse',)
+    _astroid_fields = ('body', 'handlers', 'orelse',)
     body = None
     handlers = None
     orelse = None
@@ -786,7 +830,7 @@
 
 class TryFinally(BlockRangeMixIn, Statement):
     """class representing a TryFinally node"""
-    _astng_fields = ('body', 'finalbody',)
+    _astroid_fields = ('body', 'finalbody',)
     body = None
     finalbody = None
 
@@ -798,14 +842,14 @@
         child = self.body[0]
         # py2.5 try: except: finally:
         if (isinstance(child, TryExcept) and child.fromlineno == self.fromlineno
-            and lineno > self.fromlineno and lineno <= child.tolineno):
+                and lineno > self.fromlineno and lineno <= child.tolineno):
             return child.block_range(lineno)
         return self._elsed_block_range(lineno, self.finalbody)
 
 
 class Tuple(NodeNG, Instance, ParentAssignTypeMixin):
     """class representing a Tuple node"""
-    _astng_fields = ('elts',)
+    _astroid_fields = ('elts',)
 
     def __init__(self, elts=None):
         if elts is None:
@@ -814,7 +858,7 @@
             self.elts = [const_factory(e) for e in elts]
 
     def pytype(self):
-        return '%s.tuple' % BUILTINS_MODULE
+        return '%s.tuple' % BUILTINS
 
     def getitem(self, index, context=None):
         return self.elts[index]
@@ -825,13 +869,13 @@
 
 class UnaryOp(NodeNG):
     """class representing an UnaryOp node"""
-    _astng_fields = ('operand',)
+    _astroid_fields = ('operand',)
     operand = None
 
 
 class While(BlockRangeMixIn, Statement):
     """class representing a While node"""
-    _astng_fields = ('test', 'body', 'orelse',)
+    _astroid_fields = ('test', 'body', 'orelse',)
     test = None
     body = None
     orelse = None
@@ -846,23 +890,29 @@
 
 class With(BlockRangeMixIn, AssignTypeMixin, Statement):
     """class representing a With node"""
-    _astng_fields = ('expr', 'vars', 'body')
-    expr = None
-    vars = None
+    _astroid_fields = ('items', 'body')
+    items = None
     body = None
 
     def _blockstart_toline(self):
-        if self.vars:
-            return self.vars.tolineno
-        else:
-            return self.expr.tolineno
+        return self.items[-1][0].tolineno
 
+    def get_children(self):
+        for expr, var in self.items:
+            yield expr
+            if var:
+                yield var
+        for elt in self.body:
+            yield elt
 
 class Yield(NodeNG):
     """class representing a Yield node"""
-    _astng_fields = ('value',)
+    _astroid_fields = ('value',)
     value = None
 
+class YieldFrom(Yield):
+    """ Class representing a YieldFrom node. """
+
 # constants ##############################################################
 
 CONST_CLS = {
@@ -885,19 +935,16 @@
 _update_const_classes()
 
 def const_factory(value):
-    """return an astng node for a python value"""
-    # since const_factory is called to evaluate content of container (eg list,
-    # tuple), it may be called with some node as argument that should be left
-    # untouched
-    if isinstance(value, NodeNG):
-        return value
+    """return an astroid node for a python value"""
+    # XXX we should probably be stricter here and only consider stuff in
+    # CONST_CLS or do better treatment: in case where value is not in CONST_CLS,
+    # we should rather recall the builder on this value than returning an empty
+    # node (another option being that const_factory shouldn't be called with something
+    # not in CONST_CLS)
+    assert not isinstance(value, NodeNG)
     try:
         return CONST_CLS[value.__class__](value)
     except (KeyError, AttributeError):
-        # some constants (like from gtk._gtk) don't have their class in
-        # CONST_CLS, though we can "assert isinstance(value, tuple(CONST_CLS))"
-        if isinstance(value, tuple(CONST_CLS)):
-            return Const(value)
         node = EmptyNode()
         node.object = value
         return node
diff --git a/third_party/logilab/astng/nodes.py b/third_party/logilab/astroid/nodes.py
similarity index 75%
rename from third_party/logilab/astng/nodes.py
rename to third_party/logilab/astroid/nodes.py
index 56b9980..263ab47 100644
--- a/third_party/logilab/astng/nodes.py
+++ b/third_party/logilab/astroid/nodes.py
@@ -1,22 +1,20 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
 """
 on all nodes :
  .is_statement, returning true if the node should be considered as a
@@ -28,7 +26,7 @@
  .frame(), returning the first node defining a new local scope (i.e.
   Module, Function or Class)
  .set_local(name, node), define an identifier <name> on the first parent frame,
-  with the node defining it. This is used by the astng builder and should not
+  with the node defining it. This is used by the astroid builder and should not
   be used from out there.
 
 on From and Import :
@@ -39,15 +37,15 @@
 
 __docformat__ = "restructuredtext en"
 
-from logilab.astng.node_classes import Arguments, AssAttr, Assert, Assign, \
+from astroid.node_classes import Arguments, AssAttr, Assert, Assign, \
     AssName, AugAssign, Backquote, BinOp, BoolOp, Break, CallFunc, Compare, \
     Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, \
     Dict, Discard, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, \
     From, Getattr, Global, If, IfExp, Import, Index, Keyword, \
     List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, \
-    TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, \
+    TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom, \
     const_factory
-from logilab.astng.scoped_nodes import Module, GenExpr, Lambda, DictComp, \
+from astroid.scoped_nodes import Module, GenExpr, Lambda, DictComp, \
     ListComp, SetComp, Function, Class
 
 ALL_NODE_CLASSES = (
@@ -70,6 +68,6 @@
     TryExcept, TryFinally, Tuple,
     UnaryOp,
     While, With,
-    Yield,
+    Yield, YieldFrom
     )
 
diff --git a/third_party/logilab/astng/protocols.py b/third_party/logilab/astroid/protocols.py
similarity index 82%
rename from third_party/logilab/astng/protocols.py
rename to third_party/logilab/astroid/protocols.py
index d8c02e3..e7703a0 100644
--- a/third_party/logilab/astng/protocols.py
+++ b/third_party/logilab/astroid/protocols.py
@@ -1,34 +1,52 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
 """this module contains a set of functions to handle python protocols for nodes
 where it makes sense.
 """
 
 __doctype__ = "restructuredtext en"
 
-from logilab.astng.exceptions import InferenceError, NoDefault
-from logilab.astng.node_classes import unpack_infer
-from logilab.astng.bases import copy_context, \
-     raise_if_nothing_infered, yes_if_nothing_infered, Instance, Generator, YES
-from logilab.astng.nodes import const_factory
-from logilab.astng import nodes
+from astroid.exceptions import InferenceError, NoDefault, NotFoundError
+from astroid.node_classes import unpack_infer
+from astroid.bases import copy_context, \
+     raise_if_nothing_infered, yes_if_nothing_infered, Instance, YES
+from astroid.nodes import const_factory
+from astroid import nodes
+
+BIN_OP_METHOD = {'+':  '__add__',
+                 '-':  '__sub__',
+                 '/':  '__div__',
+                 '//': '__floordiv__',
+                 '*':  '__mul__',
+                 '**': '__power__',
+                 '%':  '__mod__',
+                 '&':  '__and__',
+                 '|':  '__or__',
+                 '^':  '__xor__',
+                 '<<': '__lshift__',
+                 '>>': '__rshift__',
+                }
+
+UNARY_OP_METHOD = {'+': '__pos__',
+                   '-': '__neg__',
+                   '~': '__invert__',
+                   'not': None, # XXX not '__nonzero__'
+                  }
 
 # unary operations ############################################################
 
@@ -72,7 +90,7 @@
                '^':  lambda a, b: a ^ b,
                '<<': lambda a, b: a << b,
                '>>': lambda a, b: a >> b,
-               }
+              }
 for key, impl in BIN_OP_IMPL.items():
     BIN_OP_IMPL[key+'='] = impl
 
@@ -135,6 +153,25 @@
         # XXX else log TypeError
 nodes.Dict.infer_binary_op = yes_if_nothing_infered(dict_infer_binary_op)
 
+def instance_infer_binary_op(self, operator, other, context):
+    try:
+        methods = self.getattr(BIN_OP_METHOD[operator])
+    except (NotFoundError, KeyError):
+        # Unknown operator
+        yield YES
+    else:
+        for method in methods:
+            if not isinstance(method, nodes.Function):
+                continue
+            for result in method.infer_call_result(self, context):
+                if result is not YES:
+                    yield result
+            # We are interested only in the first infered method,
+            # don't go looking in the rest of the methods of the ancestors.
+            break
+
+Instance.infer_binary_op = yes_if_nothing_infered(instance_infer_binary_op)
+
 
 # assignment ##################################################################
 
@@ -168,7 +205,7 @@
                 assigned = stmt.getitem(index, context)
             except (AttributeError, IndexError):
                 continue
-            except TypeError, exc: # stmt is unsubscriptable Const
+            except TypeError: # stmt is unsubscriptable Const
                 continue
             if not asspath:
                 # we achieved to resolved the assignment path,
@@ -233,10 +270,14 @@
             yield self.parent.parent.frame()
             return
     if name == self.vararg:
-        yield const_factory(())
+        vararg = const_factory(())
+        vararg.parent = self
+        yield vararg
         return
     if name == self.kwarg:
-        yield const_factory({})
+        kwarg = const_factory({})
+        kwarg.parent = self
+        yield kwarg
         return
     # if there is a default value, yield it. And then yield YES to reflect
     # we can't guess given argument value
@@ -312,10 +353,13 @@
 
 def with_assigned_stmts(self, node, context=None, asspath=None):
     if asspath is None:
-        for lst in self.vars.infer(context):
-            if isinstance(lst, (nodes.Tuple, nodes.List)):
-                for item in lst.nodes:
-                    yield item
+        for _, vars in self.items:
+            if vars is None:
+                continue
+            for lst in vars.infer(context):
+                if isinstance(lst, (nodes.Tuple, nodes.List)):
+                    for item in lst.nodes:
+                        yield item
 nodes.With.assigned_stmts = raise_if_nothing_infered(with_assigned_stmts)
 
 
diff --git a/third_party/logilab/astng/raw_building.py b/third_party/logilab/astroid/raw_building.py
similarity index 76%
rename from third_party/logilab/astng/raw_building.py
rename to third_party/logilab/astroid/raw_building.py
index 395c26e..e245f91 100644
--- a/third_party/logilab/astng/raw_building.py
+++ b/third_party/logilab/astroid/raw_building.py
@@ -1,23 +1,21 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-"""this module contains a set of functions to create astng trees from scratch
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
+"""this module contains a set of functions to create astroid trees from scratch
 (build_* functions) or from living object (object_build_* functions)
 """
 
@@ -26,18 +24,25 @@
 import sys
 from os.path import abspath
 from inspect import (getargspec, isdatadescriptor, isfunction, ismethod,
-                     ismethoddescriptor, isclass, isbuiltin)
+                     ismethoddescriptor, isclass, isbuiltin, ismodule)
 
-from logilab.astng import BUILTINS_MODULE
-from logilab.astng.node_classes import CONST_CLS
-from logilab.astng.nodes import (Module, Class, Const, const_factory, From,
-    Function, EmptyNode, Name, Arguments, Dict, List, Set, Tuple)
-from logilab.astng.bases import Generator
-from logilab.astng.manager import ASTNGManager
-MANAGER = ASTNGManager()
+from astroid.node_classes import CONST_CLS
+from astroid.nodes import (Module, Class, Const, const_factory, From,
+                           Function, EmptyNode, Name, Arguments)
+from astroid.bases import BUILTINS, Generator
+from astroid.manager import AstroidManager
+MANAGER = AstroidManager()
 
 _CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types
 
+def _io_discrepancy(member):
+    # _io module names itself `io`: http://bugs.python.org/issue18602
+    member_self = getattr(member, '__self__', None)
+    return (member_self and
+            ismodule(member_self) and
+            member_self.__name__ == '_io' and
+            member.__module__ == 'io')
+
 def _attach_local_node(parent, node, name):
     node.name = name # needed by add_local_node
     parent.add_local_node(node)
@@ -70,14 +75,14 @@
 
 
 def build_module(name, doc=None):
-    """create and initialize a astng Module node"""
+    """create and initialize a astroid Module node"""
     node = Module(name, doc, pure_python=False)
     node.package = False
     node.parent = None
     return node
 
 def build_class(name, basenames=(), doc=None):
-    """create and initialize a astng Class node"""
+    """create and initialize a astroid Class node"""
     node = Class(name, doc)
     for base in basenames:
         basenode = Name()
@@ -87,7 +92,7 @@
     return node
 
 def build_function(name, args=None, defaults=None, flag=0, doc=None):
-    """create and initialize a astng Function node"""
+    """create and initialize a astroid Function node"""
     args, defaults = args or [], defaults or []
     # first argument is now a list of decorators
     func = Function(name, doc)
@@ -110,7 +115,7 @@
 
 
 def build_from_import(fromname, names):
-    """create and initialize an astng From import statement"""
+    """create and initialize an astroid From import statement"""
     return From(fromname, [(name, None) for name in names])
 
 def register_arguments(func, args=None):
@@ -132,13 +137,13 @@
             register_arguments(func, arg.elts)
 
 def object_build_class(node, member, localname):
-    """create astng for a living class object"""
+    """create astroid for a living class object"""
     basenames = [base.__name__ for base in member.__bases__]
     return _base_class_object_build(node, member, basenames,
                                     localname=localname)
 
 def object_build_function(node, member, localname):
-    """create astng for a living function object"""
+    """create astroid for a living function object"""
     args, varargs, varkw, defaults = getargspec(member)
     if varargs is not None:
         args.append(varargs)
@@ -149,11 +154,11 @@
     node.add_local_node(func, localname)
 
 def object_build_datadescriptor(node, member, name):
-    """create astng for a living data descriptor object"""
+    """create astroid for a living data descriptor object"""
     return _base_class_object_build(node, member, [], name)
 
 def object_build_methoddescriptor(node, member, localname):
-    """create astng for a living method descriptor object"""
+    """create astroid for a living method descriptor object"""
     # FIXME get arguments ?
     func = build_function(getattr(member, '__name__', None) or localname,
                           doc=member.__doc__)
@@ -163,7 +168,7 @@
     node.add_local_node(func, localname)
 
 def _base_class_object_build(node, member, basenames, name=None, localname=None):
-    """create astng for a living class object, with a given set of base names
+    """create astroid for a living class object, with a given set of base names
     (e.g. ancestors)
     """
     klass = build_class(name or getattr(member, '__name__', None) or localname,
@@ -200,23 +205,28 @@
     Function and Class nodes and some others as guessed.
     """
 
-    # astng from living objects ###############################################
+    # astroid from living objects ###############################################
 
     def __init__(self):
         self._done = {}
         self._module = None
 
     def inspect_build(self, module, modname=None, path=None):
-        """build astng from a living module (i.e. using inspect)
+        """build astroid from a living module (i.e. using inspect)
         this is used when there is no python source code available (either
         because it's a built-in module or because the .py is not available)
         """
         self._module = module
         if modname is None:
             modname = module.__name__
-        node = build_module(modname, module.__doc__)
+        try:
+            node = build_module(modname, module.__doc__)
+        except AttributeError:
+            # in jython, java modules have no __doc__ (see #109562)
+            node = build_module(modname)
         node.file = node.path = path and abspath(path) or path
-        MANAGER.astng_cache[modname] = node
+        node.name = modname
+        MANAGER.cache_module(node)
         node.package = hasattr(module, '__path__')
         self._done = {}
         self.object_build(node, module)
@@ -240,12 +250,17 @@
                 member = member.im_func
             if isfunction(member):
                 # verify this is not an imported function
-                if member.func_code.co_filename != getattr(self._module, '__file__', None):
+                filename = getattr(member.func_code, 'co_filename', None)
+                if filename is None:
+                    assert isinstance(member, object)
+                    object_build_methoddescriptor(node, member, name)
+                elif filename != getattr(self._module, '__file__', None):
                     attach_dummy_node(node, name, member)
-                    continue
-                object_build_function(node, member, name)
+                else:
+                    object_build_function(node, member, name)
             elif isbuiltin(member):
-                if self.imported_member(node, member, name):
+                if (not _io_discrepancy(member) and
+                        self.imported_member(node, member, name)):
                     #if obj is object:
                     #    print 'skippp', obj, name, member
                     continue
@@ -269,7 +284,7 @@
             elif isdatadescriptor(member):
                 assert isinstance(member, object)
                 object_build_datadescriptor(node, member, name)
-            elif isinstance(member, _CONSTANTS):
+            elif type(member) in _CONSTANTS:
                 attach_const_node(node, name, member)
             else:
                 # create an empty node so that the name is actually defined
@@ -284,7 +299,7 @@
             modname = getattr(member, '__module__', None)
         except:
             # XXX use logging
-            print 'unexpected error while building astng from living object'
+            print 'unexpected error while building astroid from living object'
             import traceback
             traceback.print_exc()
             modname = None
@@ -293,7 +308,7 @@
                 # Python 2.5.1 (r251:54863, Sep  1 2010, 22:03:14)
                 # >>> print object.__new__.__module__
                 # None
-                modname = BUILTINS_MODULE
+                modname = BUILTINS
             else:
                 attach_dummy_node(node, name, member)
                 return True
@@ -310,28 +325,28 @@
         return False
 
 
-### astng boot strapping ################################################### ###
+### astroid bootstrapping ######################################################
+Astroid_BUILDER = InspectBuilder()
 
 _CONST_PROXY = {}
-def astng_boot_strapping():
-    """astng boot strapping the builtins module"""
+def astroid_bootstrapping():
+    """astroid boot strapping the builtins module"""
     # this boot strapping is necessary since we need the Const nodes to
     # inspect_build builtins, and then we can proxy Const
-    builder = InspectBuilder()
     from logilab.common.compat import builtins
-    astng_builtin = builder.inspect_build(builtins)
+    astroid_builtin = Astroid_BUILDER.inspect_build(builtins)
     for cls, node_cls in CONST_CLS.items():
         if cls is type(None):
             proxy = build_class('NoneType')
-            proxy.parent = astng_builtin
+            proxy.parent = astroid_builtin
         else:
-            proxy = astng_builtin.getattr(cls.__name__)[0] # XXX
+            proxy = astroid_builtin.getattr(cls.__name__)[0]
         if cls in (dict, list, set, tuple):
             node_cls._proxied = proxy
         else:
             _CONST_PROXY[cls] = proxy
 
-astng_boot_strapping()
+astroid_bootstrapping()
 
 # TODO : find a nicer way to handle this situation;
 # However __proxied introduced an
@@ -340,6 +355,7 @@
     return _CONST_PROXY[const.value.__class__]
 Const._proxied = property(_set_proxied)
 
-# FIXME : is it alright that Generator._proxied is not a astng node?
-Generator._proxied = MANAGER.infer_astng_from_something(type(a for a in ()))
+from types import GeneratorType
+Generator._proxied = Class(GeneratorType.__name__, GeneratorType.__doc__)
+Astroid_BUILDER.object_build(Generator._proxied, GeneratorType)
 
diff --git a/third_party/logilab/astng/rebuilder.py b/third_party/logilab/astroid/rebuilder.py
similarity index 81%
rename from third_party/logilab/astng/rebuilder.py
rename to third_party/logilab/astroid/rebuilder.py
index bac7a09..8fa7ee9 100644
--- a/third_party/logilab/astng/rebuilder.py
+++ b/third_party/logilab/astroid/rebuilder.py
@@ -1,28 +1,27 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
 """this module contains utilities for rebuilding a _ast tree in
-order to get a single ASTNG representation
+order to get a single Astroid representation
 """
 
 import sys
-from _ast import (Expr as Discard, Str,
+from _ast import (
+    Expr as Discard, Str,
     # binary operators
     Add, Div, FloorDiv,  Mod, Mult, Pow, Sub, BitAnd, BitOr, BitXor,
     LShift, RShift,
@@ -34,8 +33,7 @@
     Eq, Gt, GtE, In, Is, IsNot, Lt, LtE, NotEq, NotIn,
     )
 
-from logilab.astng.exceptions import ASTNGBuildingException
-from logilab.astng import nodes as new
+from astroid import nodes as new
 
 
 _BIN_OP_CLASSES = {Add: '+',
@@ -49,15 +47,18 @@
                    Pow: '**',
                    Sub: '-',
                    LShift: '<<',
-                   RShift: '>>'}
+                   RShift: '>>',
+                  }
 
 _BOOL_OP_CLASSES = {And: 'and',
-                    Or: 'or'}
+                    Or: 'or',
+                   }
 
 _UNARY_OP_CLASSES = {UAdd: '+',
                      USub: '-',
                      Not: 'not',
-                     Invert: '~'}
+                     Invert: '~',
+                    }
 
 _CMP_OP_CLASSES = {Eq: '==',
                    Gt: '>',
@@ -68,11 +69,13 @@
                    Lt: '<',
                    LtE: '<=',
                    NotEq: '!=',
-                   NotIn: 'not in'}
+                   NotIn: 'not in',
+                  }
 
 CONST_NAME_TRANSFORMS = {'None':  None,
                          'True':  True,
-                         'False': False}
+                         'False': False,
+                        }
 
 REDIRECT = {'arguments': 'Arguments',
             'Attribute': 'Getattr',
@@ -88,7 +91,9 @@
             'ImportFrom': 'From',
             'keyword': 'Keyword',
             'Repr': 'Backquote',
-            }
+           }
+PY3K = sys.version_info >= (3, 0)
+PY34 = sys.version_info >= (3, 4)
 
 def _init_set_doc(node, newnode):
     newnode.doc = None
@@ -116,33 +121,47 @@
         newnode.col_offset = oldnode.col_offset
     newnode.set_line_info(newnode.last_child()) # set_line_info accepts None
 
-
+def _create_yield_node(node, parent, rebuilder, factory):
+    newnode = factory()
+    _lineno_parent(node, newnode, parent)
+    if node.value is not None:
+        newnode.value = rebuilder.visit(node.value, newnode)
+    newnode.set_line_info(newnode.last_child())
+    return newnode
 
 
 class TreeRebuilder(object):
-    """Rebuilds the _ast tree to become an ASTNG tree"""
+    """Rebuilds the _ast tree to become an Astroid tree"""
 
-    _visit_meths = {}
-    def __init__(self):
-        self.init()
-
-    def init(self):
+    def __init__(self, manager):
+        self._manager = manager
         self.asscontext = None
-        self._metaclass = ['']
         self._global_names = []
         self._from_nodes = []
         self._delayed_assattr = []
+        self._visit_meths = {}
+        self._transform = manager.transform
+
+    def visit_module(self, node, modname, package):
+        """visit a Module node by returning a fresh instance of it"""
+        newnode = new.Module(modname, None)
+        newnode.package = package
+        _lineno_parent(node, newnode, parent=None)
+        _init_set_doc(node, newnode)
+        newnode.body = [self.visit(child, newnode) for child in node.body]
+        newnode.set_line_info(newnode.last_child())
+        return self._transform(newnode)
 
     def visit(self, node, parent):
         cls = node.__class__
         if cls in self._visit_meths:
-            return self._visit_meths[cls](node, parent)
+            visit_method = self._visit_meths[cls]
         else:
             cls_name = cls.__name__
             visit_name = 'visit_' + REDIRECT.get(cls_name, cls_name).lower()
             visit_method = getattr(self, visit_name)
             self._visit_meths[cls] = visit_method
-            return visit_method(node, parent)
+        return self._transform(visit_method(node, parent))
 
     def _save_assignment(self, node, name=None):
         """save assignement situation since node.parent is not available yet"""
@@ -160,13 +179,37 @@
         newnode.args = [self.visit(child, newnode) for child in node.args]
         self.asscontext = None
         newnode.defaults = [self.visit(child, newnode) for child in node.defaults]
-        newnode.vararg = node.vararg
-        newnode.kwarg = node.kwarg
+        newnode.kwonlyargs = []
+        newnode.kw_defaults = []
+        vararg, kwarg = node.vararg, node.kwarg
+        # change added in 82732 (7c5c678e4164), vararg and kwarg
+        # are instances of `_ast.arg`, not strings
+        if vararg:
+            if PY34:
+                if vararg.annotation:
+                    newnode.varargannotation = self.visit(vararg.annotation,
+                                                          newnode)
+                vararg = vararg.arg
+            elif PY3K and node.varargannotation:
+                newnode.varargannotation = self.visit(node.varargannotation,
+                                                      newnode)
+        if kwarg:
+            if PY34:
+                if kwarg.annotation:
+                    newnode.kwargannotation = self.visit(kwarg.annotation,
+                                                         newnode)
+                kwarg = kwarg.arg
+            elif PY3K:
+                if node.kwargannotation:
+                    newnode.kwargannotation = self.visit(node.kwargannotation,
+                                                         newnode)
+        newnode.vararg = vararg
+        newnode.kwarg = kwarg
         # save argument names in locals:
-        if node.vararg:
-            newnode.parent.set_local(newnode.vararg, newnode)
-        if node.kwarg:
-            newnode.parent.set_local(newnode.kwarg, newnode)
+        if vararg:
+            newnode.parent.set_local(vararg, newnode)
+        if kwarg:
+            newnode.parent.set_local(kwarg, newnode)
         newnode.set_line_info(newnode.last_child())
         return newnode
 
@@ -202,8 +245,8 @@
         # set some function or metaclass infos  XXX explain ?
         klass = newnode.parent.frame()
         if (isinstance(klass, new.Class)
-            and isinstance(newnode.value, new.CallFunc)
-            and isinstance(newnode.value.func, new.Name)):
+                and isinstance(newnode.value, new.CallFunc)
+                and isinstance(newnode.value.func, new.Name)):
             func_name = newnode.value.func.name
             for ass_node in newnode.targets:
                 try:
@@ -216,9 +259,6 @@
                         meth.extra_decorators.append(newnode.value)
                 except (AttributeError, KeyError):
                     continue
-        elif getattr(newnode.targets[0], 'name', None) == '__metaclass__':
-            # XXX check more...
-            self._metaclass[-1] = 'type' # XXX get the actual metaclass
         newnode.set_line_info(newnode.last_child())
         return newnode
 
@@ -290,8 +330,7 @@
         return newnode
 
     def visit_class(self, node, parent):
-        """visit a Class node to become astng"""
-        self._metaclass.append(self._metaclass[-1])
+        """visit a Class node to become astroid"""
         newnode = new.Class(node.name, None)
         _lineno_parent(node, newnode, parent)
         _init_set_doc(node, newnode)
@@ -300,11 +339,6 @@
         if 'decorator_list' in node._fields and node.decorator_list:# py >= 2.6
             newnode.decorators = self.visit_decorators(node, newnode)
         newnode.set_line_info(newnode.last_child())
-        metaclass = self._metaclass.pop()
-        if not newnode.bases:
-            # no base classes, detect new / style old style according to
-            # current scope
-            newnode._newstyle = metaclass == 'type'
         newnode.parent.frame().set_local(newnode.name, newnode)
         return newnode
 
@@ -326,7 +360,7 @@
         _lineno_parent(node, newnode, parent)
         newnode.left = self.visit(node.left, newnode)
         newnode.ops = [(_CMP_OP_CLASSES[op.__class__], self.visit(expr, newnode))
-                    for (op, expr) in zip(node.ops, node.comparators)]
+                       for (op, expr) in zip(node.ops, node.comparators)]
         newnode.set_line_info(newnode.last_child())
         return newnode
 
@@ -345,13 +379,13 @@
     def visit_decorators(self, node, parent):
         """visit a Decorators node by returning a fresh instance of it"""
         # /!\ node is actually a _ast.Function node while
-        # parent is a astng.nodes.Function node
+        # parent is a astroid.nodes.Function node
         newnode = new.Decorators()
         _lineno_parent(node, newnode, parent)
         if 'decorators' in node._fields: # py < 2.6, i.e. 2.5
             decorators = node.decorators
         else:
-            decorators= node.decorator_list
+            decorators = node.decorator_list
         newnode.nodes = [self.visit(child, newnode) for child in decorators]
         newnode.set_line_info(newnode.last_child())
         return newnode
@@ -371,7 +405,7 @@
         newnode = new.Dict()
         _lineno_parent(node, newnode, parent)
         newnode.items = [(self.visit(key, newnode), self.visit(value, newnode))
-                          for key, value in zip(node.keys, node.values)]
+                         for key, value in zip(node.keys, node.values)]
         newnode.set_line_info(newnode.last_child())
         return newnode
 
@@ -457,14 +491,14 @@
     def visit_from(self, node, parent):
         """visit a From node by returning a fresh instance of it"""
         names = [(alias.name, alias.asname) for alias in node.names]
-        newnode = new.From(node.module or '', names, node.level)
+        newnode = new.From(node.module or '', names, node.level or None)
         _set_infos(node, newnode, parent)
         # store From names to add them to locals after building
         self._from_nodes.append(newnode)
         return newnode
 
     def visit_function(self, node, parent):
-        """visit an Function node to become astng"""
+        """visit an Function node to become astroid"""
         self._global_names.append({})
         newnode = new.Function(node.name, None)
         _lineno_parent(node, newnode, parent)
@@ -478,21 +512,23 @@
         decorators = getattr(node, attr)
         if decorators:
             newnode.decorators = self.visit_decorators(node, newnode)
+        if PY3K and node.returns:
+            newnode.returns = self.visit(node.returns, newnode)
         newnode.set_line_info(newnode.last_child())
         self._global_names.pop()
         frame = newnode.parent.frame()
         if isinstance(frame, new.Class):
             if newnode.name == '__new__':
-                newnode.type = 'classmethod'
+                newnode._type = 'classmethod'
             else:
-                newnode.type = 'method'
+                newnode._type = 'method'
         if newnode.decorators is not None:
             for decorator_expr in newnode.decorators.nodes:
                 if isinstance(decorator_expr, new.Name):
                     if decorator_expr.name in ('classmethod', 'staticmethod'):
-                        newnode.type = decorator_expr.name
+                        newnode._type = decorator_expr.name
                     elif decorator_expr.name == 'classproperty':
-                        newnode.type = 'classmethod'
+                        newnode._type = 'classmethod'
         frame.set_local(newnode.name, newnode)
         return newnode
 
@@ -526,7 +562,7 @@
         return newnode
 
     def visit_global(self, node, parent):
-        """visit an Global node to become astng"""
+        """visit an Global node to become astroid"""
         newnode = new.Global(node.names)
         _set_infos(node, newnode, parent)
         if self._global_names: # global at the module level, no effect
@@ -609,16 +645,6 @@
         newnode.set_line_info(newnode.last_child())
         return newnode
 
-    def visit_module(self, node, modname, package):
-        """visit a Module node by returning a fresh instance of it"""
-        newnode = new.Module(modname, None)
-        newnode.package = package
-        _lineno_parent(node, newnode, parent=None)
-        _init_set_doc(node, newnode)
-        newnode.body = [self.visit(child, newnode) for child in node.body]
-        newnode.set_line_info(newnode.last_child())
-        return newnode
-
     def visit_name(self, node, parent):
         """visit a Name node by returning a fresh instance of it"""
         # True and False can be assigned to something in py2x, so we have to
@@ -700,7 +726,7 @@
         return newnode
 
     def visit_set(self, node, parent):
-        """visit a Tuple node by returning a fresh instance of it"""
+        """visit a Set node by returning a fresh instance of it"""
         newnode = new.Set()
         _lineno_parent(node, newnode, parent)
         newnode.elts = [self.visit(child, newnode) for child in node.elts]
@@ -788,27 +814,23 @@
         return newnode
 
     def visit_with(self, node, parent):
-        """visit a With node by returning a fresh instance of it"""
         newnode = new.With()
         _lineno_parent(node, newnode, parent)
-        newnode.expr = self.visit(node.context_expr, newnode)
+        expr = self.visit(node.context_expr, newnode)
         self.asscontext = "Ass"
         if node.optional_vars is not None:
-            newnode.vars = self.visit(node.optional_vars, newnode)
+            vars = self.visit(node.optional_vars, newnode)
+        else:
+            vars = None
         self.asscontext = None
+        newnode.items = [(expr, vars)]
         newnode.body = [self.visit(child, newnode) for child in node.body]
         newnode.set_line_info(newnode.last_child())
         return newnode
 
     def visit_yield(self, node, parent):
         """visit a Yield node by returning a fresh instance of it"""
-        newnode = new.Yield()
-        _lineno_parent(node, newnode, parent)
-        if node.value is not None:
-            newnode.value = self.visit(node.value, newnode)
-        newnode.set_line_info(newnode.last_child())
-        return newnode
-
+        return _create_yield_node(node, parent, self, new.Yield)
 
 class TreeRebuilder3k(TreeRebuilder):
     """extend and overwrite TreeRebuilder for python3k"""
@@ -816,9 +838,26 @@
     def visit_arg(self, node, parent):
         """visit a arg node by returning a fresh AssName instance"""
         # the <arg> node is coming from py>=3.0, but we use AssName in py2.x
-        # XXX or we should instead introduce a Arg node in astng ?
+        # XXX or we should instead introduce a Arg node in astroid ?
         return self.visit_assname(node, parent, node.arg)
 
+    def visit_nameconstant(self, node, parent):
+        # in Python 3.4 we have NameConstant for True / False / None
+        newnode = new.Const(node.value)
+        _set_infos(node, newnode, parent)
+        return newnode
+
+    def visit_arguments(self, node, parent):
+        newnode = super(TreeRebuilder3k, self).visit_arguments(node, parent)
+        self.asscontext = "Ass"
+        newnode.kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs]
+        self.asscontext = None
+        newnode.kw_defaults = [self.visit(child, newnode) if child else None for child in node.kw_defaults]
+        newnode.annotations = [
+            self.visit(arg.annotation, newnode) if arg.annotation else None
+            for arg in node.args]
+        return newnode
+
     def visit_excepthandler(self, node, parent):
         """visit an ExceptHandler node by returning a fresh instance of it"""
         newnode = new.ExceptHandler()
@@ -857,6 +896,64 @@
         newnode.set_line_info(newnode.last_child())
         return newnode
 
+    def visit_try(self, node, parent):
+        # python 3.3 introduce a new Try node replacing TryFinally/TryExcept nodes
+        if node.finalbody:
+            newnode = new.TryFinally()
+            _lineno_parent(node, newnode, parent)
+            newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody]
+            if node.handlers:
+                excnode = new.TryExcept()
+                _lineno_parent(node, excnode, newnode)
+                excnode.body = [self.visit(child, excnode) for child in node.body]
+                excnode.handlers = [self.visit(child, excnode) for child in node.handlers]
+                excnode.orelse = [self.visit(child, excnode) for child in node.orelse]
+                excnode.set_line_info(excnode.last_child())
+                newnode.body = [excnode]
+            else:
+                newnode.body = [self.visit(child, newnode) for child in node.body]
+        elif node.handlers:
+            newnode = new.TryExcept()
+            _lineno_parent(node, newnode, parent)
+            newnode.body = [self.visit(child, newnode) for child in node.body]
+            newnode.handlers = [self.visit(child, newnode) for child in node.handlers]
+            newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
+        newnode.set_line_info(newnode.last_child())
+        return newnode
+
+    def visit_with(self, node, parent):
+        if 'items' not in node._fields:
+            # python < 3.3
+            return super(TreeRebuilder3k, self).visit_with(node, parent)
+
+        newnode = new.With()
+        _lineno_parent(node, newnode, parent)
+        def visit_child(child):
+            expr = self.visit(child.context_expr, newnode)
+            self.asscontext = 'Ass'
+            if child.optional_vars:
+                var = self.visit(child.optional_vars, newnode)
+            else:
+                var = None
+            self.asscontext = None
+            return expr, var
+        newnode.items = [visit_child(child)
+                         for child in node.items]
+        newnode.body = [self.visit(child, newnode) for child in node.body]
+        newnode.set_line_info(newnode.last_child())
+        return newnode
+
+    def visit_yieldfrom(self, node, parent):
+        return _create_yield_node(node, parent, self, new.YieldFrom)
+
+    def visit_class(self, node, parent):
+        newnode = super(TreeRebuilder3k, self).visit_class(node, parent)
+        newnode._newstyle = True
+        for keyword in node.keywords:
+            if keyword.arg == 'metaclass':
+                newnode._metaclass = self.visit(keyword, newnode).value
+                break
+        return newnode
 
 if sys.version_info >= (3, 0):
     TreeRebuilder = TreeRebuilder3k
diff --git a/third_party/logilab/astng/scoped_nodes.py b/third_party/logilab/astroid/scoped_nodes.py
similarity index 67%
rename from third_party/logilab/astng/scoped_nodes.py
rename to third_party/logilab/astroid/scoped_nodes.py
index 52b9746..eb60298 100644
--- a/third_party/logilab/astng/scoped_nodes.py
+++ b/third_party/logilab/astroid/scoped_nodes.py
@@ -1,22 +1,20 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
 """This module contains the classes for "scoped" node, i.e. which are opening a
 new local scope in the language definition : Module, Class, Function (and
 Lambda, GenExpr, DictComp and SetComp to some extent).
@@ -27,22 +25,28 @@
 
 import sys
 from itertools import chain
+try:
+    from io import BytesIO
+except ImportError:
+    from cStringIO import StringIO as BytesIO
 
 from logilab.common.compat import builtins
-from logilab.common.decorators import cached
+from logilab.common.decorators import cached, cachedproperty
 
-from logilab.astng import BUILTINS_MODULE
-from logilab.astng.exceptions import NotFoundError, NoDefault, \
-     ASTNGBuildingException, InferenceError
-from logilab.astng.node_classes import Const, DelName, DelAttr, \
-     Dict, From, List, Name, Pass, Raise, Return, Tuple, Yield, \
-     are_exclusive, LookupMixIn, const_factory as cf, unpack_infer
-from logilab.astng.bases import NodeNG, InferenceContext, Instance,\
+from astroid.exceptions import NotFoundError, \
+     AstroidBuildingException, InferenceError
+from astroid.node_classes import Const, DelName, DelAttr, \
+     Dict, From, List, Pass, Raise, Return, Tuple, Yield, YieldFrom, \
+     LookupMixIn, const_factory as cf, unpack_infer, Name, CallFunc
+from astroid.bases import NodeNG, InferenceContext, Instance,\
      YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, copy_context, \
-     BUILTINS_NAME
-from logilab.astng.mixins import FilterStmtsMixin
-from logilab.astng.bases import Statement
-from logilab.astng.manager import ASTNGManager
+     BUILTINS
+from astroid.mixins import FilterStmtsMixin
+from astroid.bases import Statement
+from astroid.manager import AstroidManager
+
+ITER_METHODS = ('__iter__', '__getitem__')
+PY3K = sys.version_info >= (3, 0)
 
 
 def remove_nodes(func, cls):
@@ -75,20 +79,20 @@
         return [Dict()] + locals.get(name, [])
     raise NotFoundError(name)
 
-MANAGER = ASTNGManager()
+MANAGER = AstroidManager()
 def builtin_lookup(name):
     """lookup a name into the builtin module
-    return the list of matching statements and the astng for the builtin
+    return the list of matching statements and the astroid for the builtin
     module
     """
-    builtin_astng = MANAGER.astng_from_module(builtins)
+    builtin_astroid = MANAGER.ast_from_module(builtins)
     if name == '__dict__':
-        return builtin_astng, ()
+        return builtin_astroid, ()
     try:
-        stmts = builtin_astng.locals[name]
+        stmts = builtin_astroid.locals[name]
     except KeyError:
         stmts = ()
-    return builtin_astng, stmts
+    return builtin_astroid, stmts
 
 
 # TODO move this Mixin to mixins.py; problem: 'Function' in _scope_lookup
@@ -210,19 +214,24 @@
 # Module  #####################################################################
 
 class Module(LocalsDictNodeNG):
-    _astng_fields = ('body',)
+    _astroid_fields = ('body',)
 
     fromlineno = 0
     lineno = 0
 
     # attributes below are set by the builder module or by raw factories
 
-    # the file from which as been extracted the astng representation. It may
+    # the file from which as been extracted the astroid representation. It may
     # be None if the representation has been built from a built-in module
     file = None
+    # Alternatively, if built from a string/bytes, this can be set
+    file_bytes = None
+    # encoding of python source file, so we can get unicode out of it (python2
+    # only)
+    file_encoding = None
     # the module name
     name = None
-    # boolean for astng built from source (i.e. ast)
+    # boolean for astroid built from source (i.e. ast)
     pure_python = None
     # boolean for package module
     package = None
@@ -230,6 +239,9 @@
     # as value
     globals = None
 
+    # Future imports
+    future_imports = None
+
     # names of python special attributes (handled by getattr impl.)
     special_attributes = set(('__name__', '__doc__', '__file__', '__path__',
                               '__dict__'))
@@ -242,11 +254,14 @@
         self.pure_python = pure_python
         self.locals = self.globals = {}
         self.body = []
+        self.future_imports = set()
 
     @property
     def file_stream(self):
+        if self.file_bytes is not None:
+            return BytesIO(self.file_bytes)
         if self.file is not None:
-            return file(self.file)
+            return open(self.file, 'rb')
         return None
 
     def block_range(self, lineno):
@@ -265,7 +280,7 @@
         return self._scope_lookup(node, name, offset)
 
     def pytype(self):
-        return '%s.module' % BUILTINS_MODULE
+        return '%s.module' % BUILTINS
 
     def display_type(self):
         return 'Module'
@@ -282,7 +297,9 @@
         if self.package:
             try:
                 return [self.import_module(name, relative_only=True)]
-            except ASTNGBuildingException:
+            except AstroidBuildingException:
+                raise NotFoundError(name)
+            except SyntaxError:
                 raise NotFoundError(name)
             except Exception:# XXX pylint tests never pass here; do we need it?
                 import traceback
@@ -336,13 +353,13 @@
             level = 0
         absmodname = self.relative_to_absolute_name(modname, level)
         try:
-            return MANAGER.astng_from_module_name(absmodname)
-        except ASTNGBuildingException:
+            return MANAGER.ast_from_module_name(absmodname)
+        except AstroidBuildingException:
             # we only want to import a sub module or package of this module,
             # skip here
             if relative_only:
                 raise
-        return MANAGER.astng_from_module_name(modname)
+        return MANAGER.ast_from_module_name(modname)
 
     def relative_to_absolute_name(self, modname, level):
         """return the absolute module name for a relative import.
@@ -350,7 +367,7 @@
         The relative import can be implicit or explicit.
         """
         # XXX this returns non sens when called on an absolute import
-        # like 'pylint.checkers.logilab.astng.utils'
+        # like 'pylint.checkers.astroid.utils'
         # XXX doesn't return absolute name if self.name isn't absolute name
         if self.absolute_import_activated() and level is None:
             return modname
@@ -387,7 +404,7 @@
             except AttributeError:
                 return [name for name in living.__dict__.keys()
                         if not name.startswith('_')]
-        # else lookup the astng
+        # else lookup the astroid
         #
         # We separate the different steps of lookup in try/excepts
         # to avoid catching too many Exceptions
@@ -419,7 +436,7 @@
 
 
 class GenExpr(ComprehensionScope):
-    _astng_fields = ('elt', 'generators')
+    _astroid_fields = ('elt', 'generators')
 
     def __init__(self):
         self.locals = {}
@@ -428,7 +445,7 @@
 
 
 class DictComp(ComprehensionScope):
-    _astng_fields = ('key', 'value', 'generators')
+    _astroid_fields = ('key', 'value', 'generators')
 
     def __init__(self):
         self.locals = {}
@@ -438,7 +455,7 @@
 
 
 class SetComp(ComprehensionScope):
-    _astng_fields = ('elt', 'generators')
+    _astroid_fields = ('elt', 'generators')
 
     def __init__(self):
         self.locals = {}
@@ -448,7 +465,7 @@
 
 class _ListComp(NodeNG):
     """class representing a ListComp node"""
-    _astng_fields = ('elt', 'generators')
+    _astroid_fields = ('elt', 'generators')
     elt = None
     generators = None
 
@@ -463,9 +480,85 @@
 
 # Function  ###################################################################
 
+def _infer_decorator_callchain(node):
+    """ Detect decorator call chaining and see if the
+    end result is a static or a classmethod.
+    """
+    current = node
+    while True:
+        if isinstance(current, CallFunc):
+            try:
+                current = current.func.infer().next()
+            except InferenceError:
+                return
+        elif isinstance(current, Function):
+            if not current.parent:
+                return
+            try:
+                # TODO: We don't handle multiple inference results right now,
+                #       because there's no flow to reason when the return
+                #       is what we are looking for, a static or a class method.
+                result = current.infer_call_result(current.parent).next()
+            except (StopIteration, InferenceError):
+                return
+            if isinstance(result, (Function, CallFunc)):
+                current = result
+            else:
+                if isinstance(result, Instance):
+                    result = result._proxied
+                if isinstance(result, Class):
+                    if (result.name == 'classmethod' and
+                            result.root().name == BUILTINS):
+                        return 'classmethod'
+                    elif (result.name == 'staticmethod' and
+                          result.root().name == BUILTINS):
+                        return 'staticmethod'
+                    else:
+                        return
+                else:
+                    # We aren't interested in anything else returned,
+                    # so go back to the function type inference.
+                    return
+        else:
+            return
+
+def _function_type(self):
+    """
+    Function type, possible values are:
+    method, function, staticmethod, classmethod.
+    """
+    # Can't infer that this node is decorated
+    # with a subclass of `classmethod` where `type` is first set,
+    # so do it here.
+    if self.decorators:
+        for node in self.decorators.nodes:
+            if isinstance(node, CallFunc):
+                _type = _infer_decorator_callchain(node)
+                if _type is None:
+                    continue
+                else:
+                    return _type
+            if not isinstance(node, Name):
+                continue
+            try:
+                for infered in node.infer():
+                    if not isinstance(infered, Class):
+                        continue
+                    for ancestor in infered.ancestors():
+                        if isinstance(ancestor, Class):
+                            if (ancestor.name == 'classmethod' and
+                                    ancestor.root().name == BUILTINS):
+                                return 'classmethod'
+                            elif (ancestor.name == 'staticmethod' and
+                                  ancestor.root().name == BUILTINS):
+                                return 'staticmethod'
+            except InferenceError:
+                pass
+    return self._type
+
 
 class Lambda(LocalsDictNodeNG, FilterStmtsMixin):
-    _astng_fields = ('args', 'body',)
+    _astroid_fields = ('args', 'body',)
     name = '<lambda>'
 
     # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod'
@@ -478,8 +571,8 @@
 
     def pytype(self):
         if 'method' in self.type:
-            return '%s.instancemethod' % BUILTINS_MODULE
-        return '%s.function' % BUILTINS_MODULE
+            return '%s.instancemethod' % BUILTINS
+        return '%s.function' % BUILTINS
 
     def display_type(self):
         if 'method' in self.type:
@@ -506,7 +599,7 @@
         return self.body.infer(context)
 
     def scope_lookup(self, node, name, offset=0):
-        if node in self.args.defaults:
+        if node in self.args.defaults or node in self.args.kw_defaults:
             frame = self.parent.frame()
             # line offset to avoid that def func(f=func) resolve the default
             # value to the defined function
@@ -518,13 +611,19 @@
 
 
 class Function(Statement, Lambda):
-    _astng_fields = ('decorators', 'args', 'body')
+    if PY3K:
+        _astroid_fields = ('decorators', 'args', 'body', 'returns')
+        returns = None
+    else:
+        _astroid_fields = ('decorators', 'args', 'body')
 
     special_attributes = set(('__name__', '__doc__', '__dict__'))
     is_function = True
     # attributes below are set by the builder module or by raw factories
     blockstart_tolineno = None
     decorators = None
+    _type = "function"
+    type = cachedproperty(_function_type)
 
     def __init__(self, name, doc):
         self.locals = {}
@@ -540,7 +639,10 @@
         self.fromlineno = self.lineno
         # lineno is the line number of the first decorator, we want the def statement lineno
         if self.decorators is not None:
-            self.fromlineno += len(self.decorators.nodes)
+            self.fromlineno += sum(node.tolineno - node.lineno + 1
+                                   for node in self.decorators.nodes)
+        if self.args.fromlineno < self.fromlineno:
+            self.args.fromlineno = self.fromlineno
         self.tolineno = lastchild.tolineno
         self.blockstart_tolineno = self.args.tolineno
 
@@ -585,10 +687,23 @@
         return self.type == 'classmethod'
 
     def is_abstract(self, pass_is_abstract=True):
-        """return true if the method is abstract
-        It's considered as abstract if the only statement is a raise of
-        NotImplementError, or, if pass_is_abstract, a pass statement
+        """Returns True if the method is abstract.
+
+        A method is considered abstract if
+         - the only statement is 'raise NotImplementedError', or
+         - the only statement is 'pass' and pass_is_abstract is True, or
+         - the method is annotated with abc.astractproperty/abc.abstractmethod
         """
+        if self.decorators:
+            for node in self.decorators.nodes:
+                try:
+                    infered = node.infer().next()
+                except InferenceError:
+                    continue
+                if infered and infered.qname() in ('abc.abstractproperty',
+                                                   'abc.abstractmethod'):
+                    return True
+
         for child_node in self.body:
             if isinstance(child_node, Raise):
                 if child_node.raises_not_implemented():
@@ -604,14 +719,15 @@
         """return true if this is a generator function"""
         # XXX should be flagged, not computed
         try:
-            return self.nodes_of_class(Yield, skip_klass=Function).next()
+            return self.nodes_of_class((Yield, YieldFrom),
+                                       skip_klass=(Function, Lambda)).next()
         except StopIteration:
             return False
 
     def infer_call_result(self, caller, context=None):
         """infer what a function is returning when called"""
         if self.is_generator():
-            yield Generator(self)
+            yield Generator()
             return
         returns = self.nodes_of_class(Return, skip_klass=Function)
         for returnnode in returns:
@@ -639,6 +755,40 @@
 
 # Class ######################################################################
 
+
+def _is_metaclass(klass, seen=None):
+    """ Return if the given class can be
+    used as a metaclass.
+    """
+    if klass.name == 'type':
+        return True
+    if seen is None:
+        seen = set()
+    for base in klass.bases:
+        try:
+            for baseobj in base.infer():
+                if baseobj in seen:
+                    continue
+                else:
+                    seen.add(baseobj)
+                if isinstance(baseobj, Instance):
+                    # not abstract
+                    return False
+                if baseobj is YES:
+                    continue
+                if baseobj is klass:
+                    continue
+                if not isinstance(baseobj, Class):
+                    continue
+                if baseobj._type == 'metaclass':
+                    return True
+                if _is_metaclass(baseobj, seen):
+                    return True
+        except InferenceError:
+            continue
+    return False
+
+
 def _class_type(klass, ancestors=None):
     """return a Class node type to differ metaclass, interface and exception
     from 'regular' classes
@@ -646,7 +796,7 @@
     # XXX we have to store ancestors in case we have a ancestor loop
     if klass._type is not None:
         return klass._type
-    if klass.name == 'type':
+    if _is_metaclass(klass):
         klass._type = 'metaclass'
     elif klass.name.endswith('Interface'):
         klass._type = 'interface'
@@ -662,7 +812,12 @@
         ancestors.add(klass)
         # print >> sys.stderr, '_class_type', repr(klass)
         for base in klass.ancestors(recurs=False):
-            if _class_type(base, ancestors) != 'class':
+            name = _class_type(base, ancestors)
+            if name != 'class':
+                if name == 'metaclass' and not _is_metaclass(klass):
+                    # don't propagate it if the current class
+                    # can't be a metaclass
+                    continue
                 klass._type = base.type
                 break
     if klass._type is None:
@@ -682,7 +837,7 @@
     # by a raw factories
 
     # a dictionary of class instances attributes
-    _astng_fields = ('decorators', 'bases', 'body') # name
+    _astroid_fields = ('decorators', 'bases', 'body') # name
 
     decorators = None
     special_attributes = set(('__name__', '__doc__', '__dict__', '__module__',
@@ -711,6 +866,11 @@
             if base._newstyle_impl(context):
                 self._newstyle = True
                 break
+        klass = self._explicit_metaclass()
+        # could be any callable, we'd need to infer the result of klass(name,
+        # bases, dict).  punt if it's not a class node.
+        if klass is not None and isinstance(klass, Class):
+            self._newstyle = klass._newstyle_impl(context)
         if self._newstyle is None:
             self._newstyle = False
         return self._newstyle
@@ -736,8 +896,8 @@
 
     def pytype(self):
         if self.newstyle:
-            return '%s.type' % BUILTINS_MODULE
-        return '%s.classobj' % BUILTINS_MODULE
+            return '%s.type' % BUILTINS
+        return '%s.classobj' % BUILTINS
 
     def display_type(self):
         return 'Class'
@@ -745,9 +905,36 @@
     def callable(self):
         return True
 
+    def _is_subtype_of(self, type_name):
+        if self.qname() == type_name:
+            return True
+        for anc in self.ancestors():
+            if anc.qname() == type_name:
+                return True
+
     def infer_call_result(self, caller, context=None):
         """infer what a class is returning when called"""
-        yield Instance(self)
+        if self._is_subtype_of('%s.type' % (BUILTINS,)) and len(caller.args) == 3:
+            name_node = caller.args[0].infer().next()
+            if isinstance(name_node, Const) and isinstance(name_node.value, basestring):
+                name = name_node.value
+            else:
+                yield YES
+                return
+            result = Class(name, None)
+            bases = caller.args[1].infer().next()
+            if isinstance(bases, (Tuple, List)):
+                result.bases = bases.itered()
+            else:
+                # There is currently no AST node that can represent an 'unknown'
+                # node (YES is not an AST node), therefore we simply return YES here
+                # although we know at least the name of the class.
+                yield YES
+                return
+            result.parent = caller.parent
+            yield result
+        else:
+            yield Instance(self)
 
     def scope_lookup(self, node, name, offset=0):
         if node in self.bases:
@@ -784,8 +971,11 @@
                 try:
                     for baseobj in stmt.infer(context):
                         if not isinstance(baseobj, Class):
-                            # duh ?
-                            continue
+                            if isinstance(baseobj, Instance):
+                                baseobj = baseobj._proxied
+                            else:
+                                # duh ?
+                                continue
                         if baseobj in yielded:
                             continue # cf xxx above
                         yielded.add(baseobj)
@@ -801,20 +991,20 @@
                     continue
 
     def local_attr_ancestors(self, name, context=None):
-        """return an iterator on astng representation of parent classes
+        """return an iterator on astroid representation of parent classes
         which have <name> defined in their locals
         """
-        for astng in self.ancestors(context=context):
-            if name in astng:
-                yield astng
+        for astroid in self.ancestors(context=context):
+            if name in astroid:
+                yield astroid
 
     def instance_attr_ancestors(self, name, context=None):
-        """return an iterator on astng representation of parent classes
+        """return an iterator on astroid representation of parent classes
         which have <name> defined in their instance attribute dictionary
         """
-        for astng in self.ancestors(context=context):
-            if name in astng.instance_attrs:
-                yield astng
+        for astroid in self.ancestors(context=context):
+            if name in astroid.instance_attrs:
+                yield astroid
 
     def has_base(self, node):
         return node in self.bases
@@ -837,14 +1027,16 @@
     local_attr = remove_nodes(local_attr, DelAttr)
 
     def instance_attr(self, name, context=None):
-        """return the astng nodes associated to name in this class instance
+        """return the astroid nodes associated to name in this class instance
         attributes dictionary and in its parents
 
         :raises `NotFoundError`:
           if no attribute with this name has been find in this class or
           its parent classes
         """
-        values = self.instance_attrs.get(name, [])
+        # Return a copy, so we don't modify self.instance_attrs,
+        # which could lead to infinite loop.
+        values = list(self.instance_attrs.get(name, []))
         # get all values from parents
         for class_node in self.instance_attr_ancestors(name, context):
             values += class_node.instance_attrs[name]
@@ -868,15 +1060,14 @@
         if name in self.special_attributes:
             if name == '__module__':
                 return [cf(self.root().qname())] + values
-            # FIXME : what is expected by passing the list of ancestors to cf:
-            # you can just do [cf(tuple())] + values without breaking any test
+            # FIXME: do we really need the actual list of ancestors?
+            # returning [Tuple()] + values don't break any test
             # this is ticket http://www.logilab.org/ticket/52785
-            if name == '__bases__':
-                return [cf(tuple(self.ancestors(recurs=False, context=context)))] + values
             # XXX need proper meta class handling + MRO implementation
-            if name == '__mro__' and self.newstyle:
-                # XXX mro is read-only but that's not our job to detect that
-                return [cf(tuple(self.ancestors(recurs=True, context=context)))] + values
+            if name == '__bases__' or (name == '__mro__' and self.newstyle):
+                node = Tuple()
+                node.items = self.ancestors(recurs=True, context=context)
+                return [node] + values
             return std_special_attributes(self, name)
         # don't modify the list in self.locals!
         values = list(values)
@@ -928,7 +1119,7 @@
             #if self.newstyle: XXX cause an infinite recursion error
             try:
                 getattribute = self.getattr('__getattribute__', context)[0]
-                if getattribute.root().name != BUILTINS_NAME:
+                if getattribute.root().name != BUILTINS:
                     # class has a custom __getattribute__ defined
                     return True
             except NotFoundError:
@@ -940,8 +1131,8 @@
         its ancestors
         """
         done = {}
-        for astng in chain(iter((self,)), self.ancestors()):
-            for meth in astng.mymethods():
+        for astroid in chain(iter((self,)), self.ancestors()):
+            for meth in astroid.mymethods():
                 if meth.name in done:
                     continue
                 done[meth.name] = None
@@ -975,3 +1166,112 @@
                 yield iface
         if missing:
             raise InferenceError()
+
+    _metaclass = None
+    def _explicit_metaclass(self):
+        """ Return the explicit defined metaclass
+        for the current class.
+
+        An explicit defined metaclass is defined
+        either by passing the ``metaclass`` keyword argument
+        in the class definition line (Python 3) or (Python 2) by
+        having a ``__metaclass__`` class attribute, or if there are
+        no explicit bases but there is a global ``__metaclass__`` variable.
+        """
+        if self._metaclass:
+            # Expects this from Py3k TreeRebuilder
+            try:
+                return next(node for node in self._metaclass.infer()
+                            if node is not YES)
+            except (InferenceError, StopIteration):
+                return None
+        if sys.version_info >= (3, ):
+            return None
+
+        if '__metaclass__' in self.locals:
+            assignment = self.locals['__metaclass__'][-1]
+        elif self.bases:
+            return None
+        elif '__metaclass__' in self.root().locals:
+            assignments = [ass for ass in self.root().locals['__metaclass__']
+                           if ass.lineno < self.lineno]
+            if not assignments:
+                return None
+            assignment = assignments[-1]
+        else:
+            return None
+
+        try:
+            infered = assignment.infer().next()
+        except InferenceError:
+            return
+        if infered is YES: # don't expose this
+            return None
+        return infered
+
+    def metaclass(self):
+        """ Return the metaclass of this class.
+
+        If this class does not define explicitly a metaclass,
+        then the first defined metaclass in ancestors will be used
+        instead.
+        """
+        klass = self._explicit_metaclass()
+        if klass is None:
+            for parent in self.ancestors():
+                klass = parent.metaclass()
+                if klass is not None:
+                    break
+        return klass
+
+    def _islots(self):
+        """ Return an iterator with the inferred slots. """
+        if '__slots__' not in self.locals:
+            return
+        for slots in self.igetattr('__slots__'):
+            # check if __slots__ is a valid type
+            for meth in ITER_METHODS:
+                try:
+                    slots.getattr(meth)
+                    break
+                except NotFoundError:
+                    continue
+            else:
+                continue
+
+            if isinstance(slots, Const):
+                # a string. Ignore the following checks,
+                # but yield the node, only if it has a value
+                if slots.value:
+                    yield slots
+                continue
+            if not hasattr(slots, 'itered'):
+                # we can't obtain the values, maybe a .deque?
+                continue
+
+            if isinstance(slots, Dict):
+                values = [item[0] for item in slots.items]
+            else:
+                values = slots.itered()
+            if values is YES:
+                continue
+
+            for elt in values:
+                try:
+                    for infered in elt.infer():
+                        if infered is YES:
+                            continue
+                        if (not isinstance(infered, Const) or
+                                not isinstance(infered.value, str)):
+                            continue
+                        if not infered.value:
+                            continue
+                        yield infered
+                except InferenceError:
+                    continue
+
+    # Cached, because inferring them all the time is expensive
+    @cached
+    def slots(self):
+        """ Return all the slots for this node. """
+        return list(self._islots())
diff --git a/third_party/logilab/astng/utils.py b/third_party/logilab/astroid/utils.py
similarity index 81%
rename from third_party/logilab/astng/utils.py
rename to third_party/logilab/astroid/utils.py
index ba317c8..936c8b5a 100644
--- a/third_party/logilab/astng/utils.py
+++ b/third_party/logilab/astroid/utils.py
@@ -1,32 +1,31 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:[email protected]
 #
-# This file is part of logilab-astng.
+# This file is part of astroid.
 #
-# logilab-astng is free software: you can redistribute it and/or modify it
+# astroid is free software: you can redistribute it and/or modify it
 # under the terms of the GNU Lesser General Public License as published by the
 # Free Software Foundation, either version 2.1 of the License, or (at your
 # option) any later version.
 #
-# logilab-astng is distributed in the hope that it will be useful, but
+# astroid is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 # for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
+# with astroid. If not, see <http://www.gnu.org/licenses/>.
 """this module contains some utilities to navigate in the tree or to
 extract information from it
 """
 
 __docformat__ = "restructuredtext en"
 
-from logilab.astng.exceptions import ASTNGBuildingException
+from astroid.exceptions import AstroidBuildingException
+from astroid.builder import parse
 
 
-class ASTWalker:
+class ASTWalker(object):
     """a walker visiting a tree in preorder, calling on the handler:
 
     * visit_<class name> on entering a node, where class name is the class of
@@ -99,7 +98,7 @@
         if methods[0] is not None:
             methods[0](node)
         if 'locals' in node.__dict__: # skip Instance and other proxy
-            for name, local_node in node.items():
+            for local_node in node.values():
                 self.visit(local_node)
         if methods[1] is not None:
             return methods[1](node)
@@ -113,27 +112,25 @@
             print "Hm, child of %s is None" % node
             continue
         if not hasattr(child, 'parent'):
-            print " ERROR: %s has child %s %x with no parent" % (node, child, id(child))
+            print " ERROR: %s has child %s %x with no parent" % (
+                node, child, id(child))
         elif not child.parent:
-            print " ERROR: %s has child %s %x with parent %r" % (node, child, id(child), child.parent)
+            print " ERROR: %s has child %s %x with parent %r" % (
+                node, child, id(child), child.parent)
         elif child.parent is not node:
-            print " ERROR: %s %x has child %s %x with wrong parent %s" % (node,
-                                      id(node), child, id(child), child.parent)
+            print " ERROR: %s %x has child %s %x with wrong parent %s" % (
+                node, id(node), child, id(child), child.parent)
         else:
             ok = True
         if not ok:
             print "lines;", node.lineno, child.lineno
             print "of module", node.root(), node.root().name
-            raise ASTNGBuildingException
+            raise AstroidBuildingException
         _check_children(child)
 
 
-from _ast import PyCF_ONLY_AST
-def parse(string):
-    return compile(string, "<string>", 'exec', PyCF_ONLY_AST)
-
 class TreeTester(object):
-    '''A helper class to see _ast tree and compare with astng tree
+    '''A helper class to see _ast tree and compare with astroid tree
 
     indent: string for tree indent representation
     lineno: bool to tell if we should print the line numbers
@@ -146,11 +143,11 @@
     .   <Print>
     .   .   nl = True
     .   ]
-    >>> print tester.astng_tree_repr()
+    >>> print tester.astroid_tree_repr()
     Module()
         body = [
         Print()
-            dest = 
+            dest =
             values = [
             ]
         ]
@@ -185,8 +182,8 @@
         if _done is None:
             _done = set()
         if node in _done:
-            self._string += '\nloop in tree: %r (%s)' % (node,
-                                            getattr(node, 'lineno', None))
+            self._string += '\nloop in tree: %r (%s)' % (
+                node, getattr(node, 'lineno', None))
             return
         _done.add(node)
         self._string += '\n' + indent +  '<%s>' % node.__class__.__name__
@@ -202,7 +199,7 @@
                     continue
                 if a in ("lineno", "col_offset") and not self.lineno:
                     continue
-                self._string +='\n' +  indent + a + " = " + repr(attr)
+                self._string += '\n' +  indent + a + " = " + repr(attr)
         for field in node._fields or ():
             attr = node_dict[field]
             if attr is None:
@@ -224,16 +221,16 @@
                 self._string += '\n' + indent + field + " = " + repr(attr)
 
 
-    def build_astng_tree(self):
-        """build astng tree from the _ast tree
+    def build_astroid_tree(self):
+        """build astroid tree from the _ast tree
         """
-        from logilab.astng.builder import ASTNGBuilder
-        tree = ASTNGBuilder().string_build(self.sourcecode)
+        from astroid.builder import AstroidBuilder
+        tree = AstroidBuilder().string_build(self.sourcecode)
         return tree
 
-    def astng_tree_repr(self, ids=False):
-        """build the astng tree and return a nice tree representation"""
-        mod = self.build_astng_tree()
+    def astroid_tree_repr(self, ids=False):
+        """build the astroid tree and return a nice tree representation"""
+        mod = self.build_astroid_tree()
         return mod.repr_tree(ids)
 
 
diff --git a/third_party/logilab/common/LICENSE.txt b/third_party/logilab/common/LICENSE.txt
index b7b5f53..d511905 100644
--- a/third_party/logilab/common/LICENSE.txt
+++ b/third_party/logilab/common/LICENSE.txt
@@ -1,8 +1,8 @@
 		    GNU GENERAL PUBLIC LICENSE
 		       Version 2, June 1991
 
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
@@ -15,7 +15,7 @@
 General Public License applies to most of the Free Software
 Foundation's software and to any other program whose authors commit to
 using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
+the GNU Lesser General Public License instead.)  You can apply it to
 your programs, too.
 
   When we speak of free software, we are referring to freedom, not
@@ -55,7 +55,7 @@
 
   The precise terms and conditions for copying, distribution and
 modification follow.
-
+
 		    GNU GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
@@ -110,7 +110,7 @@
     License.  (Exception: if the Program itself is interactive but
     does not normally print such an announcement, your work based on
     the Program is not required to print an announcement.)
-
+
 These requirements apply to the modified work as a whole.  If
 identifiable sections of that work are not derived from the Program,
 and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@
 access to copy the source code from the same place counts as
 distribution of the source code, even though third parties are not
 compelled to copy the source along with the object code.
-
+
   4. You may not copy, modify, sublicense, or distribute the Program
 except as expressly provided under this License.  Any attempt
 otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@
 
 This section is intended to make thoroughly clear what is believed to
 be a consequence of the rest of this License.
-
+
   8. If the distribution and/or use of the Program is restricted in
 certain countries either by patents or by copyrighted interfaces, the
 original copyright holder who places the Program under this License
@@ -278,7 +278,7 @@
 POSSIBILITY OF SUCH DAMAGES.
 
 		     END OF TERMS AND CONDITIONS
-
+
 	    How to Apply These Terms to Your New Programs
 
   If you develop a new program, and you want it to be of the greatest
@@ -303,17 +303,16 @@
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
 
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 Also add information on how to contact you by electronic and paper mail.
 
 If the program is interactive, make it output a short notice like this
 when it starts in an interactive mode:
 
-    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision version 69, Copyright (C) year name of author
     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
     This is free software, and you are welcome to redistribute it
     under certain conditions; type `show c' for details.
@@ -336,5 +335,5 @@
 This General Public License does not permit incorporating your program into
 proprietary programs.  If your program is a subroutine library, you may
 consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
+library.  If this is what you want to do, use the GNU Lesser General
 Public License instead of this License.
diff --git a/third_party/logilab/common/README.chromium b/third_party/logilab/common/README.chromium
index 98e5135..64683f8 100644
--- a/third_party/logilab/common/README.chromium
+++ b/third_party/logilab/common/README.chromium
@@ -1,5 +1,5 @@
 URL: http://www.logilab.org/project/logilab-common
-Version: 0.57.1
+Version: 0.63.1
 License: GPL
 License File: LICENSE.txt
 
diff --git a/third_party/logilab/common/__init__.py b/third_party/logilab/common/__init__.py
index 8d063e2..2d1324c 100644
--- a/third_party/logilab/common/__init__.py
+++ b/third_party/logilab/common/__init__.py
@@ -25,6 +25,9 @@
 :var IGNORED_EXTENSIONS: file extensions that may usually be ignored
 """
 __docformat__ = "restructuredtext en"
+
+from six.moves import range
+
 from logilab.common.__pkginfo__ import version as __version__
 
 STD_BLACKLIST = ('CVS', '.svn', '.hg', 'debian', 'dist', 'build')
@@ -57,8 +60,9 @@
 class nullobject(object):
     def __repr__(self):
         return '<nullobject>'
-    def __nonzero__(self):
+    def __bool__(self):
         return False
+    __nonzero__ = __bool__
 
 class tempattr(object):
     def __init__(self, obj, attr, value):
diff --git a/third_party/logilab/common/__pkginfo__.py b/third_party/logilab/common/__pkginfo__.py
index 83a43cf..ce0faa9 100644
--- a/third_party/logilab/common/__pkginfo__.py
+++ b/third_party/logilab/common/__pkginfo__.py
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This file is part of logilab-common.
@@ -18,19 +18,19 @@
 """logilab.common packaging information"""
 __docformat__ = "restructuredtext en"
 import sys
+import os
 
 distname = 'logilab-common'
 modname = 'common'
 subpackage_of = 'logilab'
 subpackage_master = True
 
-numversion = (0, 57, 1)
+numversion = (0, 63, 0)
 version = '.'.join([str(num) for num in numversion])
 
 license = 'LGPL' # 2.1 or later
 description = "collection of low-level Python packages and modules used by Logilab projects"
 web = "http://www.logilab.org/project/%s" % distname
-ftp = "ftp://ftp.logilab.org/pub/%s" % modname
 mailinglist = "mailto://[email protected]"
 author = "Logilab"
 author_email = "[email protected]"
@@ -40,6 +40,16 @@
 scripts = [join('bin', 'pytest')]
 include_dirs = [join('test', 'data')]
 
+install_requires = [
+        'six >= 1.4.0',
+        ]
 if sys.version_info < (2, 7):
-    install_requires = ['unittest2 >= 0.5.1']
+    install_requires.append('unittest2 >= 0.5.1')
+if os.name == 'nt':
+    install_requires.append('colorama')
 
+classifiers = ["Topic :: Utilities",
+               "Programming Language :: Python",
+               "Programming Language :: Python :: 2",
+               "Programming Language :: Python :: 3",
+               ]
diff --git a/third_party/logilab/common/changelog.py b/third_party/logilab/common/changelog.py
index 74f5124..2fff2ed 100644
--- a/third_party/logilab/common/changelog.py
+++ b/third_party/logilab/common/changelog.py
@@ -49,6 +49,8 @@
 import sys
 from stat import S_IWRITE
 
+from six import string_types
+
 BULLET = '*'
 SUBBULLET = '-'
 INDENT = ' ' * 4
@@ -64,7 +66,7 @@
     correctly printing it as X.Y.Z
     """
     def __new__(cls, versionstr):
-        if isinstance(versionstr, basestring):
+        if isinstance(versionstr, string_types):
             versionstr = versionstr.strip(' :') # XXX (syt) duh?
             parsed = cls.parse(versionstr)
         else:
@@ -76,7 +78,7 @@
         versionstr = versionstr.strip(' :')
         try:
             return [int(i) for i in versionstr.split('.')]
-        except ValueError, ex:
+        except ValueError as ex:
             raise ValueError("invalid literal for version '%s' (%s)"%(versionstr, ex))
 
     def __str__(self):
diff --git a/third_party/logilab/common/clcommands.py b/third_party/logilab/common/clcommands.py
index 411931b..4778b99 100644
--- a/third_party/logilab/common/clcommands.py
+++ b/third_party/logilab/common/clcommands.py
@@ -22,6 +22,8 @@
 command'specific
 """
 
+from __future__ import print_function
+
 __docformat__ = "restructuredtext en"
 
 import sys
@@ -115,7 +117,7 @@
         if arg in ('-h', '--help'):
             self.usage_and_exit(0)
         if self.version is not None and arg in ('--version'):
-            print self.version
+            print(self.version)
             sys.exit(0)
         rcfile = self.rcfile
         if rcfile is not None and arg in ('-C', '--rc-file'):
@@ -127,21 +129,21 @@
         try:
             command = self.get_command(arg)
         except KeyError:
-            print 'ERROR: no %s command' % arg
-            print
+            print('ERROR: no %s command' % arg)
+            print()
             self.usage_and_exit(1)
         try:
             sys.exit(command.main_run(args, rcfile))
-        except KeyboardInterrupt, exc:
-            print 'Interrupted',
+        except KeyboardInterrupt as exc:
+            print('Interrupted', end=' ')
             if str(exc):
-                print ': %s' % exc,
-            print
+                print(': %s' % exc, end=' ')
+            print()
             sys.exit(4)
-        except BadCommandUsage, err:
-            print 'ERROR:', err
-            print
-            print command.help()
+        except BadCommandUsage as err:
+            print('ERROR:', err)
+            print()
+            print(command.help())
             sys.exit(1)
 
     def create_logger(self, handler, logthreshold=None):
@@ -164,32 +166,32 @@
         """display usage for the main program (i.e. when no command supplied)
         and exit
         """
-        print 'usage:', self.pgm,
+        print('usage:', self.pgm, end=' ')
         if self.rcfile:
-            print '[--rc-file=<configuration file>]',
-        print '<command> [options] <command argument>...'
+            print('[--rc-file=<configuration file>]', end=' ')
+        print('<command> [options] <command argument>...')
         if self.doc:
-            print '\n%s' % self.doc
-        print  '''
+            print('\n%s' % self.doc)
+        print('''
 Type "%(pgm)s <command> --help" for more information about a specific
-command. Available commands are :\n''' % self.__dict__
+command. Available commands are :\n''' % self.__dict__)
         max_len = max([len(cmd) for cmd in self])
         padding = ' ' * max_len
         for cmdname, cmd in sorted(self.items()):
             if not cmd.hidden:
-                print ' ', (cmdname + padding)[:max_len], cmd.short_description()
+                print(' ', (cmdname + padding)[:max_len], cmd.short_description())
         if self.rcfile:
-            print '''
+            print('''
 Use --rc-file=<configuration file> / -C <configuration file> before the command
 to specify a configuration file. Default to %s.
-''' % self.rcfile
-        print  '''%(pgm)s -h/--help
-      display this usage information and exit''' % self.__dict__
+''' % self.rcfile)
+        print('''%(pgm)s -h/--help
+      display this usage information and exit''' % self.__dict__)
         if self.version:
-            print  '''%(pgm)s -v/--version
-      display version configuration and exit''' % self.__dict__
+            print('''%(pgm)s -v/--version
+      display version configuration and exit''' % self.__dict__)
         if self.copyright:
-            print '\n', self.copyright
+            print('\n', self.copyright)
 
     def usage_and_exit(self, status):
         self.usage()
@@ -261,7 +263,7 @@
         try:
             self.check_args(args)
             self.run(args)
-        except CommandError, err:
+        except CommandError as err:
             self.logger.error(err)
             return 2
         return 0
@@ -283,14 +285,14 @@
             command = args.pop()
             cmd = _COMMANDS[command]
             for optname, optdict in cmd.options:
-                print '--help'
-                print '--' + optname
+                print('--help')
+                print('--' + optname)
         else:
             commands = sorted(_COMMANDS.keys())
             for command in commands:
                 cmd = _COMMANDS[command]
                 if not cmd.hidden:
-                    print command
+                    print(command)
 
 
 # deprecated stuff #############################################################
diff --git a/third_party/logilab/common/cli.py b/third_party/logilab/common/cli.py
index 4283732..0ad4e4c 100644
--- a/third_party/logilab/common/cli.py
+++ b/third_party/logilab/common/cli.py
@@ -44,7 +44,8 @@
 
 __docformat__ = "restructuredtext en"
 
-from logilab.common.compat import raw_input, builtins
+from six.moves import builtins, input
+
 if not hasattr(builtins, '_'):
     builtins._ = str
 
@@ -107,7 +108,7 @@
         """loop on user input, exit on EOF"""
         while True:
             try:
-                line = raw_input('>>> ')
+                line = input('>>> ')
             except EOFError:
                 print
                 break
@@ -194,7 +195,7 @@
                     import traceback
                     traceback.print_exc()
                     print 'ERROR in help method %s'% (
-                        command_help_method.func_name)
+                        command_help_method.__name__)
 
     help_do_help = ("help", "help [topic|command]",
                     _("print help message for the given topic/command or \
diff --git a/third_party/logilab/common/compat.py b/third_party/logilab/common/compat.py
index 8983ece..f2eb590 100644
--- a/third_party/logilab/common/compat.py
+++ b/third_party/logilab/common/compat.py
@@ -26,7 +26,6 @@
     :mod:`unittest2.compatibility`
 """
 
-from __future__ import generators
 
 __docformat__ = "restructuredtext en"
 
@@ -35,7 +34,8 @@
 import types
 from warnings import warn
 
-import __builtin__ as builtins # 2to3 will tranform '__builtin__' to 'builtins'
+# not used here, but imported to preserve API
+from six.moves import builtins
 
 if sys.version_info < (3, 0):
     str_to_bytes = str
@@ -51,15 +51,6 @@
     def str_encode(string, encoding):
         return str(string)
 
-# XXX callable built-in seems back in all python versions
-try:
-    callable = builtins.callable
-except AttributeError:
-    from collections import Callable
-    def callable(something):
-        return isinstance(something, Callable)
-    del Callable
-
 # See also http://bugs.python.org/issue11776
 if sys.version_info[0] == 3:
     def method_type(callable, instance, klass):
@@ -69,11 +60,6 @@
     # alias types otherwise
     method_type = types.MethodType
 
-if sys.version_info < (3, 0):
-    raw_input = raw_input
-else:
-    raw_input = input
-
 # Pythons 2 and 3 differ on where to get StringIO
 if sys.version_info < (3, 0):
     from cStringIO import StringIO
@@ -84,160 +70,9 @@
     from io import FileIO, BytesIO, StringIO
     from imp import reload
 
-# Where do pickles come from?
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-
 from logilab.common.deprecation import deprecated
 
-from itertools import izip, chain, imap
-if sys.version_info < (3, 0):# 2to3 will remove the imports
-    izip = deprecated('izip exists in itertools since py2.3')(izip)
-    imap = deprecated('imap exists in itertools since py2.3')(imap)
-chain = deprecated('chain exists in itertools since py2.3')(chain)
-
-sum = deprecated('sum exists in builtins since py2.3')(sum)
-enumerate = deprecated('enumerate exists in builtins since py2.3')(enumerate)
-frozenset = deprecated('frozenset exists in builtins since py2.4')(frozenset)
-reversed = deprecated('reversed exists in builtins since py2.4')(reversed)
-sorted = deprecated('sorted exists in builtins since py2.4')(sorted)
-max = deprecated('max exists in builtins since py2.4')(max)
-
-
-# Python2.5 builtins
-try:
-    any = any
-    all = all
-except NameError:
-    def any(iterable):
-        """any(iterable) -> bool
-
-        Return True if bool(x) is True for any x in the iterable.
-        """
-        for elt in iterable:
-            if elt:
-                return True
-        return False
-
-    def all(iterable):
-        """all(iterable) -> bool
-
-        Return True if bool(x) is True for all values x in the iterable.
-        """
-        for elt in iterable:
-            if not elt:
-                return False
-        return True
-
-
-# Python2.5 subprocess added functions and exceptions
-try:
-    from subprocess import Popen
-except ImportError:
-    # gae or python < 2.3
-
-    class CalledProcessError(Exception):
-        """This exception is raised when a process run by check_call() returns
-        a non-zero exit status.  The exit status will be stored in the
-        returncode attribute."""
-        def __init__(self, returncode, cmd):
-            self.returncode = returncode
-            self.cmd = cmd
-        def __str__(self):
-            return "Command '%s' returned non-zero exit status %d" % (self.cmd,
-    self.returncode)
-
-    def call(*popenargs, **kwargs):
-        """Run command with arguments.  Wait for command to complete, then
-        return the returncode attribute.
-
-        The arguments are the same as for the Popen constructor.  Example:
-
-        retcode = call(["ls", "-l"])
-        """
-        # workaround: subprocess.Popen(cmd, stdout=sys.stdout) fails
-        # see http://bugs.python.org/issue1531862
-        if "stdout" in kwargs:
-            fileno = kwargs.get("stdout").fileno()
-            del kwargs['stdout']
-            return Popen(stdout=os.dup(fileno), *popenargs, **kwargs).wait()
-        return Popen(*popenargs, **kwargs).wait()
-
-    def check_call(*popenargs, **kwargs):
-        """Run command with arguments.  Wait for command to complete.  If
-        the exit code was zero then return, otherwise raise
-        CalledProcessError.  The CalledProcessError object will have the
-        return code in the returncode attribute.
-
-        The arguments are the same as for the Popen constructor.  Example:
-
-        check_call(["ls", "-l"])
-        """
-        retcode = call(*popenargs, **kwargs)
-        cmd = kwargs.get("args")
-        if cmd is None:
-            cmd = popenargs[0]
-        if retcode:
-            raise CalledProcessError(retcode, cmd)
-        return retcode
-
-try:
-    from os.path import relpath
-except ImportError: # python < 2.6
-    from os.path import curdir, abspath, sep, commonprefix, pardir, join
-    def relpath(path, start=curdir):
-        """Return a relative version of a path"""
-
-        if not path:
-            raise ValueError("no path specified")
-
-        start_list = abspath(start).split(sep)
-        path_list = abspath(path).split(sep)
-
-        # Work out how much of the filepath is shared by start and path.
-        i = len(commonprefix([start_list, path_list]))
-
-        rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
-        if not rel_list:
-            return curdir
-        return join(*rel_list)
-
-
-# XXX don't know why tests don't pass if I don't do that :
-_real_set, set = set, deprecated('set exists in builtins since py2.4')(set)
-if (2, 5) <= sys.version_info[:2]:
-    InheritableSet = _real_set
-else:
-    class InheritableSet(_real_set):
-        """hacked resolving inheritancy issue from old style class in 2.4"""
-        def __new__(cls, *args, **kwargs):
-            if args:
-                new_args = (args[0], )
-            else:
-                new_args = ()
-            obj = _real_set.__new__(cls, *new_args)
-            obj.__init__(*args, **kwargs)
-            return obj
-
-# XXX shouldn't we remove this and just let 2to3 do his job ?
-# range or xrange?
-try:
-    range = xrange
-except NameError:
-    range = range
-
-# ConfigParser was renamed to the more-standard configparser
-try:
-    import configparser
-except ImportError:
-    import ConfigParser as configparser
-
-try:
-    import json
-except ImportError:
-    try:
-        import simplejson as json
-    except ImportError:
-        json = None
+# Other projects import these from here, keep providing them for
+# backwards compat
+any = deprecated('use builtin "any"')(any)
+all = deprecated('use builtin "all"')(all)
diff --git a/third_party/logilab/common/configuration.py b/third_party/logilab/common/configuration.py
index 0eafa10..1371c76 100644
--- a/third_party/logilab/common/configuration.py
+++ b/third_party/logilab/common/configuration.py
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This file is part of logilab-common.
@@ -96,8 +96,19 @@
   multiple=4,5,6
 
   number=3
-  >>>
+
+  Note : starting with Python 2.7 ConfigParser is able to take into
+  account the order of occurrences of the options into a file (by
+  using an OrderedDict). If you have two options changing some common
+  state, like a 'disable-all-stuff' and a 'enable-some-stuff-a', their
+  order of appearance will be significant : the last specified in the
+  file wins. For earlier version of python and logilab.common newer
+  than 0.61 the behaviour is unspecified.
+
 """
+
+from __future__ import print_function
+
 __docformat__ = "restructuredtext en"
 
 __all__ = ('OptionsManagerMixIn', 'OptionsProviderMixIn',
@@ -109,16 +120,17 @@
 import re
 from os.path import exists, expanduser
 from copy import copy
-from ConfigParser import ConfigParser, NoOptionError, NoSectionError, \
-     DuplicateSectionError
 from warnings import warn
 
-from logilab.common.compat import callable, raw_input, str_encode as _encode
+from six import string_types
+from six.moves import range, configparser as cp, input
 
+from logilab.common.compat import str_encode as _encode
+from logilab.common.deprecation import deprecated
 from logilab.common.textutils import normalize_text, unquote
-from logilab.common import optik_ext as optparse
+from logilab.common import optik_ext
 
-OptionError = optparse.OptionError
+OptionError = optik_ext.OptionError
 
 REQUIRED = []
 
@@ -136,63 +148,66 @@
 
 # validation functions ########################################################
 
+# validators will return the validated value or raise optparse.OptionValueError
+# XXX add to documentation
+
 def choice_validator(optdict, name, value):
     """validate and return a converted value for option of type 'choice'
     """
     if not value in optdict['choices']:
         msg = "option %s: invalid value: %r, should be in %s"
-        raise optparse.OptionValueError(msg % (name, value, optdict['choices']))
+        raise optik_ext.OptionValueError(msg % (name, value, optdict['choices']))
     return value
 
 def multiple_choice_validator(optdict, name, value):
     """validate and return a converted value for option of type 'choice'
     """
     choices = optdict['choices']
-    values = optparse.check_csv(None, name, value)
+    values = optik_ext.check_csv(None, name, value)
     for value in values:
         if not value in choices:
             msg = "option %s: invalid value: %r, should be in %s"
-            raise optparse.OptionValueError(msg % (name, value, choices))
+            raise optik_ext.OptionValueError(msg % (name, value, choices))
     return values
 
 def csv_validator(optdict, name, value):
     """validate and return a converted value for option of type 'csv'
     """
-    return optparse.check_csv(None, name, value)
+    return optik_ext.check_csv(None, name, value)
 
 def yn_validator(optdict, name, value):
     """validate and return a converted value for option of type 'yn'
     """
-    return optparse.check_yn(None, name, value)
+    return optik_ext.check_yn(None, name, value)
 
 def named_validator(optdict, name, value):
     """validate and return a converted value for option of type 'named'
     """
-    return optparse.check_named(None, name, value)
+    return optik_ext.check_named(None, name, value)
 
 def file_validator(optdict, name, value):
     """validate and return a filepath for option of type 'file'"""
-    return optparse.check_file(None, name, value)
+    return optik_ext.check_file(None, name, value)
 
 def color_validator(optdict, name, value):
     """validate and return a valid color for option of type 'color'"""
-    return optparse.check_color(None, name, value)
+    return optik_ext.check_color(None, name, value)
 
 def password_validator(optdict, name, value):
     """validate and return a string for option of type 'password'"""
-    return optparse.check_password(None, name, value)
+    return optik_ext.check_password(None, name, value)
 
 def date_validator(optdict, name, value):
     """validate and return a mx DateTime object for option of type 'date'"""
-    return optparse.check_date(None, name, value)
+    return optik_ext.check_date(None, name, value)
 
 def time_validator(optdict, name, value):
     """validate and return a time object for option of type 'time'"""
-    return optparse.check_time(None, name, value)
+    return optik_ext.check_time(None, name, value)
 
 def bytes_validator(optdict, name, value):
     """validate and return an integer for option of type 'bytes'"""
-    return optparse.check_bytes(None, name, value)
+    return optik_ext.check_bytes(None, name, value)
 
 
 VALIDATORS = {'string': unquote,
@@ -222,14 +237,18 @@
     except TypeError:
         try:
             return VALIDATORS[opttype](value)
-        except optparse.OptionValueError:
+        except optik_ext.OptionValueError:
             raise
         except:
-            raise optparse.OptionValueError('%s value (%r) should be of type %s' %
+            raise optik_ext.OptionValueError('%s value (%r) should be of type %s' %
                                    (option, value, opttype))
 
 # user input functions ########################################################
 
+# user input functions will ask the user for input on stdin then validate
+# the result and return the validated value or raise optparse.OptionValueError
+# XXX add to documentation
+
 def input_password(optdict, question='password:'):
     from getpass import getpass
     while True:
@@ -237,23 +256,23 @@
         value2 = getpass('confirm: ')
         if value == value2:
             return value
-        print 'password mismatch, try again'
+        print('password mismatch, try again')
 
 def input_string(optdict, question):
-    value = raw_input(question).strip()
+    value = input(question).strip()
     return value or None
 
 def _make_input_function(opttype):
     def input_validator(optdict, question):
         while True:
-            value = raw_input(question)
+            value = input(question)
             if not value.strip():
                 return None
             try:
                 return _call_validator(opttype, optdict, None, value)
-            except optparse.OptionValueError, ex:
+            except optik_ext.OptionValueError as ex:
                 msg = str(ex).split(':', 1)[-1].strip()
-                print 'bad value: %s' % msg
+                print('bad value: %s' % msg)
     return input_validator
 
 INPUT_FUNCTIONS = {
@@ -264,6 +283,8 @@
 for opttype in VALIDATORS.keys():
     INPUT_FUNCTIONS.setdefault(opttype, _make_input_function(opttype))
 
+# utility functions ############################################################
+
 def expand_default(self, option):
     """monkey patch OptionParser.expand_default since we have a particular
     way to handle defaults to avoid overriding values in the configuration
@@ -278,15 +299,15 @@
         value = None
     else:
         optdict = provider.get_option_def(optname)
-        optname = provider.option_name(optname, optdict)
+        optname = provider.option_attrname(optname, optdict)
         value = getattr(provider.config, optname, optdict)
         value = format_option_value(optdict, value)
-    if value is optparse.NO_DEFAULT or not value:
+    if value is optik_ext.NO_DEFAULT or not value:
         value = self.NO_DEFAULT_VALUE
     return option.help.replace(self.default_tag, str(value))
 
 
-def convert(value, optdict, name=''):
+def _validate(value, optdict, name=''):
     """return a validated value for an option according to its type
 
     optional argument name is only used for error message formatting
@@ -297,6 +318,9 @@
         # FIXME
         return value
     return _call_validator(_type, optdict, name, value)
+convert = deprecated('[0.60] convert() was renamed _validate()')(_validate)
+
+# format and output functions ##################################################
 
 def comment(string):
     """return string as a comment"""
@@ -346,7 +370,7 @@
         value = value.pattern
     elif optdict.get('type') == 'yn':
         value = value and 'yes' or 'no'
-    elif isinstance(value, (str, unicode)) and value.isspace():
+    elif isinstance(value, string_types) and value.isspace():
         value = "'%s'" % value
     elif optdict.get('type') == 'time' and isinstance(value, (float, int, long)):
         value = format_time(value)
@@ -358,8 +382,8 @@
     """format an options section using the INI format"""
     encoding = _get_encoding(encoding, stream)
     if doc:
-        print >> stream, _encode(comment(doc), encoding)
-    print >> stream, '[%s]' % section
+        print(_encode(comment(doc), encoding), file=stream)
+    print('[%s]' % section, file=stream)
     ini_format(stream, options, encoding)
 
 def ini_format(stream, options, encoding):
@@ -369,20 +393,20 @@
         help = optdict.get('help')
         if help:
             help = normalize_text(help, line_len=79, indent='# ')
-            print >> stream
-            print >> stream, _encode(help, encoding)
+            print(file=stream)
+            print(_encode(help, encoding), file=stream)
         else:
-            print >> stream
+            print(file=stream)
         if value is None:
-            print >> stream, '#%s=' % optname
+            print('#%s=' % optname, file=stream)
         else:
             value = _encode(value, encoding).strip()
-            print >> stream, '%s=%s' % (optname, value)
+            print('%s=%s' % (optname, value), file=stream)
 
 format_section = ini_format_section
 
 def rest_format_section(stream, section, options, encoding=None, doc=None):
-    """format an options section using the INI format"""
+    """format an options section using as ReST formatted output"""
     encoding = _get_encoding(encoding, stream)
     if section:
         print >> stream, '%s\n%s' % (section, "'"*len(section))
@@ -401,6 +425,7 @@
             print >> stream, ''
             print >> stream, '  Default: ``%s``' % value.replace("`` ", "```` ``")
 
+# Options Manager ##############################################################
 
 class OptionsManagerMixIn(object):
     """MixIn to handle a configuration from both a configuration file and
@@ -423,9 +448,9 @@
 
     def reset_parsers(self, usage='', version=None):
         # configuration file parser
-        self.cfgfile_parser = ConfigParser()
+        self.cfgfile_parser = cp.ConfigParser()
         # command line parser
-        self.cmdline_parser = optparse.OptionParser(usage=usage, version=version)
+        self.cmdline_parser = optik_ext.OptionParser(usage=usage, version=version)
         self.cmdline_parser.options_manager = self
         self._optik_option_attrs = set(self.cmdline_parser.option_class.ATTRS)
 
@@ -461,7 +486,7 @@
         if group_name in self._mygroups:
             group = self._mygroups[group_name]
         else:
-            group = optparse.OptionGroup(self.cmdline_parser,
+            group = optik_ext.OptionGroup(self.cmdline_parser,
                                          title=group_name.capitalize())
             self.cmdline_parser.add_option_group(group)
             group.level = provider.level
@@ -497,9 +522,9 @@
         # default is handled here and *must not* be given to optik if you
         # want the whole machinery to work
         if 'default' in optdict:
-            if (optparse.OPTPARSE_FORMAT_DEFAULT and 'help' in optdict and
-                optdict.get('default') is not None and
-                not optdict['action'] in ('store_true', 'store_false')):
+            if ('help' in optdict
+                and optdict.get('default') is not None
+                and not optdict['action'] in ('store_true', 'store_false')):
                 optdict['help'] += ' [current: %default]'
             del optdict['default']
         args = ['--' + str(opt)]
@@ -508,7 +533,7 @@
             args.append('-' + optdict['short'])
             del optdict['short']
         # cleanup option definition dict before giving it to optik
-        for key in optdict.keys():
+        for key in list(optdict.keys()):
             if not key in self._optik_option_attrs:
                 optdict.pop(key)
         return args, optdict
@@ -555,7 +580,7 @@
         printed = False
         for section in sections:
             if printed:
-                print >> stream, '\n'
+                print('\n', file=stream)
             format_section(stream, section.upper(), options_by_section[section],
                            encoding)
             printed = True
@@ -566,7 +591,7 @@
         """
         self._monkeypatch_expand_default()
         try:
-            optparse.generate_manpage(self.cmdline_parser, pkginfo,
+            optik_ext.generate_manpage(self.cmdline_parser, pkginfo,
                                       section, stream=stream or sys.stdout,
                                       level=self._maxlevel)
         finally:
@@ -594,7 +619,7 @@
             if opt in self._all_options:
                 break # already processed
             def helpfunc(option, opt, val, p, level=helplevel):
-                print self.help(level)
+                print(self.help(level))
                 sys.exit(0)
             helpmsg = '%s verbose help.' % ' '.join(['more'] * helplevel)
             optdict = {'action' : 'callback', 'callback' : helpfunc,
@@ -616,7 +641,7 @@
                     parser._sections[sect.upper()] = values
         elif not self.quiet:
             msg = 'No config file found, using default configuration'
-            print >> sys.stderr, msg
+            print(msg, file=sys.stderr)
             return
 
     def input_config(self, onlysection=None, inputlevel=0, stream=None):
@@ -642,13 +667,13 @@
         options provider)
         """
         parser = self.cfgfile_parser
-        for provider in self.options_providers:
-            for section, option, optdict in provider.all_options():
-                try:
-                    value = parser.get(section, option)
-                    provider.set_option(option, value, optdict=optdict)
-                except (NoSectionError, NoOptionError), ex:
-                    continue
+        for section in parser.sections():
+             for option, value in parser.items(section):
+                  try:
+                       self.global_set_option(option, value)
+                  except (KeyError, OptionError):
+                       # TODO handle here undeclared options appearing in the config file
+                       continue
 
     def load_configuration(self, **kwargs):
         """override configuration according to given parameters
@@ -686,7 +711,7 @@
 
     def add_help_section(self, title, description, level=0):
         """add a dummy option section for help purpose """
-        group = optparse.OptionGroup(self.cmdline_parser,
+        group = optik_ext.OptionGroup(self.cmdline_parser,
                                      title=title.capitalize(),
                                      description=description)
         group.level = level
@@ -694,18 +719,18 @@
         self.cmdline_parser.add_option_group(group)
 
     def _monkeypatch_expand_default(self):
-        # monkey patch optparse to deal with our default values
+        # monkey patch optik_ext to deal with our default values
         try:
-            self.__expand_default_backup = optparse.HelpFormatter.expand_default
-            optparse.HelpFormatter.expand_default = expand_default
+            self.__expand_default_backup = optik_ext.HelpFormatter.expand_default
+            optik_ext.HelpFormatter.expand_default = expand_default
         except AttributeError:
             # python < 2.4: nothing to be done
             pass
     def _unmonkeypatch_expand_default(self):
         # remove monkey patch
-        if hasattr(optparse.HelpFormatter, 'expand_default'):
-            # unpatch optparse to avoid side effects
-            optparse.HelpFormatter.expand_default = self.__expand_default_backup
+        if hasattr(optik_ext.HelpFormatter, 'expand_default'):
+            # unpatch optik_ext to avoid side effects
+            optik_ext.HelpFormatter.expand_default = self.__expand_default_backup
 
     def help(self, level=0):
         """return the usage string for available options """
@@ -734,6 +759,7 @@
         assert self._inst, 'unbound method'
         return getattr(self._inst, self.method)(*args, **kwargs)
 
+# Options Provider #############################################################
 
 class OptionsProviderMixIn(object):
     """Mixin to provide options to an OptionsManager"""
@@ -745,7 +771,7 @@
     level = 0
 
     def __init__(self):
-        self.config = optparse.Values()
+        self.config = optik_ext.Values()
         for option in self.options:
             try:
                 option, optdict = option
@@ -777,41 +803,41 @@
             default = default()
         return default
 
-    def option_name(self, opt, optdict=None):
+    def option_attrname(self, opt, optdict=None):
         """get the config attribute corresponding to opt
         """
         if optdict is None:
             optdict = self.get_option_def(opt)
         return optdict.get('dest', opt.replace('-', '_'))
+    option_name = deprecated('[0.60] OptionsProviderMixIn.option_name() was renamed to option_attrname()')(option_attrname)
 
     def option_value(self, opt):
         """get the current value for the given option"""
-        return getattr(self.config, self.option_name(opt), None)
+        return getattr(self.config, self.option_attrname(opt), None)
 
     def set_option(self, opt, value, action=None, optdict=None):
         """method called to set an option (registered in the options list)
         """
-        # print "************ setting option", opt," to value", value
         if optdict is None:
             optdict = self.get_option_def(opt)
         if value is not None:
-            value = convert(value, optdict, opt)
+            value = _validate(value, optdict, opt)
         if action is None:
             action = optdict.get('action', 'store')
         if optdict.get('type') == 'named': # XXX need specific handling
-            optname = self.option_name(opt, optdict)
+            optname = self.option_attrname(opt, optdict)
             currentvalue = getattr(self.config, optname, None)
             if currentvalue:
                 currentvalue.update(value)
                 value = currentvalue
         if action == 'store':
-            setattr(self.config, self.option_name(opt, optdict), value)
+            setattr(self.config, self.option_attrname(opt, optdict), value)
         elif action in ('store_true', 'count'):
-            setattr(self.config, self.option_name(opt, optdict), 0)
+            setattr(self.config, self.option_attrname(opt, optdict), 0)
         elif action == 'store_false':
-            setattr(self.config, self.option_name(opt, optdict), 1)
+            setattr(self.config, self.option_attrname(opt, optdict), 1)
         elif action == 'append':
-            opt = self.option_name(opt, optdict)
+            opt = self.option_attrname(opt, optdict)
             _list = getattr(self.config, opt, None)
             if _list is None:
                 if isinstance(value, (list, tuple)):
@@ -839,12 +865,12 @@
             defaultstr = ': '
         else:
             defaultstr = '(default: %s): ' % format_option_value(optdict, default)
-        print ':%s:' % option
-        print optdict.get('help') or option
+        print(':%s:' % option)
+        print(optdict.get('help') or option)
         inputfunc = INPUT_FUNCTIONS[optdict['type']]
         value = inputfunc(optdict, defaultstr)
         while default is REQUIRED and not value:
-            print 'please specify a value'
+            print('please specify a value')
             value = inputfunc(optdict, '%s: ' % option)
         if value is None and default is not None:
             value = default
@@ -893,6 +919,7 @@
         for optname, optdict in options:
             yield (optname, optdict, self.option_value(optname))
 
+# configuration ################################################################
 
 class ConfigurationMixIn(OptionsManagerMixIn, OptionsProviderMixIn):
     """basic mixin for simple configurations which don't need the
@@ -913,7 +940,7 @@
                     continue
                 if not gdef in self.option_groups:
                     self.option_groups.append(gdef)
-        self.register_options_provider(self, own_group=0)
+        self.register_options_provider(self, own_group=False)
 
     def register_options(self, options):
         """add some options to the configuration"""
@@ -932,8 +959,8 @@
 
     def __getitem__(self, key):
         try:
-            return getattr(self.config, self.option_name(key))
-        except (optparse.OptionValueError, AttributeError):
+            return getattr(self.config, self.option_attrname(key))
+        except (optik_ext.OptionValueError, AttributeError):
             raise KeyError(key)
 
     def __setitem__(self, key, value):
@@ -941,7 +968,7 @@
 
     def get(self, key, default=None):
         try:
-            return getattr(self.config, self.option_name(key))
+            return getattr(self.config, self.option_attrname(key))
         except (OptionError, AttributeError):
             return default
 
@@ -977,20 +1004,21 @@
     def __getitem__(self, key):
         provider = self.config._all_options[key]
         try:
-            return getattr(provider.config, provider.option_name(key))
+            return getattr(provider.config, provider.option_attrname(key))
         except AttributeError:
             raise KeyError(key)
 
     def __setitem__(self, key, value):
-        self.config.global_set_option(self.config.option_name(key), value)
+        self.config.global_set_option(self.config.option_attrname(key), value)
 
     def get(self, key, default=None):
         provider = self.config._all_options[key]
         try:
-            return getattr(provider.config, provider.option_name(key))
+            return getattr(provider.config, provider.option_attrname(key))
         except AttributeError:
             return default
 
+# other functions ##############################################################
 
 def read_old_config(newconfig, changes, configfile):
     """initialize newconfig from a deprecated configuration file
@@ -1055,8 +1083,13 @@
             newconfig.set_option(optname, oldconfig[optname], optdict=optdef)
 
 
-def merge_options(options):
-    """preprocess options to remove duplicate"""
+def merge_options(options, optgroup=None):
+    """preprocess a list of options and remove duplicates, returning a new list
+    (tuple actually) of options.
+
+    Options dictionaries are copied to avoid later side-effect. Also, if
+    `otpgroup` argument is specified, ensure all options are in the given group.
+    """
     alloptions = {}
     options = list(options)
     for i in range(len(options)-1, -1, -1):
@@ -1065,5 +1098,9 @@
             options.pop(i)
             alloptions[optname].update(optdict)
         else:
+            optdict = optdict.copy()
+            options[i] = (optname, optdict)
             alloptions[optname] = optdict
+        if optgroup is not None:
+            alloptions[optname]['group'] = optgroup
     return tuple(options)
diff --git a/third_party/logilab/common/corbautils.py b/third_party/logilab/common/corbautils.py
index 8dfb2ba..65c301d 100644
--- a/third_party/logilab/common/corbautils.py
+++ b/third_party/logilab/common/corbautils.py
@@ -72,7 +72,7 @@
         name = [CosNaming.NameComponent(id, kind)]
         try:
             context = context.bind_new_context(name)
-        except CosNaming.NamingContext.AlreadyBound, ex:
+        except CosNaming.NamingContext.AlreadyBound as ex:
             context = context.resolve(name)._narrow(CosNaming.NamingContext)
             assert context is not None, \
                    'test context exists but is not a NamingContext'
@@ -81,7 +81,7 @@
     name = [CosNaming.NameComponent(id, kind)]
     try:
         context.bind(name, object._this())
-    except CosNaming.NamingContext.AlreadyBound, ex:
+    except CosNaming.NamingContext.AlreadyBound as ex:
         context.rebind(name, object._this())
 
 def activate_POA():
diff --git a/third_party/logilab/common/daemon.py b/third_party/logilab/common/daemon.py
index c8342a8..6324b20 100644
--- a/third_party/logilab/common/daemon.py
+++ b/third_party/logilab/common/daemon.py
@@ -26,6 +26,8 @@
 import time
 import warnings
 
+from six.moves import range
+
 def setugid(user):
     """Change process user and group ID
 
@@ -46,7 +48,7 @@
             raise OSError(err, os.strerror(err), 'initgroups')
     os.setgid(passwd.pw_gid)
     os.setuid(passwd.pw_uid)
-    os.putenv('HOME', passwd.pw_dir)
+    os.environ['HOME'] = passwd.pw_dir
 
 
 def daemonize(pidfile=None, uid=None, umask=077):
@@ -77,7 +79,7 @@
     for i in range(3):
         try:
             os.dup2(null, i)
-        except OSError, e:
+        except OSError as e:
             if e.errno != errno.EBADF:
                 raise
     os.close(null)
diff --git a/third_party/logilab/common/date.py b/third_party/logilab/common/date.py
index b069a6f..544d03e 100644
--- a/third_party/logilab/common/date.py
+++ b/third_party/logilab/common/date.py
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This file is part of logilab-common.
@@ -22,11 +22,14 @@
 
 import math
 import re
-from locale import getpreferredencoding
+import sys
+from locale import getlocale, LC_TIME
 from datetime import date, time, datetime, timedelta
 from time import strptime as time_strptime
 from calendar import monthrange, timegm
 
+from six.moves import range
+
 try:
     from mx.DateTime import RelativeDateTime, Date, DateTimeType
 except ImportError:
@@ -130,7 +133,7 @@
     end = datefactory(end.year, end.month, end.day, end)
     holidays = [str2date(datestr, begin)
                 for datestr in FRENCH_MOBILE_HOLIDAYS.values()]
-    for year in xrange(begin.year, end.year+1):
+    for year in range(begin.year, end.year+1):
         for datestr in FRENCH_FIXED_HOLIDAYS.values():
             date = str2date(datestr % year, begin)
             if date not in holidays:
@@ -187,8 +190,8 @@
     end = todate(end)
     if incmonth:
         while begin < end:
-            begin = next_month(begin, incmonth)
             yield begin
+            begin = next_month(begin, incmonth)
     else:
         incr = get_step(begin, incday or 1)
         while begin < end:
@@ -279,29 +282,34 @@
 
 def ustrftime(somedate, fmt='%Y-%m-%d'):
     """like strftime, but returns a unicode string instead of an encoded
-    string which' may be problematic with localized date.
-
-    encoding is guessed by locale.getpreferredencoding()
+    string which may be problematic with localized date.
     """
-    encoding = getpreferredencoding(do_setlocale=False) or 'UTF-8'
-    try:
-        return unicode(somedate.strftime(str(fmt)), encoding)
-    except ValueError, exc:
-        if somedate.year >= 1900:
-            raise
-        # datetime is not happy with dates before 1900
-        # we try to work around this, assuming a simple
-        # format string
-        fields = {'Y': somedate.year,
-                  'm': somedate.month,
-                  'd': somedate.day,
-                  }
-        if isinstance(somedate, datetime):
-            fields.update({'H': somedate.hour,
-                           'M': somedate.minute,
-                           'S': somedate.second})
-        fmt = re.sub('%([YmdHMS])', r'%(\1)02d', fmt)
-        return unicode(fmt) % fields
+    if sys.version_info >= (3, 3):
+        # datetime.date.strftime() supports dates since year 1 in Python >=3.3.
+        return somedate.strftime(fmt)
+    else:
+        try:
+            if sys.version_info < (3, 0):
+                encoding = getlocale(LC_TIME)[1] or 'ascii'
+                return unicode(somedate.strftime(str(fmt)), encoding)
+            else:
+                return somedate.strftime(fmt)
+        except ValueError:
+            if somedate.year >= 1900:
+                raise
+            # datetime is not happy with dates before 1900
+            # we try to work around this, assuming a simple
+            # format string
+            fields = {'Y': somedate.year,
+                      'm': somedate.month,
+                      'd': somedate.day,
+                      }
+            if isinstance(somedate, datetime):
+                fields.update({'H': somedate.hour,
+                               'M': somedate.minute,
+                               'S': somedate.second})
+            fmt = re.sub('%([YmdHMS])', r'%(\1)02d', fmt)
+            return unicode(fmt) % fields
 
 def utcdatetime(dt):
     if dt.tzinfo is None:
diff --git a/third_party/logilab/common/dbf.py b/third_party/logilab/common/dbf.py
index 8def2d2..bb1b9e6 100644
--- a/third_party/logilab/common/dbf.py
+++ b/third_party/logilab/common/dbf.py
@@ -36,7 +36,8 @@
 import sys
 import csv
 import tempfile
-import ConfigParser
+
+from six.moves import range
 
 class Dbase:
     def __init__(self):
diff --git a/third_party/logilab/common/debugger.py b/third_party/logilab/common/debugger.py
index 7556322..1f540a1 100644
--- a/third_party/logilab/common/debugger.py
+++ b/third_party/logilab/common/debugger.py
@@ -26,6 +26,9 @@
 
 
 """
+
+from __future__ import print_function
+
 __docformat__ = "restructuredtext en"
 
 try:
@@ -36,9 +39,10 @@
 import os.path as osp
 import sys
 from pdb import Pdb
-from cStringIO import StringIO
 import inspect
 
+from logilab.common.compat import StringIO
+
 try:
     from IPython import PyColorize
 except ImportError:
@@ -182,8 +186,8 @@
         if not arg:
             try:
                 source, start_lineno = getsource(self.curframe)
-                print colorize(''.join(source), start_lineno,
-                               self.curframe.f_lineno)
+                print(colorize(''.join(source), start_lineno,
+                               self.curframe.f_lineno))
             except KeyboardInterrupt:
                 pass
             except IOError:
diff --git a/third_party/logilab/common/decorators.py b/third_party/logilab/common/decorators.py
index 43c3652..beafa20 100644
--- a/third_party/logilab/common/decorators.py
+++ b/third_party/logilab/common/decorators.py
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This file is part of logilab-common.
@@ -16,26 +16,28 @@
 # You should have received a copy of the GNU Lesser General Public License along
 # with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
 """ A few useful function/method decorators. """
+
+from __future__ import print_function
+
 __docformat__ = "restructuredtext en"
 
 import sys
+import types
 from time import clock, time
+from inspect import isgeneratorfunction, getargspec
 
-from logilab.common.compat import callable, method_type
+from logilab.common.compat import method_type
 
 # XXX rewrite so we can use the decorator syntax when keyarg has to be specified
 
-def _is_generator_function(callableobj):
-    return callableobj.func_code.co_flags & 0x20
-
 class cached_decorator(object):
     def __init__(self, cacheattr=None, keyarg=None):
         self.cacheattr = cacheattr
         self.keyarg = keyarg
     def __call__(self, callableobj=None):
-        assert not _is_generator_function(callableobj), \
+        assert not isgeneratorfunction(callableobj), \
                'cannot cache generator function: %s' % callableobj
-        if callableobj.func_code.co_argcount == 1 or self.keyarg == 0:
+        if len(getargspec(callableobj).args) == 1 or self.keyarg == 0:
             cache = _SingleValueCache(callableobj, self.cacheattr)
         elif self.keyarg:
             cache = _MultiValuesKeyArgCache(callableobj, self.keyarg, self.cacheattr)
@@ -67,7 +69,6 @@
         try:
             wrapped.__doc__ = self.callable.__doc__
             wrapped.__name__ = self.callable.__name__
-            wrapped.func_name = self.callable.func_name
         except:
             pass
         return wrapped
@@ -226,8 +227,8 @@
         t = time()
         c = clock()
         res = f(*args, **kwargs)
-        print '%s clock: %.9f / time: %.9f' % (f.__name__,
-                                               clock() - c, time() - t)
+        print('%s clock: %.9f / time: %.9f' % (f.__name__,
+                                               clock() - c, time() - t))
         return res
     return wrap
 
@@ -249,7 +250,9 @@
 
 
 def monkeypatch(klass, methodname=None):
-    """Decorator extending class with the decorated callable
+    """Decorator extending class with the decorated callable. This is basically
+    a syntactic sugar vs class assignment.
+
     >>> class A:
     ...     pass
     >>> @monkeypatch(A)
@@ -273,11 +276,6 @@
             raise AttributeError('%s has no __name__ attribute: '
                                  'you should provide an explicit `methodname`'
                                  % func)
-        if callable(func) and sys.version_info < (3, 0):
-            setattr(klass, name, method_type(func, None, klass))
-        else:
-            # likely a property
-            # this is quite borderline but usage already in the wild ...
-            setattr(klass, name, func)
+        setattr(klass, name, func)
         return func
     return decorator
diff --git a/third_party/logilab/common/deprecation.py b/third_party/logilab/common/deprecation.py
index 4b2c3f4..9ceff17 100644
--- a/third_party/logilab/common/deprecation.py
+++ b/third_party/logilab/common/deprecation.py
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This file is part of logilab-common.
@@ -22,15 +22,153 @@
 import sys
 from warnings import warn
 
-class class_deprecated(type):
-    """metaclass to print a warning on instantiation of a deprecated class"""
+from logilab.common.changelog import Version
 
-    def __call__(cls, *args, **kwargs):
-        msg = getattr(cls, "__deprecation_warning__",
-                      "%(cls)s is deprecated") % {'cls': cls.__name__}
-        warn(msg, DeprecationWarning, stacklevel=2)
-        return type.__call__(cls, *args, **kwargs)
 
+class DeprecationWrapper(object):
+    """proxy to print a warning on access to any attribute of the wrapped object
+    """
+    def __init__(self, proxied, msg=None):
+        self._proxied = proxied
+        self._msg = msg
+
+    def __getattr__(self, attr):
+        warn(self._msg, DeprecationWarning, stacklevel=2)
+        return getattr(self._proxied, attr)
+
+    def __setattr__(self, attr, value):
+        if attr in ('_proxied', '_msg'):
+            self.__dict__[attr] = value
+        else:
+            warn(self._msg, DeprecationWarning, stacklevel=2)
+            setattr(self._proxied, attr, value)
+
+
+class DeprecationManager(object):
+    """Manage the deprecation message handling. Messages are dropped for
+    versions more recent than the 'compatible' version. Example::
+
+        deprecator = deprecation.DeprecationManager("module_name")
+        deprecator.compatibility('1.3')
+
+        deprecator.warn('1.2', "message.")
+
+        @deprecator.deprecated('1.2', 'Message')
+        def any_func():
+            pass
+
+        class AnyClass(object):
+            __metaclass__ = deprecator.class_deprecated('1.2')
+    """
+    def __init__(self, module_name=None):
+        """
+        """
+        self.module_name = module_name
+        self.compatible_version = None
+
+    def compatibility(self, compatible_version):
+        """Set the compatible version.
+        """
+        self.compatible_version = Version(compatible_version)
+
+    def deprecated(self, version=None, reason=None, stacklevel=2, name=None, doc=None):
+        """Display a deprecation message only if the version is older than the
+        compatible version.
+        """
+        def decorator(func):
+            message = reason or 'The function "%s" is deprecated'
+            if '%s' in message:
+                message %= func.__name__
+            def wrapped(*args, **kwargs):
+                self.warn(version, message, stacklevel+1)
+                return func(*args, **kwargs)
+            return wrapped
+        return decorator
+
+    def class_deprecated(self, version=None):
+        class metaclass(type):
+            """metaclass to print a warning on instantiation of a deprecated class"""
+
+            def __call__(cls, *args, **kwargs):
+                msg = getattr(cls, "__deprecation_warning__",
+                              "%(cls)s is deprecated") % {'cls': cls.__name__}
+                self.warn(version, msg, stacklevel=3)
+                return type.__call__(cls, *args, **kwargs)
+        return metaclass
+
+    def moved(self, version, modpath, objname):
+        """use to tell that a callable has been moved to a new module.
+
+        It returns a callable wrapper, so that when its called a warning is printed
+        telling where the object can be found, import is done (and not before) and
+        the actual object is called.
+
+        NOTE: the usage is somewhat limited on classes since it will fail if the
+        wrapper is use in a class ancestors list, use the `class_moved` function
+        instead (which has no lazy import feature though).
+        """
+        def callnew(*args, **kwargs):
+            from logilab.common.modutils import load_module_from_name
+            message = "object %s has been moved to module %s" % (objname, modpath)
+            self.warn(version, message)
+            m = load_module_from_name(modpath)
+            return getattr(m, objname)(*args, **kwargs)
+        return callnew
+
+    def class_renamed(self, version, old_name, new_class, message=None):
+        clsdict = {}
+        if message is None:
+            message = '%s is deprecated, use %s' % (old_name, new_class.__name__)
+        clsdict['__deprecation_warning__'] = message
+        try:
+            # new-style class
+            return self.class_deprecated(version)(old_name, (new_class,), clsdict)
+        except (NameError, TypeError):
+            # old-style class
+            class DeprecatedClass(new_class):
+                """FIXME: There might be a better way to handle old/new-style class
+                """
+                def __init__(self, *args, **kwargs):
+                    self.warn(version, message, stacklevel=3)
+                    new_class.__init__(self, *args, **kwargs)
+            return DeprecatedClass
+
+    def class_moved(self, version, new_class, old_name=None, message=None):
+        """nice wrapper around class_renamed when a class has been moved into
+        another module
+        """
+        if old_name is None:
+            old_name = new_class.__name__
+        if message is None:
+            message = 'class %s is now available as %s.%s' % (
+                old_name, new_class.__module__, new_class.__name__)
+        return self.class_renamed(version, old_name, new_class, message)
+
+    def warn(self, version=None, reason="", stacklevel=2):
+        """Display a deprecation message only if the version is older than the
+        compatible version.
+        """
+        if (self.compatible_version is None
+            or version is None
+            or Version(version) < self.compatible_version):
+            if self.module_name and version:
+                reason = '[%s %s] %s' % (self.module_name, version, reason)
+            elif self.module_name:
+                reason = '[%s] %s' % (self.module_name, reason)
+            elif version:
+                reason = '[%s] %s' % (version, reason)
+            warn(reason, DeprecationWarning, stacklevel=stacklevel)
+
+_defaultdeprecator = DeprecationManager()
+
+def deprecated(reason=None, stacklevel=2, name=None, doc=None):
+    return _defaultdeprecator.deprecated(None, reason, stacklevel, name, doc)
+
+class_deprecated = _defaultdeprecator.class_deprecated()
+
+def moved(modpath, objname):
+    return _defaultdeprecator.moved(None, modpath, objname)
+moved.__doc__ = _defaultdeprecator.moved.__doc__
 
 def class_renamed(old_name, new_class, message=None):
     """automatically creates a class which fires a DeprecationWarning
@@ -39,74 +177,12 @@
     >>> Set = class_renamed('Set', set, 'Set is now replaced by set')
     >>> s = Set()
     sample.py:57: DeprecationWarning: Set is now replaced by set
-      s = Set()
+    s = Set()
     >>>
     """
-    clsdict = {}
-    if message is None:
-        message = '%s is deprecated, use %s' % (old_name, new_class.__name__)
-    clsdict['__deprecation_warning__'] = message
-    try:
-        # new-style class
-        return class_deprecated(old_name, (new_class,), clsdict)
-    except (NameError, TypeError):
-        # old-style class
-        class DeprecatedClass(new_class):
-            """FIXME: There might be a better way to handle old/new-style class
-            """
-            def __init__(self, *args, **kwargs):
-                warn(message, DeprecationWarning, stacklevel=2)
-                new_class.__init__(self, *args, **kwargs)
-        return DeprecatedClass
-
+    return _defaultdeprecator.class_renamed(None, old_name, new_class, message)
 
 def class_moved(new_class, old_name=None, message=None):
-    """nice wrapper around class_renamed when a class has been moved into
-    another module
-    """
-    if old_name is None:
-        old_name = new_class.__name__
-    if message is None:
-        message = 'class %s is now available as %s.%s' % (
-            old_name, new_class.__module__, new_class.__name__)
-    return class_renamed(old_name, new_class, message)
-
-def deprecated(reason=None, stacklevel=2, name=None, doc=None):
-    """Decorator that raises a DeprecationWarning to print a message
-    when the decorated function is called.
-    """
-    def deprecated_decorator(func):
-        message = reason or 'The function "%s" is deprecated'
-        if '%s' in message:
-            message = message % func.func_name
-        def wrapped(*args, **kwargs):
-            warn(message, DeprecationWarning, stacklevel=stacklevel)
-            return func(*args, **kwargs)
-        try:
-            wrapped.__name__ = name or func.__name__
-        except TypeError: # readonly attribute in 2.3
-            pass
-        wrapped.__doc__ = doc or func.__doc__
-        return wrapped
-    return deprecated_decorator
-
-def moved(modpath, objname):
-    """use to tell that a callable has been moved to a new module.
-
-    It returns a callable wrapper, so that when its called a warning is printed
-    telling where the object can be found, import is done (and not before) and
-    the actual object is called.
-
-    NOTE: the usage is somewhat limited on classes since it will fail if the
-    wrapper is use in a class ancestors list, use the `class_moved` function
-    instead (which has no lazy import feature though).
-    """
-    def callnew(*args, **kwargs):
-        from logilab.common.modutils import load_module_from_name
-        message = "object %s has been moved to module %s" % (objname, modpath)
-        warn(message, DeprecationWarning, stacklevel=2)
-        m = load_module_from_name(modpath)
-        return getattr(m, objname)(*args, **kwargs)
-    return callnew
-
+    return _defaultdeprecator.class_moved(None, new_class, old_name, message)
+class_moved.__doc__ = _defaultdeprecator.class_moved.__doc__
 
diff --git a/third_party/logilab/common/fileutils.py b/third_party/logilab/common/fileutils.py
index 4ac9270..b30cf5f 100644
--- a/third_party/logilab/common/fileutils.py
+++ b/third_party/logilab/common/fileutils.py
@@ -23,6 +23,9 @@
 write_open_mode, ensure_fs_mode, export
 :sort: path manipulation, file manipulation
 """
+
+from __future__ import print_function
+
 __docformat__ = "restructuredtext en"
 
 import sys
@@ -32,12 +35,11 @@
 from os.path import abspath
 from os import sep, mkdir, remove, listdir, stat, chmod, walk
 from stat import ST_MODE, S_IWRITE
-from cStringIO import StringIO
 
 from logilab.common import STD_BLACKLIST as BASE_BLACKLIST, IGNORED_EXTENSIONS
 from logilab.common.shellutils import find
 from logilab.common.deprecation import deprecated
-from logilab.common.compat import FileIO, any
+from logilab.common.compat import FileIO
 
 def first_level_directory(path):
     """Return the first level directory of a path.
@@ -375,7 +377,7 @@
             src = join(directory, filename)
             dest = to_dir + src[len(from_dir):]
             if verbose:
-                print >> sys.stderr, src, '->', dest
+                print(src, '->', dest, file=sys.stderr)
             if exists(dest):
                 remove(dest)
             shutil.copy2(src, dest)
@@ -397,6 +399,6 @@
             src = join(dirpath, filename)
             if islink(src) and not exists(src):
                 if verbose:
-                    print 'remove dead link', src
+                    print('remove dead link', src)
                 remove(src)
 
diff --git a/third_party/logilab/common/graph.py b/third_party/logilab/common/graph.py
index 75a2ee7..cef1c98 100644
--- a/third_party/logilab/common/graph.py
+++ b/third_party/logilab/common/graph.py
@@ -28,7 +28,8 @@
 import os
 import sys
 import tempfile
-from logilab.common.compat import str_encode
+import codecs
+import errno
 
 def escape(value):
     """Make <value> usable in a dot file."""
@@ -63,7 +64,7 @@
             assert charset.lower() in ('utf-8', 'iso-8859-1', 'latin1'), \
                    'unsupported charset %s' % charset
             self.emit('charset="%s"' % charset)
-        for param in additionnal_param.iteritems():
+        for param in sorted(additionnal_param.items()):
             self.emit('='.join(param))
 
     def get_source(self):
@@ -106,21 +107,26 @@
             ppng, outputfile = tempfile.mkstemp(".png", name)
             os.close(pdot)
             os.close(ppng)
-        pdot = open(dot_sourcepath, 'w')
-        pdot.write(str_encode(self.source, 'utf8'))
+        pdot = codecs.open(dot_sourcepath, 'w', encoding='utf8')
+        pdot.write(self.source)
         pdot.close()
         if target != 'dot':
             if sys.platform == 'win32':
                 use_shell = True
             else:
                 use_shell = False
-            if mapfile:
-                subprocess.call([self.renderer,  '-Tcmapx', '-o', mapfile, '-T', target, dot_sourcepath, '-o', outputfile],
-                                shell=use_shell)
-            else:
-                subprocess.call([self.renderer, '-T',  target,
-                                 dot_sourcepath, '-o',  outputfile],
-                                shell=use_shell)
+            try:
+                if mapfile:
+                    subprocess.call([self.renderer,  '-Tcmapx', '-o', mapfile, '-T', target, dot_sourcepath, '-o', outputfile],
+                                    shell=use_shell)
+                else:
+                    subprocess.call([self.renderer, '-T',  target,
+                                     dot_sourcepath, '-o',  outputfile],
+                                    shell=use_shell)
+            except OSError as e:
+                if e.errno == errno.ENOENT:
+                    e.strerror = 'File not found: {0}'.format(self.renderer)
+                    raise
             os.unlink(dot_sourcepath)
         return outputfile
 
@@ -134,14 +140,14 @@
         """
         attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()]
         n_from, n_to = normalize_node_id(name1), normalize_node_id(name2)
-        self.emit('%s -> %s [%s];' % (n_from, n_to, ", ".join(attrs)) )
+        self.emit('%s -> %s [%s];' % (n_from, n_to, ', '.join(sorted(attrs))) )
 
     def emit_node(self, name, **props):
         """emit a node with given properties.
         node properties: see http://www.graphviz.org/doc/info/attrs.html
         """
         attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()]
-        self.emit('%s [%s];' % (normalize_node_id(name), ", ".join(attrs)))
+        self.emit('%s [%s];' % (normalize_node_id(name), ', '.join(sorted(attrs))))
 
 def normalize_node_id(nid):
     """Returns a suitable DOT node id for `nid`."""
@@ -226,10 +232,10 @@
     if vertices is None:
         vertices = graph_dict.keys()
     for vertice in vertices:
-        _get_cycles(graph_dict, vertice, [], result)
+        _get_cycles(graph_dict, [], set(), result, vertice)
     return result
 
-def _get_cycles(graph_dict, vertice=None, path=None, result=None):
+def _get_cycles(graph_dict, path, visited, result, vertice):
     """recursive function doing the real work for get_cycles"""
     if vertice in path:
         cycle = [vertice]
@@ -248,7 +254,10 @@
     path.append(vertice)
     try:
         for node in graph_dict[vertice]:
-            _get_cycles(graph_dict, node, path, result)
+            # don't check already visited nodes again
+            if node not in visited:
+                _get_cycles(graph_dict, path, visited, result, node)
+                visited.add(node)
     except KeyError:
         pass
     path.pop()
diff --git a/third_party/logilab/common/hg.py b/third_party/logilab/common/hg.py
deleted file mode 100644
index edf2d3b..0000000
--- a/third_party/logilab/common/hg.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:[email protected]
-#
-# This file is part of logilab-common.
-#
-# logilab-common is free software: you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option) any
-# later version.
-#
-# logilab-common is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
-"""mercurial utilities (mercurial should be installed)"""
-
-__docformat__ = "restructuredtext en"
-
-import os
-import sys
-import os.path as osp
-
-try:
-    from mercurial.error import RepoError
-    from mercurial.__version__ import version as hg_version
-except ImportError:
-    from mercurial.repo import RepoError
-    from mercurial.version import get_version
-    hg_version = get_version()
-
-from mercurial.hg import repository as Repository
-from mercurial.ui import ui as Ui
-from mercurial.node import short
-try:
-    # mercurial >= 1.2 (?)
-    from mercurial.cmdutil import walkchangerevs
-except ImportError, ex:
-    from mercurial.commands import walkchangerevs
-try:
-    # mercurial >= 1.1 (.1?)
-    from mercurial.util import cachefunc
-except ImportError, ex:
-    def cachefunc(func):
-        return func
-try:
-    # mercurial >= 1.3.1
-    from mercurial import encoding
-    _encoding = encoding.encoding
-except ImportError:
-    try:
-        from mercurial.util import _encoding
-    except ImportError:
-        import locale
-        # stay compatible with mercurial 0.9.1 (etch debian release)
-        # (borrowed from mercurial.util 1.1.2)
-        try:
-            _encoding = os.environ.get("HGENCODING")
-            if sys.platform == 'darwin' and not _encoding:
-                # On darwin, getpreferredencoding ignores the locale environment and
-                # always returns mac-roman. We override this if the environment is
-                # not C (has been customized by the user).
-                locale.setlocale(locale.LC_CTYPE, '')
-                _encoding = locale.getlocale()[1]
-            if not _encoding:
-                _encoding = locale.getpreferredencoding() or 'ascii'
-        except locale.Error:
-            _encoding = 'ascii'
-try:
-    # demandimport causes problems when activated, ensure it isn't
-    # XXX put this in apycot where the pb has been noticed?
-    from mercurial import demandimport
-    demandimport.disable()
-except:
-    pass
-
-Ui.warn = lambda *args, **kwargs: 0 # make it quiet
-
-def find_repository(path):
-    """returns <path>'s mercurial repository
-
-    None if <path> is not under hg control
-    """
-    path = osp.realpath(osp.abspath(path))
-    while not osp.isdir(osp.join(path, ".hg")):
-        oldpath = path
-        path = osp.dirname(path)
-        if path == oldpath:
-            return None
-    return path
-
-
-def get_repository(path):
-    """Simple function that open a hg repository"""
-    repopath = find_repository(path)
-    if repopath is None:
-        raise RuntimeError('no repository found in %s' % osp.abspath(path))
-    return Repository(Ui(), path=repopath)
-
-def incoming(wdrepo, masterrepo):
-    try:
-        return wdrepo.findincoming(masterrepo)
-    except AttributeError:
-        from mercurial import hg, discovery
-        revs, checkout = hg.addbranchrevs(wdrepo, masterrepo, ('', []), None)
-        common, incoming, rheads = discovery.findcommonincoming(
-            wdrepo, masterrepo, heads=revs)
-        if not masterrepo.local():
-            from mercurial import bundlerepo, changegroup
-            if revs is None and masterrepo.capable('changegroupsubset'):
-                revs = rheads
-            if revs is None:
-                cg = masterrepo.changegroup(incoming, "incoming")
-            else:
-                cg = masterrepo.changegroupsubset(incoming, revs, 'incoming')
-            fname = changegroup.writebundle(cg, None, "HG10UN")
-            # use the created uncompressed bundlerepo
-            masterrepo = bundlerepo.bundlerepository(wdrepo.ui, wdrepo.root, fname)
-        return masterrepo.changelog.nodesbetween(incoming, revs)[0]
-
-def outgoing(wdrepo, masterrepo):
-    try:
-        return wdrepo.findoutgoing(masterrepo)
-    except AttributeError:
-        from mercurial import hg, discovery
-        revs, checkout = hg.addbranchrevs(wdrepo, wdrepo, ('', []), None)
-        o = discovery.findoutgoing(wdrepo, masterrepo)
-        return wdrepo.changelog.nodesbetween(o, revs)[0]
diff --git a/third_party/logilab/common/logging_ext.py b/third_party/logilab/common/logging_ext.py
index 1b7a1e6..3b6a580 100644
--- a/third_party/logilab/common/logging_ext.py
+++ b/third_party/logilab/common/logging_ext.py
@@ -24,6 +24,8 @@
 import sys
 import logging
 
+from six import string_types
+
 from logilab.common.textutils import colorize_ansi
 
 
@@ -112,7 +114,11 @@
     else:
         try:
             if rotation_parameters is None:
-                handler = logging.FileHandler(logfile)
+                if os.name == 'posix' and sys.version_info >= (2, 6):
+                    from logging.handlers import WatchedFileHandler
+                    handler = WatchedFileHandler(logfile)
+                else:
+                    handler = logging.FileHandler(logfile)
             else:
                 from logging.handlers import TimedRotatingFileHandler
                 handler = TimedRotatingFileHandler(
@@ -127,14 +133,25 @@
             logthreshold = logging.DEBUG
         else:
             logthreshold = logging.ERROR
-    elif isinstance(logthreshold, basestring):
+    elif isinstance(logthreshold, string_types):
         logthreshold = getattr(logging, THRESHOLD_MAP.get(logthreshold,
                                                           logthreshold))
     return logthreshold
 
-def get_formatter(logformat=LOG_FORMAT, logdateformat=LOG_DATE_FORMAT):
+def _colorable_terminal():
     isatty = hasattr(sys.__stdout__, 'isatty') and sys.__stdout__.isatty()
-    if isatty and sys.platform != 'win32':
+    if not isatty:
+        return False
+    if os.name == 'nt':
+        try:
+            from colorama import init as init_win32_colors
+        except ImportError:
+            return False
+        init_win32_colors()
+    return True
+
+def get_formatter(logformat=LOG_FORMAT, logdateformat=LOG_DATE_FORMAT):
+    if _colorable_terminal():
         fmt = ColorFormatter(logformat, logdateformat)
         def col_fact(record):
             if 'XXX' in record.message:
diff --git a/third_party/logilab/common/modutils.py b/third_party/logilab/common/modutils.py
index ce0c297..a426a3a 100644
--- a/third_party/logilab/common/modutils.py
+++ b/third_party/logilab/common/modutils.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This file is part of logilab-common.
@@ -27,6 +27,7 @@
 :type BUILTIN_MODULES: dict
 :var BUILTIN_MODULES: dictionary with builtin module names has key
 """
+
 __docformat__ = "restructuredtext en"
 
 import sys
@@ -34,6 +35,9 @@
 from os.path import splitext, join, abspath, isdir, dirname, exists, basename
 from imp import find_module, load_module, C_BUILTIN, PY_COMPILED, PKG_DIRECTORY
 from distutils.sysconfig import get_config_var, get_python_lib, get_python_version
+from distutils.errors import DistutilsPlatformError
+
+from six.moves import range
 
 try:
     import zipimport
@@ -53,12 +57,18 @@
 if sys.platform.startswith('win'):
     PY_SOURCE_EXTS = ('py', 'pyw')
     PY_COMPILED_EXTS = ('dll', 'pyd')
-    STD_LIB_DIR = get_python_lib(standard_lib=1)
 else:
     PY_SOURCE_EXTS = ('py',)
     PY_COMPILED_EXTS = ('so',)
-    # extend lib dir with some arch-dependant paths
-    STD_LIB_DIR = join(get_config_var("LIBDIR"), "python%s" % get_python_version())
+
+try:
+    STD_LIB_DIR = get_python_lib(standard_lib=1)
+# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to
+# non-valid path, see https://bugs.pypy.org/issue1164
+except DistutilsPlatformError:
+    STD_LIB_DIR = '//'
+
+EXT_LIB_DIR = get_python_lib()
 
 BUILTIN_MODULES = dict(zip(sys.builtin_module_names,
                            [1]*len(sys.builtin_module_names)))
@@ -84,7 +94,7 @@
     def __getattribute__(self, attr):
         try:
             return super(LazyObject, self).__getattribute__(attr)
-        except AttributeError, ex:
+        except AttributeError as ex:
             return getattr(self._getobj(), attr)
 
     def __call__(self, *args, **kwargs):
@@ -92,7 +102,7 @@
 
 
 def load_module_from_name(dotted_name, path=None, use_sys=1):
-    """Load a Python module from it's name.
+    """Load a Python module from its name.
 
     :type dotted_name: str
     :param dotted_name: python name of a module or package
@@ -117,7 +127,7 @@
 
 
 def load_module_from_modpath(parts, path=None, use_sys=1):
-    """Load a python module from it's splitted name.
+    """Load a python module from its splitted name.
 
     :type parts: list(str) or tuple(str)
     :param parts:
@@ -151,6 +161,9 @@
         if len(modpath) != len(parts):
             # even with use_sys=False, should try to get outer packages from sys.modules
             module = sys.modules.get(curname)
+        elif use_sys:
+            # because it may have been indirectly loaded through a parent
+            module = sys.modules.get(curname)
         if module is None:
             mp_file, mp_filename, mp_desc = find_module(part, path)
             module = load_module(curname, mp_file, mp_filename, mp_desc)
@@ -230,10 +243,7 @@
                     return extrapath[path_].split('.') + submodpath
     for path in sys.path:
         path = abspath(path)
-        if path and base[:len(path)] == path:
-            if filename.find('site-packages') != -1 and \
-                   path.find('site-packages') == -1:
-                continue
+        if path and base.startswith(path):
             modpath = [pkg for pkg in base[len(path):].split(os.sep) if pkg]
             if _check_init(path, modpath[:-1]):
                 return modpath
@@ -446,13 +456,16 @@
 
 def cleanup_sys_modules(directories):
     """remove submodules of `directories` from `sys.modules`"""
-    for modname, module in sys.modules.items():
+    cleaned = []
+    for modname, module in list(sys.modules.items()):
         modfile = getattr(module, '__file__', None)
         if modfile:
             for directory in directories:
                 if modfile.startswith(directory):
+                    cleaned.append(modname)
                     del sys.modules[modname]
                     break
+    return cleaned
 
 
 def is_python_source(filename):
@@ -484,7 +497,7 @@
     modname = modname.split('.')[0]
     try:
         filename = file_from_modpath([modname])
-    except ImportError, ex:
+    except ImportError as ex:
         # import failed, i'm probably not so wrong by supposing it's
         # not standard...
         return 0
@@ -493,13 +506,11 @@
     if filename is None:
         return 1
     filename = abspath(filename)
+    if filename.startswith(EXT_LIB_DIR):
+        return 0
     for path in std_path:
-        path = abspath(path)
-        if filename.startswith(path):
-            pfx_len = len(path)
-            if filename[pfx_len+1:pfx_len+14] != 'site-packages':
-                return 1
-            return 0
+        if filename.startswith(abspath(path)):
+            return 1
     return False
 
 
@@ -565,10 +576,15 @@
             if importer.find_module(modpath[0]):
                 if not importer.find_module('/'.join(modpath)):
                     raise ImportError('No module named %s in %s/%s' % (
-                        '.'.join(modpath[1:]), file, modpath))
+                        '.'.join(modpath[1:]), filepath, modpath))
                 return ZIPFILE, abspath(filepath) + '/' + '/'.join(modpath), filepath
     raise ImportError('No module named %s' % '.'.join(modpath))
 
+try:
+    import pkg_resources
+except ImportError:
+    pkg_resources = None
+
 def _module_file(modpath, path=None):
     """get a module type / file path
 
@@ -599,16 +615,35 @@
         checkeggs = True
     except AttributeError:
         checkeggs = False
+    # pkg_resources support (aka setuptools namespace packages)
+    if (pkg_resources is not None
+            and modpath[0] in pkg_resources._namespace_packages
+            and modpath[0] in sys.modules
+            and len(modpath) > 1):
+        # setuptools has added into sys.modules a module object with proper
+        # __path__, get back information from there
+        module = sys.modules[modpath.pop(0)]
+        path = module.__path__
     imported = []
     while modpath:
+        modname = modpath[0]
+        # take care to changes in find_module implementation wrt builtin modules
+        #
+        # Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23)
+        # >>> imp.find_module('posix')
+        # (None, 'posix', ('', '', 6))
+        #
+        # Python 3.3.1 (default, Apr 26 2013, 12:08:46)
+        # >>> imp.find_module('posix')
+        # (None, None, ('', '', 6))
         try:
-            _, mp_filename, mp_desc = find_module(modpath[0], path)
+            _, mp_filename, mp_desc = find_module(modname, path)
         except ImportError:
             if checkeggs:
                 return _search_zip(modpath, pic)[:2]
             raise
         else:
-            if checkeggs:
+            if checkeggs and mp_filename:
                 fullabspath = [abspath(x) for x in _path]
                 try:
                     pathindex = fullabspath.index(dirname(abspath(mp_filename)))
@@ -628,7 +663,21 @@
             if mtype != PKG_DIRECTORY:
                 raise ImportError('No module %s in %s' % ('.'.join(modpath),
                                                           '.'.join(imported)))
-            path = [mp_filename]
+            # XXX guess if package is using pkgutil.extend_path by looking for
+            # those keywords in the first four Kbytes
+            try:
+                with open(join(mp_filename, '__init__.py')) as stream:
+                    data = stream.read(4096)
+            except IOError:
+                path = [mp_filename]
+            else:
+                if 'pkgutil' in data and 'extend_path' in data:
+                    # extend_path is called, search sys.path for module/packages
+                    # of this name see pkgutil.extend_path documentation
+                    path = [join(p, *imported) for p in sys.path
+                            if isdir(join(p, *imported))]
+                else:
+                    path = [mp_filename]
     return mtype, mp_filename
 
 def _is_python_file(filename):
diff --git a/third_party/logilab/common/optik_ext.py b/third_party/logilab/common/optik_ext.py
index 39bbe18..1fd2a7f 100644
--- a/third_party/logilab/common/optik_ext.py
+++ b/third_party/logilab/common/optik_ext.py
@@ -46,6 +46,8 @@
     argument of this type will be converted to a float value in bytes
     according to byte units (b, kb, mb, gb, tb)
 """
+from __future__ import print_function
+
 __docformat__ = "restructuredtext en"
 
 import re
@@ -65,10 +67,9 @@
 except ImportError:
     HAS_MX_DATETIME = False
 
+from logilab.common.textutils import splitstrip, TIME_UNITS, BYTE_UNITS, \
+    apply_units
 
-OPTPARSE_FORMAT_DEFAULT = sys.version_info >= (2, 4)
-
-from logilab.common.textutils import splitstrip
 
 def check_regexp(option, opt, value):
     """check a regexp value by trying to compile it
@@ -168,18 +169,15 @@
     raise OptionValueError(msg % (opt, value))
 
 def check_time(option, opt, value):
-    from logilab.common.textutils import TIME_UNITS, apply_units
     if isinstance(value, (int, long, float)):
         return value
     return apply_units(value, TIME_UNITS)
 
 def check_bytes(option, opt, value):
-    from logilab.common.textutils import BYTE_UNITS, apply_units
     if hasattr(value, '__int__'):
         return value
     return apply_units(value, BYTE_UNITS)
 
-import types
 
 class Option(BaseOption):
     """override optik.Option to add some new option types
@@ -214,7 +212,7 @@
             if self.choices is None:
                 raise OptionError(
                     "must supply a list of choices for type 'choice'", self)
-            elif type(self.choices) not in (types.TupleType, types.ListType):
+            elif not isinstance(self.choices, (tuple, list)):
                 raise OptionError(
                     "choices must be a list of strings ('%s' supplied)"
                     % str(type(self.choices)).split("'")[1], self)
@@ -227,10 +225,7 @@
     def process(self, opt, value, values, parser):
         # First, convert the value(s) to the right type.  Howl if any
         # value(s) are bogus.
-        try:
-            value = self.convert_value(opt, value)
-        except AttributeError: # py < 2.4
-            value = self.check_value(opt, value)
+        value = self.convert_value(opt, value)
         if self.type == 'named':
             existant = getattr(values, self.dest)
             if existant:
@@ -388,9 +383,9 @@
     formatter = ManHelpFormatter()
     formatter.output_level = level
     formatter.parser = optparser
-    print >> stream, formatter.format_head(optparser, pkginfo, section)
-    print >> stream, optparser.format_option_help(formatter)
-    print >> stream, formatter.format_tail(pkginfo)
+    print(formatter.format_head(optparser, pkginfo, section), file=stream)
+    print(optparser.format_option_help(formatter), file=stream)
+    print(formatter.format_tail(pkginfo), file=stream)
 
 
 __all__ = ('OptionParser', 'Option', 'OptionGroup', 'OptionValueError',
diff --git a/third_party/logilab/common/pdf_ext.py b/third_party/logilab/common/pdf_ext.py
deleted file mode 100644
index 71c483b..0000000
--- a/third_party/logilab/common/pdf_ext.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:[email protected]
-#
-# This file is part of logilab-common.
-#
-# logilab-common is free software: you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option) any
-# later version.
-#
-# logilab-common is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
-"""Manipulate pdf and fdf files (pdftk recommended).
-
-Notes regarding pdftk, pdf forms and fdf files (form definition file)
-fields names can be extracted with:
-
-    pdftk orig.pdf generate_fdf output truc.fdf
-
-to merge fdf and pdf:
-
-    pdftk orig.pdf fill_form test.fdf output result.pdf [flatten]
-
-without flatten, one could further edit the resulting form.
-with flatten, everything is turned into text.
-
-
-
-
-"""
-__docformat__ = "restructuredtext en"
-# XXX seems very unix specific
-# TODO: check availability of pdftk at import
-
-
-import os
-
-HEAD="""%FDF-1.2
-%\xE2\xE3\xCF\xD3
-1 0 obj
-<<
-/FDF
-<<
-/Fields [
-"""
-
-TAIL="""]
->>
->>
-endobj
-trailer
-
-<<
-/Root 1 0 R
->>
-%%EOF
-"""
-
-def output_field( f ):
-    return "\xfe\xff" + "".join( [ "\x00"+c for c in f ] )
-
-def extract_keys(lines):
-    keys = []
-    for line in lines:
-        if line.startswith('/V'):
-            pass #print 'value',line
-        elif line.startswith('/T'):
-            key = line[7:-2]
-            key = ''.join(key.split('\x00'))
-            keys.append( key )
-    return keys
-
-def write_field(out, key, value):
-    out.write("<<\n")
-    if value:
-        out.write("/V (%s)\n" %value)
-    else:
-        out.write("/V /\n")
-    out.write("/T (%s)\n" % output_field(key) )
-    out.write(">> \n")
-
-def write_fields(out, fields):
-    out.write(HEAD)
-    for (key, value, comment) in fields:
-        write_field(out, key, value)
-        write_field(out, key+"a", value) # pour copie-carbone sur autres pages
-    out.write(TAIL)
-
-def extract_keys_from_pdf(filename):
-    # what about using 'pdftk filename dump_data_fields' and parsing the output ?
-    os.system('pdftk %s generate_fdf output /tmp/toto.fdf' % filename)
-    lines = file('/tmp/toto.fdf').readlines()
-    return extract_keys(lines)
-
-
-def fill_pdf(infile, outfile, fields):
-    write_fields(file('/tmp/toto.fdf', 'w'), fields)
-    os.system('pdftk %s fill_form /tmp/toto.fdf output %s flatten' % (infile, outfile))
-
-def testfill_pdf(infile, outfile):
-    keys = extract_keys_from_pdf(infile)
-    fields = []
-    for key in keys:
-        fields.append( (key, key, '') )
-    fill_pdf(infile, outfile, fields)
-
diff --git a/third_party/logilab/common/pyro_ext.py b/third_party/logilab/common/pyro_ext.py
index 0f4d279..5204b1b 100644
--- a/third_party/logilab/common/pyro_ext.py
+++ b/third_party/logilab/common/pyro_ext.py
@@ -118,7 +118,7 @@
     nsgroup, nsid = ns_group_and_id(nsid, defaultnsgroup)
     try:
         nsd = locate_ns(nshost)
-    except errors.PyroError, ex:
+    except errors.PyroError as ex:
         # name server not responding
         _LOGGER.error('can\'t locate pyro name server: %s', ex)
     else:
@@ -159,7 +159,7 @@
     try:
         nsd = locate_ns(nshost)
         pyrouri = nsd.resolve('%s.%s' % (nsgroup, nsid))
-    except errors.ProtocolError, ex:
+    except errors.ProtocolError as ex:
         raise errors.PyroError(
             'Could not connect to the Pyro name server (host: %s)' % nshost)
     except errors.NamingError:
diff --git a/third_party/logilab/common/pytest.py b/third_party/logilab/common/pytest.py
index 2d6ccf9..f94db7a 100644
--- a/third_party/logilab/common/pytest.py
+++ b/third_party/logilab/common/pytest.py
@@ -92,6 +92,9 @@
  * ``tata`` and ``titi`` match``rouge ^ carre``
  * ``titi`` match ``rouge and not carre``
 """
+
+from __future__ import print_function
+
 __docformat__ = "restructuredtext en"
 
 PYTEST_DOC = """%prog [OPTIONS] [testfile [testpattern]]
@@ -105,9 +108,6 @@
 
 pytest one (will run both test_thisone and test_thatone)
 pytest path/to/mytests.py -s not (will skip test_notthisone)
-
-pytest --coverage test_foo.py
-  (only if logilab.devtools is available)
 """
 
 ENABLE_DBC = False
@@ -118,13 +118,13 @@
 from time import time, clock
 import warnings
 import types
+from inspect import isgeneratorfunction, isclass
 
 from logilab.common.fileutils import abspath_listdir
 from logilab.common import textutils
 from logilab.common import testlib, STD_BLACKLIST
 # use the same unittest module as testlib
 from logilab.common.testlib import unittest, start_interactive_mode
-from logilab.common.compat import any
 import doctest
 
 import unittest as unittest_legacy
@@ -206,7 +206,7 @@
     and / or tester.
     """
     namespace = {}
-    execfile(path, namespace)
+    exec(open(path, 'rb').read(), namespace)
     if 'update_parser' in namespace:
         namespace['update_parser'](parser)
     return namespace.get('CustomPyTester', PyTester)
@@ -309,7 +309,7 @@
     we **have** to clean sys.modules to make sure the correct test_utils
     module is ran in B
     """
-    for modname, mod in sys.modules.items():
+    for modname, mod in list(sys.modules.items()):
         if mod is None:
             continue
         if not hasattr(mod, '__file__'):
@@ -336,8 +336,8 @@
     def show_report(self):
         """prints the report and returns appropriate exitcode"""
         # everything has been ran, print report
-        print "*" * 79
-        print self.report
+        print("*" * 79)
+        print(self.report)
 
     def get_errcode(self):
         # errcode set explicitly
@@ -360,13 +360,13 @@
                     dirs.remove(skipped)
             basename = osp.basename(dirname)
             if this_is_a_testdir(basename):
-                print "going into", dirname
+                print("going into", dirname)
                 # we found a testdir, let's explore it !
                 if not self.testonedir(dirname, exitfirst):
                     break
                 dirs[:] = []
         if self.report.ran == 0:
-            print "no test dir found testing here:", here
+            print("no test dir found testing here:", here)
             # if no test was found during the visit, consider
             # the local directory as a test directory even if
             # it doesn't have a traditional test directory name
@@ -385,10 +385,11 @@
                     try:
                         restartfile = open(FILE_RESTART, "w")
                         restartfile.close()
-                    except Exception, e:
-                        print >> sys.__stderr__, "Error while overwriting \
-succeeded test file :", osp.join(os.getcwd(), FILE_RESTART)
-                        raise e
+                    except Exception:
+                        print("Error while overwriting succeeded test file :",
+                              osp.join(os.getcwd(), FILE_RESTART),
+                              file=sys.__stderr__)
+                        raise
                 # run test and collect information
                 prog = self.testfile(filename, batchmode=True)
                 if exitfirst and (prog is None or not prog.result.wasSuccessful()):
@@ -412,15 +413,13 @@
             try:
                 restartfile = open(FILE_RESTART, "w")
                 restartfile.close()
-            except Exception, e:
-                print >> sys.__stderr__, "Error while overwriting \
-succeeded test file :", osp.join(os.getcwd(), FILE_RESTART)
-                raise e
+            except Exception:
+                print("Error while overwriting succeeded test file :",
+                      osp.join(os.getcwd(), FILE_RESTART), file=sys.__stderr__)
+                raise
         modname = osp.basename(filename)[:-3]
-        try:
-            print >> sys.stderr, ('  %s  ' % osp.basename(filename)).center(70, '=')
-        except TypeError: # < py 2.4 bw compat
-            print >> sys.stderr, ('  %s  ' % osp.basename(filename)).center(70)
+        print(('  %s  ' % osp.basename(filename)).center(70, '='),
+              file=sys.__stderr__)
         try:
             tstart, cstart = time(), clock()
             try:
@@ -428,16 +427,17 @@
                                                  options=self.options, outstream=sys.stderr)
             except KeyboardInterrupt:
                 raise
-            except SystemExit, exc:
+            except SystemExit as exc:
                 self.errcode = exc.code
                 raise
             except testlib.SkipTest:
-                print "Module skipped:", filename
+                print("Module skipped:", filename)
                 self.report.skip_module(filename)
                 return None
             except Exception:
                 self.report.failed_to_test_module(filename)
-                print >> sys.stderr, 'unhandled exception occurred while testing', modname
+                print('unhandled exception occurred while testing', modname,
+                      file=sys.stderr)
                 import traceback
                 traceback.print_exc(file=sys.stderr)
                 return None
@@ -488,7 +488,7 @@
         from django.test.utils import teardown_test_environment
         from django.test.utils import destroy_test_db
         teardown_test_environment()
-        print 'destroying', self.dbname
+        print('destroying', self.dbname)
         destroy_test_db(self.dbname, verbosity=0)
 
     def testall(self, exitfirst=False):
@@ -506,7 +506,7 @@
             else:
                 basename = osp.basename(dirname)
                 if basename in ('test', 'tests'):
-                    print "going into", dirname
+                    print("going into", dirname)
                     # we found a testdir, let's explore it !
                     if not self.testonedir(dirname, exitfirst):
                         break
@@ -547,7 +547,8 @@
             os.chdir(dirname)
         self.load_django_settings(dirname)
         modname = osp.basename(filename)[:-3]
-        print >>sys.stderr, ('  %s  ' % osp.basename(filename)).center(70, '=')
+        print(('  %s  ' % osp.basename(filename)).center(70, '='),
+              file=sys.stderr)
         try:
             try:
                 tstart, cstart = time(), clock()
@@ -559,12 +560,12 @@
                 return testprog
             except SystemExit:
                 raise
-            except Exception, exc:
+            except Exception as exc:
                 import traceback
                 traceback.print_exc()
                 self.report.failed_to_test_module(filename)
-                print 'unhandled exception occurred while testing', modname
-                print 'error: %s' % exc
+                print('unhandled exception occurred while testing', modname)
+                print('error: %s' % exc)
                 return None
         finally:
             self.after_testfile()
@@ -604,7 +605,7 @@
                       action="callback", help="Verbose output")
     parser.add_option('-i', '--pdb', callback=rebuild_and_store,
                       dest="pdb", action="callback",
-                      help="Enable test failure inspection (conflicts with --coverage)")
+                      help="Enable test failure inspection")
     parser.add_option('-x', '--exitfirst', callback=rebuild_and_store,
                       dest="exitfirst", default=False,
                       action="callback", help="Exit on first failure "
@@ -631,14 +632,6 @@
     parser.add_option('-m', '--match', default=None, dest='tags_pattern',
                       help="only execute test whose tag match the current pattern")
 
-    try:
-        from logilab.devtools.lib.coverage import Coverage
-        parser.add_option('--coverage', dest="coverage", default=False,
-                          action="store_true",
-                          help="run tests with pycoverage (conflicts with --pdb)")
-    except ImportError:
-        pass
-
     if DJANGO_FOUND:
         parser.add_option('-J', '--django', dest='django', default=False,
                           action="store_true",
@@ -652,8 +645,6 @@
     """
     # parse the command line
     options, args = parser.parse_args()
-    if options.pdb and getattr(options, 'coverage', False):
-        parser.error("'pdb' and 'coverage' options are exclusive")
     filenames = [arg for arg in args if arg.endswith('.py')]
     if filenames:
         if len(filenames) > 1:
@@ -683,16 +674,9 @@
     options, explicitfile = parseargs(parser)
     # mock a new command line
     sys.argv[1:] = parser.newargs
-    covermode = getattr(options, 'coverage', None)
     cvg = None
     if not '' in sys.path:
         sys.path.insert(0, '')
-    if covermode:
-        # control_import_coverage(rootdir)
-        from logilab.devtools.lib.coverage import Coverage
-        cvg = Coverage([rootdir])
-        cvg.erase()
-        cvg.start()
     if DJANGO_FOUND and options.django:
         tester = DjangoTester(cvg, options)
     else:
@@ -710,7 +694,7 @@
                 prof = hotshot.Profile(options.profile)
                 prof.runcall(cmd, *args)
                 prof.close()
-                print 'profile data saved in', options.profile
+                print('profile data saved in', options.profile)
             else:
                 cmd(*args)
         except SystemExit:
@@ -719,12 +703,7 @@
             import traceback
             traceback.print_exc()
     finally:
-        if covermode:
-            cvg.stop()
-            cvg.save()
         tester.show_report()
-        if covermode:
-            print 'coverage information stored, use it with pycoverage -ra'
         sys.exit(tester.errcode)
 
 class SkipAwareTestProgram(unittest.TestProgram):
@@ -816,7 +795,7 @@
             else:
                 self.testNames = (self.defaultTest, )
             self.createTests()
-        except getopt.error, msg:
+        except getopt.error as msg:
             self.usageExit(msg)
 
     def runTests(self):
@@ -865,7 +844,7 @@
                     removeSucceededTests(self.test, succeededtests)
                 finally:
                     restartfile.close()
-            except Exception, ex:
+            except Exception as ex:
                 raise Exception("Error while reading succeeded tests into %s: %s"
                                 % (osp.join(os.getcwd(), FILE_RESTART), ex))
 
@@ -907,17 +886,16 @@
         else:
             if isinstance(test, testlib.TestCase):
                 meth = test._get_test_method()
-                func = meth.im_func
-                testname = '%s.%s' % (meth.im_class.__name__, func.__name__)
+                testname = '%s.%s' % (test.__name__, meth.__name__)
             elif isinstance(test, types.FunctionType):
                 func = test
                 testname = func.__name__
             elif isinstance(test, types.MethodType):
-                func = test.im_func
-                testname = '%s.%s' % (test.im_class.__name__, func.__name__)
+                cls = test.__self__.__class__
+                testname = '%s.%s' % (cls.__name__, test.__name__)
             else:
                 return True # Not sure when this happens
-            if testlib.is_generator(test) and skipgenerator:
+            if isgeneratorfunction(test) and skipgenerator:
                 return self.does_match_tags(test) # Let inner tests decide at run time
         if self._this_is_skipped(testname):
             return False # this was explicitly skipped
@@ -1025,8 +1003,7 @@
     def _collect_tests(self, module):
         tests = {}
         for obj in vars(module).values():
-            if (issubclass(type(obj), (types.ClassType, type)) and
-                 issubclass(obj, unittest.TestCase)):
+            if isclass(obj) and issubclass(obj, unittest.TestCase):
                 classname = obj.__name__
                 if classname[0] == '_' or self._this_is_skipped(classname):
                     continue
diff --git a/third_party/logilab/common/registry.py b/third_party/logilab/common/registry.py
new file mode 100644
index 0000000..a52b2eb
--- /dev/null
+++ b/third_party/logilab/common/registry.py
@@ -0,0 +1,1119 @@
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:[email protected]
+#
+# This file is part of Logilab-common.
+#
+# Logilab-common is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by the
+# Free Software Foundation, either version 2.1 of the License, or (at your
+# option) any later version.
+#
+# Logilab-common is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with Logilab-common.  If not, see <http://www.gnu.org/licenses/>.
+"""This module provides bases for predicates dispatching (the pattern in use
+here is similar to what's refered as multi-dispatch or predicate-dispatch in the
+literature, though a bit different since the idea is to select across different
+implementation 'e.g. classes), not to dispatch a message to a function or
+method. It contains the following classes:
+
+* :class:`RegistryStore`, the top level object which loads implementation
+  objects and stores them into registries. You'll usually use it to access
+  registries and their contained objects;
+
+* :class:`Registry`, the base class which contains objects semantically grouped
+  (for instance, sharing a same API, hence the 'implementation' name). You'll
+  use it to select the proper implementation according to a context. Notice you
+  may use registries on their own without using the store.
+
+.. Note::
+
+  implementation objects are usually designed to be accessed through the
+  registry and not by direct instantiation, besides to use it as base classe.
+
+The selection procedure is delegated to a selector, which is responsible for
+scoring the object according to some context. At the end of the selection, if an
+implementation has been found, an instance of this class is returned. A selector
+is built from one or more predicates combined together using AND, OR, NOT
+operators (actually `&`, `|` and `~`). You'll thus find some base classes to
+build predicates:
+
+* :class:`Predicate`, the abstract base predicate class
+
+* :class:`AndPredicate`, :class:`OrPredicate`, :class:`NotPredicate`, which you
+  shouldn't have to use directly. You'll use `&`, `|` and '~' operators between
+  predicates directly
+
+* :func:`objectify_predicate`
+
+You'll eventually find one concrete predicate: :class:`yes`
+
+.. autoclass:: RegistryStore
+.. autoclass:: Registry
+
+Predicates
+----------
+.. autoclass:: Predicate
+.. autofunc:: objectify_predicate
+.. autoclass:: yes
+
+Debugging
+---------
+.. autoclass:: traced_selection
+
+Exceptions
+----------
+.. autoclass:: RegistryException
+.. autoclass:: RegistryNotFound
+.. autoclass:: ObjectNotFound
+.. autoclass:: NoSelectableObject
+"""
+
+from __future__ import print_function
+
+__docformat__ = "restructuredtext en"
+
+import sys
+import types
+import weakref
+import traceback as tb
+from os import listdir, stat
+from os.path import join, isdir, exists
+from logging import getLogger
+from warnings import warn
+
+from six import string_types, add_metaclass
+
+from logilab.common.modutils import modpath_from_file
+from logilab.common.logging_ext import set_log_methods
+from logilab.common.decorators import classproperty
+
+
+class RegistryException(Exception):
+    """Base class for registry exception."""
+
+class RegistryNotFound(RegistryException):
+    """Raised when an unknown registry is requested.
+
+    This is usually a programming/typo error.
+    """
+
+class ObjectNotFound(RegistryException):
+    """Raised when an unregistered object is requested.
+
+    This may be a programming/typo or a misconfiguration error.
+    """
+
+class NoSelectableObject(RegistryException):
+    """Raised when no object is selectable for a given context."""
+    def __init__(self, args, kwargs, objects):
+        self.args = args
+        self.kwargs = kwargs
+        self.objects = objects
+
+    def __str__(self):
+        return ('args: %s, kwargs: %s\ncandidates: %s'
+                % (self.args, self.kwargs.keys(), self.objects))
+
+
+def _modname_from_path(path, extrapath=None):
+    modpath = modpath_from_file(path, extrapath)
+    # omit '__init__' from package's name to avoid loading that module
+    # once for each name when it is imported by some other object
+    # module. This supposes import in modules are done as::
+    #
+    #   from package import something
+    #
+    # not::
+    #
+    #   from package.__init__ import something
+    #
+    # which seems quite correct.
+    if modpath[-1] == '__init__':
+        modpath.pop()
+    return '.'.join(modpath)
+
+
+def _toload_info(path, extrapath, _toload=None):
+    """Return a dictionary of <modname>: <modpath> and an ordered list of
+    (file, module name) to load
+    """
+    if _toload is None:
+        assert isinstance(path, list)
+        _toload = {}, []
+    for fileordir in path:
+        if isdir(fileordir) and exists(join(fileordir, '__init__.py')):
+            subfiles = [join(fileordir, fname) for fname in listdir(fileordir)]
+            _toload_info(subfiles, extrapath, _toload)
+        elif fileordir[-3:] == '.py':
+            modname = _modname_from_path(fileordir, extrapath)
+            _toload[0][modname] = fileordir
+            _toload[1].append((fileordir, modname))
+    return _toload
+
+
+class RegistrableObject(object):
+    """This is the base class for registrable objects which are selected
+    according to a context.
+
+    :attr:`__registry__`
+      name of the registry for this object (string like 'views',
+      'templates'...). You may want to define `__registries__` directly if your
+      object should be registered in several registries.
+
+    :attr:`__regid__`
+      object's identifier in the registry (string like 'main',
+      'primary', 'folder_box')
+
+    :attr:`__select__`
+      class'selector
+
+    Moreover, the `__abstract__` attribute may be set to True to indicate that a
+    class is abstract and should not be registered.
+
+    You don't have to inherit from this class to put it in a registry (having
+    `__regid__` and `__select__` is enough), though this is needed for classes
+    that should be automatically registered.
+    """
+
+    __registry__ = None
+    __regid__ = None
+    __select__ = None
+    __abstract__ = True # see doc snipppets below (in Registry class)
+
+    @classproperty
+    def __registries__(cls):
+        if cls.__registry__ is None:
+            return ()
+        return (cls.__registry__,)
+
+
+class RegistrableInstance(RegistrableObject):
+    """Inherit this class if you want instances of the classes to be
+    automatically registered.
+    """
+
+    def __new__(cls, *args, **kwargs):
+        """Add a __module__ attribute telling the module where the instance was
+        created, for automatic registration.
+        """
+        obj = super(RegistrableInstance, cls).__new__(cls)
+        # XXX subclass must no override __new__
+        filepath = tb.extract_stack(limit=2)[0][0]
+        obj.__module__ = _modname_from_path(filepath)
+        return obj
+
+
+class Registry(dict):
+    """The registry store a set of implementations associated to identifier:
+
+    * to each identifier are associated a list of implementations
+
+    * to select an implementation of a given identifier, you should use one of the
+      :meth:`select` or :meth:`select_or_none` method
+
+    * to select a list of implementations for a context, you should use the
+      :meth:`possible_objects` method
+
+    * dictionary like access to an identifier will return the bare list of
+      implementations for this identifier.
+
+    To be usable in a registry, the only requirement is to have a `__select__`
+    attribute.
+
+    At the end of the registration process, the :meth:`__registered__`
+    method is called on each registered object which have them, given the
+    registry in which it's registered as argument.
+
+    Registration methods:
+
+    .. automethod: register
+    .. automethod: unregister
+
+    Selection methods:
+
+    .. automethod: select
+    .. automethod: select_or_none
+    .. automethod: possible_objects
+    .. automethod: object_by_id
+    """
+    def __init__(self, debugmode):
+        super(Registry, self).__init__()
+        self.debugmode = debugmode
+
+    def __getitem__(self, name):
+        """return the registry (list of implementation objects) associated to
+        this name
+        """
+        try:
+            return super(Registry, self).__getitem__(name)
+        except KeyError:
+            exc = ObjectNotFound(name)
+            exc.__traceback__ = sys.exc_info()[-1]
+            raise exc
+
+    @classmethod
+    def objid(cls, obj):
+        """returns a unique identifier for an object stored in the registry"""
+        return '%s.%s' % (obj.__module__, cls.objname(obj))
+
+    @classmethod
+    def objname(cls, obj):
+        """returns a readable name for an object stored in the registry"""
+        return getattr(obj, '__name__', id(obj))
+
+    def initialization_completed(self):
+        """call method __registered__() on registered objects when the callback
+        is defined"""
+        for objects in self.values():
+            for objectcls in objects:
+                registered = getattr(objectcls, '__registered__', None)
+                if registered:
+                    registered(self)
+        if self.debugmode:
+            wrap_predicates(_lltrace)
+
+    def register(self, obj, oid=None, clear=False):
+        """base method to add an object in the registry"""
+        assert not '__abstract__' in obj.__dict__, obj
+        assert obj.__select__, obj
+        oid = oid or obj.__regid__
+        assert oid, ('no explicit name supplied to register object %s, '
+                     'which has no __regid__ set' % obj)
+        if clear:
+            objects = self[oid] =  []
+        else:
+            objects = self.setdefault(oid, [])
+        assert not obj in objects, 'object %s is already registered' % obj
+        objects.append(obj)
+
+    def register_and_replace(self, obj, replaced):
+        """remove <replaced> and register <obj>"""
+        # XXXFIXME this is a duplication of unregister()
+        # remove register_and_replace in favor of unregister + register
+        # or simplify by calling unregister then register here
+        if not isinstance(replaced, string_types):
+            replaced = self.objid(replaced)
+        # prevent from misspelling
+        assert obj is not replaced, 'replacing an object by itself: %s' % obj
+        registered_objs = self.get(obj.__regid__, ())
+        for index, registered in enumerate(registered_objs):
+            if self.objid(registered) == replaced:
+                del registered_objs[index]
+                break
+        else:
+            self.warning('trying to replace %s that is not registered with %s',
+                         replaced, obj)
+        self.register(obj)
+
+    def unregister(self, obj):
+        """remove object <obj> from this registry"""
+        objid = self.objid(obj)
+        oid = obj.__regid__
+        for registered in self.get(oid, ()):
+            # use self.objid() to compare objects because vreg will probably
+            # have its own version of the object, loaded through execfile
+            if self.objid(registered) == objid:
+                self[oid].remove(registered)
+                break
+        else:
+            self.warning('can\'t remove %s, no id %s in the registry',
+                         objid, oid)
+
+    def all_objects(self):
+        """return a list containing all objects in this registry.
+        """
+        result = []
+        for objs in self.values():
+            result += objs
+        return result
+
+    # dynamic selection methods ################################################
+
+    def object_by_id(self, oid, *args, **kwargs):
+        """return object with the `oid` identifier. Only one object is expected
+        to be found.
+
+        raise :exc:`ObjectNotFound` if there are no object with id `oid` in this
+        registry
+
+        raise :exc:`AssertionError` if there is more than one object there
+        """
+        objects = self[oid]
+        assert len(objects) == 1, objects
+        return objects[0](*args, **kwargs)
+
+    def select(self, __oid, *args, **kwargs):
+        """return the most specific object among those with the given oid
+        according to the given context.
+
+        raise :exc:`ObjectNotFound` if there are no object with id `oid` in this
+        registry
+
+        raise :exc:`NoSelectableObject` if no object can be selected
+        """
+        obj =  self._select_best(self[__oid], *args, **kwargs)
+        if obj is None:
+            raise NoSelectableObject(args, kwargs, self[__oid] )
+        return obj
+
+    def select_or_none(self, __oid, *args, **kwargs):
+        """return the most specific object among those with the given oid
+        according to the given context, or None if no object applies.
+        """
+        try:
+            return self._select_best(self[__oid], *args, **kwargs)
+        except ObjectNotFound:
+            return None
+
+    def possible_objects(self, *args, **kwargs):
+        """return an iterator on possible objects in this registry for the given
+        context
+        """
+        for objects in self.values():
+            obj = self._select_best(objects,  *args, **kwargs)
+            if obj is None:
+                continue
+            yield obj
+
+    def _select_best(self, objects, *args, **kwargs):
+        """return an instance of the most specific object according
+        to parameters
+
+        return None if not object apply (don't raise `NoSelectableObject` since
+        it's costly when searching objects using `possible_objects`
+        (e.g. searching for hooks).
+        """
+        score, winners = 0, None
+        for obj in objects:
+            objectscore = obj.__select__(obj, *args, **kwargs)
+            if objectscore > score:
+                score, winners = objectscore, [obj]
+            elif objectscore > 0 and objectscore == score:
+                winners.append(obj)
+        if winners is None:
+            return None
+        if len(winners) > 1:
+            # log in production environement / test, error while debugging
+            msg = 'select ambiguity: %s\n(args: %s, kwargs: %s)'
+            if self.debugmode:
+                # raise bare exception in debug mode
+                raise Exception(msg % (winners, args, kwargs.keys()))
+            self.error(msg, winners, args, kwargs.keys())
+        # return the result of calling the object
+        return self.selected(winners[0], args, kwargs)
+
+    def selected(self, winner, args, kwargs):
+        """override here if for instance you don't want "instanciation"
+        """
+        return winner(*args, **kwargs)
+
+    # these are overridden by set_log_methods below
+    # only defining here to prevent pylint from complaining
+    info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None
+
+
+def obj_registries(cls, registryname=None):
+    """return a tuple of registry names (see __registries__)"""
+    if registryname:
+        return (registryname,)
+    return cls.__registries__
+
+
+class RegistryStore(dict):
+    """This class is responsible for loading objects and storing them
+    in their registry which is created on the fly as needed.
+
+    It handles dynamic registration of objects and provides a
+    convenient api to access them. To be recognized as an object that
+    should be stored into one of the store's registry
+    (:class:`Registry`), an object must provide the following
+    attributes, used control how they interact with the registry:
+
+    :attr:`__registries__`
+      list of registry names (string like 'views', 'templates'...) into which
+      the object should be registered
+
+    :attr:`__regid__`
+      object identifier in the registry (string like 'main',
+      'primary', 'folder_box')
+
+    :attr:`__select__`
+      the object predicate selectors
+
+    Moreover, the :attr:`__abstract__` attribute may be set to `True`
+    to indicate that an object is abstract and should not be registered
+    (such inherited attributes not considered).
+
+    .. Note::
+
+      When using the store to load objects dynamically, you *always* have
+      to use **super()** to get the methods and attributes of the
+      superclasses, and not use the class identifier. If not, you'll get into
+      trouble at reload time.
+
+      For example, instead of writing::
+
+          class Thing(Parent):
+              __regid__ = 'athing'
+              __select__ = yes()
+
+              def f(self, arg1):
+                  Parent.f(self, arg1)
+
+      You must write::
+
+          class Thing(Parent):
+              __regid__ = 'athing'
+              __select__ = yes()
+
+              def f(self, arg1):
+                  super(Thing, self).f(arg1)
+
+    Controlling object registration
+    -------------------------------
+
+    Dynamic loading is triggered by calling the
+    :meth:`register_objects` method, given a list of directories to
+    inspect for python modules.
+
+    .. automethod: register_objects
+
+    For each module, by default, all compatible objects are registered
+    automatically. However if some objects come as replacement of
+    other objects, or have to be included only if some condition is
+    met, you'll have to define a `registration_callback(vreg)`
+    function in the module and explicitly register **all objects** in
+    this module, using the api defined below.
+
+
+    .. automethod:: RegistryStore.register_all
+    .. automethod:: RegistryStore.register_and_replace
+    .. automethod:: RegistryStore.register
+    .. automethod:: RegistryStore.unregister
+
+    .. Note::
+        Once the function `registration_callback(vreg)` is implemented in a
+        module, all the objects from this module have to be explicitly
+        registered as it disables the automatic object registration.
+
+
+    Examples:
+
+    .. sourcecode:: python
+
+       def registration_callback(store):
+          # register everything in the module except BabarClass
+          store.register_all(globals().values(), __name__, (BabarClass,))
+
+          # conditionally register BabarClass
+          if 'babar_relation' in store.schema:
+              store.register(BabarClass)
+
+    In this example, we register all application object classes defined in the module
+    except `BabarClass`. This class is then registered only if the 'babar_relation'
+    relation type is defined in the instance schema.
+
+    .. sourcecode:: python
+
+       def registration_callback(store):
+          store.register(Elephant)
+          # replace Babar by Celeste
+          store.register_and_replace(Celeste, Babar)
+
+    In this example, we explicitly register classes one by one:
+
+    * the `Elephant` class
+    * the `Celeste` to replace `Babar`
+
+    If at some point we register a new appobject class in this module, it won't be
+    registered at all without modification to the `registration_callback`
+    implementation. The first example will register it though, thanks to the call
+    to the `register_all` method.
+
+    Controlling registry instantiation
+    ----------------------------------
+
+    The `REGISTRY_FACTORY` class dictionary allows to specify which class should
+    be instantiated for a given registry name. The class associated to `None`
+    key will be the class used when there is no specific class for a name.
+    """
+
+    def __init__(self, debugmode=False):
+        super(RegistryStore, self).__init__()
+        self.debugmode = debugmode
+
+    def reset(self):
+        """clear all registries managed by this store"""
+        # don't use self.clear, we want to keep existing subdictionaries
+        for subdict in self.values():
+            subdict.clear()
+        self._lastmodifs = {}
+
+    def __getitem__(self, name):
+        """return the registry (dictionary of class objects) associated to
+        this name
+        """
+        try:
+            return super(RegistryStore, self).__getitem__(name)
+        except KeyError:
+            exc = RegistryNotFound(name)
+            exc.__traceback__ = sys.exc_info()[-1]
+            raise exc
+
+    # methods for explicit (un)registration ###################################
+
+    # default class, when no specific class set
+    REGISTRY_FACTORY = {None: Registry}
+
+    def registry_class(self, regid):
+        """return existing registry named regid or use factory to create one and
+        return it"""
+        try:
+            return self.REGISTRY_FACTORY[regid]
+        except KeyError:
+            return self.REGISTRY_FACTORY[None]
+
+    def setdefault(self, regid):
+        try:
+            return self[regid]
+        except RegistryNotFound:
+            self[regid] = self.registry_class(regid)(self.debugmode)
+            return self[regid]
+
+    def register_all(self, objects, modname, butclasses=()):
+        """register registrable objects into `objects`.
+
+        Registrable objects are properly configured subclasses of
+        :class:`RegistrableObject`.  Objects which are not defined in the module
+        `modname` or which are in `butclasses` won't be registered.
+
+        Typical usage is:
+
+        .. sourcecode:: python
+
+            store.register_all(globals().values(), __name__, (ClassIWantToRegisterExplicitly,))
+
+        So you get partially automatic registration, keeping manual registration
+        for some object (to use
+        :meth:`~logilab.common.registry.RegistryStore.register_and_replace` for
+        instance).
+        """
+        assert isinstance(modname, string_types), \
+            'modname expected to be a module name (ie string), got %r' % modname
+        for obj in objects:
+            if self.is_registrable(obj) and obj.__module__ == modname and not obj in butclasses:
+                if isinstance(obj, type):
+                    self._load_ancestors_then_object(modname, obj, butclasses)
+                else:
+                    self.register(obj)
+
+    def register(self, obj, registryname=None, oid=None, clear=False):
+        """register `obj` implementation into `registryname` or
+        `obj.__registries__` if not specified, with identifier `oid` or
+        `obj.__regid__` if not specified.
+
+        If `clear` is true, all objects with the same identifier will be
+        previously unregistered.
+        """
+        assert not obj.__dict__.get('__abstract__'), obj
+        for registryname in obj_registries(obj, registryname):
+            registry = self.setdefault(registryname)
+            registry.register(obj, oid=oid, clear=clear)
+            self.debug("register %s in %s['%s']",
+                       registry.objname(obj), registryname, oid or obj.__regid__)
+            self._loadedmods.setdefault(obj.__module__, {})[registry.objid(obj)] = obj
+
+    def unregister(self, obj, registryname=None):
+        """unregister `obj` object from the registry `registryname` or
+        `obj.__registries__` if not specified.
+        """
+        for registryname in obj_registries(obj, registryname):
+            registry = self[registryname]
+            registry.unregister(obj)
+            self.debug("unregister %s from %s['%s']",
+                       registry.objname(obj), registryname, obj.__regid__)
+
+    def register_and_replace(self, obj, replaced, registryname=None):
+        """register `obj` object into `registryname` or
+        `obj.__registries__` if not specified. If found, the `replaced` object
+        will be unregistered first (else a warning will be issued as it is
+        generally unexpected).
+        """
+        for registryname in obj_registries(obj, registryname):
+            registry = self[registryname]
+            registry.register_and_replace(obj, replaced)
+            self.debug("register %s in %s['%s'] instead of %s",
+                       registry.objname(obj), registryname, obj.__regid__,
+                       registry.objname(replaced))
+
+    # initialization methods ###################################################
+
+    def init_registration(self, path, extrapath=None):
+        """reset registry and walk down path to return list of (path, name)
+        file modules to be loaded"""
+        # XXX make this private by renaming it to _init_registration ?
+        self.reset()
+        # compute list of all modules that have to be loaded
+        self._toloadmods, filemods = _toload_info(path, extrapath)
+        # XXX is _loadedmods still necessary ? It seems like it's useful
+        #     to avoid loading same module twice, especially with the
+        #     _load_ancestors_then_object logic but this needs to be checked
+        self._loadedmods = {}
+        return filemods
+
+    def register_objects(self, path, extrapath=None):
+        """register all objects found walking down <path>"""
+        # load views from each directory in the instance's path
+        # XXX inline init_registration ?
+        filemods = self.init_registration(path, extrapath)
+        for filepath, modname in filemods:
+            self.load_file(filepath, modname)
+        self.initialization_completed()
+
+    def initialization_completed(self):
+        """call initialization_completed() on all known registries"""
+        for reg in self.values():
+            reg.initialization_completed()
+
+    def _mdate(self, filepath):
+        """ return the modification date of a file path """
+        try:
+            return stat(filepath)[-2]
+        except OSError:
+            # this typically happens on emacs backup files (.#foo.py)
+            self.warning('Unable to load %s. It is likely to be a backup file',
+                         filepath)
+            return None
+
+    def is_reload_needed(self, path):
+        """return True if something module changed and the registry should be
+        reloaded
+        """
+        lastmodifs = self._lastmodifs
+        for fileordir in path:
+            if isdir(fileordir) and exists(join(fileordir, '__init__.py')):
+                if self.is_reload_needed([join(fileordir, fname)
+                                          for fname in listdir(fileordir)]):
+                    return True
+            elif fileordir[-3:] == '.py':
+                mdate = self._mdate(fileordir)
+                if mdate is None:
+                    continue # backup file, see _mdate implementation
+                elif "flymake" in fileordir:
+                    # flymake + pylint in use, don't consider these they will corrupt the registry
+                    continue
+                if fileordir not in lastmodifs or lastmodifs[fileordir] < mdate:
+                    self.info('File %s changed since last visit', fileordir)
+                    return True
+        return False
+
+    def load_file(self, filepath, modname):
+        """ load registrable objects (if any) from a python file """
+        from logilab.common.modutils import load_module_from_name
+        if modname in self._loadedmods:
+            return
+        self._loadedmods[modname] = {}
+        mdate = self._mdate(filepath)
+        if mdate is None:
+            return # backup file, see _mdate implementation
+        elif "flymake" in filepath:
+            # flymake + pylint in use, don't consider these they will corrupt the registry
+            return
+        # set update time before module loading, else we get some reloading
+        # weirdness in case of syntax error or other error while importing the
+        # module
+        self._lastmodifs[filepath] = mdate
+        # load the module
+        module = load_module_from_name(modname)
+        self.load_module(module)
+
+    def load_module(self, module):
+        """Automatically handle module objects registration.
+
+        Instances are registered as soon as they are hashable and have the
+        following attributes:
+
+        * __regid__ (a string)
+        * __select__ (a callable)
+        * __registries__ (a tuple/list of string)
+
+        For classes this is a bit more complicated :
+
+        - first ensure parent classes are already registered
+
+        - class with __abstract__ == True in their local dictionary are skipped
+
+        - object class needs to have registries and identifier properly set to a
+          non empty string to be registered.
+        """
+        self.info('loading %s from %s', module.__name__, module.__file__)
+        if hasattr(module, 'registration_callback'):
+            module.registration_callback(self)
+        else:
+            self.register_all(vars(module).values(), module.__name__)
+
+    def _load_ancestors_then_object(self, modname, objectcls, butclasses=()):
+        """handle class registration according to rules defined in
+        :meth:`load_module`
+        """
+        # backward compat, we used to allow whatever else than classes
+        if not isinstance(objectcls, type):
+            if self.is_registrable(objectcls) and objectcls.__module__ == modname:
+                self.register(objectcls)
+            return
+        # imported classes
+        objmodname = objectcls.__module__
+        if objmodname != modname:
+            # The module of the object is not the same as the currently
+            # worked on module, or this is actually an instance, which
+            # has no module at all
+            if objmodname in self._toloadmods:
+                # if this is still scheduled for loading, let's proceed immediately,
+                # but using the object module
+                self.load_file(self._toloadmods[objmodname], objmodname)
+            return
+        # ensure object hasn't been already processed
+        clsid = '%s.%s' % (modname, objectcls.__name__)
+        if clsid in self._loadedmods[modname]:
+            return
+        self._loadedmods[modname][clsid] = objectcls
+        # ensure ancestors are registered
+        for parent in objectcls.__bases__:
+            self._load_ancestors_then_object(modname, parent, butclasses)
+        # ensure object is registrable
+        if objectcls in butclasses or not self.is_registrable(objectcls):
+            return
+        # backward compat
+        reg = self.setdefault(obj_registries(objectcls)[0])
+        if reg.objname(objectcls)[0] == '_':
+            warn("[lgc 0.59] object whose name start with '_' won't be "
+                 "skipped anymore at some point, use __abstract__ = True "
+                 "instead (%s)" % objectcls, DeprecationWarning)
+            return
+        # register, finally
+        self.register(objectcls)
+
+    @classmethod
+    def is_registrable(cls, obj):
+        """ensure `obj` should be registered
+
+        as arbitrary stuff may be registered, do a lot of check and warn about
+        weird cases (think to dumb proxy objects)
+        """
+        if isinstance(obj, type):
+            if not issubclass(obj, RegistrableObject):
+                # ducktyping backward compat
+                if not (getattr(obj, '__registries__', None)
+                        and getattr(obj, '__regid__', None)
+                        and getattr(obj, '__select__', None)):
+                    return False
+            elif issubclass(obj, RegistrableInstance):
+                return False
+        elif not isinstance(obj, RegistrableInstance):
+            return False
+        if not obj.__regid__:
+            return False # no regid
+        registries = obj.__registries__
+        if not registries:
+            return False # no registries
+        selector = obj.__select__
+        if not selector:
+            return False # no selector
+        if obj.__dict__.get('__abstract__', False):
+            return False
+        # then detect potential problems that should be warned
+        if not isinstance(registries, (tuple, list)):
+            cls.warning('%s has __registries__ which is not a list or tuple', obj)
+            return False
+        if not callable(selector):
+            cls.warning('%s has not callable __select__', obj)
+            return False
+        return True
+
+    # these are overridden by set_log_methods below
+    # only defining here to prevent pylint from complaining
+    info = warning = error = critical = exception = debug = lambda msg, *a, **kw: None
+
+
+# init logging
+set_log_methods(RegistryStore, getLogger('registry.store'))
+set_log_methods(Registry, getLogger('registry'))
+
+
+# helpers for debugging selectors
+TRACED_OIDS = None
+
+def _trace_selector(cls, selector, args, ret):
+    vobj = args[0]
+    if TRACED_OIDS == 'all' or vobj.__regid__ in TRACED_OIDS:
+        print('%s -> %s for %s(%s)' % (cls, ret, vobj, vobj.__regid__))
+
+def _lltrace(selector):
+    """use this decorator on your predicates so they become traceable with
+    :class:`traced_selection`
+    """
+    def traced(cls, *args, **kwargs):
+        ret = selector(cls, *args, **kwargs)
+        if TRACED_OIDS is not None:
+            _trace_selector(cls, selector, args, ret)
+        return ret
+    traced.__name__ = selector.__name__
+    traced.__doc__ = selector.__doc__
+    return traced
+
+class traced_selection(object): # pylint: disable=C0103
+    """
+    Typical usage is :
+
+    .. sourcecode:: python
+
+        >>> from logilab.common.registry import traced_selection
+        >>> with traced_selection():
+        ...     # some code in which you want to debug selectors
+        ...     # for all objects
+
+    This will yield lines like this in the logs::
+
+        selector one_line_rset returned 0 for <class 'elephant.Babar'>
+
+    You can also give to :class:`traced_selection` the identifiers of objects on
+    which you want to debug selection ('oid1' and 'oid2' in the example above).
+
+    .. sourcecode:: python
+
+        >>> with traced_selection( ('regid1', 'regid2') ):
+        ...     # some code in which you want to debug selectors
+        ...     # for objects with __regid__ 'regid1' and 'regid2'
+
+    A potentially useful point to set up such a tracing function is
+    the `logilab.common.registry.Registry.select` method body.
+    """
+
+    def __init__(self, traced='all'):
+        self.traced = traced
+
+    def __enter__(self):
+        global TRACED_OIDS
+        TRACED_OIDS = self.traced
+
+    def __exit__(self, exctype, exc, traceback):
+        global TRACED_OIDS
+        TRACED_OIDS = None
+        return traceback is None
+
+# selector base classes and operations ########################################
+
+def objectify_predicate(selector_func):
+    """Most of the time, a simple score function is enough to build a selector.
+    The :func:`objectify_predicate` decorator turn it into a proper selector
+    class::
+
+        @objectify_predicate
+        def one(cls, req, rset=None, **kwargs):
+            return 1
+
+        class MyView(View):
+            __select__ = View.__select__ & one()
+
+    """
+    return type(selector_func.__name__, (Predicate,),
+                {'__doc__': selector_func.__doc__,
+                 '__call__': lambda self, *a, **kw: selector_func(*a, **kw)})
+
+
+_PREDICATES = {}
+
+def wrap_predicates(decorator):
+    for predicate in _PREDICATES.values():
+        if not '_decorators' in predicate.__dict__:
+            predicate._decorators = set()
+        if decorator in predicate._decorators:
+            continue
+        predicate._decorators.add(decorator)
+        predicate.__call__ = decorator(predicate.__call__)
+
+class PredicateMetaClass(type):
+    def __new__(mcs, *args, **kwargs):
+        # use __new__ so subclasses doesn't have to call Predicate.__init__
+        inst = type.__new__(mcs, *args, **kwargs)
+        proxy = weakref.proxy(inst, lambda p: _PREDICATES.pop(id(p)))
+        _PREDICATES[id(proxy)] = proxy
+        return inst
+
+
+@add_metaclass(PredicateMetaClass)
+class Predicate(object):
+    """base class for selector classes providing implementation
+    for operators ``&``, ``|`` and  ``~``
+
+    This class is only here to give access to binary operators, the selector
+    logic itself should be implemented in the :meth:`__call__` method. Notice it
+    should usually accept any arbitrary arguments (the context), though that may
+    vary depending on your usage of the registry.
+
+    a selector is called to help choosing the correct object for a
+    particular context by returning a score (`int`) telling how well
+    the implementation given as first argument fit to the given context.
+
+    0 score means that the class doesn't apply.
+    """
+
+    @property
+    def func_name(self):
+        # backward compatibility
+        return self.__class__.__name__
+
+    def search_selector(self, selector):
+        """search for the given selector, selector instance or tuple of
+        selectors in the selectors tree. Return None if not found.
+        """
+        if self is selector:
+            return self
+        if (isinstance(selector, type) or isinstance(selector, tuple)) and \
+               isinstance(self, selector):
+            return self
+        return None
+
+    def __str__(self):
+        return self.__class__.__name__
+
+    def __and__(self, other):
+        return AndPredicate(self, other)
+    def __rand__(self, other):
+        return AndPredicate(other, self)
+    def __iand__(self, other):
+        return AndPredicate(self, other)
+    def __or__(self, other):
+        return OrPredicate(self, other)
+    def __ror__(self, other):
+        return OrPredicate(other, self)
+    def __ior__(self, other):
+        return OrPredicate(self, other)
+
+    def __invert__(self):
+        return NotPredicate(self)
+
+    # XXX (function | function) or (function & function) not managed yet
+
+    def __call__(self, cls, *args, **kwargs):
+        return NotImplementedError("selector %s must implement its logic "
+                                   "in its __call__ method" % self.__class__)
+
+    def __repr__(self):
+        return u'<Predicate %s at %x>' % (self.__class__.__name__, id(self))
+
+
+class MultiPredicate(Predicate):
+    """base class for compound selector classes"""
+
+    def __init__(self, *selectors):
+        self.selectors = self.merge_selectors(selectors)
+
+    def __str__(self):
+        return '%s(%s)' % (self.__class__.__name__,
+                           ','.join(str(s) for s in self.selectors))
+
+    @classmethod
+    def merge_selectors(cls, selectors):
+        """deal with selector instanciation when necessary and merge
+        multi-selectors if possible:
+
+        AndPredicate(AndPredicate(sel1, sel2), AndPredicate(sel3, sel4))
+        ==> AndPredicate(sel1, sel2, sel3, sel4)
+        """
+        merged_selectors = []
+        for selector in selectors:
+            # XXX do we really want magic-transformations below?
+            # if so, wanna warn about them?
+            if isinstance(selector, types.FunctionType):
+                selector = objectify_predicate(selector)()
+            if isinstance(selector, type) and issubclass(selector, Predicate):
+                selector = selector()
+            assert isinstance(selector, Predicate), selector
+            if isinstance(selector, cls):
+                merged_selectors += selector.selectors
+            else:
+                merged_selectors.append(selector)
+        return merged_selectors
+
+    def search_selector(self, selector):
+        """search for the given selector or selector instance (or tuple of
+        selectors) in the selectors tree. Return None if not found
+        """
+        for childselector in self.selectors:
+            if childselector is selector:
+                return childselector
+            found = childselector.search_selector(selector)
+            if found is not None:
+                return found
+        # if not found in children, maybe we are looking for self?
+        return super(MultiPredicate, self).search_selector(selector)
+
+
+class AndPredicate(MultiPredicate):
+    """and-chained selectors"""
+    def __call__(self, cls, *args, **kwargs):
+        score = 0
+        for selector in self.selectors:
+            partscore = selector(cls, *args, **kwargs)
+            if not partscore:
+                return 0
+            score += partscore
+        return score
+
+
+class OrPredicate(MultiPredicate):
+    """or-chained selectors"""
+    def __call__(self, cls, *args, **kwargs):
+        for selector in self.selectors:
+            partscore = selector(cls, *args, **kwargs)
+            if partscore:
+                return partscore
+        return 0
+
+class NotPredicate(Predicate):
+    """negation selector"""
+    def __init__(self, selector):
+        self.selector = selector
+
+    def __call__(self, cls, *args, **kwargs):
+        score = self.selector(cls, *args, **kwargs)
+        return int(not score)
+
+    def __str__(self):
+        return 'NOT(%s)' % self.selector
+
+
+class yes(Predicate): # pylint: disable=C0103
+    """Return the score given as parameter, with a default score of 0.5 so any
+    other selector take precedence.
+
+    Usually used for objects which can be selected whatever the context, or
+    also sometimes to add arbitrary points to a score.
+
+    Take care, `yes(0)` could be named 'no'...
+    """
+    def __init__(self, score=0.5):
+        self.score = score
+
+    def __call__(self, *args, **kwargs):
+        return self.score
+
+
+# deprecated stuff #############################################################
+
+from logilab.common.deprecation import deprecated
+
+@deprecated('[lgc 0.59] use Registry.objid class method instead')
+def classid(cls):
+    return '%s.%s' % (cls.__module__, cls.__name__)
+
+@deprecated('[lgc 0.59] use obj_registries function instead')
+def class_registries(cls, registryname):
+    return obj_registries(cls, registryname)
+
diff --git a/third_party/logilab/common/shellutils.py b/third_party/logilab/common/shellutils.py
index c713913..4e68956 100644
--- a/third_party/logilab/common/shellutils.py
+++ b/third_party/logilab/common/shellutils.py
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This file is part of logilab-common.
@@ -18,6 +18,9 @@
 """shell/term utilities, useful to write some python scripts instead of shell
 scripts.
 """
+
+from __future__ import print_function
+
 __docformat__ = "restructuredtext en"
 
 import os
@@ -31,11 +34,15 @@
 import errno
 import string
 import random
+import subprocess
 from os.path import exists, isdir, islink, basename, join
 
+from six import string_types
+from six.moves import range, input as raw_input
+
 from logilab.common import STD_BLACKLIST, _handle_blacklist
-from logilab.common.compat import raw_input
 from logilab.common.compat import str_to_bytes
+from logilab.common.deprecation import deprecated
 
 try:
     from logilab.common.proc import ProcInfo, NoSuchProcess
@@ -113,7 +120,7 @@
             destination = join(destination, basename(source))
         try:
             _action(source, destination)
-        except OSError, ex:
+        except OSError as ex:
             raise OSError('Unable to move %r to %r (%s)' % (
                 source, destination, ex))
 
@@ -159,7 +166,7 @@
     :return:
       the list of all matching files
     """
-    if isinstance(exts, basestring):
+    if isinstance(exts, string_types):
         exts = (exts,)
     if exclude:
         def match(filename, exts):
@@ -224,20 +231,19 @@
             outfile.write(zfobj.read(name))
             outfile.close()
 
+
 class Execute:
     """This is a deadlock safe version of popen2 (no stdin), that returns
     an object with errorlevel, out and err.
     """
 
     def __init__(self, command):
-        outfile = tempfile.mktemp()
-        errfile = tempfile.mktemp()
-        self.status = os.system("( %s ) >%s 2>%s" %
-                                (command, outfile, errfile)) >> 8
-        self.out = open(outfile, "r").read()
-        self.err = open(errfile, "r").read()
-        os.remove(outfile)
-        os.remove(errfile)
+        cmd = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        self.out, self.err = cmd.communicate()
+        self.status = os.WEXITSTATUS(cmd.returncode)
+
+Execute = deprecated('Use subprocess.Popen instead')(Execute)
+
 
 def acquire_lock(lock_file, max_try=10, delay=10, max_delay=3600):
     """Acquire a lock represented by a file on the file system
@@ -253,7 +259,7 @@
             os.write(fd, str_to_bytes(str(os.getpid())) )
             os.close(fd)
             return True
-        except OSError, e:
+        except OSError as e:
             if e.errno == errno.EEXIST:
                 try:
                     fd = open(lock_file, "r")
@@ -315,9 +321,22 @@
 
     text = property(_get_text, _set_text, _del_text)
 
-    def update(self):
-        """Update the progression bar."""
-        self._current += 1
+    def update(self, offset=1, exact=False):
+        """Move FORWARD to new cursor position (cursor will never go backward).
+
+        :offset: fraction of ``size``
+
+        :exact:
+
+          - False: offset relative to current cursor position if True
+          - True: offset as an asbsolute position
+
+        """
+        if exact:
+            self._current = offset
+        else:
+            self._current += offset
+
         progress = int((float(self._current)/float(self._total))*self._size)
         if progress > self._progress:
             self._progress = progress
@@ -325,7 +344,7 @@
 
     def refresh(self):
         """Refresh the progression bar display."""
-        self._stream.write(self._fstr % ('.' * min(self._progress, self._size)) )
+        self._stream.write(self._fstr % ('=' * min(self._progress, self._size)) )
         if self._last_text_write_size or self._current_text:
             template = ' %%-%is' % (self._last_text_write_size)
             text = self._current_text
@@ -412,7 +431,7 @@
             if self._print:
                 self._print(msg)
             else:
-                print msg
+                print(msg)
             tries -= 1
         raise Exception('unable to get a sensible answer')
 
@@ -438,6 +457,6 @@
 def generate_password(length=8, vocab=string.ascii_letters + string.digits):
     """dumb password generation function"""
     pwd = ''
-    for i in xrange(length):
+    for i in range(length):
         pwd += random.choice(vocab)
     return pwd
diff --git a/third_party/logilab/common/table.py b/third_party/logilab/common/table.py
index 744bb78..2f3df69 100644
--- a/third_party/logilab/common/table.py
+++ b/third_party/logilab/common/table.py
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This file is part of logilab-common.
@@ -16,8 +16,12 @@
 # You should have received a copy of the GNU Lesser General Public License along
 # with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
 """Table management module."""
+
+from __future__ import print_function
+
 __docformat__ = "restructuredtext en"
 
+from six.moves import range
 
 class Table(object):
     """Table defines a data table with column and row names.
@@ -48,6 +52,8 @@
         else:
             return list(self) == list(other)
 
+    __hash__ = object.__hash__
+
     def __ne__(self, other):
         return not self == other
 
@@ -440,7 +446,7 @@
         # The first cell <=> an empty one
         col_names_line = [' '*col_start]
         for col_name in self.col_names:
-            col_names_line.append(col_name.encode('iso-8859-1') + ' '*5)
+            col_names_line.append(col_name + ' '*5)
         lines.append('|' + '|'.join(col_names_line) + '|')
         max_line_length = len(lines[0])
 
@@ -448,7 +454,7 @@
         for row_index, row in enumerate(self.data):
             line = []
             # First, build the row_name's cell
-            row_name = self.row_names[row_index].encode('iso-8859-1')
+            row_name = self.row_names[row_index]
             line.append(row_name + ' '*(col_start-len(row_name)))
 
             # Then, build all the table's cell for this line.
@@ -648,7 +654,7 @@
                 'table.py', 'exec'))
             self.rules.append(rule)
         except SyntaxError:
-            print "Bad Stylesheet Rule : %s [skipped]"%rule
+            print("Bad Stylesheet Rule : %s [skipped]" % rule)
 
 
     def add_rowsum_rule(self, dest_cell, row_index, start_col, end_col):
@@ -743,14 +749,14 @@
     def render_row_cell(self, row_name, table, table_style):
         """Renders the cell for 'row_id' row
         """
-        cell_value = row_name.encode('iso-8859-1')
+        cell_value = row_name
         return self._render_cell_content(cell_value, table_style, 0)
 
 
     def render_col_cell(self, col_name, table, table_style):
         """Renders the cell for 'col_id' row
         """
-        cell_value = col_name.encode('iso-8859-1')
+        cell_value = col_name
         col_index = table.col_names.index(col_name)
         return self._render_cell_content(cell_value, table_style, col_index +1)
 
diff --git a/third_party/logilab/common/tasksqueue.py b/third_party/logilab/common/tasksqueue.py
index e95a77e..ed74cf5 100644
--- a/third_party/logilab/common/tasksqueue.py
+++ b/third_party/logilab/common/tasksqueue.py
@@ -20,7 +20,8 @@
 __docformat__ = "restructuredtext en"
 
 from bisect import insort_left
-from Queue import Queue
+
+from six.moves import queue
 
 LOW = 0
 MEDIUM = 10
@@ -31,11 +32,11 @@
     'MEDIUM': MEDIUM,
     'HIGH': HIGH,
     }
-REVERSE_PRIORITY = dict((values, key) for key, values in PRIORITY.iteritems())
+REVERSE_PRIORITY = dict((values, key) for key, values in PRIORITY.items())
 
 
 
-class PrioritizedTasksQueue(Queue):
+class PrioritizedTasksQueue(queue.Queue):
 
     def _init(self, maxsize):
         """Initialize the queue representation"""
@@ -94,5 +95,7 @@
     def __eq__(self, other):
         return self.id == other.id
 
+    __hash__ = object.__hash__
+
     def merge(self, other):
         pass
diff --git a/third_party/logilab/common/testlib.py b/third_party/logilab/common/testlib.py
index da49387..31efe56 100644
--- a/third_party/logilab/common/testlib.py
+++ b/third_party/logilab/common/testlib.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This file is part of logilab-common.
@@ -36,6 +36,9 @@
 'regrtest', 'smoketest' and 'unittest'.
 
 """
+
+from __future__ import print_function
+
 __docformat__ = "restructuredtext en"
 # modified copy of some functions from test/regrtest.py from PyXml
 # disable camel case warning
@@ -52,9 +55,13 @@
 import warnings
 from shutil import rmtree
 from operator import itemgetter
-from ConfigParser import ConfigParser
-from logilab.common.deprecation import deprecated
 from itertools import dropwhile
+from inspect import isgeneratorfunction
+
+from six import string_types
+from six.moves import builtins, range, configparser, input
+
+from logilab.common.deprecation import deprecated
 
 import unittest as unittest_legacy
 if not getattr(unittest_legacy, "__package__", None):
@@ -62,31 +69,13 @@
         import unittest2 as unittest
         from unittest2 import SkipTest
     except ImportError:
-        sys.exit("You have to install python-unittest2 to use this module")
+        raise ImportError("You have to install python-unittest2 to use %s" % __name__)
 else:
     import unittest
     from unittest import SkipTest
 
-try:
-    from functools import wraps
-except ImportError:
-    def wraps(wrapped):
-        def proxy(callable):
-            callable.__name__ = wrapped.__name__
-            return callable
-        return proxy
-try:
-    from test import test_support
-except ImportError:
-    # not always available
-    class TestSupport:
-        def unload(self, test):
-            pass
-    test_support = TestSupport()
+from functools import wraps
 
-# pylint: disable=W0622
-from logilab.common.compat import any, InheritableSet, callable
-# pylint: enable=W0622
 from logilab.common.debugger import Debugger, colorize_source
 from logilab.common.decorators import cached, classproperty
 from logilab.common import textutils
@@ -97,23 +86,7 @@
 DEFAULT_PREFIXES = ('test', 'regrtest', 'smoketest', 'unittest',
                     'func', 'validation')
 
-
-if sys.version_info >= (2, 6):
-    # FIXME : this does not work as expected / breaks tests on testlib
-    # however testlib does not work on py3k for many reasons ...
-    from inspect import CO_GENERATOR
-else:
-    from compiler.consts import CO_GENERATOR
-
-if sys.version_info >= (3, 0):
-    def is_generator(function):
-        flags = function.__code__.co_flags
-        return flags & CO_GENERATOR
-
-else:
-    def is_generator(function):
-        flags = function.func_code.co_flags
-        return flags & CO_GENERATOR
+is_generator = deprecated('[lgc 0.63] use inspect.isgeneratorfunction')(isgeneratorfunction)
 
 # used by unittest to count the number of relevant levels in the traceback
 __unittest = 1
@@ -122,6 +95,21 @@
 def with_tempdir(callable):
     """A decorator ensuring no temporary file left when the function return
     Work only for temporary file create with the tempfile module"""
+    if isgeneratorfunction(callable):
+        def proxy(*args, **kwargs):
+            old_tmpdir = tempfile.gettempdir()
+            new_tmpdir = tempfile.mkdtemp(prefix="temp-lgc-")
+            tempfile.tempdir = new_tmpdir
+            try:
+                for x in callable(*args, **kwargs):
+                    yield x
+            finally:
+                try:
+                    rmtree(new_tmpdir, ignore_errors=True)
+                finally:
+                    tempfile.tempdir = old_tmpdir
+        return proxy
+
     @wraps(callable)
     def proxy(*args, **kargs):
 
@@ -190,27 +178,27 @@
     else:
         while True:
             testindex = 0
-            print "Choose a test to debug:"
+            print("Choose a test to debug:")
             # order debuggers in the same way than errors were printed
-            print "\n".join(['\t%s : %s' % (i, descr) for i, (_, descr)
-                in enumerate(descrs)])
-            print "Type 'exit' (or ^D) to quit"
-            print
+            print("\n".join(['\t%s : %s' % (i, descr) for i, (_, descr)
+                  in enumerate(descrs)]))
+            print("Type 'exit' (or ^D) to quit")
+            print()
             try:
-                todebug = raw_input('Enter a test name: ')
+                todebug = input('Enter a test name: ')
                 if todebug.strip().lower() == 'exit':
-                    print
+                    print()
                     break
                 else:
                     try:
                         testindex = int(todebug)
                         debugger = debuggers[descrs[testindex][0]]
                     except (ValueError, IndexError):
-                        print "ERROR: invalid test number %r" % (todebug, )
+                        print("ERROR: invalid test number %r" % (todebug, ))
                     else:
                         debugger.start()
             except (EOFError, KeyboardInterrupt):
-                print
+                print()
                 break
 
 
@@ -364,7 +352,7 @@
     if tearDownModule is not None:
         try:
             tearDownModule()
-        except Exception, e:
+        except Exception as e:
             if isinstance(result, _DebugResult):
                 raise
             errorName = 'tearDownModule (%s)' % previousModule
@@ -392,7 +380,7 @@
     if setUpModule is not None:
         try:
             setUpModule()
-        except Exception, e:
+        except Exception as e:
             if isinstance(result, _DebugResult):
                 raise
             result._moduleSetUpFailed = True
@@ -448,7 +436,7 @@
         instance.name = name
         return instance
 
-class Tags(InheritableSet): # 2.4 compat
+class Tags(set):
     """A set of tag able validate an expression"""
 
     def __init__(self, *tags, **kwargs):
@@ -456,7 +444,7 @@
         if kwargs:
            raise TypeError("%s are an invalid keyword argument for this function" % kwargs.keys())
 
-        if len(tags) == 1 and not isinstance(tags[0], basestring):
+        if len(tags) == 1 and not isinstance(tags[0], string_types):
             tags = tags[0]
         super(Tags, self).__init__(tags, **kwargs)
 
@@ -484,14 +472,8 @@
 
     def __init__(self, methodName='runTest'):
         super(TestCase, self).__init__(methodName)
-        # internal API changed in python2.4 and needed by DocTestCase
-        if sys.version_info >= (2, 4):
-            self.__exc_info = sys.exc_info
-            self.__testMethodName = self._testMethodName
-        else:
-            # let's give easier access to _testMethodName to every subclasses
-            if hasattr(self, "__testMethodName"):
-                self._testMethodName = self.__testMethodName
+        self.__exc_info = sys.exc_info
+        self.__testMethodName = self._testMethodName
         self._current_test_descr = None
         self._options_ = None
 
@@ -533,6 +515,14 @@
             func(*args, **kwargs)
         except (KeyboardInterrupt, SystemExit):
             raise
+        except unittest.SkipTest as e:
+            if hasattr(result, 'addSkip'):
+                result.addSkip(self, str(e))
+            else:
+                warnings.warn("TestResult has no addSkip method, skips not reported",
+                              RuntimeWarning, 2)
+                result.addSuccess(self)
+            return False
         except:
             result.addError(self, self.__exc_info())
             return False
@@ -559,13 +549,23 @@
         # if result.cvg:
         #     result.cvg.start()
         testMethod = self._get_test_method()
+        if (getattr(self.__class__, "__unittest_skip__", False) or
+            getattr(testMethod, "__unittest_skip__", False)):
+            # If the class or method was skipped.
+            try:
+                skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
+                            or getattr(testMethod, '__unittest_skip_why__', ''))
+                self._addSkip(result, skip_why)
+            finally:
+                result.stopTest(self)
+            return
         if runcondition and not runcondition(testMethod):
             return # test is skipped
         result.startTest(self)
         try:
             if not self.quiet_run(result, self.setUp):
                 return
-            generative = is_generator(testMethod.im_func)
+            generative = isgeneratorfunction(testMethod)
             # generative tests
             if generative:
                 self._proceed_generative(result, testMethod,
@@ -587,10 +587,11 @@
                             restartfile.write(descr+os.linesep)
                         finally:
                             restartfile.close()
-                    except Exception, ex:
-                        print >> sys.__stderr__, "Error while saving \
-succeeded test into", osp.join(os.getcwd(), FILE_RESTART)
-                        raise ex
+                    except Exception:
+                        print("Error while saving succeeded test into",
+                              osp.join(os.getcwd(), FILE_RESTART),
+                              file=sys.__stderr__)
+                        raise
                 result.addSuccess(self)
         finally:
             # if result.cvg:
@@ -647,10 +648,10 @@
             return 1
         except KeyboardInterrupt:
             raise
-        except InnerTestSkipped, e:
+        except InnerTestSkipped as e:
             result.addSkip(self, e)
             return 1
-        except SkipTest, e:
+        except SkipTest as e:
             result.addSkip(self, e)
             return 0
         except:
@@ -704,7 +705,7 @@
                 base = ''
             self.fail(base + '\n'.join(msgs))
 
-    @deprecated('Please use assertItemsEqual instead.')
+    @deprecated('Please use assertCountEqual instead.')
     def assertUnorderedIterableEquals(self, got, expected, msg=None):
         """compares two iterable and shows difference between both
 
@@ -826,10 +827,10 @@
             parser = make_parser()
             try:
                 parser.parse(stream)
-            except SAXParseException, ex:
+            except SAXParseException as ex:
                 if msg is None:
                     stream.seek(0)
-                    for _ in xrange(ex.getLineNumber()):
+                    for _ in range(ex.getLineNumber()):
                         line = stream.readline()
                     pointer = ('' * (ex.getLineNumber() - 1)) + '^'
                     msg = 'XML stream not well formed: %s\n%s%s' % (ex, line, pointer)
@@ -867,7 +868,7 @@
             ParseError = ExpatError
         try:
             parse(data)
-        except (ExpatError, ParseError), ex:
+        except (ExpatError, ParseError) as ex:
             if msg is None:
                 if hasattr(data, 'readlines'): #file like object
                     data.seek(0)
@@ -888,11 +889,11 @@
                     line_number_length = len('%i' % end)
                     line_pattern = " %%%ii: %%s" % line_number_length
 
-                    for line_no in xrange(start, ex.lineno):
+                    for line_no in range(start, ex.lineno):
                         context_lines.append(line_pattern % (line_no, lines[line_no-1]))
                     context_lines.append(line_pattern % (ex.lineno, lines[ex.lineno-1]))
                     context_lines.append('%s^\n' % (' ' * (1 + line_number_length + 2 +ex.offset)))
-                    for line_no in xrange(ex.lineno+1, end+1):
+                    for line_no in range(ex.lineno+1, end+1):
                         context_lines.append(line_pattern % (line_no, lines[line_no-1]))
 
                 rich_context = ''.join(context_lines)
@@ -920,7 +921,7 @@
                 self.fail( "tuple %s has %i children%s (%i expected)"%(tup,
                     len(tup[2]),
                         ('', 's')[len(tup[2])>1], len(element)))
-            for index in xrange(len(tup[2])):
+            for index in range(len(tup[2])):
                 self.assertXMLEqualsTuple(element[index], tup[2][index])
         #check text
         if element.text or len(tup)>3:
@@ -950,7 +951,7 @@
     def assertTextEquals(self, text1, text2, junk=None,
             msg_prefix='Text differ', striplines=False):
         """compare two multiline strings (using difflib and splitlines())
-        
+
         :param text1: a Python BaseString
         :param text2: a second Python Basestring
         :param junk: List of Caracters
@@ -958,9 +959,9 @@
         :param striplines: Boolean to trigger line stripping before comparing
         """
         msg = []
-        if not isinstance(text1, basestring):
+        if not isinstance(text1, string_types):
             msg.append('text1 is not a string (%s)'%(type(text1)))
-        if not isinstance(text2, basestring):
+        if not isinstance(text2, string_types):
             msg.append('text2 is not a string (%s)'%(type(text2)))
         if msg:
             self.fail('\n'.join(msg))
@@ -1016,13 +1017,13 @@
         ipath_a, idirs_a, ifiles_a = data_a = None, None, None
         while True:
             try:
-                ipath_a, idirs_a, ifiles_a = datas_a = iter_a.next()
+                ipath_a, idirs_a, ifiles_a = datas_a = next(iter_a)
                 partial_iter = False
-                ipath_b, idirs_b, ifiles_b = datas_b = iter_b.next()
+                ipath_b, idirs_b, ifiles_b = datas_b = next(iter_b)
                 partial_iter = True
 
 
-                self.assert_(ipath_a == ipath_b,
+                self.assertTrue(ipath_a == ipath_b,
                     "unexpected %s in %s while looking %s from %s" %
                     (ipath_a, path_a, ipath_b, path_b))
 
@@ -1040,7 +1041,7 @@
 
 
                 msgs = [ "%s: %s"% (name, items)
-                    for name, items in errors.iteritems() if items]
+                    for name, items in errors.items() if items]
 
                 if msgs:
                     msgs.insert(0, "%s and %s differ :" % (
@@ -1080,9 +1081,9 @@
                 msg = '%r is not an instance of %s but of %s'
             msg = msg % (obj, klass, type(obj))
         if strict:
-            self.assert_(obj.__class__ is klass, msg)
+            self.assertTrue(obj.__class__ is klass, msg)
         else:
-            self.assert_(isinstance(obj, klass), msg)
+            self.assertTrue(isinstance(obj, klass), msg)
 
     @deprecated('Please use assertIsNone instead.')
     def assertNone(self, obj, msg=None):
@@ -1092,14 +1093,14 @@
         """
         if msg is None:
             msg = "reference to %r when None expected"%(obj,)
-        self.assert_( obj is None, msg )
+        self.assertTrue( obj is None, msg )
 
     @deprecated('Please use assertIsNotNone instead.')
     def assertNotNone(self, obj, msg=None):
         """assert obj is not None"""
         if msg is None:
             msg = "unexpected reference to None"
-        self.assert_( obj is not None, msg )
+        self.assertTrue( obj is not None, msg )
 
     @deprecated('Non-standard. Please use assertAlmostEqual instead.')
     def assertFloatAlmostEquals(self, obj, other, prec=1e-5,
@@ -1117,7 +1118,7 @@
             msg = "%r != %r" % (obj, other)
         if relative:
             prec = prec*math.fabs(obj)
-        self.assert_(math.fabs(obj - other) < prec, msg)
+        self.assertTrue(math.fabs(obj - other) < prec, msg)
 
     def failUnlessRaises(self, excClass, callableObj=None, *args, **kwargs):
         """override default failUnlessRaises method to return the raised
@@ -1146,7 +1147,7 @@
             return _assert(excClass, callableObj, *args, **kwargs)
         try:
             callableObj(*args, **kwargs)
-        except excClass, exc:
+        except excClass as exc:
             class ProxyException:
                 def __init__(self, obj):
                     self._obj = obj
@@ -1166,6 +1167,16 @@
 
     assertRaises = failUnlessRaises
 
+    if sys.version_info >= (3,2):
+        assertItemsEqual = unittest.TestCase.assertCountEqual
+    else:
+        assertCountEqual = unittest.TestCase.assertItemsEqual
+        if sys.version_info < (2,7):
+            def assertIsNotNone(self, value, *args, **kwargs):
+                self.assertNotEqual(None, value, *args, **kwargs)
+
+TestCase.assertItemsEqual = deprecated('assertItemsEqual is deprecated, use assertCountEqual')(
+    TestCase.assertItemsEqual)
 
 import doctest
 
@@ -1184,10 +1195,6 @@
     def _get_test(self, obj, name, module, globs, source_lines):
         """override default _get_test method to be able to skip tests
         according to skipped attribute's value
-
-        Note: Python (<=2.4) use a _name_filter which could be used for that
-              purpose but it's no longer available in 2.5
-              Python 2.5 seems to have a [SKIP] flag
         """
         if getattr(obj, '__name__', '') in self.skipped:
             return None
@@ -1205,16 +1212,19 @@
         # pylint: disable=W0613
         try:
             finder = DocTestFinder(skipped=self.skipped)
-            if sys.version_info >= (2, 4):
-                suite = doctest.DocTestSuite(self.module, test_finder=finder)
-                if sys.version_info >= (2, 5):
-                    # XXX iirk
-                    doctest.DocTestCase._TestCase__exc_info = sys.exc_info
-            else:
-                suite = doctest.DocTestSuite(self.module)
+            suite = doctest.DocTestSuite(self.module, test_finder=finder)
+            # XXX iirk
+            doctest.DocTestCase._TestCase__exc_info = sys.exc_info
         except AttributeError:
             suite = SkippedSuite()
-        return suite.run(result)
+        # doctest may gork the builtins dictionnary
+        # This happen to the "_" entry used by gettext
+        old_builtins = builtins.__dict__.copy()
+        try:
+            return suite.run(result)
+        finally:
+            builtins.__dict__.clear()
+            builtins.__dict__.update(old_builtins)
     run = __call__
 
     def test(self):
@@ -1242,11 +1252,11 @@
         """ignore quit"""
 
 
-class MockConfigParser(ConfigParser):
+class MockConfigParser(configparser.ConfigParser):
     """fake ConfigParser.ConfigParser"""
 
     def __init__(self, options):
-        ConfigParser.__init__(self)
+        configparser.ConfigParser.__init__(self)
         for section, pairs in options.iteritems():
             self.add_section(section)
             for key, value in pairs.iteritems():
diff --git a/third_party/logilab/common/textutils.py b/third_party/logilab/common/textutils.py
index bdeed41..9046f97 100644
--- a/third_party/logilab/common/textutils.py
+++ b/third_party/logilab/common/textutils.py
@@ -284,11 +284,14 @@
     dict of {'key': 'value'}. When the same key is encountered multiple time,
     value is turned into a list containing all values.
 
-    >>> text_to_dict('''multiple=1
+    >>> d = text_to_dict('''multiple=1
     ... multiple= 2
     ... single =3
     ... ''')
-    {'single': '3', 'multiple': ['1', '2']}
+    >>> d['single']
+    '3'
+    >>> d['multiple']
+    ['1', '2']
 
     """
     res = {}
@@ -313,6 +316,8 @@
 __VALUE_URE = r'-?(([0-9]+\.[0-9]*)|((0x?)?[0-9]+))'
 __UNITS_URE = r'[a-zA-Z]+'
 _VALUE_RE = re.compile(r'(?P<value>%s)(?P<unit>%s)?'%(__VALUE_URE, __UNITS_URE))
+_VALIDATION_RE = re.compile(r'^((%s)(%s))*(%s)?$' % (__VALUE_URE, __UNITS_URE,
+                                                    __VALUE_URE))
 
 BYTE_UNITS = {
     "b": 1,
@@ -352,12 +357,12 @@
     """
     if inter is None:
         inter = final
-    string = _BLANK_RE.sub('', string)
+    fstring = _BLANK_RE.sub('', string)
+    if not (fstring and _VALIDATION_RE.match(fstring)):
+        raise ValueError("Invalid unit string: %r." % string)
     values = []
-    for match in value_reg.finditer(string):
+    for match in value_reg.finditer(fstring):
         dic = match.groupdict()
-        #import sys
-        #print >> sys.stderr, dic
         lit, unit = dic["value"], dic.get("unit")
         value = inter(lit)
         if unit is not None:
diff --git a/third_party/logilab/common/umessage.py b/third_party/logilab/common/umessage.py
index 85d564c..a5e4799 100644
--- a/third_party/logilab/common/umessage.py
+++ b/third_party/logilab/common/umessage.py
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This file is part of logilab-common.
@@ -15,12 +15,8 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
-"""Unicode email support (extends email from stdlib).
+"""Unicode email support (extends email from stdlib)"""
 
-
-
-
-"""
 __docformat__ = "restructuredtext en"
 
 import email
@@ -48,9 +44,13 @@
     for decoded, charset in decode_header(string):
         if not charset :
             charset = 'iso-8859-15'
-        parts.append(unicode(decoded, charset, 'replace'))
+        parts.append(decoded.decode(charset, 'replace'))
 
-    return u' '.join(parts)
+    if sys.version_info < (3, 3):
+        # decoding was non-RFC compliant wrt to whitespace handling
+        # see http://bugs.python.org/issue1079
+        return u' '.join(parts)
+    return u''.join(parts)
 
 def message_from_file(fd):
     try:
@@ -79,27 +79,13 @@
             return decode_QP(value)
         return value
 
+    def __getitem__(self, header):
+        return self.get(header)
+
     def get_all(self, header, default=()):
         return [decode_QP(val) for val in self.message.get_all(header, default)
                 if val is not None]
 
-    def get_payload(self, index=None, decode=False):
-        message = self.message
-        if index is None:
-            payload = message.get_payload(index, decode)
-            if isinstance(payload, list):
-                return [UMessage(msg) for msg in payload]
-            if message.get_content_maintype() != 'text':
-                return payload
-
-            charset = message.get_content_charset() or 'iso-8859-1'
-            if search_function(charset) is None:
-                charset = 'iso-8859-1'
-            return unicode(payload or '', charset, "replace")
-        else:
-            payload = UMessage(message.get_payload(index, decode))
-        return payload
-
     def is_multipart(self):
         return self.message.is_multipart()
 
@@ -110,20 +96,61 @@
         for part in self.message.walk():
             yield UMessage(part)
 
-    def get_content_maintype(self):
-        return unicode(self.message.get_content_maintype())
+    if sys.version_info < (3, 0):
 
-    def get_content_type(self):
-        return unicode(self.message.get_content_type())
+        def get_payload(self, index=None, decode=False):
+            message = self.message
+            if index is None:
+                payload = message.get_payload(index, decode)
+                if isinstance(payload, list):
+                    return [UMessage(msg) for msg in payload]
+                if message.get_content_maintype() != 'text':
+                    return payload
 
-    def get_filename(self, failobj=None):
-        value = self.message.get_filename(failobj)
-        if value is failobj:
-            return value
-        try:
-            return unicode(value)
-        except UnicodeDecodeError:
-            return u'error decoding filename'
+                charset = message.get_content_charset() or 'iso-8859-1'
+                if search_function(charset) is None:
+                    charset = 'iso-8859-1'
+                return unicode(payload or '', charset, "replace")
+            else:
+                payload = UMessage(message.get_payload(index, decode))
+            return payload
+
+        def get_content_maintype(self):
+            return unicode(self.message.get_content_maintype())
+
+        def get_content_type(self):
+            return unicode(self.message.get_content_type())
+
+        def get_filename(self, failobj=None):
+            value = self.message.get_filename(failobj)
+            if value is failobj:
+                return value
+            try:
+                return unicode(value)
+            except UnicodeDecodeError:
+                return u'error decoding filename'
+
+    else:
+
+        def get_payload(self, index=None, decode=False):
+            message = self.message
+            if index is None:
+                payload = message.get_payload(index, decode)
+                if isinstance(payload, list):
+                    return [UMessage(msg) for msg in payload]
+                return payload
+            else:
+                payload = UMessage(message.get_payload(index, decode))
+            return payload
+
+        def get_content_maintype(self):
+            return self.message.get_content_maintype()
+
+        def get_content_type(self):
+            return self.message.get_content_type()
+
+        def get_filename(self, failobj=None):
+            return self.message.get_filename(failobj)
 
     # other convenience methods ###############################################
 
diff --git a/third_party/logilab/common/ureports/__init__.py b/third_party/logilab/common/ureports/__init__.py
index dcffcfa..65e9641 100644
--- a/third_party/logilab/common/ureports/__init__.py
+++ b/third_party/logilab/common/ureports/__init__.py
@@ -20,13 +20,11 @@
 A way to create simple reports using python objects, primarily designed to be
 formatted as text and html.
 """
-from __future__ import generators
 __docformat__ = "restructuredtext en"
 
 import sys
-from cStringIO import StringIO
-from StringIO import StringIO as UStringIO
 
+from logilab.common.compat import StringIO
 from logilab.common.textutils import linesep
 
 
@@ -158,7 +156,7 @@
         self.writeln = writeln
         self.__compute_funcs.append((write, writeln))
         for child in layout.children:
-            stream = UStringIO()
+            stream = StringIO()
             child.accept(self)
             yield stream.getvalue()
         self.__compute_funcs.pop()
diff --git a/third_party/logilab/common/ureports/docbook_writer.py b/third_party/logilab/common/ureports/docbook_writer.py
index e75cbe0..857068c 100644
--- a/third_party/logilab/common/ureports/docbook_writer.py
+++ b/third_party/logilab/common/ureports/docbook_writer.py
@@ -16,9 +16,10 @@
 # You should have received a copy of the GNU Lesser General Public License along
 # with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
 """HTML formatting drivers for ureports"""
-from __future__ import generators
 __docformat__ = "restructuredtext en"
 
+from six.moves import range
+
 from logilab.common.ureports import HTMLWriter
 
 class DocbookWriter(HTMLWriter):
diff --git a/third_party/logilab/common/ureports/html_writer.py b/third_party/logilab/common/ureports/html_writer.py
index 1d09503..eba34ea 100644
--- a/third_party/logilab/common/ureports/html_writer.py
+++ b/third_party/logilab/common/ureports/html_writer.py
@@ -20,6 +20,8 @@
 
 from cgi import escape
 
+from six.moves import range
+
 from logilab.common.ureports import BaseWriter
 
 
@@ -32,100 +34,100 @@
 
     def handle_attrs(self, layout):
         """get an attribute string from layout member attributes"""
-        attrs = ''
+        attrs = u''
         klass = getattr(layout, 'klass', None)
         if klass:
-            attrs += ' class="%s"' % klass
+            attrs += u' class="%s"' % klass
         nid = getattr(layout, 'id', None)
         if nid:
-            attrs += ' id="%s"' % nid
+            attrs += u' id="%s"' % nid
         return attrs
 
     def begin_format(self, layout):
         """begin to format a layout"""
         super(HTMLWriter, self).begin_format(layout)
         if self.snippet is None:
-            self.writeln('<html>')
-            self.writeln('<body>')
+            self.writeln(u'<html>')
+            self.writeln(u'<body>')
 
     def end_format(self, layout):
         """finished to format a layout"""
         if self.snippet is None:
-            self.writeln('</body>')
-            self.writeln('</html>')
+            self.writeln(u'</body>')
+            self.writeln(u'</html>')
 
 
     def visit_section(self, layout):
         """display a section as html, using div + h[section level]"""
         self.section += 1
-        self.writeln('<div%s>' % self.handle_attrs(layout))
+        self.writeln(u'<div%s>' % self.handle_attrs(layout))
         self.format_children(layout)
-        self.writeln('</div>')
+        self.writeln(u'</div>')
         self.section -= 1
 
     def visit_title(self, layout):
         """display a title using <hX>"""
-        self.write('<h%s%s>' % (self.section, self.handle_attrs(layout)))
+        self.write(u'<h%s%s>' % (self.section, self.handle_attrs(layout)))
         self.format_children(layout)
-        self.writeln('</h%s>' % self.section)
+        self.writeln(u'</h%s>' % self.section)
 
     def visit_table(self, layout):
         """display a table as html"""
-        self.writeln('<table%s>' % self.handle_attrs(layout))
+        self.writeln(u'<table%s>' % self.handle_attrs(layout))
         table_content = self.get_table_content(layout)
         for i in range(len(table_content)):
             row = table_content[i]
             if i == 0 and layout.rheaders:
-                self.writeln('<tr class="header">')
+                self.writeln(u'<tr class="header">')
             elif i+1 == len(table_content) and layout.rrheaders:
-                self.writeln('<tr class="header">')
+                self.writeln(u'<tr class="header">')
             else:
-                self.writeln('<tr class="%s">' % (i%2 and 'even' or 'odd'))
+                self.writeln(u'<tr class="%s">' % (i%2 and 'even' or 'odd'))
             for j in range(len(row)):
-                cell = row[j] or '&#160;'
+                cell = row[j] or u'&#160;'
                 if (layout.rheaders and i == 0) or \
                    (layout.cheaders and j == 0) or \
                    (layout.rrheaders and i+1 == len(table_content)) or \
                    (layout.rcheaders and j+1 == len(row)):
-                    self.writeln('<th>%s</th>' % cell)
+                    self.writeln(u'<th>%s</th>' % cell)
                 else:
-                    self.writeln('<td>%s</td>' % cell)
-            self.writeln('</tr>')
-        self.writeln('</table>')
+                    self.writeln(u'<td>%s</td>' % cell)
+            self.writeln(u'</tr>')
+        self.writeln(u'</table>')
 
     def visit_list(self, layout):
         """display a list as html"""
-        self.writeln('<ul%s>' % self.handle_attrs(layout))
+        self.writeln(u'<ul%s>' % self.handle_attrs(layout))
         for row in list(self.compute_content(layout)):
-            self.writeln('<li>%s</li>' % row)
-        self.writeln('</ul>')
+            self.writeln(u'<li>%s</li>' % row)
+        self.writeln(u'</ul>')
 
     def visit_paragraph(self, layout):
         """display links (using <p>)"""
-        self.write('<p>')
+        self.write(u'<p>')
         self.format_children(layout)
-        self.write('</p>')
+        self.write(u'</p>')
 
     def visit_span(self, layout):
         """display links (using <p>)"""
-        self.write('<span%s>' % self.handle_attrs(layout))
+        self.write(u'<span%s>' % self.handle_attrs(layout))
         self.format_children(layout)
-        self.write('</span>')
+        self.write(u'</span>')
 
     def visit_link(self, layout):
         """display links (using <a>)"""
-        self.write(' <a href="%s"%s>%s</a>' % (layout.url,
-                                               self.handle_attrs(layout),
-                                               layout.label))
+        self.write(u' <a href="%s"%s>%s</a>' % (layout.url,
+                                                self.handle_attrs(layout),
+                                                layout.label))
     def visit_verbatimtext(self, layout):
         """display verbatim text (using <pre>)"""
-        self.write('<pre>')
-        self.write(layout.data.replace('&', '&amp;').replace('<', '&lt;'))
-        self.write('</pre>')
+        self.write(u'<pre>')
+        self.write(layout.data.replace(u'&', u'&amp;').replace(u'<', u'&lt;'))
+        self.write(u'</pre>')
 
     def visit_text(self, layout):
         """add some text"""
         data = layout.data
         if layout.escaped:
-            data = data.replace('&', '&amp;').replace('<', '&lt;')
+            data = data.replace(u'&', u'&amp;').replace(u'<', u'&lt;')
         self.write(data)
diff --git a/third_party/logilab/common/ureports/nodes.py b/third_party/logilab/common/ureports/nodes.py
index d63b582..a9585b3 100644
--- a/third_party/logilab/common/ureports/nodes.py
+++ b/third_party/logilab/common/ureports/nodes.py
@@ -23,6 +23,8 @@
 
 from logilab.common.tree import VNode
 
+from six import string_types
+
 class BaseComponent(VNode):
     """base report component
 
@@ -79,7 +81,7 @@
         super(Text, self).__init__(**kwargs)
         #if isinstance(data, unicode):
         #    data = data.encode('ascii')
-        assert isinstance(data, (str, unicode)), data.__class__
+        assert isinstance(data, string_types), data.__class__
         self.escaped = escaped
         self.data = data
 
diff --git a/third_party/logilab/common/ureports/text_writer.py b/third_party/logilab/common/ureports/text_writer.py
index 04c8f26..a25cf26 100644
--- a/third_party/logilab/common/ureports/text_writer.py
+++ b/third_party/logilab/common/ureports/text_writer.py
@@ -16,8 +16,13 @@
 # You should have received a copy of the GNU Lesser General Public License along
 # with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
 """Text formatting drivers for ureports"""
+
+from __future__ import print_function
+
 __docformat__ = "restructuredtext en"
 
+from six.moves import range
+
 from logilab.common.textutils import linesep
 from logilab.common.ureports import BaseWriter
 
@@ -54,7 +59,7 @@
         try:
             self.writeln(TITLE_UNDERLINES[self.section] * len(title))
         except IndexError:
-            print "FIXME TITLE TOO DEEP. TURNING TITLE INTO TEXT"
+            print("FIXME TITLE TOO DEEP. TURNING TITLE INTO TEXT")
 
     def visit_paragraph(self, layout):
         """enter a paragraph"""
diff --git a/third_party/logilab/common/urllib2ext.py b/third_party/logilab/common/urllib2ext.py
index 08797a4..3e7a36d 100644
--- a/third_party/logilab/common/urllib2ext.py
+++ b/third_party/logilab/common/urllib2ext.py
@@ -62,7 +62,7 @@
                 if result < 1:
                     raise GssapiAuthError("HTTPGssapiAuthHandler: step 2 failed with %d" % result)
             return server_response
-        except GssapiAuthError, exc:
+        except GssapiAuthError as exc:
             logging.error(repr(exc))
         finally:
             self.clean_context()
diff --git a/third_party/logilab/common/visitor.py b/third_party/logilab/common/visitor.py
index 802d2be..ed2b70f 100644
--- a/third_party/logilab/common/visitor.py
+++ b/third_party/logilab/common/visitor.py
@@ -35,12 +35,14 @@
             filter_func = no_filter
         self._list = list_func(node, filter_func)
 
-    def next(self):
+    def __next__(self):
         try:
             return self._list.pop(0)
         except :
             return None
 
+    next = __next__
+
 # Base Visitor ################################################################
 class Visitor(object):
 
@@ -61,10 +63,10 @@
 
     def _visit(self, node):
         iterator = self._get_iterator(node)
-        n = iterator.next()
+        n = next(iterator)
         while n:
             result = n.accept(self)
-            n = iterator.next()
+            n = next(iterator)
         return result
 
     def _get_iterator(self, node):
diff --git a/third_party/pylint.py b/third_party/pylint.py
index 4aca4b6..bf1860b 100755
--- a/third_party/pylint.py
+++ b/third_party/pylint.py
@@ -16,7 +16,13 @@
 
 Copyright (c) 2012 The Chromium Authors. All rights reserved.
 """
+import os
 import sys
+
+# Add local modules to the search path.
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(
+    __file__)), 'logilab'))
+
 from pylint import lint
 
 args = sys.argv[1:]
diff --git a/third_party/pylint/README.chromium b/third_party/pylint/README.chromium
index c9dc46c..22c1fbb 100644
--- a/third_party/pylint/README.chromium
+++ b/third_party/pylint/README.chromium
@@ -1,5 +1,5 @@
-URL: http://www.logilab.org/project/pylint
-Version: 0.25.1
+URL: http://www.pylint.org/
+Version: 1.3.1
 License: GPL
 License File: LICENSE.txt
 
diff --git a/third_party/pylint/__init__.py b/third_party/pylint/__init__.py
index 0c4bd13..eed1b62 100644
--- a/third_party/pylint/__init__.py
+++ b/third_party/pylint/__init__.py
@@ -1,3 +1,6 @@
+# Copyright (c) 2003-2012 LOGILAB S.A. (Paris, FRANCE).
+# http://www.logilab.fr/ -- mailto:[email protected]
+#
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
 # Foundation; either version 2 of the License, or (at your option) any later
@@ -9,8 +12,33 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2002-2008 LOGILAB S.A. (Paris, FRANCE).
-http://www.logilab.fr/ -- mailto:[email protected]  
-"""
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+import sys
 
+def run_pylint():
+    """run pylint"""
+    from pylint.lint import Run
+    Run(sys.argv[1:])
+
+def run_pylint_gui():
+    """run pylint-gui"""
+    try:
+        from pylint.gui import Run
+        Run(sys.argv[1:])
+    except ImportError:
+        sys.exit('tkinter is not available')
+
+def run_epylint():
+    """run pylint"""
+    from pylint.epylint import Run
+    Run()
+
+def run_pyreverse():
+    """run pyreverse"""
+    from pylint.pyreverse.main import Run
+    Run(sys.argv[1:])
+
+def run_symilar():
+    """run symilar"""
+    from pylint.checkers.similar import Run
+    Run(sys.argv[1:])
diff --git a/third_party/pylint/__main__.py b/third_party/pylint/__main__.py
new file mode 100644
index 0000000..7716361
--- /dev/null
+++ b/third_party/pylint/__main__.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+import pylint
+pylint.run_pylint()
diff --git a/third_party/pylint/__pkginfo__.py b/third_party/pylint/__pkginfo__.py
index 2a6ac3a..f8bd352 100644
--- a/third_party/pylint/__pkginfo__.py
+++ b/third_party/pylint/__pkginfo__.py
@@ -1,5 +1,5 @@
 # pylint: disable=W0622,C0103
-# Copyright (c) 2003-2011 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -13,35 +13,40 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """pylint packaging information"""
+import sys
 
 modname = distname = 'pylint'
 
-numversion = (0, 25, 1)
+numversion = (1, 3, 1)
 version = '.'.join([str(num) for num in numversion])
 
-install_requires = ['logilab-common >= 0.53.0', 'logilab-astng >= 0.21.1']
+if sys.version_info < (2, 6):
+    install_requires = ['logilab-common >= 0.53.0', 'astroid >= 1.2.1',
+                        'StringFormat']
+else:
+    install_requires = ['logilab-common >= 0.53.0', 'astroid >= 1.2.1']
 
 license = 'GPL'
-copyright = 'Logilab S.A.'
 description = "python code static checker"
-web = "http://www.logilab.org/project/%s" % distname
-ftp = "ftp://ftp.logilab.org/pub/%s" % modname
-mailinglist = "mailto://[email protected]"
+web = 'http://www.pylint.org'
+mailinglist = "mailto://[email protected]"
 author = 'Logilab'
 author_email = '[email protected]'
 
-classifiers =  ['Development Status :: 4 - Beta',
-                'Environment :: Console',
-                'Intended Audience :: Developers',
-                'License :: OSI Approved :: GNU General Public License (GPL)',
-                'Operating System :: OS Independent',
-                'Programming Language :: Python',
-                'Topic :: Software Development :: Debuggers',
-                'Topic :: Software Development :: Quality Assurance',
-                'Topic :: Software Development :: Testing',
-                ]
+classifiers = ['Development Status :: 4 - Beta',
+               'Environment :: Console',
+               'Intended Audience :: Developers',
+               'License :: OSI Approved :: GNU General Public License (GPL)',
+               'Operating System :: OS Independent',
+               'Programming Language :: Python',
+               'Programming Language :: Python :: 2',
+               'Programming Language :: Python :: 3',
+               'Topic :: Software Development :: Debuggers',
+               'Topic :: Software Development :: Quality Assurance',
+               'Topic :: Software Development :: Testing'
+              ]
 
 
 long_desc = """\
@@ -66,3 +71,4 @@
            for filename in ('pylint', 'pylint-gui', "symilar", "epylint",
                             "pyreverse")]
 
+include_dirs = ['test']
diff --git a/third_party/pylint/checkers/__init__.py b/third_party/pylint/checkers/__init__.py
index 969066b..693a5ff 100644
--- a/third_party/pylint/checkers/__init__.py
+++ b/third_party/pylint/checkers/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -12,7 +12,7 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """utilities methods and classes for checkers
 
 Base id of standard checkers (used in msg and report ids):
@@ -29,7 +29,8 @@
 11: typecheck
 12: logging
 13: string_format
-14-50: not yet used: reserved for future internal checkers.
+14: string_constant
+15-50: not yet used: reserved for future internal checkers.
 51-99: perhaps used: reserved for external checkers
 
 The raw_metrics checker has no number associated since it doesn't emit any
@@ -37,14 +38,14 @@
 
 """
 
+import sys
 import tokenize
-from os import listdir
-from os.path import dirname, join, isdir, splitext
+import warnings
 
-from logilab.astng.utils import ASTWalker
 from logilab.common.configuration import OptionsProviderMixIn
 
-from pylint.reporters import diff_string, EmptyReport
+from pylint.reporters import diff_string
+from pylint.utils import register_plugins
 
 def table_lines_from_stats(stats, old_stats, columns):
     """get values listed in <columns> from <stats> and <old_stats>,
@@ -67,7 +68,7 @@
     return lines
 
 
-class BaseChecker(OptionsProviderMixIn, ASTWalker):
+class BaseChecker(OptionsProviderMixIn):
     """base class for checkers"""
     # checker name (you may reuse an existing one)
     name = None
@@ -85,22 +86,14 @@
 
         linter is an object implementing ILinter
         """
-        ASTWalker.__init__(self, self)
         self.name = self.name.lower()
         OptionsProviderMixIn.__init__(self)
         self.linter = linter
-        # messages that are active for the current check
-        self.active_msgs = set()
 
     def add_message(self, msg_id, line=None, node=None, args=None):
         """add a message of a given type"""
         self.linter.add_message(msg_id, line, node, args)
 
-    def package_dir(self):
-        """return the base directory for the analysed package"""
-        return dirname(self.linter.base_file)
-
-
     # dummy methods implementing the IChecker interface
 
     def open(self):
@@ -109,6 +102,7 @@
     def close(self):
         """called after visiting project (i.e set of modules)"""
 
+
 class BaseRawChecker(BaseChecker):
     """base class for raw checkers"""
 
@@ -119,45 +113,31 @@
 
         stream must implement the readline method
         """
+        warnings.warn("Modules that need access to the tokens should "
+                      "use the ITokenChecker interface.",
+                      DeprecationWarning)
         stream = node.file_stream
-        stream.seek(0) # XXX may be removed with astng > 0.23
-        self.process_tokens(tokenize.generate_tokens(stream.readline))
+        stream.seek(0) # XXX may be removed with astroid > 0.23
+        if sys.version_info <= (3, 0):
+            self.process_tokens(tokenize.generate_tokens(stream.readline))
+        else:
+            self.process_tokens(tokenize.tokenize(stream.readline))
 
     def process_tokens(self, tokens):
         """should be overridden by subclasses"""
         raise NotImplementedError()
 
 
-PY_EXTS = ('.py', '.pyc', '.pyo', '.pyw', '.so', '.dll')
+class BaseTokenChecker(BaseChecker):
+    """Base class for checkers that want to have access to the token stream."""
+
+    def process_tokens(self, tokens):
+        """Should be overridden by subclasses."""
+        raise NotImplementedError()
+
 
 def initialize(linter):
     """initialize linter with checkers in this package """
-    package_load(linter, __path__[0])
+    register_plugins(linter, __path__[0])
 
-def package_load(linter, directory):
-    """load all module and package in the given directory, looking for a
-    'register' function in each one, used to register pylint checkers
-    """
-    globs = globals()
-    imported = {}
-    for filename in listdir(directory):
-        basename, extension = splitext(filename)
-        if basename in imported or basename == '__pycache__':
-            continue
-        if extension in PY_EXTS and basename != '__init__' or (
-             not extension and basename != 'CVS' and
-             isdir(join(directory, basename))):
-            try:
-                module = __import__(basename, globs, globs, None)
-            except ValueError:
-                # empty module name (usually emacs auto-save files)
-                continue
-            except ImportError, exc:
-                import sys
-                print >> sys.stderr, "Problem importing module %s: %s" % (filename, exc)
-            else:
-                if hasattr(module, 'register'):
-                    module.register(linter)
-                    imported[basename] = 1
-
-__all__ = ('CheckerHandler', 'BaseChecker', 'initialize', 'package_load')
+__all__ = ('BaseChecker', 'initialize')
diff --git a/third_party/pylint/checkers/base.py b/third_party/pylint/checkers/base.py
index 2062ae2..469aeb8 100644
--- a/third_party/pylint/checkers/base.py
+++ b/third_party/pylint/checkers/base.py
@@ -1,6 +1,7 @@
-# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
-# Copyright (c) 2009-2010 Arista Networks, Inc.
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
+# Copyright (c) 2009-2010 Arista Networks, Inc.
+#
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
 # Foundation; either version 2 of the License, or (at your option) any later
@@ -12,18 +13,29 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """basic checker for Python code"""
 
-
-from logilab import astng
+import sys
+import astroid
 from logilab.common.ureports import Table
-from logilab.astng import are_exclusive
+from astroid import are_exclusive, InferenceError
+import astroid.bases
 
-from pylint.interfaces import IASTNGChecker
+from pylint.interfaces import IAstroidChecker
+from pylint.utils import EmptyReport
 from pylint.reporters import diff_string
-from pylint.checkers import BaseChecker, EmptyReport
-from pylint.checkers.utils import check_messages, clobber_in_except, is_inside_except
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import (
+    check_messages,
+    clobber_in_except,
+    is_builtin_object,
+    is_inside_except,
+    overrides_a_method,
+    safe_infer,
+    get_argument_from_call,
+    NoSuchArgumentError,
+    )
 
 
 import re
@@ -34,8 +46,21 @@
 CONST_NAME_RGX = re.compile('(([A-Z_][A-Z0-9_]*)|(__.*__))$')
 COMP_VAR_RGX = re.compile('[A-Za-z_][A-Za-z0-9_]*$')
 DEFAULT_NAME_RGX = re.compile('[a-z_][a-z0-9_]{2,30}$')
+CLASS_ATTRIBUTE_RGX = re.compile(r'([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$')
 # do not require a doc string on system methods
 NO_REQUIRED_DOC_RGX = re.compile('__.*__')
+REVERSED_METHODS = (('__getitem__', '__len__'),
+                    ('__reversed__', ))
+
+PY33 = sys.version_info >= (3, 3)
+PY3K = sys.version_info >= (3, 0)
+BAD_FUNCTIONS = ['map', 'filter', 'apply']
+if sys.version_info < (3, 0):
+    BAD_FUNCTIONS.append('input')
+    BAD_FUNCTIONS.append('file')
+
+# Name categories that are always consistent with all naming conventions.
+EXEMPT_NAME_CATEGORIES = set(('exempt', 'ignore'))
 
 del re
 
@@ -43,8 +68,8 @@
     """return True if the node is inside a kind of for loop"""
     parent = node.parent
     while parent is not None:
-        if isinstance(parent, (astng.For, astng.ListComp, astng.SetComp,
-                               astng.DictComp, astng.GenExpr)):
+        if isinstance(parent, (astroid.For, astroid.ListComp, astroid.SetComp,
+                               astroid.DictComp, astroid.GenExpr)):
             return True
         parent = parent.parent
     return False
@@ -61,6 +86,77 @@
             return True
     return False
 
+def _loop_exits_early(loop):
+    """Returns true if a loop has a break statement in its body."""
+    loop_nodes = (astroid.For, astroid.While)
+    # Loop over body explicitly to avoid matching break statements
+    # in orelse.
+    for child in loop.body:
+        if isinstance(child, loop_nodes):
+            # break statement may be in orelse of child loop.
+            for orelse in (child.orelse or ()):
+                for _ in orelse.nodes_of_class(astroid.Break, skip_klass=loop_nodes):
+                    return True
+            continue
+        for _ in child.nodes_of_class(astroid.Break, skip_klass=loop_nodes):
+            return True
+    return False
+
+if sys.version_info < (3, 0):
+    PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty'))
+else:
+    PROPERTY_CLASSES = set(('builtins.property', 'abc.abstractproperty'))
+ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod',
+                   'abc.abstractclassmethod', 'abc.abstractstaticmethod'))
+
+def _determine_function_name_type(node):
+    """Determine the name type whose regex the a function's name should match.
+
+    :param node: A function node.
+    :returns: One of ('function', 'method', 'attr')
+    """
+    if not node.is_method():
+        return 'function'
+    if node.decorators:
+        decorators = node.decorators.nodes
+    else:
+        decorators = []
+    for decorator in decorators:
+        # If the function is a property (decorated with @property
+        # or @abc.abstractproperty), the name type is 'attr'.
+        if (isinstance(decorator, astroid.Name) or
+                (isinstance(decorator, astroid.Getattr) and
+                 decorator.attrname == 'abstractproperty')):
+            infered = safe_infer(decorator)
+            if infered and infered.qname() in PROPERTY_CLASSES:
+                return 'attr'
+        # If the function is decorated using the prop_method.{setter,getter}
+        # form, treat it like an attribute as well.
+        elif (isinstance(decorator, astroid.Getattr) and
+              decorator.attrname in ('setter', 'deleter')):
+            return 'attr'
+    return 'method'
+
+def decorated_with_abc(func):
+    """ Determine if the `func` node is decorated
+    with `abc` decorators (abstractmethod et co.)
+    """
+    if func.decorators:
+        for node in func.decorators.nodes:
+            try:
+                infered = node.infer().next()
+            except InferenceError:
+                continue
+            if infered and infered.qname() in ABC_METHODS:
+                return True
+
+def has_abstract_methods(node):
+    """ Determine if the given `node` has
+    abstract methods, defined with `abc` module.
+    """
+    return any(decorated_with_abc(meth)
+               for meth in node.mymethods())
+
 def report_by_type_stats(sect, stats, old_stats):
     """make a report of
 
@@ -112,115 +208,191 @@
     """
     if node.decorators:
         for decorator in node.decorators.nodes:
-            if (isinstance(decorator, astng.Getattr) and
-                decorator.expr.name == node.name):
+            if (isinstance(decorator, astroid.Getattr) and
+                    getattr(decorator.expr, 'name', None) == node.name):
                 return True
     return False
 
 class _BasicChecker(BaseChecker):
-    __implements__ = IASTNGChecker
+    __implements__ = IAstroidChecker
     name = 'basic'
 
 class BasicErrorChecker(_BasicChecker):
     msgs = {
-    'E0100': ('__init__ method is a generator',
-              'Used when the special class method __init__ is turned into a '
-              'generator by a yield in its body.'),
-    'E0101': ('Explicit return in __init__',
-              'Used when the special class method __init__ has an explicit \
-              return value.'),
-    'E0102': ('%s already defined line %s',
-              'Used when a function / class / method is redefined.'),
-    'E0103': ('%r not properly in loop',
-              'Used when break or continue keywords are used outside a loop.'),
+        'E0100': ('__init__ method is a generator',
+                  'init-is-generator',
+                  'Used when the special class method __init__ is turned into a '
+                  'generator by a yield in its body.'),
+        'E0101': ('Explicit return in __init__',
+                  'return-in-init',
+                  'Used when the special class method __init__ has an explicit '
+                  'return value.'),
+        'E0102': ('%s already defined line %s',
+                  'function-redefined',
+                  'Used when a function / class / method is redefined.'),
+        'E0103': ('%r not properly in loop',
+                  'not-in-loop',
+                  'Used when break or continue keywords are used outside a loop.'),
+        'E0104': ('Return outside function',
+                  'return-outside-function',
+                  'Used when a "return" statement is found outside a function or '
+                  'method.'),
+        'E0105': ('Yield outside function',
+                  'yield-outside-function',
+                  'Used when a "yield" statement is found outside a function or '
+                  'method.'),
+        'E0106': ('Return with argument inside generator',
+                  'return-arg-in-generator',
+                  'Used when a "return" statement with an argument is found '
+                  'outside in a generator function or method (e.g. with some '
+                  '"yield" statements).',
+                  {'maxversion': (3, 3)}),
+        'E0107': ("Use of the non-existent %s operator",
+                  'nonexistent-operator',
+                  "Used when you attempt to use the C-style pre-increment or"
+                  "pre-decrement operator -- and ++, which doesn't exist in Python."),
+        'E0108': ('Duplicate argument name %s in function definition',
+                  'duplicate-argument-name',
+                  'Duplicate argument names in function definitions are syntax'
+                  ' errors.'),
+        'E0110': ('Abstract class with abstract methods instantiated',
+                  'abstract-class-instantiated',
+                  'Used when an abstract class with `abc.ABCMeta` as metaclass '
+                  'has abstract methods and is instantiated.',
+                  {'minversion': (3, 0)}),
+        'W0120': ('Else clause on loop without a break statement',
+                  'useless-else-on-loop',
+                  'Loops should only have an else clause if they can exit early '
+                  'with a break statement, otherwise the statements under else '
+                  'should be on the same scope as the loop itself.'),
+        }
 
-    'E0104': ('Return outside function',
-              'Used when a "return" statement is found outside a function or '
-              'method.'),
-    'E0105': ('Yield outside function',
-              'Used when a "yield" statement is found outside a function or '
-              'method.'),
-    'E0106': ('Return with argument inside generator',
-              'Used when a "return" statement with an argument is found '
-              'outside in a generator function or method (e.g. with some '
-              '"yield" statements).'),
-    'E0107': ("Use of the non-existent %s operator",
-              "Used when you attempt to use the C-style pre-increment or"
-              "pre-decrement operator -- and ++, which doesn't exist in Python."),
-    }
-
-    def __init__(self, linter):
-        _BasicChecker.__init__(self, linter)
-
-    @check_messages('E0102')
+    @check_messages('function-redefined')
     def visit_class(self, node):
         self._check_redefinition('class', node)
 
-    @check_messages('E0100', 'E0101', 'E0102', 'E0106')
+    @check_messages('init-is-generator', 'return-in-init',
+                    'function-redefined', 'return-arg-in-generator',
+                    'duplicate-argument-name')
     def visit_function(self, node):
         if not redefined_by_decorator(node):
             self._check_redefinition(node.is_method() and 'method' or 'function', node)
         # checks for max returns, branch, return in __init__
-        returns = node.nodes_of_class(astng.Return,
-                                      skip_klass=(astng.Function, astng.Class))
+        returns = node.nodes_of_class(astroid.Return,
+                                      skip_klass=(astroid.Function, astroid.Class))
         if node.is_method() and node.name == '__init__':
             if node.is_generator():
-                self.add_message('E0100', node=node)
+                self.add_message('init-is-generator', node=node)
             else:
                 values = [r.value for r in returns]
-                if  [v for v in values if not (v is None or
-                    (isinstance(v, astng.Const) and v.value is None)
-                    or  (isinstance(v, astng.Name) and v.name == 'None'))]:
-                    self.add_message('E0101', node=node)
+                # Are we returning anything but None from constructors
+                if [v for v in values
+                        if not (v is None or
+                                (isinstance(v, astroid.Const) and v.value is None) or
+                                (isinstance(v, astroid.Name)  and v.name == 'None')
+                               )]:
+                    self.add_message('return-in-init', node=node)
         elif node.is_generator():
             # make sure we don't mix non-None returns and yields
-            for retnode in returns:
-                if isinstance(retnode.value, astng.Const) and \
-                       retnode.value.value is not None:
-                    self.add_message('E0106', node=node,
-                                     line=retnode.fromlineno)
+            if not PY33:
+                for retnode in returns:
+                    if isinstance(retnode.value, astroid.Const) and \
+                           retnode.value.value is not None:
+                        self.add_message('return-arg-in-generator', node=node,
+                                         line=retnode.fromlineno)
+        # Check for duplicate names
+        args = set()
+        for name in node.argnames():
+            if name in args:
+                self.add_message('duplicate-argument-name', node=node, args=(name,))
+            else:
+                args.add(name)
 
-    @check_messages('E0104')
+
+    @check_messages('return-outside-function')
     def visit_return(self, node):
-        if not isinstance(node.frame(), astng.Function):
-            self.add_message('E0104', node=node)
+        if not isinstance(node.frame(), astroid.Function):
+            self.add_message('return-outside-function', node=node)
 
-    @check_messages('E0105')
+    @check_messages('yield-outside-function')
     def visit_yield(self, node):
-        if not isinstance(node.frame(), astng.Function):
-            self.add_message('E0105', node=node)
+        if not isinstance(node.frame(), (astroid.Function, astroid.Lambda)):
+            self.add_message('yield-outside-function', node=node)
 
-    @check_messages('E0103')
+    @check_messages('not-in-loop')
     def visit_continue(self, node):
         self._check_in_loop(node, 'continue')
 
-    @check_messages('E0103')
+    @check_messages('not-in-loop')
     def visit_break(self, node):
         self._check_in_loop(node, 'break')
 
-    @check_messages('E0107')
+    @check_messages('useless-else-on-loop')
+    def visit_for(self, node):
+        self._check_else_on_loop(node)
+
+    @check_messages('useless-else-on-loop')
+    def visit_while(self, node):
+        self._check_else_on_loop(node)
+
+    @check_messages('nonexistent-operator')
     def visit_unaryop(self, node):
-        """check use of the non-existent ++ adn -- operator operator"""
+        """check use of the non-existent ++ and -- operator operator"""
         if ((node.op in '+-') and
-            isinstance(node.operand, astng.UnaryOp) and
-            (node.operand.op == node.op)):
-            self.add_message('E0107', node=node, args=node.op*2)
+                isinstance(node.operand, astroid.UnaryOp) and
+                (node.operand.op == node.op)):
+            self.add_message('nonexistent-operator', node=node, args=node.op*2)
+
+    @check_messages('abstract-class-instantiated')
+    def visit_callfunc(self, node):
+        """ Check instantiating abstract class with
+        abc.ABCMeta as metaclass.
+        """
+        try:
+            infered = node.func.infer().next()
+        except astroid.InferenceError:
+            return
+        if not isinstance(infered, astroid.Class):
+            return
+        # __init__ was called
+        metaclass = infered.metaclass()
+        if metaclass is None:
+            # Python 3.4 has `abc.ABC`, which won't be detected
+            # by ClassNode.metaclass()
+            for ancestor in infered.ancestors():
+                if (ancestor.qname() == 'abc.ABC' and
+                        has_abstract_methods(infered)):
+                    self.add_message('abstract-class-instantiated', node=node)
+                    break
+            return
+        if (metaclass.qname() == 'abc.ABCMeta' and
+                has_abstract_methods(infered)):
+            self.add_message('abstract-class-instantiated', node=node)
+
+    def _check_else_on_loop(self, node):
+        """Check that any loop with an else clause has a break statement."""
+        if node.orelse and not _loop_exits_early(node):
+            self.add_message('useless-else-on-loop', node=node,
+                             # This is not optimal, but the line previous
+                             # to the first statement in the else clause
+                             # will usually be the one that contains the else:.
+                             line=node.orelse[0].lineno - 1)
 
     def _check_in_loop(self, node, node_name):
         """check that a node is inside a for or while loop"""
         _node = node.parent
         while _node:
-            if isinstance(_node, (astng.For, astng.While)):
+            if isinstance(_node, (astroid.For, astroid.While)):
                 break
             _node = _node.parent
         else:
-            self.add_message('E0103', node=node, args=node_name)
+            self.add_message('not-in-loop', node=node, args=node_name)
 
     def _check_redefinition(self, redeftype, node):
         """check for redefinition of a function / method / class name"""
         defined_self = node.parent.frame()[node.name]
         if defined_self is not node and not are_exclusive(node, defined_self):
-            self.add_message('E0102', node=node,
+            self.add_message('function-redefined', node=node,
                              args=(redeftype, defined_self.fromlineno))
 
 
@@ -228,7 +400,6 @@
 class BasicChecker(_BasicChecker):
     """checks for :
     * doc strings
-    * modules / classes / functions / methods / arguments / variables name
     * number of arguments, local variables, branches, returns and statements in
 functions, methods
     * required module attributes
@@ -237,60 +408,92 @@
     * uses of the global statement
     """
 
-    __implements__ = IASTNGChecker
+    __implements__ = IAstroidChecker
 
     name = 'basic'
     msgs = {
-    'W0101': ('Unreachable code',
-              'Used when there is some code behind a "return" or "raise" \
-              statement, which will never be accessed.'),
-    'W0102': ('Dangerous default value %s as argument',
-              'Used when a mutable value as list or dictionary is detected in \
-              a default value for an argument.'),
-    'W0104': ('Statement seems to have no effect',
-              'Used when a statement doesn\'t have (or at least seems to) \
-              any effect.'),
-    'W0105': ('String statement has no effect',
-              'Used when a string is used as a statement (which of course \
-              has no effect). This is a particular case of W0104 with its \
-              own message so you can easily disable it if you\'re using \
-              those strings as documentation, instead of comments.'),
-    'W0106': ('Expression "%s" is assigned to nothing',
-              'Used when an expression that is not a function call is assigned\
-              to nothing. Probably something else was intended.'),
-    'W0108': ('Lambda may not be necessary',
-              'Used when the body of a lambda expression is a function call \
-              on the same argument list as the lambda itself; such lambda \
-              expressions are in all but a few cases replaceable with the \
-              function being called in the body of the lambda.'),
-    'W0109': ("Duplicate key %r in dictionary",
-              "Used when a dictionary expression binds the same key multiple \
-              times."),
-    'W0122': ('Use of the exec statement',
-              'Used when you use the "exec" statement, to discourage its \
-              usage. That doesn\'t mean you can not use it !'),
+        'W0101': ('Unreachable code',
+                  'unreachable',
+                  'Used when there is some code behind a "return" or "raise" '
+                  'statement, which will never be accessed.'),
+        'W0102': ('Dangerous default value %s as argument',
+                  'dangerous-default-value',
+                  'Used when a mutable value as list or dictionary is detected in '
+                  'a default value for an argument.'),
+        'W0104': ('Statement seems to have no effect',
+                  'pointless-statement',
+                  'Used when a statement doesn\'t have (or at least seems to) '
+                  'any effect.'),
+        'W0105': ('String statement has no effect',
+                  'pointless-string-statement',
+                  'Used when a string is used as a statement (which of course '
+                  'has no effect). This is a particular case of W0104 with its '
+                  'own message so you can easily disable it if you\'re using '
+                  'those strings as documentation, instead of comments.'),
+        'W0106': ('Expression "%s" is assigned to nothing',
+                  'expression-not-assigned',
+                  'Used when an expression that is not a function call is assigned '
+                  'to nothing. Probably something else was intended.'),
+        'W0108': ('Lambda may not be necessary',
+                  'unnecessary-lambda',
+                  'Used when the body of a lambda expression is a function call '
+                  'on the same argument list as the lambda itself; such lambda '
+                  'expressions are in all but a few cases replaceable with the '
+                  'function being called in the body of the lambda.'),
+        'W0109': ("Duplicate key %r in dictionary",
+                  'duplicate-key',
+                  'Used when a dictionary expression binds the same key multiple '
+                  'times.'),
+        'W0122': ('Use of exec',
+                  'exec-used',
+                  'Used when you use the "exec" statement (function for Python 3), to discourage its '
+                  'usage. That doesn\'t mean you can not use it !'),
+        'W0123': ('Use of eval',
+                  'eval-used',
+                  'Used when you use the "eval" function, to discourage its '
+                  'usage. Consider using `ast.literal_eval` for safely evaluating '
+                  'strings containing Python expressions '
+                  'from untrusted sources. '),
+        'W0141': ('Used builtin function %r',
+                  'bad-builtin',
+                  'Used when a black listed builtin function is used (see the '
+                  'bad-function option). Usual black listed functions are the ones '
+                  'like map, or filter , where Python offers now some cleaner '
+                  'alternative like list comprehension.'),
+        'W0142': ('Used * or ** magic',
+                  'star-args',
+                  'Used when a function or method is called using `*args` or '
+                  '`**kwargs` to dispatch arguments. This doesn\'t improve '
+                  'readability and should be used with care.'),
+        'W0150': ("%s statement in finally block may swallow exception",
+                  'lost-exception',
+                  'Used when a break or a return statement is found inside the '
+                  'finally clause of a try...finally block: the exceptions raised '
+                  'in the try clause will be silently swallowed instead of being '
+                  're-raised.'),
+        'W0199': ('Assert called on a 2-uple. Did you mean \'assert x,y\'?',
+                  'assert-on-tuple',
+                  'A call of assert on a tuple will always evaluate to true if '
+                  'the tuple is not empty, and will always evaluate to false if '
+                  'it is.'),
+        'W0121': ('Use raise ErrorClass(args) instead of raise ErrorClass, args.',
+                  'old-raise-syntax',
+                  "Used when the alternate raise syntax 'raise foo, bar' is used "
+                  "instead of 'raise foo(bar)'.",
+                  {'maxversion': (3, 0)}),
 
-    'W0141': ('Used builtin function %r',
-              'Used when a black listed builtin function is used (see the '
-              'bad-function option). Usual black listed functions are the ones '
-              'like map, or filter , where Python offers now some cleaner '
-              'alternative like list comprehension.'),
-    'W0142': ('Used * or ** magic',
-              'Used when a function or method is called using `*args` or '
-              '`**kwargs` to dispatch arguments. This doesn\'t improve '
-              'readability and should be used with care.'),
-    'W0150': ("%s statement in finally block may swallow exception",
-              "Used when a break or a return statement is found inside the \
-              finally clause of a try...finally block: the exceptions raised \
-              in the try clause will be silently swallowed instead of being \
-              re-raised."),
-    'W0199': ('Assert called on a 2-uple. Did you mean \'assert x,y\'?',
-              'A call of assert on a tuple will always evaluate to true if '
-              'the tuple is not empty, and will always evaluate to false if '
-              'it is.'),
+        'C0121': ('Missing required attribute "%s"', # W0103
+                  'missing-module-attribute',
+                  'Used when an attribute required for modules is missing.'),
 
-    'C0121': ('Missing required attribute "%s"', # W0103
-              'Used when an attribute required for modules is missing.'),
+        'E0109': ('Missing argument to reversed()',
+                  'missing-reversed-argument',
+                  'Used when reversed() builtin didn\'t receive an argument.'),
+        'E0111': ('The first reversed() argument is not a sequence',
+                  'bad-reversed-sequence',
+                  'Used when the first argument to reversed() builtin '
+                  'isn\'t a sequence (does not implement __reversed__, '
+                  'nor __getitem__ and __len__'),
 
     }
 
@@ -299,15 +502,15 @@
                  'metavar' : '<attributes>',
                  'help' : 'Required attributes for module, separated by a '
                           'comma'}
-                ),
+               ),
                ('bad-functions',
-                {'default' : ('map', 'filter', 'apply', 'input'),
+                {'default' : BAD_FUNCTIONS,
                  'type' :'csv', 'metavar' : '<builtin function names>',
                  'help' : 'List of builtins function names that should not be '
                           'used, separated by a comma'}
-                ),
-               )
-    reports = ( ('RP0101', 'Statistics by type', report_by_type_stats), )
+               ),
+              )
+    reports = (('RP0101', 'Statistics by type', report_by_type_stats),)
 
     def __init__(self, linter):
         _BasicChecker.__init__(self, linter)
@@ -320,14 +523,14 @@
         self._tryfinallys = []
         self.stats = self.linter.add_stats(module=0, function=0,
                                            method=0, class_=0)
-
+    @check_messages('missing-module-attribute')
     def visit_module(self, node):
         """check module name, docstring and required arguments
         """
         self.stats['module'] += 1
         for attr in self.config.required_attributes:
             if attr not in node:
-                self.add_message('C0121', node=node, args=attr)
+                self.add_message('missing-module-attribute', node=node, args=attr)
 
     def visit_class(self, node):
         """check module name, docstring and redefinition
@@ -335,31 +538,45 @@
         """
         self.stats['class'] += 1
 
-    @check_messages('W0104', 'W0105')
+    @check_messages('pointless-statement', 'pointless-string-statement',
+                    'expression-not-assigned')
     def visit_discard(self, node):
         """check for various kind of statements without effect"""
         expr = node.value
-        if isinstance(expr, astng.Const) and isinstance(expr.value,
-                                                        basestring):
+        if isinstance(expr, astroid.Const) and isinstance(expr.value,
+                                                          basestring):
             # treat string statement in a separated message
-            self.add_message('W0105', node=node)
+            # Handle PEP-257 attribute docstrings.
+            # An attribute docstring is defined as being a string right after
+            # an assignment at the module level, class level or __init__ level.
+            scope = expr.scope()
+            if isinstance(scope, (astroid.Class, astroid.Module, astroid.Function)):
+                if isinstance(scope, astroid.Function) and scope.name != '__init__':
+                    pass
+                else:
+                    sibling = expr.previous_sibling()
+                    if (sibling is not None and sibling.scope() is scope and
+                            isinstance(sibling, astroid.Assign)):
+                        return
+            self.add_message('pointless-string-statement', node=node)
             return
         # ignore if this is :
         # * a direct function call
         # * the unique child of a try/except body
         # * a yield (which are wrapped by a discard node in _ast XXX)
         # warn W0106 if we have any underlying function call (we can't predict
-        # side effects), else W0104
-        if (isinstance(expr, (astng.Yield, astng.CallFunc)) or
-            (isinstance(node.parent, astng.TryExcept) and
-             node.parent.body == [node])):
+        # side effects), else pointless-statement
+        if (isinstance(expr, (astroid.Yield, astroid.CallFunc)) or
+                (isinstance(node.parent, astroid.TryExcept) and
+                 node.parent.body == [node])):
             return
-        if any(expr.nodes_of_class(astng.CallFunc)):
-            self.add_message('W0106', node=node, args=expr.as_string())
+        if any(expr.nodes_of_class(astroid.CallFunc)):
+            self.add_message('expression-not-assigned', node=node,
+                             args=expr.as_string())
         else:
-            self.add_message('W0104', node=node)
+            self.add_message('pointless-statement', node=node)
 
-    @check_messages('W0108')
+    @check_messages('unnecessary-lambda')
     def visit_lambda(self, node):
         """check whether or not the lambda is suspicious
         """
@@ -374,11 +591,11 @@
             # of the lambda.
             return
         call = node.body
-        if not isinstance(call, astng.CallFunc):
+        if not isinstance(call, astroid.CallFunc):
             # The body of the lambda must be a function call expression
             # for the lambda to be unnecessary.
             return
-        # XXX are lambda still different with astng >= 0.18 ?
+        # XXX are lambda still different with astroid >= 0.18 ?
         # *args and **kwargs need to be treated specially, since they
         # are structured differently between the lambda and the function
         # call (in the lambda they appear in the args.args list and are
@@ -388,15 +605,15 @@
         ordinary_args = list(node.args.args)
         if node.args.kwarg:
             if (not call.kwargs
-                or not isinstance(call.kwargs, astng.Name)
-                or node.args.kwarg != call.kwargs.name):
+                    or not isinstance(call.kwargs, astroid.Name)
+                    or node.args.kwarg != call.kwargs.name):
                 return
         elif call.kwargs:
             return
         if node.args.vararg:
             if (not call.starargs
-                or not isinstance(call.starargs, astng.Name)
-                or node.args.vararg != call.starargs.name):
+                    or not isinstance(call.starargs, astroid.Name)
+                    or node.args.vararg != call.starargs.name):
                 return
         elif call.starargs:
             return
@@ -405,12 +622,18 @@
         if len(ordinary_args) != len(call.args):
             return
         for i in xrange(len(ordinary_args)):
-            if not isinstance(call.args[i], astng.Name):
+            if not isinstance(call.args[i], astroid.Name):
                 return
             if node.args.args[i].name != call.args[i].name:
                 return
-        self.add_message('W0108', line=node.fromlineno, node=node)
+        if (isinstance(node.body.func, astroid.Getattr) and
+            isinstance(node.body.func.expr, astroid.CallFunc)):
+            # Chained call, the intermediate call might
+            # return something else (but we don't check that, yet).
+            return
+        self.add_message('unnecessary-lambda', line=node.fromlineno, node=node)
 
+    @check_messages('dangerous-default-value')
     def visit_function(self, node):
         """check function name, docstring, arguments, redefinition,
         variable names, max locals
@@ -420,16 +643,20 @@
         for default in node.args.defaults:
             try:
                 value = default.infer().next()
-            except astng.InferenceError:
+            except astroid.InferenceError:
                 continue
-            if isinstance(value, (astng.Dict, astng.List)):
+            builtins = astroid.bases.BUILTINS
+            if (isinstance(value, astroid.Instance) and
+                    value.qname() in ['.'.join([builtins, x]) for x in ('set', 'dict', 'list')]):
                 if value is default:
                     msg = default.as_string()
+                elif type(value) is astroid.Instance:
+                    msg = '%s (%s)' % (default.as_string(), value.qname())
                 else:
                     msg = '%s (%s)' % (default.as_string(), value.as_string())
-                self.add_message('W0102', node=node, args=(msg,))
+                self.add_message('dangerous-default-value', node=node, args=(msg,))
 
-    @check_messages('W0101', 'W0150')
+    @check_messages('unreachable', 'lost-exception')
     def visit_return(self, node):
         """1 - check is the node has a right sibling (if so, that's some
         unreachable code)
@@ -438,16 +665,16 @@
         """
         self._check_unreachable(node)
         # Is it inside final body of a try...finally bloc ?
-        self._check_not_in_finally(node, 'return', (astng.Function,))
+        self._check_not_in_finally(node, 'return', (astroid.Function,))
 
-    @check_messages('W0101')
+    @check_messages('unreachable')
     def visit_continue(self, node):
         """check is the node has a right sibling (if so, that's some unreachable
         code)
         """
         self._check_unreachable(node)
 
-    @check_messages('W0101', 'W0150')
+    @check_messages('unreachable', 'lost-exception')
     def visit_break(self, node):
         """1 - check is the node has a right sibling (if so, that's some
         unreachable code)
@@ -457,36 +684,48 @@
         # 1 - Is it right sibling ?
         self._check_unreachable(node)
         # 2 - Is it inside final body of a try...finally bloc ?
-        self._check_not_in_finally(node, 'break', (astng.For, astng.While,))
+        self._check_not_in_finally(node, 'break', (astroid.For, astroid.While,))
 
-    @check_messages('W0101')
+    @check_messages('unreachable', 'old-raise-syntax')
     def visit_raise(self, node):
-        """check is the node has a right sibling (if so, that's some unreachable
+        """check if the node has a right sibling (if so, that's some unreachable
         code)
         """
         self._check_unreachable(node)
+        if sys.version_info >= (3, 0):
+            return
+        if node.exc is not None and node.inst is not None and node.tback is None:
+            self.add_message('old-raise-syntax', node=node)
 
-    @check_messages('W0122')
+    @check_messages('exec-used')
     def visit_exec(self, node):
         """just print a warning on exec statements"""
-        self.add_message('W0122', node=node)
+        self.add_message('exec-used', node=node)
 
-    @check_messages('W0141', 'W0142')
+    @check_messages('bad-builtin', 'star-args', 'eval-used',
+                    'exec-used', 'missing-reversed-argument',
+                    'bad-reversed-sequence')
     def visit_callfunc(self, node):
         """visit a CallFunc node -> check if this is not a blacklisted builtin
         call and check for * or ** use
         """
-        if isinstance(node.func, astng.Name):
+        if isinstance(node.func, astroid.Name):
             name = node.func.name
             # ignore the name if it's not a builtin (i.e. not defined in the
             # locals nor globals scope)
             if not (name in node.frame() or
                     name in node.root()):
+                if name == 'exec':
+                    self.add_message('exec-used', node=node)
+                elif name == 'reversed':
+                    self._check_reversed(node)
+                elif name == 'eval':
+                    self.add_message('eval-used', node=node)
                 if name in self.config.bad_functions:
-                    self.add_message('W0141', node=node, args=name)
+                    self.add_message('bad-builtin', node=node, args=name)
         if node.starargs or node.kwargs:
             scope = node.scope()
-            if isinstance(scope, astng.Function):
+            if isinstance(scope, astroid.Function):
                 toprocess = [(n, vn) for (n, vn) in ((node.starargs, scope.args.vararg),
                                                      (node.kwargs, scope.args.kwarg)) if n]
                 if toprocess:
@@ -494,25 +733,25 @@
                         if getattr(cfnode, 'name', None) == fargname:
                             toprocess.remove((cfnode, fargname))
                     if not toprocess:
-                        return # W0142 can be skipped
-            self.add_message('W0142', node=node.func)
+                        return # star-args can be skipped
+            self.add_message('star-args', node=node.func)
 
-    @check_messages('W0199')
+    @check_messages('assert-on-tuple')
     def visit_assert(self, node):
         """check the use of an assert statement on a tuple."""
-        if node.fail is None and isinstance(node.test, astng.Tuple) and \
-           len(node.test.elts) == 2:
-             self.add_message('W0199', line=node.fromlineno, node=node)
+        if node.fail is None and isinstance(node.test, astroid.Tuple) and \
+                len(node.test.elts) == 2:
+            self.add_message('assert-on-tuple', node=node)
 
-    @check_messages('W0109')
+    @check_messages('duplicate-key')
     def visit_dict(self, node):
         """check duplicate key in dictionary"""
         keys = set()
-        for k, v in node.items:
-            if isinstance(k, astng.Const):
+        for k, _ in node.items:
+            if isinstance(k, astroid.Const):
                 key = k.value
                 if key in keys:
-                    self.add_message('W0109', node=node, args=key)
+                    self.add_message('duplicate-key', node=node, args=key)
                 keys.add(key)
 
     def visit_tryfinally(self, node):
@@ -527,7 +766,7 @@
         """check unreachable code"""
         unreach_stmt = node.next_sibling()
         if unreach_stmt is not None:
-            self.add_message('W0101', node=unreach_stmt)
+            self.add_message('unreachable', node=unreach_stmt)
 
     def _check_not_in_finally(self, node, node_name, breaker_classes=()):
         """check that a node is not inside a finally clause of a
@@ -542,91 +781,132 @@
         _node = node
         while _parent and not isinstance(_parent, breaker_classes):
             if hasattr(_parent, 'finalbody') and _node in _parent.finalbody:
-                self.add_message('W0150', node=node, args=node_name)
+                self.add_message('lost-exception', node=node, args=node_name)
                 return
             _node = _parent
             _parent = _node.parent
 
+    def _check_reversed(self, node):
+        """ check that the argument to `reversed` is a sequence """
+        try:
+            argument = safe_infer(get_argument_from_call(node, position=0))
+        except NoSuchArgumentError:
+            self.add_message('missing-reversed-argument', node=node)
+        else:
+            if argument is astroid.YES:
+                return
+            if argument is None:
+                # nothing was infered
+                # try to see if we have iter()
+                if isinstance(node.args[0], astroid.CallFunc):
+                    try:
+                        func = node.args[0].func.infer().next()
+                    except InferenceError:
+                        return
+                    if (getattr(func, 'name', None) == 'iter' and
+                            is_builtin_object(func)):
+                        self.add_message('bad-reversed-sequence', node=node)
+                return
 
+            if isinstance(argument, astroid.Instance):
+                if (argument._proxied.name == 'dict' and
+                        is_builtin_object(argument._proxied)):
+                    self.add_message('bad-reversed-sequence', node=node)
+                    return
+                elif any(ancestor.name == 'dict' and is_builtin_object(ancestor)
+                         for ancestor in argument._proxied.ancestors()):
+                    # mappings aren't accepted by reversed()
+                    self.add_message('bad-reversed-sequence', node=node)
+                    return
+
+                for methods in REVERSED_METHODS:
+                    for meth in methods:
+                        try:
+                            argument.getattr(meth)
+                        except astroid.NotFoundError:
+                            break
+                    else:
+                        break
+                else:
+                    # check if it is a .deque. It doesn't seem that
+                    # we can retrieve special methods
+                    # from C implemented constructs
+                    if argument._proxied.qname().endswith(".deque"):
+                        return
+                    self.add_message('bad-reversed-sequence', node=node)
+            elif not isinstance(argument, (astroid.List, astroid.Tuple)):
+                # everything else is not a proper sequence for reversed()
+                self.add_message('bad-reversed-sequence', node=node)
+
+_NAME_TYPES = {
+    'module': (MOD_NAME_RGX, 'module'),
+    'const': (CONST_NAME_RGX, 'constant'),
+    'class': (CLASS_NAME_RGX, 'class'),
+    'function': (DEFAULT_NAME_RGX, 'function'),
+    'method': (DEFAULT_NAME_RGX, 'method'),
+    'attr': (DEFAULT_NAME_RGX, 'attribute'),
+    'argument': (DEFAULT_NAME_RGX, 'argument'),
+    'variable': (DEFAULT_NAME_RGX, 'variable'),
+    'class_attribute': (CLASS_ATTRIBUTE_RGX, 'class attribute'),
+    'inlinevar': (COMP_VAR_RGX, 'inline iteration'),
+}
+
+def _create_naming_options():
+    name_options = []
+    for name_type, (rgx, human_readable_name) in _NAME_TYPES.iteritems():
+        name_type = name_type.replace('_', '-')
+        name_options.append((
+            '%s-rgx' % (name_type,),
+            {'default': rgx, 'type': 'regexp', 'metavar': '<regexp>',
+             'help': 'Regular expression matching correct %s names' % (human_readable_name,)}))
+        name_options.append((
+            '%s-name-hint' % (name_type,),
+            {'default': rgx.pattern, 'type': 'string', 'metavar': '<string>',
+             'help': 'Naming hint for %s names' % (human_readable_name,)}))
+    return tuple(name_options)
 
 class NameChecker(_BasicChecker):
     msgs = {
-    'C0102': ('Black listed name "%s"',
-              'Used when the name is listed in the black list (unauthorized \
-              names).'),
-    'C0103': ('Invalid name "%s" (should match %s)',
-              'Used when the name doesn\'t match the regular expression \
-              associated to its type (constant, variable, class...).'),
-
+        'C0102': ('Black listed name "%s"',
+                  'blacklisted-name',
+                  'Used when the name is listed in the black list (unauthorized '
+                  'names).'),
+        'C0103': ('Invalid %s name "%s"%s',
+                  'invalid-name',
+                  'Used when the name doesn\'t match the regular expression '
+                  'associated to its type (constant, variable, class...).'),
     }
-    options = (('module-rgx',
-                {'default' : MOD_NAME_RGX,
-                 'type' :'regexp', 'metavar' : '<regexp>',
-                 'help' : 'Regular expression which should only match correct '
-                          'module names'}
-                ),
-               ('const-rgx',
-                {'default' : CONST_NAME_RGX,
-                 'type' :'regexp', 'metavar' : '<regexp>',
-                 'help' : 'Regular expression which should only match correct '
-                          'module level names'}
-                ),
-               ('class-rgx',
-                {'default' : CLASS_NAME_RGX,
-                 'type' :'regexp', 'metavar' : '<regexp>',
-                 'help' : 'Regular expression which should only match correct '
-                          'class names'}
-                ),
-               ('function-rgx',
-                {'default' : DEFAULT_NAME_RGX,
-                 'type' :'regexp', 'metavar' : '<regexp>',
-                 'help' : 'Regular expression which should only match correct '
-                          'function names'}
-                ),
-               ('method-rgx',
-                {'default' : DEFAULT_NAME_RGX,
-                 'type' :'regexp', 'metavar' : '<regexp>',
-                 'help' : 'Regular expression which should only match correct '
-                          'method names'}
-                ),
-               ('attr-rgx',
-                {'default' : DEFAULT_NAME_RGX,
-                 'type' :'regexp', 'metavar' : '<regexp>',
-                 'help' : 'Regular expression which should only match correct '
-                          'instance attribute names'}
-                ),
-               ('argument-rgx',
-                {'default' : DEFAULT_NAME_RGX,
-                 'type' :'regexp', 'metavar' : '<regexp>',
-                 'help' : 'Regular expression which should only match correct '
-                          'argument names'}),
-               ('variable-rgx',
-                {'default' : DEFAULT_NAME_RGX,
-                 'type' :'regexp', 'metavar' : '<regexp>',
-                 'help' : 'Regular expression which should only match correct '
-                          'variable names'}
-                ),
-               ('inlinevar-rgx',
-                {'default' : COMP_VAR_RGX,
-                 'type' :'regexp', 'metavar' : '<regexp>',
-                 'help' : 'Regular expression which should only match correct '
-                          'list comprehension / generator expression variable \
-                          names'}
-                ),
-               # XXX use set
-               ('good-names',
+
+    options = (('good-names',
                 {'default' : ('i', 'j', 'k', 'ex', 'Run', '_'),
                  'type' :'csv', 'metavar' : '<names>',
                  'help' : 'Good variable names which should always be accepted,'
                           ' separated by a comma'}
-                ),
+               ),
                ('bad-names',
                 {'default' : ('foo', 'bar', 'baz', 'toto', 'tutu', 'tata'),
                  'type' :'csv', 'metavar' : '<names>',
                  'help' : 'Bad variable names which should always be refused, '
                           'separated by a comma'}
-                ),
-               )
+               ),
+               ('name-group',
+                {'default' : (),
+                 'type' :'csv', 'metavar' : '<name1:name2>',
+                 'help' : ('Colon-delimited sets of names that determine each'
+                           ' other\'s naming style when the name regexes'
+                           ' allow several styles.')}
+               ),
+               ('include-naming-hint',
+                {'default': False, 'type' : 'yn', 'metavar' : '<y_or_n>',
+                 'help': 'Include a hint for the correct naming format with invalid-name'}
+               ),
+              ) + _create_naming_options()
+
+
+    def __init__(self, linter):
+        _BasicChecker.__init__(self, linter)
+        self._name_category = {}
+        self._name_group = {}
 
     def open(self):
         self.stats = self.linter.add_stats(badname_module=0,
@@ -635,140 +915,242 @@
                                            badname_const=0,
                                            badname_variable=0,
                                            badname_inlinevar=0,
-                                           badname_argument=0)
+                                           badname_argument=0,
+                                           badname_class_attribute=0)
+        for group in self.config.name_group:
+            for name_type in group.split(':'):
+                self._name_group[name_type] = 'group_%s' % (group,)
 
-    @check_messages('C0102', 'C0103')
+    @check_messages('blacklisted-name', 'invalid-name')
     def visit_module(self, node):
         self._check_name('module', node.name.split('.')[-1], node)
 
-    @check_messages('C0102', 'C0103')
+    @check_messages('blacklisted-name', 'invalid-name')
     def visit_class(self, node):
         self._check_name('class', node.name, node)
-        for attr, anodes in node.instance_attrs.items():
-            self._check_name('attr', attr, anodes[0])
+        for attr, anodes in node.instance_attrs.iteritems():
+            if not list(node.instance_attr_ancestors(attr)):
+                self._check_name('attr', attr, anodes[0])
 
-    @check_messages('C0102', 'C0103')
+    @check_messages('blacklisted-name', 'invalid-name')
     def visit_function(self, node):
-        self._check_name(node.is_method() and 'method' or 'function',
+        # Do not emit any warnings if the method is just an implementation
+        # of a base class method.
+        if node.is_method() and overrides_a_method(node.parent.frame(), node.name):
+            return
+        self._check_name(_determine_function_name_type(node),
                          node.name, node)
-        # check arguments name
+        # Check argument names
         args = node.args.args
         if args is not None:
             self._recursive_check_names(args, node)
 
-    @check_messages('C0102', 'C0103')
+    @check_messages('blacklisted-name', 'invalid-name')
+    def visit_global(self, node):
+        for name in node.names:
+            self._check_name('const', name, node)
+
+    @check_messages('blacklisted-name', 'invalid-name')
     def visit_assname(self, node):
         """check module level assigned names"""
         frame = node.frame()
         ass_type = node.ass_type()
-        if isinstance(ass_type, (astng.Comprehension, astng.Comprehension)):
+        if isinstance(ass_type, astroid.Comprehension):
             self._check_name('inlinevar', node.name, node)
-        elif isinstance(frame, astng.Module):
-            if isinstance(ass_type, astng.Assign) and not in_loop(ass_type):
-                self._check_name('const', node.name, node)
-            elif isinstance(ass_type, astng.ExceptHandler):
+        elif isinstance(frame, astroid.Module):
+            if isinstance(ass_type, astroid.Assign) and not in_loop(ass_type):
+                if isinstance(safe_infer(ass_type.value), astroid.Class):
+                    self._check_name('class', node.name, node)
+                else:
+                    self._check_name('const', node.name, node)
+            elif isinstance(ass_type, astroid.ExceptHandler):
                 self._check_name('variable', node.name, node)
-        elif isinstance(frame, astng.Function):
+        elif isinstance(frame, astroid.Function):
             # global introduced variable aren't in the function locals
-            if node.name in frame:
+            if node.name in frame and node.name not in frame.argnames():
                 self._check_name('variable', node.name, node)
+        elif isinstance(frame, astroid.Class):
+            if not list(frame.local_attr_ancestors(node.name)):
+                self._check_name('class_attribute', node.name, node)
 
     def _recursive_check_names(self, args, node):
         """check names in a possibly recursive list <arg>"""
         for arg in args:
-            if isinstance(arg, astng.AssName):
+            if isinstance(arg, astroid.AssName):
                 self._check_name('argument', arg.name, node)
             else:
                 self._recursive_check_names(arg.elts, node)
 
+    def _find_name_group(self, node_type):
+        return self._name_group.get(node_type, node_type)
+
+    def _is_multi_naming_match(self, match):
+        return (match is not None and
+                match.lastgroup is not None and
+                match.lastgroup not in EXEMPT_NAME_CATEGORIES)
+
     def _check_name(self, node_type, name, node):
         """check for a name using the type's regexp"""
         if is_inside_except(node):
             clobbering, _ = clobber_in_except(node)
             if clobbering:
                 return
-
         if name in self.config.good_names:
             return
         if name in self.config.bad_names:
             self.stats['badname_' + node_type] += 1
-            self.add_message('C0102', node=node, args=name)
+            self.add_message('blacklisted-name', node=node, args=name)
             return
         regexp = getattr(self.config, node_type + '_rgx')
-        if regexp.match(name) is None:
-            self.add_message('C0103', node=node, args=(name, regexp.pattern))
+        match = regexp.match(name)
+
+        if self._is_multi_naming_match(match):
+            name_group = self._find_name_group(node_type)
+            if name_group not in self._name_category:
+                self._name_category[name_group] = match.lastgroup
+            elif self._name_category[name_group] != match.lastgroup:
+                match = None
+
+        if match is None:
+            type_label = _NAME_TYPES[node_type][1]
+            hint = ''
+            if self.config.include_naming_hint:
+                hint = ' (hint: %s)' % (getattr(self.config, node_type + '_name_hint'))
+            self.add_message('invalid-name', node=node, args=(type_label, name, hint))
             self.stats['badname_' + node_type] += 1
 
 
 class DocStringChecker(_BasicChecker):
     msgs = {
-    'C0111': ('Missing docstring', # W0131
-              'Used when a module, function, class or method has no docstring.\
-              Some special methods like __init__ doesn\'t necessary require a \
-              docstring.'),
-    'C0112': ('Empty docstring', # W0132
-              'Used when a module, function, class or method has an empty \
-              docstring (it would be too easy ;).'),
-    }
+        'C0111': ('Missing %s docstring', # W0131
+                  'missing-docstring',
+                  'Used when a module, function, class or method has no docstring.'
+                  'Some special methods like __init__ doesn\'t necessary require a '
+                  'docstring.'),
+        'C0112': ('Empty %s docstring', # W0132
+                  'empty-docstring',
+                  'Used when a module, function, class or method has an empty '
+                  'docstring (it would be too easy ;).'),
+        }
     options = (('no-docstring-rgx',
                 {'default' : NO_REQUIRED_DOC_RGX,
                  'type' : 'regexp', 'metavar' : '<regexp>',
                  'help' : 'Regular expression which should only match '
-                          'functions or classes name which do not require a '
-                          'docstring'}
-                ),
-               )
+                          'function or class names that do not require a '
+                          'docstring.'}
+               ),
+               ('docstring-min-length',
+                {'default' : -1,
+                 'type' : 'int', 'metavar' : '<int>',
+                 'help': ('Minimum line length for functions/classes that'
+                          ' require docstrings, shorter ones are exempt.')}
+               ),
+              )
+
 
     def open(self):
         self.stats = self.linter.add_stats(undocumented_module=0,
                                            undocumented_function=0,
                                            undocumented_method=0,
                                            undocumented_class=0)
-
+    @check_messages('missing-docstring', 'empty-docstring')
     def visit_module(self, node):
         self._check_docstring('module', node)
 
+    @check_messages('missing-docstring', 'empty-docstring')
     def visit_class(self, node):
         if self.config.no_docstring_rgx.match(node.name) is None:
             self._check_docstring('class', node)
-
+    @check_messages('missing-docstring', 'empty-docstring')
     def visit_function(self, node):
         if self.config.no_docstring_rgx.match(node.name) is None:
             ftype = node.is_method() and 'method' or 'function'
-            if isinstance(node.parent.frame(), astng.Class):
+            if isinstance(node.parent.frame(), astroid.Class):
                 overridden = False
                 # check if node is from a method overridden by its ancestor
                 for ancestor in node.parent.frame().ancestors():
                     if node.name in ancestor and \
-                       isinstance(ancestor[node.name], astng.Function):
+                       isinstance(ancestor[node.name], astroid.Function):
                         overridden = True
                         break
-                if not overridden:
-                    self._check_docstring(ftype, node)
+                self._check_docstring(ftype, node,
+                                      report_missing=not overridden)
             else:
                 self._check_docstring(ftype, node)
 
-    def _check_docstring(self, node_type, node):
+    def _check_docstring(self, node_type, node, report_missing=True):
         """check the node has a non empty docstring"""
         docstring = node.doc
         if docstring is None:
+            if not report_missing:
+                return
+            if node.body:
+                lines = node.body[-1].lineno - node.body[0].lineno + 1
+            else:
+                lines = 0
+            max_lines = self.config.docstring_min_length
+
+            if node_type != 'module' and max_lines > -1 and lines < max_lines:
+                return
             self.stats['undocumented_'+node_type] += 1
-            self.add_message('C0111', node=node)
+            if (node.body and isinstance(node.body[0], astroid.Discard) and
+                isinstance(node.body[0].value, astroid.CallFunc)):
+                # Most likely a string with a format call. Let's see.
+                func = safe_infer(node.body[0].value.func)
+                if (isinstance(func, astroid.BoundMethod)
+                    and isinstance(func.bound, astroid.Instance)):
+                    # Strings in Python 3, others in Python 2.
+                    if PY3K and func.bound.name == 'str':
+                        return
+                    elif func.bound.name in ('str', 'unicode', 'bytes'):
+                        return
+            self.add_message('missing-docstring', node=node, args=(node_type,))
         elif not docstring.strip():
             self.stats['undocumented_'+node_type] += 1
-            self.add_message('C0112', node=node)
+            self.add_message('empty-docstring', node=node, args=(node_type,))
 
 
 class PassChecker(_BasicChecker):
-    """check is the pass statement is really necessary"""
+    """check if the pass statement is really necessary"""
     msgs = {'W0107': ('Unnecessary pass statement',
+                      'unnecessary-pass',
                       'Used when a "pass" statement that can be avoided is '
-                      'encountered.)'),
-            }
-
+                      'encountered.'),
+           }
+    @check_messages('unnecessary-pass')
     def visit_pass(self, node):
         if len(node.parent.child_sequence(node)) > 1:
-            self.add_message('W0107', node=node)
+            self.add_message('unnecessary-pass', node=node)
+
+
+class LambdaForComprehensionChecker(_BasicChecker):
+    """check for using a lambda where a comprehension would do.
+
+    See <http://www.artima.com/weblogs/viewpost.jsp?thread=98196>
+    where GvR says comprehensions would be clearer.
+    """
+
+    msgs = {'W0110': ('map/filter on lambda could be replaced by comprehension',
+                      'deprecated-lambda',
+                      'Used when a lambda is the first argument to "map" or '
+                      '"filter". It could be clearer as a list '
+                      'comprehension or generator expression.',
+                      {'maxversion': (3, 0)}),
+           }
+
+    @check_messages('deprecated-lambda')
+    def visit_callfunc(self, node):
+        """visit a CallFunc node, check if map or filter are called with a
+        lambda
+        """
+        if not node.args:
+            return
+        if not isinstance(node.args[0], astroid.Lambda):
+            return
+        infered = safe_infer(node.func)
+        if (is_builtin_object(infered)
+                and infered.name in ['map', 'filter']):
+            self.add_message('deprecated-lambda', node=node)
 
 
 def register(linter):
@@ -778,3 +1160,4 @@
     linter.register_checker(NameChecker(linter))
     linter.register_checker(DocStringChecker(linter))
     linter.register_checker(PassChecker(linter))
+    linter.register_checker(LambdaForComprehensionChecker(linter))
diff --git a/third_party/pylint/checkers/classes.py b/third_party/pylint/checkers/classes.py
index 60d20b6..232e130 100644
--- a/third_party/pylint/checkers/classes.py
+++ b/third_party/pylint/checkers/classes.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2003-2011 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -12,17 +12,28 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """classes checker for Python code
 """
 from __future__ import generators
 
-from logilab import astng
-from logilab.astng import YES, Instance, are_exclusive
+import sys
 
-from pylint.interfaces import IASTNGChecker
+import astroid
+from astroid import YES, Instance, are_exclusive, AssAttr, Class
+from astroid.bases import Generator
+
+from pylint.interfaces import IAstroidChecker
 from pylint.checkers import BaseChecker
-from pylint.checkers.utils import PYMETHODS, overrides_a_method, check_messages
+from pylint.checkers.utils import (
+    PYMETHODS, overrides_a_method, check_messages, is_attr_private,
+    is_attr_protected, node_frame_class, safe_infer)
+
+if sys.version_info >= (3, 0):
+    NEXT_METHOD = '__next__'
+else:
+    NEXT_METHOD = 'next'
+ITER_METHODS = ('__iter__', '__getitem__')
 
 def class_is_abstract(node):
     """return true if the given class node should be considered as an abstract
@@ -37,79 +48,132 @@
 
 MSGS = {
     'F0202': ('Unable to check methods signature (%s / %s)',
+              'method-check-failed',
               'Used when PyLint has been unable to check methods signature \
               compatibility for an unexpected reason. Please report this kind \
               if you don\'t make sense of it.'),
 
-    'E0202': ('An attribute affected in %s line %s hide this method',
+    'E0202': ('An attribute defined in %s line %s hides this method',
+              'method-hidden',
               'Used when a class defines a method which is hidden by an '
               'instance attribute from an ancestor class or set by some '
               'client code.'),
     'E0203': ('Access to member %r before its definition line %s',
+              'access-member-before-definition',
               'Used when an instance member is accessed before it\'s actually\
               assigned.'),
     'W0201': ('Attribute %r defined outside __init__',
+              'attribute-defined-outside-init',
               'Used when an instance attribute is defined outside the __init__\
               method.'),
 
     'W0212': ('Access to a protected member %s of a client class', # E0214
+              'protected-access',
               'Used when a protected member (i.e. class member with a name \
               beginning with an underscore) is access outside the class or a \
               descendant of the class where it\'s defined.'),
 
     'E0211': ('Method has no argument',
+              'no-method-argument',
               'Used when a method which should have the bound instance as \
               first argument has no argument defined.'),
     'E0213': ('Method should have "self" as first argument',
+              'no-self-argument',
               'Used when a method has an attribute different the "self" as\
               first argument. This is considered as an error since this is\
               a so common convention that you shouldn\'t break it!'),
-    'C0202': ('Class method should have %s as first argument', # E0212
-              'Used when a class method has an attribute different than "cls"\
-              as first argument, to easily differentiate them from regular \
-              instance methods.'),
-    'C0203': ('Metaclass method should have "mcs" as first argument', # E0214
-              'Used when a metaclass method has an attribute different the \
-              "mcs" as first argument.'),
+    'C0202': ('Class method %s should have %s as first argument', # E0212
+              'bad-classmethod-argument',
+              'Used when a class method has a first argument named differently '
+              'than the value specified in valid-classmethod-first-arg option '
+              '(default to "cls"), recommended to easily differentiate them '
+              'from regular instance methods.'),
+    'C0203': ('Metaclass method %s should have %s as first argument', # E0214
+              'bad-mcs-method-argument',
+              'Used when a metaclass method has a first agument named '
+              'differently than the value specified in valid-classmethod-first'
+              '-arg option (default to "cls"), recommended to easily '
+              'differentiate them from regular instance methods.'),
+    'C0204': ('Metaclass class method %s should have %s as first argument',
+              'bad-mcs-classmethod-argument',
+              'Used when a metaclass class method has a first argument named '
+              'differently than the value specified in valid-metaclass-'
+              'classmethod-first-arg option (default to "mcs"), recommended to '
+              'easily differentiate them from regular instance methods.'),
 
     'W0211': ('Static method with %r as first argument',
-              'Used when a static method has "self" or "cls" as first argument.'
-              ),
+              'bad-staticmethod-argument',
+              'Used when a static method has "self" or a value specified in '
+              'valid-classmethod-first-arg option or '
+              'valid-metaclass-classmethod-first-arg option as first argument.'
+             ),
     'R0201': ('Method could be a function',
+              'no-self-use',
               'Used when a method doesn\'t use its bound instance, and so could\
               be written as a function.'
-              ),
+             ),
 
     'E0221': ('Interface resolved to %s is not a class',
+              'interface-is-not-class',
               'Used when a class claims to implement an interface which is not \
               a class.'),
     'E0222': ('Missing method %r from %s interface',
+              'missing-interface-method',
               'Used when a method declared in an interface is missing from a \
               class implementing this interface'),
     'W0221': ('Arguments number differs from %s method',
+              'arguments-differ',
               'Used when a method has a different number of arguments than in \
               the implemented interface or in an overridden method.'),
     'W0222': ('Signature differs from %s method',
+              'signature-differs',
               'Used when a method signature is different than in the \
               implemented interface or in an overridden method.'),
     'W0223': ('Method %r is abstract in class %r but is not overridden',
+              'abstract-method',
               'Used when an abstract method (i.e. raise NotImplementedError) is \
               not overridden in concrete class.'
-              ),
+             ),
     'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224
+              'unresolved-interface',
               'Used when a PyLint as failed to find interfaces implemented by \
                a class'),
 
 
     'W0231': ('__init__ method from base class %r is not called',
+              'super-init-not-called',
               'Used when an ancestor class method has an __init__ method \
               which is not called by a derived class.'),
     'W0232': ('Class has no __init__ method',
+              'no-init',
               'Used when a class has no __init__ method, neither its parent \
               classes.'),
     'W0233': ('__init__ method from a non direct base class %r is called',
+              'non-parent-init-called',
               'Used when an __init__ method is called on a class which is not \
               in the direct ancestors for the analysed class.'),
+    'W0234': ('__iter__ returns non-iterator',
+              'non-iterator-returned',
+              'Used when an __iter__ method returns something which is not an \
+               iterable (i.e. has no `%s` method)' % NEXT_METHOD),
+    'E0235': ('__exit__ must accept 3 arguments: type, value, traceback',
+              'bad-context-manager',
+              'Used when the __exit__ special method, belonging to a \
+               context manager, does not accept 3 arguments \
+               (type, value, traceback).'),
+    'E0236': ('Invalid object %r in __slots__, must contain '
+              'only non empty strings',
+              'invalid-slots-object',
+              'Used when an invalid (non-string) object occurs in __slots__.'),
+    'E0237': ('Assigning to attribute %r not defined in class slots',
+              'assigning-non-slot',
+              'Used when assigning to an attribute not defined '
+              'in the class slots.'),
+    'E0238': ('Invalid __slots__ object',
+              'invalid-slots',
+              'Used when an invalid __slots__ is found in class. '
+              'Only a string, an iterable or a sequence is permitted.')
+
 
     }
 
@@ -124,7 +188,7 @@
     * unreachable code
     """
 
-    __implements__ = (IASTNGChecker,)
+    __implements__ = (IAstroidChecker,)
 
     # configuration section name
     name = 'classes'
@@ -134,38 +198,43 @@
     # configuration options
     options = (('ignore-iface-methods',
                 {'default' : (#zope interface
-        'isImplementedBy', 'deferred', 'extends', 'names',
-        'namesAndDescriptions', 'queryDescriptionFor',  'getBases',
-        'getDescriptionFor', 'getDoc', 'getName', 'getTaggedValue',
-        'getTaggedValueTags', 'isEqualOrExtendedBy', 'setTaggedValue',
-        'isImplementedByInstancesOf',
-        # twisted
-        'adaptWith',
-        # logilab.common interface
-        'is_implemented_by'),
+                    'isImplementedBy', 'deferred', 'extends', 'names',
+                    'namesAndDescriptions', 'queryDescriptionFor', 'getBases',
+                    'getDescriptionFor', 'getDoc', 'getName', 'getTaggedValue',
+                    'getTaggedValueTags', 'isEqualOrExtendedBy', 'setTaggedValue',
+                    'isImplementedByInstancesOf',
+                    # twisted
+                    'adaptWith',
+                    # logilab.common interface
+                    'is_implemented_by'),
                  'type' : 'csv',
                  'metavar' : '<method names>',
                  'help' : 'List of interface methods to ignore, \
 separated by a comma. This is used for instance to not check methods defines \
 in Zope\'s Interface base class.'}
-                ),
-
+               ),
                ('defining-attr-methods',
                 {'default' : ('__init__', '__new__', 'setUp'),
                  'type' : 'csv',
                  'metavar' : '<method names>',
                  'help' : 'List of method names used to declare (i.e. assign) \
 instance attributes.'}
-                ),
+               ),
                ('valid-classmethod-first-arg',
                 {'default' : ('cls',),
                  'type' : 'csv',
                  'metavar' : '<argument names>',
                  'help' : 'List of valid names for the first argument in \
 a class method.'}
-                ),
-
-               )
+               ),
+               ('valid-metaclass-classmethod-first-arg',
+                {'default' : ('mcs',),
+                 'type' : 'csv',
+                 'metavar' : '<argument names>',
+                 'help' : 'List of valid names for the first argument in \
+a metaclass class method.'}
+               ),
+              )
 
     def __init__(self, linter=None):
         BaseChecker.__init__(self, linter)
@@ -183,10 +252,11 @@
         if node.type == 'class':
             try:
                 node.local_attr('__init__')
-            except astng.NotFoundError:
-                self.add_message('W0232', args=node, node=node)
+            except astroid.NotFoundError:
+                self.add_message('no-init', args=node, node=node)
+        self._check_slots(node)
 
-    @check_messages('E0203', 'W0201')
+    @check_messages('access-member-before-definition', 'attribute-defined-outside-init')
     def leave_class(self, cnode):
         """close a class node:
         check that instance attributes are defined in __init__ and check
@@ -197,36 +267,42 @@
         if cnode.type != 'metaclass':
             self._check_accessed_members(cnode, accessed)
         # checks attributes are defined in an allowed method such as __init__
-        if 'W0201' not in self.active_msgs:
+        if not self.linter.is_message_enabled('attribute-defined-outside-init'):
             return
         defining_methods = self.config.defining_attr_methods
-        for attr, nodes in cnode.instance_attrs.items():
+        current_module = cnode.root()
+        for attr, nodes in cnode.instance_attrs.iteritems():
+            # skip nodes which are not in the current module and it may screw up
+            # the output, while it's not worth it
             nodes = [n for n in nodes if not
-                    isinstance(n.statement(), (astng.Delete, astng.AugAssign))]
+                     isinstance(n.statement(), (astroid.Delete, astroid.AugAssign))
+                     and n.root() is current_module]
             if not nodes:
                 continue # error detected by typechecking
-            attr_defined = False
             # check if any method attr is defined in is a defining method
-            for node in nodes:
-                if node.frame().name in defining_methods:
-                    attr_defined = True
-            if not attr_defined:
-                # check attribute is defined in a parent's __init__
-                for parent in cnode.instance_attr_ancestors(attr):
-                    attr_defined = False
-                    # check if any parent method attr is defined in is a defining method
-                    for node in parent.instance_attrs[attr]:
-                        if node.frame().name in defining_methods:
-                            attr_defined = True
-                    if attr_defined:
-                        # we're done :)
-                        break
-                else:
-                    # check attribute is defined as a class attribute
-                    try:
-                        cnode.local_attr(attr)
-                    except astng.NotFoundError:
-                        self.add_message('W0201', args=attr, node=node)
+            if any(node.frame().name in defining_methods
+                   for node in nodes):
+                continue
+
+            # check attribute is defined in a parent's __init__
+            for parent in cnode.instance_attr_ancestors(attr):
+                attr_defined = False
+                # check if any parent method attr is defined in is a defining method
+                for node in parent.instance_attrs[attr]:
+                    if node.frame().name in defining_methods:
+                        attr_defined = True
+                if attr_defined:
+                    # we're done :)
+                    break
+            else:
+                # check attribute is defined as a class attribute
+                try:
+                    cnode.local_attr(attr)
+                except astroid.NotFoundError:
+                    for node in nodes:
+                        if node.frame().name not in defining_methods:
+                            self.add_message('attribute-defined-outside-init',
+                                             args=attr, node=node)
 
     def visit_function(self, node):
         """check method arguments, overriding"""
@@ -242,26 +318,125 @@
             return
         # check signature if the method overloads inherited method
         for overridden in klass.local_attr_ancestors(node.name):
-            # get astng for the searched method
+            # get astroid for the searched method
             try:
                 meth_node = overridden[node.name]
             except KeyError:
                 # we have found the method but it's not in the local
                 # dictionary.
-                # This may happen with astng build from living objects
+                # This may happen with astroid build from living objects
                 continue
-            if not isinstance(meth_node, astng.Function):
+            if not isinstance(meth_node, astroid.Function):
                 continue
             self._check_signature(node, meth_node, 'overridden')
             break
-        # check if the method overload an attribute
+        if node.decorators:
+            for decorator in node.decorators.nodes:
+                if isinstance(decorator, astroid.Getattr) and \
+                        decorator.attrname in ('getter', 'setter', 'deleter'):
+                    # attribute affectation will call this method, not hiding it
+                    return
+                if isinstance(decorator, astroid.Name) and decorator.name == 'property':
+                    # attribute affectation will either call a setter or raise
+                    # an attribute error, anyway not hiding the function
+                    return
+        # check if the method is hidden by an attribute
         try:
             overridden = klass.instance_attr(node.name)[0] # XXX
-            args = (overridden.root().name, overridden.fromlineno)
-            self.add_message('E0202', args=args, node=node)
-        except astng.NotFoundError:
+            overridden_frame = overridden.frame()
+            if (isinstance(overridden_frame, astroid.Function)
+                    and overridden_frame.type == 'method'):
+                overridden_frame = overridden_frame.parent.frame()
+            if (isinstance(overridden_frame, Class)
+                    and klass._is_subtype_of(overridden_frame.qname())):
+                args = (overridden.root().name, overridden.fromlineno)
+                self.add_message('method-hidden', args=args, node=node)
+        except astroid.NotFoundError:
             pass
 
+        # check non-iterators in __iter__
+        if node.name == '__iter__':
+            self._check_iter(node)
+        elif node.name == '__exit__':
+            self._check_exit(node)
+
+    def _check_slots(self, node):
+        if '__slots__' not in node.locals:
+            return
+        for slots in node.igetattr('__slots__'):
+            # check if __slots__ is a valid type
+            for meth in ITER_METHODS:
+                try:
+                    slots.getattr(meth)
+                    break
+                except astroid.NotFoundError:
+                    continue
+            else:
+                self.add_message('invalid-slots', node=node)
+                continue
+
+            if isinstance(slots, astroid.Const):
+                # a string, ignore the following checks
+                continue
+            if not hasattr(slots, 'itered'):
+                # we can't obtain the values, maybe a .deque?
+                continue
+
+            if isinstance(slots, astroid.Dict):
+                values = [item[0] for item in slots.items]
+            else:
+                values = slots.itered()
+            if values is YES:
+                return
+
+            for elt in values:
+                try:
+                    self._check_slots_elt(elt)
+                except astroid.InferenceError:
+                    continue
+
+    def _check_slots_elt(self, elt):
+        for infered in elt.infer():
+            if infered is YES:
+                continue
+            if (not isinstance(infered, astroid.Const) or
+                    not isinstance(infered.value, str)):
+                self.add_message('invalid-slots-object',
+                                 args=infered.as_string(),
+                                 node=elt)
+                continue
+            if not infered.value:
+                self.add_message('invalid-slots-object',
+                                 args=infered.as_string(),
+                                 node=elt)
+
+    def _check_iter(self, node):
+        try:
+            infered = node.infer_call_result(node)
+        except astroid.InferenceError:
+            return
+
+        for infered_node in infered:
+            if (infered_node is YES
+                    or isinstance(infered_node, Generator)):
+                continue
+            if isinstance(infered_node, astroid.Instance):
+                try:
+                    infered_node.local_attr(NEXT_METHOD)
+                except astroid.NotFoundError:
+                    self.add_message('non-iterator-returned',
+                                     node=node)
+                    break
+
+    def _check_exit(self, node):
+        positional = sum(1 for arg in node.args.args if arg.name != 'self')
+        if positional < 3 and not node.args.vararg:
+            self.add_message('bad-context-manager',
+                             node=node)
+        elif positional > 3:
+            self.add_message('bad-context-manager',
+                             node=node)
+
     def leave_function(self, node):
         """on method node, check if this method couldn't be a function
 
@@ -272,15 +447,15 @@
         if node.is_method():
             if node.args.args is not None:
                 self._first_attrs.pop()
-            if 'R0201' not in self.active_msgs:
+            if not self.linter.is_message_enabled('no-self-use'):
                 return
             class_node = node.parent.frame()
             if (self._meth_could_be_func and node.type == 'method'
-                and not node.name in PYMETHODS
-                and not (node.is_abstract() or
-                         overrides_a_method(class_node, node.name))
-                and class_node.type != 'interface'):
-                self.add_message('R0201', node=node)
+                    and not node.name in PYMETHODS
+                    and not (node.is_abstract() or
+                             overrides_a_method(class_node, node.name))
+                    and class_node.type != 'interface'):
+                self.add_message('no-self-use', node=node)
 
     def visit_getattr(self, node):
         """check if the getattr is an access to a class member
@@ -289,32 +464,92 @@
         methods)
         """
         attrname = node.attrname
-        if self._first_attrs and isinstance(node.expr, astng.Name) and \
-               node.expr.name == self._first_attrs[-1]:
+        # Check self
+        if self.is_first_attr(node):
             self._accessed[-1].setdefault(attrname, []).append(node)
             return
-        if 'W0212' not in self.active_msgs:
+        if not self.linter.is_message_enabled('protected-access'):
             return
-        if attrname[0] == '_' and not attrname == '_' and not (
-             attrname.startswith('__') and attrname.endswith('__')):
-            # XXX move this in a reusable function
-            klass = node.frame()
-            while klass is not None and not isinstance(klass, astng.Class):
-                if klass.parent is None:
-                    klass = None
-                else:
-                    klass = klass.parent.frame()
+
+        self._check_protected_attribute_access(node)
+
+    def visit_assattr(self, node):
+        if isinstance(node.ass_type(), astroid.AugAssign) and self.is_first_attr(node):
+            self._accessed[-1].setdefault(node.attrname, []).append(node)
+        self._check_in_slots(node)
+
+    def _check_in_slots(self, node):
+        """ Check that the given assattr node
+        is defined in the class slots.
+        """
+        infered = safe_infer(node.expr)
+        if infered and isinstance(infered, Instance):
+            klass = infered._proxied
+            if '__slots__' not in klass.locals or not klass.newstyle:
+                return
+
+            slots = klass.slots()
+            # If any ancestor doesn't use slots, the slots
+            # defined for this class are superfluous.
+            if any('__slots__' not in ancestor.locals and
+                   ancestor.name != 'object'
+                   for ancestor in klass.ancestors()):
+                return
+
+            if not any(slot.value == node.attrname for slot in slots):
+                # If we have a '__dict__' in slots, then
+                # assigning any name is valid.
+                if not any(slot.value == '__dict__' for slot in slots):
+                    self.add_message('assigning-non-slot',
+                                     args=(node.attrname, ), node=node)
+
+    @check_messages('protected-access')
+    def visit_assign(self, assign_node):
+        node = assign_node.targets[0]
+        if not isinstance(node, AssAttr):
+            return
+
+        if self.is_first_attr(node):
+            return
+
+        self._check_protected_attribute_access(node)
+
+    def _check_protected_attribute_access(self, node):
+        '''Given an attribute access node (set or get), check if attribute
+        access is legitimate. Call _check_first_attr with node before calling
+        this method. Valid cases are:
+        * self._attr in a method or cls._attr in a classmethod. Checked by
+        _check_first_attr.
+        * Klass._attr inside "Klass" class.
+        * Klass2._attr inside "Klass" class when Klass2 is a base class of
+            Klass.
+        '''
+        attrname = node.attrname
+
+        if is_attr_protected(attrname):
+
+            klass = node_frame_class(node)
+
             # XXX infer to be more safe and less dirty ??
             # in classes, check we are not getting a parent method
             # through the class object or through super
             callee = node.expr.as_string()
-            if klass is None or not (callee == klass.name or
-                callee in klass.basenames
-                or (isinstance(node.expr, astng.CallFunc)
-                    and isinstance(node.expr.func, astng.Name)
-                    and node.expr.func.name == 'super')):
-                self.add_message('W0212', node=node, args=attrname)
 
+            # We are not in a class, no remaining valid case
+            if klass is None:
+                self.add_message('protected-access', node=node, args=attrname)
+                return
+
+            # If the expression begins with a call to super, that's ok.
+            if isinstance(node.expr, astroid.CallFunc) and \
+               isinstance(node.expr.func, astroid.Name) and \
+               node.expr.func.name == 'super':
+                return
+
+            # We are in a class, one remaining valid cases, Klass._attr inside
+            # Klass
+            if not (callee == klass.name or callee in klass.basenames):
+                self.add_message('protected-access', node=node, args=attrname)
 
     def visit_name(self, node):
         """check if the name handle an access to a class member
@@ -327,15 +562,15 @@
     def _check_accessed_members(self, node, accessed):
         """check that accessed members are defined"""
         # XXX refactor, probably much simpler now that E0201 is in type checker
-        for attr, nodes in accessed.items():
+        for attr, nodes in accessed.iteritems():
             # deactivate "except doesn't do anything", that's expected
             # pylint: disable=W0704
-            # is it a class attribute ?
             try:
+                # is it a class attribute ?
                 node.local_attr(attr)
                 # yes, stop here
                 continue
-            except astng.NotFoundError:
+            except astroid.NotFoundError:
                 pass
             # is it an instance attribute of a parent class ?
             try:
@@ -347,9 +582,22 @@
             # is it an instance attribute ?
             try:
                 defstmts = node.instance_attr(attr)
-            except astng.NotFoundError:
+            except astroid.NotFoundError:
                 pass
             else:
+                # filter out augment assignment nodes
+                defstmts = [stmt for stmt in defstmts if stmt not in nodes]
+                if not defstmts:
+                    # only augment assignment for this node, no-member should be
+                    # triggered by the typecheck checker
+                    continue
+                # filter defstmts to only pick the first one when there are
+                # several assignments in the same scope
+                scope = defstmts[0].scope()
+                defstmts = [stmt for i, stmt in enumerate(defstmts)
+                            if i == 0 or stmt.scope() is not scope]
+                # if there are still more than one, don't attempt to be smarter
+                # than we can be
                 if len(defstmts) == 1:
                     defstmt = defstmts[0]
                     # check that if the node is accessed in the same method as
@@ -359,15 +607,17 @@
                     for _node in nodes:
                         if _node.frame() is frame and _node.fromlineno < lno \
                            and not are_exclusive(_node.statement(), defstmt, ('AttributeError', 'Exception', 'BaseException')):
-                            self.add_message('E0203', node=_node,
-                                             args=(attr, lno))
+                            self.add_message('access-member-before-definition',
+                                             node=_node, args=(attr, lno))
 
     def _check_first_arg_for_type(self, node, metaclass=0):
         """check the name of first argument, expect:
 
         * 'self' for a regular method
-        * 'cls' for a class method
-        * 'mcs' for a metaclass
+        * 'cls' for a class method or a metaclass regular method (actually
+          valid-classmethod-first-arg value)
+        * 'mcs' for a metaclass class method (actually
+          valid-metaclass-classmethod-first-arg)
         * not one of the above for a static method
         """
         # don't care about functions with unknown argument (builtins)
@@ -378,31 +628,50 @@
         first = self._first_attrs[-1]
         # static method
         if node.type == 'staticmethod':
-            if first_arg in ('self', 'cls', 'mcs'):
-                self.add_message('W0211', args=first, node=node)
+            if (first_arg == 'self' or
+                    first_arg in self.config.valid_classmethod_first_arg or
+                    first_arg in self.config.valid_metaclass_classmethod_first_arg):
+                self.add_message('bad-staticmethod-argument', args=first, node=node)
+                return
             self._first_attrs[-1] = None
         # class / regular method with no args
         elif not node.args.args:
-            self.add_message('E0211', node=node)
-        # metaclass method
+            self.add_message('no-method-argument', node=node)
+        # metaclass
         elif metaclass:
-            if first != 'mcs':
-                self.add_message('C0203', node=node)
-        # class method
-        elif node.type == 'classmethod':
-            if first not in self.config.valid_classmethod_first_arg:
-                if len(self.config.valid_classmethod_first_arg) == 1:
-                    valid = repr(self.config.valid_classmethod_first_arg[0])
-                else:
-                    valid = ', '.join(
-                      repr(v)
-                      for v in self.config.valid_classmethod_first_arg[:-1])
-                    valid = '%s or %r' % (
-                        valid, self.config.valid_classmethod_first_arg[-1])
-                self.add_message('C0202', args=valid, node=node)
-        # regular method without self as argument
-        elif first != 'self':
-            self.add_message('E0213', node=node)
+            # metaclass __new__ or classmethod
+            if node.type == 'classmethod':
+                self._check_first_arg_config(
+                    first,
+                    self.config.valid_metaclass_classmethod_first_arg, node,
+                    'bad-mcs-classmethod-argument', node.name)
+            # metaclass regular method
+            else:
+                self._check_first_arg_config(
+                    first,
+                    self.config.valid_classmethod_first_arg, node, 'bad-mcs-method-argument',
+                    node.name)
+        # regular class
+        else:
+            # class method
+            if node.type == 'classmethod':
+                self._check_first_arg_config(
+                    first,
+                    self.config.valid_classmethod_first_arg, node, 'bad-classmethod-argument',
+                    node.name)
+            # regular method without self as argument
+            elif first != 'self':
+                self.add_message('no-self-argument', node=node)
+
+    def _check_first_arg_config(self, first, config, node, message,
+                                method_name):
+        if first not in config:
+            if len(config) == 1:
+                valid = repr(config[0])
+            else:
+                valid = ', '.join(repr(v) for v in config[:-1])
+                valid = '%s or %r' % (valid, config[-1])
+            self.add_message(message, args=(method_name, valid), node=node)
 
     def _check_bases_classes(self, node):
         """check that the given class node implements abstract methods from
@@ -417,8 +686,11 @@
                 continue
             # owner is not this class, it must be a parent class
             # check that the ancestor's method is not abstract
+            if method.name in node.locals:
+                # it is redefined as an attribute or with a descriptor
+                continue
             if method.is_abstract(pass_is_abstract=False):
-                self.add_message('W0223', node=node,
+                self.add_message('abstract-method', node=node,
                                  args=(method.name, owner.name))
 
     def _check_interfaces(self, node):
@@ -428,9 +700,9 @@
         e0221_hack = [False]
         def iface_handler(obj):
             """filter interface objects, it should be classes"""
-            if not isinstance(obj, astng.Class):
+            if not isinstance(obj, astroid.Class):
                 e0221_hack[0] = True
-                self.add_message('E0221', node=node,
+                self.add_message('interface-is-not-class', node=node,
                                  args=(obj.as_string(),))
                 return False
             return True
@@ -443,11 +715,11 @@
                         # don't check method beginning with an underscore,
                         # usually belonging to the interface implementation
                         continue
-                    # get class method astng
+                    # get class method astroid
                     try:
                         method = node_method(node, name)
-                    except astng.NotFoundError:
-                        self.add_message('E0222', args=(name, iface.name),
+                    except astroid.NotFoundError:
+                        self.add_message('missing-interface-method', args=(name, iface.name),
                                          node=node)
                         continue
                     # ignore inherited methods
@@ -455,37 +727,38 @@
                         continue
                     # check signature
                     self._check_signature(method, imethod,
-                                         '%s interface' % iface.name)
-        except astng.InferenceError:
+                                          '%s interface' % iface.name)
+        except astroid.InferenceError:
             if e0221_hack[0]:
                 return
             implements = Instance(node).getattr('__implements__')[0]
             assignment = implements.parent
-            assert isinstance(assignment, astng.Assign)
+            assert isinstance(assignment, astroid.Assign)
             # assignment.expr can be a Name or a Tuple or whatever.
             # Use as_string() for the message
             # FIXME: in case of multiple interfaces, find which one could not
             #        be resolved
-            self.add_message('F0220', node=implements,
+            self.add_message('unresolved-interface', node=implements,
                              args=(node.name, assignment.value.as_string()))
 
     def _check_init(self, node):
         """check that the __init__ method call super or ancestors'__init__
         method
         """
-        if not set(('W0231', 'W0233')) & self.active_msgs:
+        if (not self.linter.is_message_enabled('super-init-not-called') and
+                not self.linter.is_message_enabled('non-parent-init-called')):
             return
         klass_node = node.parent.frame()
         to_call = _ancestors_to_call(klass_node)
         not_called_yet = dict(to_call)
-        for stmt in node.nodes_of_class(astng.CallFunc):
+        for stmt in node.nodes_of_class(astroid.CallFunc):
             expr = stmt.func
-            if not isinstance(expr, astng.Getattr) \
+            if not isinstance(expr, astroid.Getattr) \
                    or expr.attrname != '__init__':
                 continue
             # skip the test if using super
-            if isinstance(expr.expr, astng.CallFunc) and \
-               isinstance(expr.expr.func, astng.Name) and \
+            if isinstance(expr.expr, astroid.CallFunc) and \
+                   isinstance(expr.expr.func, astroid.Name) and \
                expr.expr.func.name == 'super':
                 return
             try:
@@ -496,22 +769,22 @@
                     del not_called_yet[klass]
                 except KeyError:
                     if klass not in to_call:
-                        self.add_message('W0233', node=expr, args=klass.name)
-            except astng.InferenceError:
+                        self.add_message('non-parent-init-called', node=expr, args=klass.name)
+            except astroid.InferenceError:
                 continue
-        for klass in not_called_yet.keys():
-            if klass.name == 'object':
+        for klass, method in not_called_yet.iteritems():
+            if klass.name == 'object' or method.parent.name == 'object':
                 continue
-            self.add_message('W0231', args=klass.name, node=node)
+            self.add_message('super-init-not-called', args=klass.name, node=node)
 
     def _check_signature(self, method1, refmethod, class_type):
         """check that the signature of the two given methods match
 
         class_type is in 'class', 'interface'
         """
-        if not (isinstance(method1, astng.Function)
-                and isinstance(refmethod, astng.Function)):
-            self.add_message('F0202', args=(method1, refmethod), node=method1)
+        if not (isinstance(method1, astroid.Function)
+                and isinstance(refmethod, astroid.Function)):
+            self.add_message('method-check-failed', args=(method1, refmethod), node=method1)
             return
         # don't care about functions with unknown argument (builtins)
         if method1.args.args is None or refmethod.args.args is None:
@@ -519,11 +792,19 @@
         # if we use *args, **kwargs, skip the below checks
         if method1.args.vararg or method1.args.kwarg:
             return
+        if is_attr_private(method1.name):
+            return
         if len(method1.args.args) != len(refmethod.args.args):
-            self.add_message('W0221', args=class_type, node=method1)
+            self.add_message('arguments-differ', args=class_type, node=method1)
         elif len(method1.args.defaults) < len(refmethod.args.defaults):
-            self.add_message('W0222', args=class_type, node=method1)
+            self.add_message('signature-differs', args=class_type, node=method1)
 
+    def is_first_attr(self, node):
+        """Check that attribute lookup name use first attribute variable name
+        (self for method, cls for classmethod and mcs for metaclass).
+        """
+        return self._first_attrs and isinstance(node.expr, astroid.Name) and \
+                   node.expr.name == self._first_attrs[-1]
 
 def _ancestors_to_call(klass_node, method='__init__'):
     """return a dictionary where keys are the list of base classes providing
@@ -532,21 +813,20 @@
     to_call = {}
     for base_node in klass_node.ancestors(recurs=False):
         try:
-            base_node.local_attr(method)
-            to_call[base_node] = 1
-        except astng.NotFoundError:
+            to_call[base_node] = base_node.igetattr(method).next()
+        except astroid.InferenceError:
             continue
     return to_call
 
 
 def node_method(node, method_name):
-    """get astng for <method_name> on the given class node, ensuring it
+    """get astroid for <method_name> on the given class node, ensuring it
     is a Function node
     """
     for n in node.local_attr(method_name):
-        if isinstance(n, astng.Function):
+        if isinstance(n, astroid.Function):
             return n
-    raise astng.NotFoundError(method_name)
+    raise astroid.NotFoundError(method_name)
 
 def register(linter):
     """required method to auto register this checker """
diff --git a/third_party/pylint/checkers/design_analysis.py b/third_party/pylint/checkers/design_analysis.py
index 0deb6c7..0725ccf 100644
--- a/third_party/pylint/checkers/design_analysis.py
+++ b/third_party/pylint/checkers/design_analysis.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2003-2006 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -12,24 +12,21 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-"""check for signs of poor design
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+"""check for signs of poor design"""
 
+from astroid import Function, If, InferenceError
 
- see http://intranet.logilab.fr/jpl/view?rql=Any%20X%20where%20X%20eid%201243
- FIXME: missing 13, 15, 16
-"""
-
-from logilab.astng import Function, If, InferenceError
-
-from pylint.interfaces import IASTNGChecker
+from pylint.interfaces import IAstroidChecker
 from pylint.checkers import BaseChecker
+from pylint.checkers.utils import check_messages
 
 import re
 
 # regexp for ignored argument name
 IGNORED_ARGUMENT_NAMES = re.compile('_.*')
 
+
 def class_is_abstract(klass):
     """return true if the given class node should be considered as an abstract
     class
@@ -43,49 +40,61 @@
 
 MSGS = {
     'R0901': ('Too many ancestors (%s/%s)',
+              'too-many-ancestors',
               'Used when class has too many parent classes, try to reduce \
-              this to get a more simple (and so easier to use) class.'),
+              this to get a simpler (and so easier to use) class.'),
     'R0902': ('Too many instance attributes (%s/%s)',
+              'too-many-instance-attributes',
               'Used when class has too many instance attributes, try to reduce \
-              this to get a more simple (and so easier to use) class.'),
+              this to get a simpler (and so easier to use) class.'),
     'R0903': ('Too few public methods (%s/%s)',
+              'too-few-public-methods',
               'Used when class has too few public methods, so be sure it\'s \
               really worth it.'),
     'R0904': ('Too many public methods (%s/%s)',
+              'too-many-public-methods',
               'Used when class has too many public methods, try to reduce \
-              this to get a more simple (and so easier to use) class.'),
-    
+              this to get a simpler (and so easier to use) class.'),
+
     'R0911': ('Too many return statements (%s/%s)',
+              'too-many-return-statements',
               'Used when a function or method has too many return statement, \
               making it hard to follow.'),
     'R0912': ('Too many branches (%s/%s)',
+              'too-many-branches',
               'Used when a function or method has too many branches, \
               making it hard to follow.'),
     'R0913': ('Too many arguments (%s/%s)',
+              'too-many-arguments',
               'Used when a function or method takes too many arguments.'),
     'R0914': ('Too many local variables (%s/%s)',
+              'too-many-locals',
               'Used when a function or method has too many local variables.'),
     'R0915': ('Too many statements (%s/%s)',
+              'too-many-statements',
               'Used when a function or method has too many statements. You \
               should then split it in smaller functions / methods.'),
-    
+
     'R0921': ('Abstract class not referenced',
+              'abstract-class-not-used',
               'Used when an abstract class is not used as ancestor anywhere.'),
     'R0922': ('Abstract class is only referenced %s times',
+              'abstract-class-little-used',
               'Used when an abstract class is used less than X times as \
               ancestor.'),
     'R0923': ('Interface not implemented',
+              'interface-not-implemented',
               'Used when an interface class is not implemented anywhere.'),
     }
 
 
 class MisdesignChecker(BaseChecker):
-    """checks for sign of poor/misdesign:                                      
-    * number of methods, attributes, local variables...                        
-    * size, complexity of functions, methods                                   
+    """checks for sign of poor/misdesign:
+    * number of methods, attributes, local variables...
+    * size, complexity of functions, methods
     """
-    
-    __implements__ = (IASTNGChecker,)
+
+    __implements__ = (IAstroidChecker,)
 
     # configuration section name
     name = 'design'
@@ -96,93 +105,98 @@
     options = (('max-args',
                 {'default' : 5, 'type' : 'int', 'metavar' : '<int>',
                  'help': 'Maximum number of arguments for function / method'}
-                ),
+               ),
                ('ignored-argument-names',
                 {'default' : IGNORED_ARGUMENT_NAMES,
                  'type' :'regexp', 'metavar' : '<regexp>',
                  'help' : 'Argument names that match this expression will be '
                           'ignored. Default to name with leading underscore'}
-                ),
+               ),
                ('max-locals',
                 {'default' : 15, 'type' : 'int', 'metavar' : '<int>',
                  'help': 'Maximum number of locals for function / method body'}
-                ),
+               ),
                ('max-returns',
                 {'default' : 6, 'type' : 'int', 'metavar' : '<int>',
                  'help': 'Maximum number of return / yield for function / '
                          'method body'}
-                ),
-               ('max-branchs',
+               ),
+               ('max-branches',
                 {'default' : 12, 'type' : 'int', 'metavar' : '<int>',
                  'help': 'Maximum number of branch for function / method body'}
-                ),
+               ),
                ('max-statements',
                 {'default' : 50, 'type' : 'int', 'metavar' : '<int>',
                  'help': 'Maximum number of statements in function / method '
                          'body'}
-                ),
+               ),
                ('max-parents',
                 {'default' : 7,
                  'type' : 'int',
                  'metavar' : '<num>',
                  'help' : 'Maximum number of parents for a class (see R0901).'}
-                ),
+               ),
                ('max-attributes',
                 {'default' : 7,
                  'type' : 'int',
                  'metavar' : '<num>',
                  'help' : 'Maximum number of attributes for a class \
 (see R0902).'}
-                ),
+               ),
                ('min-public-methods',
                 {'default' : 2,
                  'type' : 'int',
                  'metavar' : '<num>',
                  'help' : 'Minimum number of public methods for a class \
 (see R0903).'}
-                ),
+               ),
                ('max-public-methods',
                 {'default' : 20,
                  'type' : 'int',
                  'metavar' : '<num>',
                  'help' : 'Maximum number of public methods for a class \
 (see R0904).'}
-                ),
-               )
+               ),
+              )
 
     def __init__(self, linter=None):
         BaseChecker.__init__(self, linter)
         self.stats = None
         self._returns = None
-        self._branchs = None
+        self._branches = None
         self._used_abstracts = None
         self._used_ifaces = None
         self._abstracts = None
         self._ifaces = None
         self._stmts = 0
-        
+
     def open(self):
         """initialize visit variables"""
         self.stats = self.linter.add_stats()
         self._returns = []
-        self._branchs = []
+        self._branches = []
         self._used_abstracts = {}
         self._used_ifaces = {}
         self._abstracts = []
         self._ifaces = []
 
+    # Check 'R0921', 'R0922', 'R0923'
     def close(self):
         """check that abstract/interface classes are used"""
         for abstract in self._abstracts:
             if not abstract in self._used_abstracts:
-                self.add_message('R0921', node=abstract)
+                self.add_message('abstract-class-not-used', node=abstract)
             elif self._used_abstracts[abstract] < 2:
-                self.add_message('R0922', node=abstract,
+                self.add_message('abstract-class-little-used', node=abstract,
                                  args=self._used_abstracts[abstract])
         for iface in self._ifaces:
             if not iface in self._used_ifaces:
-                self.add_message('R0923', node=iface)
-                
+                self.add_message('interface-not-implemented', node=iface)
+
+    @check_messages('too-many-ancestors', 'too-many-instance-attributes',
+                    'too-few-public-methods', 'too-many-public-methods',
+                    'abstract-class-not-used', 'abstract-class-little-used',
+                    'interface-not-implemented')
     def visit_class(self, node):
         """check size of inheritance hierarchy and number of instance attributes
         """
@@ -190,13 +204,13 @@
         # Is the total inheritance hierarchy is 7 or less?
         nb_parents = len(list(node.ancestors()))
         if nb_parents > self.config.max_parents:
-            self.add_message('R0901', node=node,
+            self.add_message('too-many-ancestors', node=node,
                              args=(nb_parents, self.config.max_parents))
         # Does the class contain less than 20 attributes for
         # non-GUI classes (40 for GUI)?
         # FIXME detect gui classes
         if len(node.instance_attrs) > self.config.max_attributes:
-            self.add_message('R0902', node=node,
+            self.add_message('too-many-instance-attributes', node=node,
                              args=(len(node.instance_attrs),
                                    self.config.max_attributes))
         # update abstract / interface classes structures
@@ -212,23 +226,30 @@
             for iface in node.interfaces():
                 self._used_ifaces[iface] = 1
         except InferenceError:
-            # XXX log ? 
+            # XXX log ?
             pass
         for parent in node.ancestors():
             try:
                 self._used_abstracts[parent] += 1
             except KeyError:
                 self._used_abstracts[parent] = 1
-            
+
+    @check_messages('too-many-ancestors', 'too-many-instance-attributes',
+                    'too-few-public-methods', 'too-many-public-methods',
+                    'abstract-class-not-used', 'abstract-class-little-used',
+                    'interface-not-implemented')
     def leave_class(self, node):
         """check number of public methods"""
         nb_public_methods = 0
+        special_methods = set()
         for method in node.methods():
             if not method.name.startswith('_'):
                 nb_public_methods += 1
+            if method.name.startswith("__"):
+                special_methods.add(method.name)
         # Does the class contain less than 20 public methods ?
         if nb_public_methods > self.config.max_public_methods:
-            self.add_message('R0904', node=node,
+            self.add_message('too-many-public-methods', node=node,
                              args=(nb_public_methods,
                                    self.config.max_public_methods))
         # stop here for exception, metaclass and interface classes
@@ -240,7 +261,8 @@
                              args=(nb_public_methods,
                                    self.config.min_public_methods))
 
-        
+    @check_messages('too-many-return-statements', 'too-many-branches',
+                    'too-many-arguments', 'too-many-locals', 'too-many-statements')
     def visit_function(self, node):
         """check function name, docstring, arguments, redefinition,
         variable names, max locals
@@ -248,7 +270,7 @@
         self._inc_branch()
         # init branch and returns counters
         self._returns.append(0)
-        self._branchs.append(0)
+        self._branches.append(0)
         # check number of arguments
         args = node.args.args
         if args is not None:
@@ -257,33 +279,34 @@
                  if self.config.ignored_argument_names.match(arg.name)])
             argnum = len(args) - ignored_args_num
             if  argnum > self.config.max_args:
-                self.add_message('R0913', node=node,
+                self.add_message('too-many-arguments', node=node,
                                  args=(len(args), self.config.max_args))
         else:
             ignored_args_num = 0
         # check number of local variables
         locnum = len(node.locals) - ignored_args_num
         if locnum > self.config.max_locals:
-            self.add_message('R0914', node=node,
+            self.add_message('too-many-locals', node=node,
                              args=(locnum, self.config.max_locals))
         # init statements counter
         self._stmts = 1
 
+    @check_messages('too-many-return-statements', 'too-many-branches', 'too-many-arguments', 'too-many-locals', 'too-many-statements')
     def leave_function(self, node):
         """most of the work is done here on close:
         checks for max returns, branch, return in __init__
         """
         returns = self._returns.pop()
         if returns > self.config.max_returns:
-            self.add_message('R0911', node=node,
+            self.add_message('too-many-return-statements', node=node,
                              args=(returns, self.config.max_returns))
-        branchs = self._branchs.pop()
-        if branchs > self.config.max_branchs:
-            self.add_message('R0912', node=node,
-                             args=(branchs, self.config.max_branchs))
+        branches = self._branches.pop()
+        if branches > self.config.max_branches:
+            self.add_message('too-many-branches', node=node,
+                             args=(branches, self.config.max_branches))
         # check number of statements
         if self._stmts > self.config.max_statements:
-            self.add_message('R0915', node=node,
+            self.add_message('too-many-statements', node=node,
                              args=(self._stmts, self.config.max_statements))
 
     def visit_return(self, _):
@@ -291,7 +314,7 @@
         if not self._returns:
             return # return outside function, reported by the base checker
         self._returns[-1] += 1
-        
+
     def visit_default(self, node):
         """default visit method -> increments the statements counter if
         necessary
@@ -300,42 +323,42 @@
             self._stmts += 1
 
     def visit_tryexcept(self, node):
-        """increments the branchs counter"""
-        branchs = len(node.handlers)
+        """increments the branches counter"""
+        branches = len(node.handlers)
         if node.orelse:
-            branchs += 1
-        self._inc_branch(branchs)
-        self._stmts += branchs
-        
+            branches += 1
+        self._inc_branch(branches)
+        self._stmts += branches
+
     def visit_tryfinally(self, _):
-        """increments the branchs counter"""
+        """increments the branches counter"""
         self._inc_branch(2)
         self._stmts += 2
-        
+
     def visit_if(self, node):
-        """increments the branchs counter"""
-        branchs = 1
+        """increments the branches counter"""
+        branches = 1
         # don't double count If nodes coming from some 'elif'
-        if node.orelse and (len(node.orelse)>1 or
+        if node.orelse and (len(node.orelse) > 1 or
                             not isinstance(node.orelse[0], If)):
-            branchs += 1
-        self._inc_branch(branchs)
-        self._stmts += branchs
-        
+            branches += 1
+        self._inc_branch(branches)
+        self._stmts += branches
+
     def visit_while(self, node):
-        """increments the branchs counter"""
-        branchs = 1
+        """increments the branches counter"""
+        branches = 1
         if node.orelse:
-            branchs += 1
-        self._inc_branch(branchs)
-        
+            branches += 1
+        self._inc_branch(branches)
+
     visit_for = visit_while
 
-    def _inc_branch(self, branchsnum=1):
-        """increments the branchs counter"""
-        branchs = self._branchs
-        for i in xrange(len(branchs)):
-            branchs[i] += branchsnum
+    def _inc_branch(self, branchesnum=1):
+        """increments the branches counter"""
+        branches = self._branches
+        for i in xrange(len(branches)):
+            branches[i] += branchesnum
 
     # FIXME: make a nice report...
 
diff --git a/third_party/pylint/checkers/exceptions.py b/third_party/pylint/checkers/exceptions.py
index 08f4334..94186eb 100644
--- a/third_party/pylint/checkers/exceptions.py
+++ b/third_party/pylint/checkers/exceptions.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2003-2007 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
@@ -11,53 +11,107 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """exceptions handling (raising, catching, exceptions classes) checker
 """
 import sys
 
 from logilab.common.compat import builtins
 BUILTINS_NAME = builtins.__name__
-from logilab import astng
-from logilab.astng import YES, Instance, unpack_infer
+import astroid
+from astroid import YES, Instance, unpack_infer
 
 from pylint.checkers import BaseChecker
-from pylint.checkers.utils import is_empty, is_raising
-from pylint.interfaces import IASTNGChecker
+from pylint.checkers.utils import is_empty, is_raising, check_messages
+from pylint.interfaces import IAstroidChecker
 
+def infer_bases(klass):
+    """ Fully infer the bases of the klass node.
 
+    This doesn't use .ancestors(), because we need
+    the non-inferable nodes (YES nodes),
+    which can't be retrieved from .ancestors()
+    """
+    for base in klass.bases:
+        try:
+            inferit = base.infer().next()
+        except astroid.InferenceError:
+            continue
+        if inferit is YES:
+            yield inferit
+        else:
+            for base in infer_bases(inferit):
+                yield base
+
+PY3K = sys.version_info >= (3, 0)
 OVERGENERAL_EXCEPTIONS = ('Exception',)
 
 MSGS = {
-    'E0701': (
-    'Bad except clauses order (%s)',
-    'Used when except clauses are not in the correct order (from the \
-    more specific to the more generic). If you don\'t fix the order, \
-    some exceptions may not be catched by the most specific handler.'),
+    'E0701': ('Bad except clauses order (%s)',
+              'bad-except-order',
+              'Used when except clauses are not in the correct order (from the '
+              'more specific to the more generic). If you don\'t fix the order, '
+              'some exceptions may not be catched by the most specific handler.'),
     'E0702': ('Raising %s while only classes, instances or string are allowed',
+              'raising-bad-type',
               'Used when something which is neither a class, an instance or a \
               string is raised (i.e. a `TypeError` will be raised).'),
+    'E0703': ('Exception context set to something which is not an '
+              'exception, nor None',
+              'bad-exception-context',
+              'Used when using the syntax "raise ... from ...", '
+              'where the exception context is not an exception, '
+              'nor None.',
+              {'minversion': (3, 0)}),
     'E0710': ('Raising a new style class which doesn\'t inherit from BaseException',
+              'raising-non-exception',
               'Used when a new style class which doesn\'t inherit from \
                BaseException is raised.'),
     'E0711': ('NotImplemented raised - should raise NotImplementedError',
+              'notimplemented-raised',
               'Used when NotImplemented is raised instead of \
               NotImplementedError'),
-    
+    'E0712': ('Catching an exception which doesn\'t inherit from BaseException: %s',
+              'catching-non-exception',
+              'Used when a class which doesn\'t inherit from \
+               BaseException is used as an exception in an except clause.'),
+
     'W0701': ('Raising a string exception',
+              'raising-string',
               'Used when a string exception is raised.'),
     'W0702': ('No exception type(s) specified',
+              'bare-except',
               'Used when an except clause doesn\'t specify exceptions type to \
               catch.'),
     'W0703': ('Catching too general exception %s',
+              'broad-except',
               'Used when an except catches a too general exception, \
               possibly burying unrelated errors.'),
     'W0704': ('Except doesn\'t do anything',
+              'pointless-except',
               'Used when an except clause does nothing but "pass" and there is\
               no "else" clause.'),
     'W0710': ('Exception doesn\'t inherit from standard "Exception" class',
+              'nonstandard-exception',
               'Used when a custom exception class is raised but doesn\'t \
-              inherit from the builtin "Exception" class.'),
+              inherit from the builtin "Exception" class.',
+              {'maxversion': (3, 0)}),
+    'W0711': ('Exception to catch is the result of a binary "%s" operation',
+              'binary-op-exception',
+              'Used when the exception to catch is of the form \
+              "except A or B:".  If intending to catch multiple, \
+              rewrite as "except (A, B):"'),
+    'W0712': ('Implicit unpacking of exceptions is not supported in Python 3',
+              'unpacking-in-except',
+              'Python3 will not allow implicit unpacking of exceptions in except '
+              'clauses. '
+              'See http://www.python.org/dev/peps/pep-3110/',
+              {'maxversion': (3, 0)}),
+    'W0713': ('Indexing exceptions will not work on Python 3',
+              'indexing-exception',
+              'Indexing exceptions will not work on Python 3. Use '
+              '`exception.args[index]` instead.',
+              {'maxversion': (3, 0)}),
     }
 
 
@@ -67,12 +121,12 @@
     EXCEPTIONS_MODULE = "builtins"
 
 class ExceptionsChecker(BaseChecker):
-    """checks for                                                              
-    * excepts without exception filter                                         
+    """checks for
+    * excepts without exception filter
     * type of raise argument : string, Exceptions, other values
     """
-    
-    __implements__ = IASTNGChecker
+
+    __implements__ = IAstroidChecker
 
     name = 'exceptions'
     msgs = MSGS
@@ -83,21 +137,39 @@
                  'help' : 'Exceptions that will emit a warning '
                           'when being caught. Defaults to "%s"' % (
                               ', '.join(OVERGENERAL_EXCEPTIONS),)}
-                ),
-               )
+               ),
+              )
 
+    @check_messages('raising-string', 'nonstandard-exception', 'raising-bad-type',
+                    'raising-non-exception', 'notimplemented-raised', 'bad-exception-context')
     def visit_raise(self, node):
         """visit raise possibly inferring value"""
         # ignore empty raise
         if node.exc is None:
             return
+        if PY3K and node.cause:
+            try:
+                cause = node.cause.infer().next()
+            except astroid.InferenceError:
+                pass
+            else:
+                if cause is YES:
+                    return
+                if isinstance(cause, astroid.Const):
+                    if cause.value is not None:
+                        self.add_message('bad-exception-context',
+                                         node=node)
+                elif (not isinstance(cause, astroid.Class) and
+                      not inherit_from_std_ex(cause)):
+                    self.add_message('bad-exception-context',
+                                     node=node)
         expr = node.exc
         if self._check_raise_value(node, expr):
             return
         else:
             try:
                 value = unpack_infer(expr).next()
-            except astng.InferenceError:
+            except astroid.InferenceError:
                 return
             self._check_raise_value(node, value)
 
@@ -105,78 +177,115 @@
         """check for bad values, string exception and class inheritance
         """
         value_found = True
-        if isinstance(expr, astng.Const):
+        if isinstance(expr, astroid.Const):
             value = expr.value
             if isinstance(value, str):
-                self.add_message('W0701', node=node)
+                self.add_message('raising-string', node=node)
             else:
-                self.add_message('E0702', node=node,
+                self.add_message('raising-bad-type', node=node,
                                  args=value.__class__.__name__)
-        elif (isinstance(expr, astng.Name) and \
+        elif (isinstance(expr, astroid.Name) and \
                  expr.name in ('None', 'True', 'False')) or \
-                 isinstance(expr, (astng.List, astng.Dict, astng.Tuple, 
-                                   astng.Module, astng.Function)):
-            self.add_message('E0702', node=node, args=expr.name)
-        elif ( (isinstance(expr, astng.Name) and expr.name == 'NotImplemented')
-               or (isinstance(expr, astng.CallFunc) and
-                   isinstance(expr.func, astng.Name) and
-                   expr.func.name == 'NotImplemented') ):
-            self.add_message('E0711', node=node)
-        elif isinstance(expr, astng.BinOp) and expr.op == '%':
-            self.add_message('W0701', node=node)
-        elif isinstance(expr, (Instance, astng.Class)):
+                 isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple,
+                                   astroid.Module, astroid.Function)):
+            self.add_message('raising-bad-type', node=node, args=expr.name)
+        elif ((isinstance(expr, astroid.Name) and expr.name == 'NotImplemented')
+              or (isinstance(expr, astroid.CallFunc) and
+                  isinstance(expr.func, astroid.Name) and
+                  expr.func.name == 'NotImplemented')):
+            self.add_message('notimplemented-raised', node=node)
+        elif isinstance(expr, astroid.BinOp) and expr.op == '%':
+            self.add_message('raising-string', node=node)
+        elif isinstance(expr, (Instance, astroid.Class)):
             if isinstance(expr, Instance):
                 expr = expr._proxied
-            if (isinstance(expr, astng.Class) and
+            if (isinstance(expr, astroid.Class) and
                     not inherit_from_std_ex(expr) and
                     expr.root().name != BUILTINS_NAME):
                 if expr.newstyle:
-                    self.add_message('E0710', node=node)
+                    self.add_message('raising-non-exception', node=node)
                 else:
-                    self.add_message('W0710', node=node)
+                    self.add_message('nonstandard-exception', node=node)
             else:
                 value_found = False
         else:
             value_found = False
         return value_found
 
+    @check_messages('unpacking-in-except')
+    def visit_excepthandler(self, node):
+        """Visit an except handler block and check for exception unpacking."""
+        if isinstance(node.name, (astroid.Tuple, astroid.List)):
+            self.add_message('unpacking-in-except', node=node)
 
+    @check_messages('indexing-exception')
+    def visit_subscript(self, node):
+        """ Look for indexing exceptions. """
+        try:
+            for infered in node.value.infer():
+                if not isinstance(infered, astroid.Instance):
+                    continue
+                if inherit_from_std_ex(infered):
+                    self.add_message('indexing-exception', node=node)
+        except astroid.InferenceError:
+            return
+
+    @check_messages('bare-except', 'broad-except', 'pointless-except',
+                    'binary-op-exception', 'bad-except-order',
+                    'catching-non-exception')
     def visit_tryexcept(self, node):
         """check for empty except"""
         exceptions_classes = []
         nb_handlers = len(node.handlers)
         for index, handler  in enumerate(node.handlers):
             # single except doing nothing but "pass" without else clause
-            if nb_handlers == 1 and is_empty(handler.body) and not node.orelse:
-                self.add_message('W0704', node=handler.type or handler.body[0])
+            if is_empty(handler.body) and not node.orelse:
+                self.add_message('pointless-except', node=handler.type or handler.body[0])
             if handler.type is None:
-                if nb_handlers == 1 and not is_raising(handler.body):
-                    self.add_message('W0702', node=handler)
+                if not is_raising(handler.body):
+                    self.add_message('bare-except', node=handler)
                 # check if a "except:" is followed by some other
                 # except
-                elif index < (nb_handlers - 1):
+                if index < (nb_handlers - 1):
                     msg = 'empty except clause should always appear last'
-                    self.add_message('E0701', node=node, args=msg)
+                    self.add_message('bad-except-order', node=node, args=msg)
+
+            elif isinstance(handler.type, astroid.BoolOp):
+                self.add_message('binary-op-exception', node=handler, args=handler.type.op)
             else:
                 try:
                     excs = list(unpack_infer(handler.type))
-                except astng.InferenceError:
+                except astroid.InferenceError:
                     continue
                 for exc in excs:
-                    # XXX skip other non class nodes 
-                    if exc is YES or not isinstance(exc, astng.Class):
+                    # XXX skip other non class nodes
+                    if exc is YES or not isinstance(exc, astroid.Class):
                         continue
                     exc_ancestors = [anc for anc in exc.ancestors()
-                                     if isinstance(anc, astng.Class)]
+                                     if isinstance(anc, astroid.Class)]
                     for previous_exc in exceptions_classes:
                         if previous_exc in exc_ancestors:
                             msg = '%s is an ancestor class of %s' % (
                                 previous_exc.name, exc.name)
-                            self.add_message('E0701', node=handler.type, args=msg)
+                            self.add_message('bad-except-order', node=handler.type, args=msg)
                     if (exc.name in self.config.overgeneral_exceptions
-                        and exc.root().name == EXCEPTIONS_MODULE
-                        and nb_handlers == 1 and not is_raising(handler.body)):
-                        self.add_message('W0703', args=exc.name, node=handler.type)
+                            and exc.root().name == EXCEPTIONS_MODULE
+                            and not is_raising(handler.body)):
+                        self.add_message('broad-except', args=exc.name, node=handler.type)
+
+                    if (not inherit_from_std_ex(exc) and
+                            exc.root().name != BUILTINS_NAME):
+                        # try to see if the exception is based on a C based
+                        # exception, by infering all the base classes and
+                        # looking for inference errors
+                        bases = infer_bases(exc)
+                        fully_infered = all(inferit is not YES
+                                            for inferit in bases)
+                        if fully_infered:
+                            self.add_message('catching-non-exception',
+                                             node=handler.type,
+                                             args=(exc.name, ))
+
                 exceptions_classes += excs
 
 
diff --git a/third_party/pylint/checkers/format.py b/third_party/pylint/checkers/format.py
index 0784e6a..cb31256 100644
--- a/third_party/pylint/checkers/format.py
+++ b/third_party/pylint/checkers/format.py
@@ -1,5 +1,5 @@
-# Copyright (c) 2003-2010 Sylvain Thenault ([email protected]).
-# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
+#
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
 # Foundation; either version 2 of the License, or (at your option) any later
@@ -11,7 +11,7 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """Python code format's checker.
 
 By default try to follow Guido's style guide :
@@ -21,131 +21,382 @@
 Some parts of the process_token method is based from The Tab Nanny std module.
 """
 
-import re, sys
+import keyword
+import sys
 import tokenize
+
 if not hasattr(tokenize, 'NL'):
     raise ValueError("tokenize.NL doesn't exist -- tokenize module too old")
 
-from logilab.common.textutils import pretty_match
-from logilab.astng import nodes
+from astroid import nodes
 
-from pylint.interfaces import IRawChecker, IASTNGChecker
-from pylint.checkers import BaseRawChecker
+from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker
+from pylint.checkers import BaseTokenChecker
 from pylint.checkers.utils import check_messages
+from pylint.utils import WarningScope, OPTION_RGX
+
+_CONTINUATION_BLOCK_OPENERS = ['elif', 'except', 'for', 'if', 'while', 'def', 'class']
+_KEYWORD_TOKENS = ['assert', 'del', 'elif', 'except', 'for', 'if', 'in', 'not',
+                   'raise', 'return', 'while', 'yield']
+if sys.version_info < (3, 0):
+    _KEYWORD_TOKENS.append('print')
+
+_SPACED_OPERATORS = ['==', '<', '>', '!=', '<>', '<=', '>=',
+                     '+=', '-=', '*=', '**=', '/=', '//=', '&=', '|=', '^=',
+                     '%=', '>>=', '<<=']
+_OPENING_BRACKETS = ['(', '[', '{']
+_CLOSING_BRACKETS = [')', ']', '}']
+_TAB_LENGTH = 8
+
+_EOL = frozenset([tokenize.NEWLINE, tokenize.NL, tokenize.COMMENT])
+_JUNK_TOKENS = (tokenize.COMMENT, tokenize.NL)
+
+# Whitespace checking policy constants
+_MUST = 0
+_MUST_NOT = 1
+_IGNORE = 2
+
+# Whitespace checking config constants
+_DICT_SEPARATOR = 'dict-separator'
+_TRAILING_COMMA = 'trailing-comma'
+_NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR]
 
 MSGS = {
     'C0301': ('Line too long (%s/%s)',
+              'line-too-long',
               'Used when a line is longer than a given number of characters.'),
     'C0302': ('Too many lines in module (%s)', # was W0302
+              'too-many-lines',
               'Used when a module has too much lines, reducing its readability.'
-              ),
-
+             ),
+    'C0303': ('Trailing whitespace',
+              'trailing-whitespace',
+              'Used when there is whitespace between the end of a line and the '
+              'newline.'),
+    'C0304': ('Final newline missing',
+              'missing-final-newline',
+              'Used when the last line in a file is missing a newline.'),
     'W0311': ('Bad indentation. Found %s %s, expected %s',
+              'bad-indentation',
               'Used when an unexpected number of indentation\'s tabulations or '
               'spaces has been found.'),
+    'C0330': ('Wrong %s indentation%s.\n%s%s',
+              'bad-continuation',
+              'TODO'),
     'W0312': ('Found indentation with %ss instead of %ss',
+              'mixed-indentation',
               'Used when there are some mixed tabs and spaces in a module.'),
     'W0301': ('Unnecessary semicolon', # was W0106
+              'unnecessary-semicolon',
               'Used when a statement is ended by a semi-colon (";"), which \
               isn\'t necessary (that\'s python, not C ;).'),
     'C0321': ('More than one statement on a single line',
-              'Used when more than on statement are found on the same line.'),
-    'C0322': ('Operator not preceded by a space\n%s',
-              'Used when one of the following operator (!= | <= | == | >= | < '
-              '| > | = | \+= | -= | \*= | /= | %) is not preceded by a space.'),
-    'C0323': ('Operator not followed by a space\n%s',
-              'Used when one of the following operator (!= | <= | == | >= | < '
-              '| > | = | \+= | -= | \*= | /= | %) is not followed by a space.'),
-    'C0324': ('Comma not followed by a space\n%s',
-              'Used when a comma (",") is not followed by a space.'),
-    }
-
-if sys.version_info < (3, 0):
-
-    MSGS.update({
+              'multiple-statements',
+              'Used when more than on statement are found on the same line.',
+              {'scope': WarningScope.NODE}),
+    'C0325' : ('Unnecessary parens after %r keyword',
+               'superfluous-parens',
+               'Used when a single item in parentheses follows an if, for, or '
+               'other keyword.'),
+    'C0326': ('%s space %s %s %s\n%s',
+              'bad-whitespace',
+              ('Used when a wrong number of spaces is used around an operator, '
+               'bracket or block opener.'),
+              {'old_names': [('C0323', 'no-space-after-operator'),
+                             ('C0324', 'no-space-after-comma'),
+                             ('C0322', 'no-space-before-operator')]}),
     'W0331': ('Use of the <> operator',
-              'Used when the deprecated "<>" operator is used instead \
-              of "!=".'),
-    'W0332': ('Use l as long integer identifier',
+              'old-ne-operator',
+              'Used when the deprecated "<>" operator is used instead '
+              'of "!=".',
+              {'maxversion': (3, 0)}),
+    'W0332': ('Use of "l" as long integer identifier',
+              'lowercase-l-suffix',
               'Used when a lower case "l" is used to mark a long integer. You '
               'should use a upper case "L" since the letter "l" looks too much '
-              'like the digit "1"'),
+              'like the digit "1"',
+              {'maxversion': (3, 0)}),
     'W0333': ('Use of the `` operator',
+              'backtick',
               'Used when the deprecated "``" (backtick) operator is used '
-              'instead  of the str() function.'),
-    })
-
-# simple quoted string rgx
-SQSTRING_RGX = r'"([^"\\]|\\.)*?"'
-# simple apostrophed rgx
-SASTRING_RGX = r"'([^'\\]|\\.)*?'"
-# triple quoted string rgx
-TQSTRING_RGX = r'"""([^"]|("(?!"")))*?(""")'
-# triple apostrophed string rgx # FIXME english please
-TASTRING_RGX = r"'''([^']|('(?!'')))*?(''')"
-
-# finally, the string regular expression
-STRING_RGX = re.compile('(%s)|(%s)|(%s)|(%s)' % (TQSTRING_RGX, TASTRING_RGX,
-                                                 SQSTRING_RGX, SASTRING_RGX),
-                        re.MULTILINE|re.DOTALL)
-
-COMMENT_RGX = re.compile("#.*$", re.M)
-
-OPERATORS = r'!=|<=|==|>=|<|>|=|\+=|-=|\*=|/=|%'
-
-OP_RGX_MATCH_1 = r'[^(]*(?<!\s|\^|<|>|=|\+|-|\*|/|!|%%|&|\|)(%s).*' % OPERATORS
-OP_RGX_SEARCH_1 = r'(?<!\s|\^|<|>|=|\+|-|\*|/|!|%%|&|\|)(%s)' % OPERATORS
-
-OP_RGX_MATCH_2 = r'[^(]*(%s)(?!\s|=|>|<).*' % OPERATORS
-OP_RGX_SEARCH_2 = r'(%s)(?!\s|=|>)' % OPERATORS
-
-BAD_CONSTRUCT_RGXS = (
-
-    (re.compile(OP_RGX_MATCH_1, re.M),
-     re.compile(OP_RGX_SEARCH_1, re.M),
-     'C0322'),
-
-    (re.compile(OP_RGX_MATCH_2, re.M),
-     re.compile(OP_RGX_SEARCH_2, re.M),
-     'C0323'),
-
-    (re.compile(r'.*,[^(\s|\]|}|\))].*', re.M), 
-     re.compile(r',[^\s)]', re.M),
-     'C0324'),
-    )
+              'instead  of the str() function.',
+              {'scope': WarningScope.NODE, 'maxversion': (3, 0)}),
+    }
 
 
-def get_string_coords(line):
-    """return a list of string positions (tuple (start, end)) in the line
-    """
-    result = []
-    for match in re.finditer(STRING_RGX, line):
-        result.append( (match.start(), match.end()) )
+def _underline_token(token):
+    length = token[3][1] - token[2][1]
+    offset = token[2][1]
+    return token[4] + (' ' * offset) + ('^' * length)
+
+
+def _column_distance(token1, token2):
+    if token1 == token2:
+        return 0
+    if token2[3] < token1[3]:
+        token1, token2 = token2, token1
+    if token1[3][0] != token2[2][0]:
+        return None
+    return token2[2][1] - token1[3][1]
+
+
+def _last_token_on_line_is(tokens, line_end, token):
+    return (line_end > 0 and tokens.token(line_end-1) == token or
+            line_end > 1 and tokens.token(line_end-2) == token
+            and tokens.type(line_end-1) == tokenize.COMMENT)
+
+
+def _token_followed_by_eol(tokens, position):
+    return (tokens.type(position+1) == tokenize.NL or
+            tokens.type(position+1) == tokenize.COMMENT and
+            tokens.type(position+2) == tokenize.NL)
+
+
+def _get_indent_length(line):
+    """Return the length of the indentation on the given token's line."""
+    result = 0
+    for char in line:
+        if char == ' ':
+            result += 1
+        elif char == '\t':
+            result += _TAB_LENGTH
+        else:
+            break
     return result
 
-def in_coords(match, string_coords):
-    """return true if the match is in the string coord"""
-    mstart = match.start()
-    for start, end in string_coords:
-        if mstart >= start and mstart < end:
-            return True
-    return False
 
-def check_line(line):
-    """check a line for a bad construction
-    if it founds one, return a message describing the problem
-    else return None
+def _get_indent_hint_line(bar_positions, bad_position):
+    """Return a line with |s for each of the positions in the given lists."""
+    if not bar_positions:
+        return ''
+    markers = [(pos, '|') for pos in bar_positions]
+    markers.append((bad_position, '^'))
+    markers.sort()
+    line = [' '] * (markers[-1][0] + 1)
+    for position, marker in markers:
+        line[position] = marker
+    return ''.join(line)
+
+
+class _ContinuedIndent(object):
+    __slots__ = ('valid_outdent_offsets',
+                 'valid_continuation_offsets',
+                 'context_type',
+                 'token',
+                 'position')
+
+    def __init__(self,
+                 context_type,
+                 token,
+                 position,
+                 valid_outdent_offsets,
+                 valid_continuation_offsets):
+        self.valid_outdent_offsets = valid_outdent_offsets
+        self.valid_continuation_offsets = valid_continuation_offsets
+        self.context_type = context_type
+        self.position = position
+        self.token = token
+
+
+# The contexts for hanging indents.
+# A hanging indented dictionary value after :
+HANGING_DICT_VALUE = 'dict-value'
+# Hanging indentation in an expression.
+HANGING = 'hanging'
+# Hanging indentation in a block header.
+HANGING_BLOCK = 'hanging-block'
+# Continued indentation inside an expression.
+CONTINUED = 'continued'
+# Continued indentation in a block header.
+CONTINUED_BLOCK = 'continued-block'
+
+SINGLE_LINE = 'single'
+WITH_BODY = 'multi'
+
+_CONTINUATION_MSG_PARTS = {
+    HANGING_DICT_VALUE: ('hanging', ' in dict value'),
+    HANGING: ('hanging', ''),
+    HANGING_BLOCK: ('hanging', ' before block'),
+    CONTINUED: ('continued', ''),
+    CONTINUED_BLOCK: ('continued', ' before block'),
+}
+
+
+def _Offsets(*args):
+    """Valid indentation offsets for a continued line."""
+    return dict((a, None) for a in args)
+
+
+def _BeforeBlockOffsets(single, with_body):
+    """Valid alternative indent offsets for continued lines before blocks.
+
+    :param single: Valid offset for statements on a single logical line.
+    :param with_body: Valid offset for statements on several lines.
     """
-    cleanstr = COMMENT_RGX.sub('', STRING_RGX.sub('', line))
-    for rgx_match, rgx_search, msg_id in BAD_CONSTRUCT_RGXS:
-        if rgx_match.match(cleanstr):
-            string_positions = get_string_coords(line)
-            for match in re.finditer(rgx_search, line):
-                if not in_coords(match, string_positions):
-                    return msg_id, pretty_match(match, line.rstrip())
+    return {single: SINGLE_LINE, with_body: WITH_BODY}
 
 
-class FormatChecker(BaseRawChecker):
+class TokenWrapper(object):
+    """A wrapper for readable access to token information."""
+
+    def __init__(self, tokens):
+        self._tokens = tokens
+
+    def token(self, idx):
+        return self._tokens[idx][1]
+
+    def type(self, idx):
+        return self._tokens[idx][0]
+
+    def start_line(self, idx):
+        return self._tokens[idx][2][0]
+
+    def start_col(self, idx):
+        return self._tokens[idx][2][1]
+
+    def line(self, idx):
+        return self._tokens[idx][4]
+
+
+class ContinuedLineState(object):
+    """Tracker for continued indentation inside a logical line."""
+
+    def __init__(self, tokens, config):
+        self._line_start = -1
+        self._cont_stack = []
+        self._is_block_opener = False
+        self.retained_warnings = []
+        self._config = config
+        self._tokens = TokenWrapper(tokens)
+
+    @property
+    def has_content(self):
+        return bool(self._cont_stack)
+
+    @property
+    def _block_indent_size(self):
+        return len(self._config.indent_string.replace('\t', ' ' * _TAB_LENGTH))
+
+    @property
+    def _continuation_size(self):
+        return self._config.indent_after_paren
+
+    def handle_line_start(self, pos):
+        """Record the first non-junk token at the start of a line."""
+        if self._line_start > -1:
+            return
+        self._is_block_opener = self._tokens.token(pos) in _CONTINUATION_BLOCK_OPENERS
+        self._line_start = pos
+
+    def next_physical_line(self):
+        """Prepares the tracker for a new physical line (NL)."""
+        self._line_start = -1
+        self._is_block_opener = False
+
+    def next_logical_line(self):
+        """Prepares the tracker for a new logical line (NEWLINE).
+
+        A new logical line only starts with block indentation.
+        """
+        self.next_physical_line()
+        self.retained_warnings = []
+        self._cont_stack = []
+
+    def add_block_warning(self, token_position, state, valid_offsets):
+        self.retained_warnings.append((token_position, state, valid_offsets))
+
+    def get_valid_offsets(self, idx):
+        """"Returns the valid offsets for the token at the given position."""
+        # The closing brace on a dict or the 'for' in a dict comprehension may
+        # reset two indent levels because the dict value is ended implicitly
+        stack_top = -1
+        if self._tokens.token(idx) in ('}', 'for') and self._cont_stack[-1].token == ':':
+            stack_top = -2
+        indent = self._cont_stack[stack_top]
+        if self._tokens.token(idx) in _CLOSING_BRACKETS:
+            valid_offsets = indent.valid_outdent_offsets
+        else:
+            valid_offsets = indent.valid_continuation_offsets
+        return indent, valid_offsets.copy()
+
+    def _hanging_indent_after_bracket(self, bracket, position):
+        """Extracts indentation information for a hanging indent."""
+        indentation = _get_indent_length(self._tokens.line(position))
+        if self._is_block_opener and self._continuation_size == self._block_indent_size:
+            return _ContinuedIndent(
+                HANGING_BLOCK,
+                bracket,
+                position,
+                _Offsets(indentation + self._continuation_size, indentation),
+                _BeforeBlockOffsets(indentation + self._continuation_size,
+                                    indentation + self._continuation_size * 2))
+        elif bracket == ':':
+            # If the dict key was on the same line as the open brace, the new
+            # correct indent should be relative to the key instead of the
+            # current indent level
+            paren_align = self._cont_stack[-1].valid_outdent_offsets
+            next_align = self._cont_stack[-1].valid_continuation_offsets.copy()
+            next_align[next_align.keys()[0] + self._continuation_size] = True
+            # Note that the continuation of
+            # d = {
+            #       'a': 'b'
+            #            'c'
+            # }
+            # is handled by the special-casing for hanging continued string indents.
+            return _ContinuedIndent(HANGING_DICT_VALUE, bracket, position, paren_align, next_align)
+        else:
+            return _ContinuedIndent(
+                HANGING,
+                bracket,
+                position,
+                _Offsets(indentation, indentation + self._continuation_size),
+                _Offsets(indentation + self._continuation_size))
+
+    def _continuation_inside_bracket(self, bracket, pos):
+        """Extracts indentation information for a continued indent."""
+        indentation = _get_indent_length(self._tokens.line(pos))
+        if self._is_block_opener and self._tokens.start_col(pos+1) - indentation == self._block_indent_size:
+            return _ContinuedIndent(
+                CONTINUED_BLOCK,
+                bracket,
+                pos,
+                _Offsets(self._tokens.start_col(pos)),
+                _BeforeBlockOffsets(self._tokens.start_col(pos+1),
+                                    self._tokens.start_col(pos+1) + self._continuation_size))
+        else:
+            return _ContinuedIndent(
+                CONTINUED,
+                bracket,
+                pos,
+                _Offsets(self._tokens.start_col(pos)),
+                _Offsets(self._tokens.start_col(pos+1)))
+
+    def pop_token(self):
+        self._cont_stack.pop()
+
+    def push_token(self, token, position):
+        """Pushes a new token for continued indentation on the stack.
+
+        Tokens that can modify continued indentation offsets are:
+          * opening brackets
+          * 'lambda'
+          * : inside dictionaries
+
+        push_token relies on the caller to filter out those
+        interesting tokens.
+
+        :param token: The concrete token
+        :param position: The position of the token in the stream.
+        """
+        if _token_followed_by_eol(self._tokens, position):
+            self._cont_stack.append(
+                self._hanging_indent_after_bracket(token, position))
+        else:
+            self._cont_stack.append(
+                self._continuation_inside_bracket(token, position))
+
+
+class FormatChecker(BaseTokenChecker):
     """checks for :
     * unauthorized constructions
     * strict indentation
@@ -153,7 +404,7 @@
     * use of <> instead of !=
     """
 
-    __implements__ = (IRawChecker, IASTNGChecker)
+    __implements__ = (ITokenChecker, IAstroidChecker, IRawChecker)
 
     # configuration section name
     name = 'format'
@@ -164,38 +415,290 @@
     options = (('max-line-length',
                 {'default' : 80, 'type' : "int", 'metavar' : '<int>',
                  'help' : 'Maximum number of characters on a single line.'}),
+               ('ignore-long-lines',
+                {'type': 'regexp', 'metavar': '<regexp>',
+                 'default': r'^\s*(# )?<?https?://\S+>?$',
+                 'help': ('Regexp for a line that is allowed to be longer than '
+                          'the limit.')}),
+               ('single-line-if-stmt',
+                {'default': False, 'type' : 'yn', 'metavar' : '<y_or_n>',
+                 'help' : ('Allow the body of an if to be on the same '
+                           'line as the test if there is no else.')}),
+               ('no-space-check',
+                {'default': ','.join(_NO_SPACE_CHECK_CHOICES),
+                 'type': 'multiple_choice',
+                 'choices': _NO_SPACE_CHECK_CHOICES,
+                 'help': ('List of optional constructs for which whitespace '
+                          'checking is disabled')}),
                ('max-module-lines',
                 {'default' : 1000, 'type' : 'int', 'metavar' : '<int>',
                  'help': 'Maximum number of lines in a module'}
-                ),
+               ),
                ('indent-string',
                 {'default' : '    ', 'type' : "string", 'metavar' : '<string>',
-                 'help' : 'String used as indentation unit. This is usually \
-"    " (4 spaces) or "\\t" (1 tab).'}),
-               )
+                 'help' : 'String used as indentation unit. This is usually '
+                          '"    " (4 spaces) or "\\t" (1 tab).'}),
+               ('indent-after-paren',
+                {'type': 'int', 'metavar': '<int>', 'default': 4,
+                 'help': 'Number of spaces of indent required inside a hanging '
+                         ' or continued line.'}),
+              )
+
     def __init__(self, linter=None):
-        BaseRawChecker.__init__(self, linter)
+        BaseTokenChecker.__init__(self, linter)
         self._lines = None
         self._visited_lines = None
+        self._bracket_stack = [None]
 
-    def process_module(self, node):
-        """extracts encoding from the stream and decodes each line, so that
-        international text's length is properly calculated.
-        """
-        stream = node.file_stream
-        stream.seek(0) # XXX may be removed with astng > 0.23
-        readline = stream.readline
-        if sys.version_info < (3, 0):
-            if node.file_encoding is not None:
-                readline = lambda: stream.readline().decode(node.file_encoding, 'replace')
-        self.process_tokens(tokenize.generate_tokens(readline))
+    def _pop_token(self):
+        self._bracket_stack.pop()
+        self._current_line.pop_token()
 
-    def new_line(self, tok_type, line, line_num, junk):
+    def _push_token(self, token, idx):
+        self._bracket_stack.append(token)
+        self._current_line.push_token(token, idx)
+
+    def new_line(self, tokens, line_end, line_start):
         """a new line has been encountered, process it if necessary"""
-        if not tok_type in junk:
+        if _last_token_on_line_is(tokens, line_end, ';'):
+            self.add_message('unnecessary-semicolon', line=tokens.start_line(line_end))
+
+        line_num = tokens.start_line(line_start)
+        line = tokens.line(line_start)
+        if tokens.type(line_start) not in _JUNK_TOKENS:
             self._lines[line_num] = line.split('\n')[0]
         self.check_lines(line, line_num)
 
+    def process_module(self, module):
+        self._keywords_with_parens = set()
+        if 'print_function' in module.future_imports:
+            self._keywords_with_parens.add('print')
+
+    def _check_keyword_parentheses(self, tokens, start):
+        """Check that there are not unnecessary parens after a keyword.
+
+        Parens are unnecessary if there is exactly one balanced outer pair on a
+        line, and it is followed by a colon, and contains no commas (i.e. is not a
+        tuple).
+
+        Args:
+        tokens: list of Tokens; the entire list of Tokens.
+        start: int; the position of the keyword in the token list.
+        """
+        # If the next token is not a paren, we're fine.
+        if self._inside_brackets(':') and tokens[start][1] == 'for':
+            self._pop_token()
+        if tokens[start+1][1] != '(':
+            return
+
+        found_and_or = False
+        depth = 0
+        keyword_token = tokens[start][1]
+        line_num = tokens[start][2][0]
+
+        for i in xrange(start, len(tokens) - 1):
+            token = tokens[i]
+
+            # If we hit a newline, then assume any parens were for continuation.
+            if token[0] == tokenize.NL:
+                return
+
+            if token[1] == '(':
+                depth += 1
+            elif token[1] == ')':
+                depth -= 1
+                if not depth:
+                    # ')' can't happen after if (foo), since it would be a syntax error.
+                    if (tokens[i+1][1] in (':', ')', ']', '}', 'in') or
+                            tokens[i+1][0] in (tokenize.NEWLINE,
+                                               tokenize.ENDMARKER,
+                                               tokenize.COMMENT)):
+                        # The empty tuple () is always accepted.
+                        if i == start + 2:
+                            return
+                        if keyword_token == 'not':
+                            if not found_and_or:
+                                self.add_message('superfluous-parens', line=line_num,
+                                                 args=keyword_token)
+                        elif keyword_token in ('return', 'yield'):
+                            self.add_message('superfluous-parens', line=line_num,
+                                             args=keyword_token)
+                        elif keyword_token not in self._keywords_with_parens:
+                            if not (tokens[i+1][1] == 'in' and found_and_or):
+                                self.add_message('superfluous-parens', line=line_num,
+                                                 args=keyword_token)
+                    return
+            elif depth == 1:
+                # This is a tuple, which is always acceptable.
+                if token[1] == ',':
+                    return
+                # 'and' and 'or' are the only boolean operators with lower precedence
+                # than 'not', so parens are only required when they are found.
+                elif token[1] in ('and', 'or'):
+                    found_and_or = True
+                # A yield inside an expression must always be in parentheses,
+                # quit early without error.
+                elif token[1] == 'yield':
+                    return
+                # A generator expression always has a 'for' token in it, and
+                # the 'for' token is only legal inside parens when it is in a
+                # generator expression.  The parens are necessary here, so bail
+                # without an error.
+                elif token[1] == 'for':
+                    return
+
+    def _opening_bracket(self, tokens, i):
+        self._push_token(tokens[i][1], i)
+        # Special case: ignore slices
+        if tokens[i][1] == '[' and tokens[i+1][1] == ':':
+            return
+
+        if (i > 0 and (tokens[i-1][0] == tokenize.NAME and
+                       not (keyword.iskeyword(tokens[i-1][1]))
+                       or tokens[i-1][1] in _CLOSING_BRACKETS)):
+            self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT))
+        else:
+            self._check_space(tokens, i, (_IGNORE, _MUST_NOT))
+
+    def _closing_bracket(self, tokens, i):
+        if self._inside_brackets(':'):
+            self._pop_token()
+        self._pop_token()
+        # Special case: ignore slices
+        if tokens[i-1][1] == ':' and tokens[i][1] == ']':
+            return
+        policy_before = _MUST_NOT
+        if tokens[i][1] in _CLOSING_BRACKETS and tokens[i-1][1] == ',':
+            if _TRAILING_COMMA in self.config.no_space_check:
+                policy_before = _IGNORE
+
+        self._check_space(tokens, i, (policy_before, _IGNORE))
+
+    def _check_equals_spacing(self, tokens, i):
+        """Check the spacing of a single equals sign."""
+        if self._inside_brackets('(') or self._inside_brackets('lambda'):
+            self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT))
+        else:
+            self._check_space(tokens, i, (_MUST, _MUST))
+
+    def _open_lambda(self, tokens, i): # pylint:disable=unused-argument
+        self._push_token('lambda', i)
+
+    def _handle_colon(self, tokens, i):
+        # Special case: ignore slices
+        if self._inside_brackets('['):
+            return
+        if (self._inside_brackets('{') and
+                _DICT_SEPARATOR in self.config.no_space_check):
+            policy = (_IGNORE, _IGNORE)
+        else:
+            policy = (_MUST_NOT, _MUST)
+        self._check_space(tokens, i, policy)
+
+        if self._inside_brackets('lambda'):
+            self._pop_token()
+        elif self._inside_brackets('{'):
+            self._push_token(':', i)
+
+    def _handle_comma(self, tokens, i):
+        # Only require a following whitespace if this is
+        # not a hanging comma before a closing bracket.
+        if tokens[i+1][1] in _CLOSING_BRACKETS:
+            self._check_space(tokens, i, (_MUST_NOT, _IGNORE))
+        else:
+            self._check_space(tokens, i, (_MUST_NOT, _MUST))
+        if self._inside_brackets(':'):
+            self._pop_token()
+
+    def _check_surrounded_by_space(self, tokens, i):
+        """Check that a binary operator is surrounded by exactly one space."""
+        self._check_space(tokens, i, (_MUST, _MUST))
+
+    def _check_space(self, tokens, i, policies):
+        def _policy_string(policy):
+            if policy == _MUST:
+                return 'Exactly one', 'required'
+            else:
+                return 'No', 'allowed'
+
+        def _name_construct(token):
+            if tokens[i][1] == ',':
+                return 'comma'
+            elif tokens[i][1] == ':':
+                return ':'
+            elif tokens[i][1] in '()[]{}':
+                return 'bracket'
+            elif tokens[i][1] in ('<', '>', '<=', '>=', '!=', '=='):
+                return 'comparison'
+            else:
+                if self._inside_brackets('('):
+                    return 'keyword argument assignment'
+                else:
+                    return 'assignment'
+
+        good_space = [True, True]
+        pairs = [(tokens[i-1], tokens[i]), (tokens[i], tokens[i+1])]
+
+        for other_idx, (policy, token_pair) in enumerate(zip(policies, pairs)):
+            if token_pair[other_idx][0] in _EOL or policy == _IGNORE:
+                continue
+
+            distance = _column_distance(*token_pair)
+            if distance is None:
+                continue
+            good_space[other_idx] = (
+                (policy == _MUST and distance == 1) or
+                (policy == _MUST_NOT and distance == 0))
+
+        warnings = []
+        if not any(good_space) and policies[0] == policies[1]:
+            warnings.append((policies[0], 'around'))
+        else:
+            for ok, policy, position in zip(good_space, policies, ('before', 'after')):
+                if not ok:
+                    warnings.append((policy, position))
+        for policy, position in warnings:
+            construct = _name_construct(tokens[i])
+            count, state = _policy_string(policy)
+            self.add_message('bad-whitespace', line=tokens[i][2][0],
+                             args=(count, state, position, construct,
+                                   _underline_token(tokens[i])))
+
+    def _inside_brackets(self, left):
+        return self._bracket_stack[-1] == left
+
+    def _handle_old_ne_operator(self, tokens, i):
+        if tokens[i][1] == '<>':
+            self.add_message('old-ne-operator', line=tokens[i][2][0])
+
+    def _prepare_token_dispatcher(self):
+        raw = [
+            (_KEYWORD_TOKENS,
+             self._check_keyword_parentheses),
+
+            (_OPENING_BRACKETS, self._opening_bracket),
+
+            (_CLOSING_BRACKETS, self._closing_bracket),
+
+            (['='], self._check_equals_spacing),
+
+            (_SPACED_OPERATORS, self._check_surrounded_by_space),
+
+            ([','], self._handle_comma),
+
+            ([':'], self._handle_colon),
+
+            (['lambda'], self._open_lambda),
+
+            (['<>'], self._handle_old_ne_operator),
+            ]
+
+        dispatch = {}
+        for tokens, handler in raw:
+            for token in tokens:
+                dispatch[token] = handler
+        return dispatch
+
     def process_tokens(self, tokens):
         """process tokens and search for :
 
@@ -205,70 +708,123 @@
          _ optionally bad construct (if given, bad_construct must be a compiled
            regular expression).
         """
-        indent = tokenize.INDENT
-        dedent = tokenize.DEDENT
-        newline = tokenize.NEWLINE
-        junk = (tokenize.COMMENT, tokenize.NL)
+        self._bracket_stack = [None]
         indents = [0]
-        check_equal = 0
+        check_equal = False
         line_num = 0
-        previous = None
         self._lines = {}
         self._visited_lines = {}
-        for (tok_type, token, start, _, line) in tokens:
+        token_handlers = self._prepare_token_dispatcher()
+
+        self._current_line = ContinuedLineState(tokens, self.config)
+        for idx, (tok_type, token, start, _, line) in enumerate(tokens):
             if start[0] != line_num:
-                if previous is not None and previous[0] == tokenize.OP and previous[1] == ';':
-                    self.add_message('W0301', line=previous[2])
-                previous = None
                 line_num = start[0]
-                self.new_line(tok_type, line, line_num, junk)
-            if tok_type not in (indent, dedent, newline) + junk:
-                previous = tok_type, token, start[0]
+                # A tokenizer oddity: if an indented line contains a multi-line
+                # docstring, the line member of the INDENT token does not contain
+                # the full line; therefore we check the next token on the line.
+                if tok_type == tokenize.INDENT:
+                    self.new_line(TokenWrapper(tokens), idx-1, idx+1)
+                else:
+                    self.new_line(TokenWrapper(tokens), idx-1, idx)
 
-            if tok_type == tokenize.OP:
-                if token == '<>':
-                    self.add_message('W0331', line=line_num)
-            elif tok_type == tokenize.NUMBER:
-                if token.endswith('l'):
-                    self.add_message('W0332', line=line_num)
-
-            elif tok_type == newline:
+            if tok_type == tokenize.NEWLINE:
                 # a program statement, or ENDMARKER, will eventually follow,
                 # after some (possibly empty) run of tokens of the form
                 #     (NL | COMMENT)* (INDENT | DEDENT+)?
                 # If an INDENT appears, setting check_equal is wrong, and will
                 # be undone when we see the INDENT.
-                check_equal = 1
-
-            elif tok_type == indent:
-                check_equal = 0
+                check_equal = True
+                self._process_retained_warnings(TokenWrapper(tokens), idx)
+                self._current_line.next_logical_line()
+            elif tok_type == tokenize.INDENT:
+                check_equal = False
                 self.check_indent_level(token, indents[-1]+1, line_num)
                 indents.append(indents[-1]+1)
-
-            elif tok_type == dedent:
+            elif tok_type == tokenize.DEDENT:
                 # there's nothing we need to check here!  what's important is
                 # that when the run of DEDENTs ends, the indentation of the
                 # program statement (or ENDMARKER) that triggered the run is
                 # equal to what's left at the top of the indents stack
-                check_equal = 1
+                check_equal = True
                 if len(indents) > 1:
                     del indents[-1]
-
-            elif check_equal and tok_type not in junk:
-                # this is the first "real token" following a NEWLINE, so it
+            elif tok_type == tokenize.NL:
+                self._check_continued_indentation(TokenWrapper(tokens), idx+1)
+                self._current_line.next_physical_line()
+            elif tok_type != tokenize.COMMENT:
+                self._current_line.handle_line_start(idx)
+                # This is the first concrete token following a NEWLINE, so it
                 # must be the first token of the next program statement, or an
                 # ENDMARKER; the "line" argument exposes the leading whitespace
                 # for this statement; in the case of ENDMARKER, line is an empty
                 # string, so will properly match the empty string with which the
                 # "indents" stack was seeded
-                check_equal = 0
-                self.check_indent_level(line, indents[-1], line_num)
+                if check_equal:
+                    check_equal = False
+                    self.check_indent_level(line, indents[-1], line_num)
+
+            if tok_type == tokenize.NUMBER and token.endswith('l'):
+                self.add_message('lowercase-l-suffix', line=line_num)
+
+            try:
+                handler = token_handlers[token]
+            except KeyError:
+                pass
+            else:
+                handler(tokens, idx)
 
         line_num -= 1 # to be ok with "wc -l"
         if line_num > self.config.max_module_lines:
-            self.add_message('C0302', args=line_num, line=1)
+            self.add_message('too-many-lines', args=line_num, line=1)
 
-    @check_messages('C0321' ,'C03232', 'C0323', 'C0324')
+    def _process_retained_warnings(self, tokens, current_pos):
+        single_line_block_stmt = not _last_token_on_line_is(tokens, current_pos, ':')
+
+        for indent_pos, state, offsets in self._current_line.retained_warnings:
+            block_type = offsets[tokens.start_col(indent_pos)]
+            hints = dict((k, v) for k, v in offsets.iteritems()
+                         if v != block_type)
+            if single_line_block_stmt and block_type == WITH_BODY:
+                self._add_continuation_message(state, hints, tokens, indent_pos)
+            elif not single_line_block_stmt and block_type == SINGLE_LINE:
+                self._add_continuation_message(state, hints, tokens, indent_pos)
+
+    def _check_continued_indentation(self, tokens, next_idx):
+        def same_token_around_nl(token_type):
+            return (tokens.type(next_idx) == token_type and
+                    tokens.type(next_idx-2) == token_type)
+
+        # Do not issue any warnings if the next line is empty.
+        if not self._current_line.has_content or tokens.type(next_idx) == tokenize.NL:
+            return
+
+        state, valid_offsets = self._current_line.get_valid_offsets(next_idx)
+        # Special handling for hanging comments and strings. If the last line ended
+        # with a comment (string) and the new line contains only a comment, the line
+        # may also be indented to the start of the previous token.
+        if same_token_around_nl(tokenize.COMMENT) or same_token_around_nl(tokenize.STRING):
+            valid_offsets[tokens.start_col(next_idx-2)] = True
+
+        # We can only decide if the indentation of a continued line before opening
+        # a new block is valid once we know of the body of the block is on the
+        # same line as the block opener. Since the token processing is single-pass,
+        # emitting those warnings is delayed until the block opener is processed.
+        if (state.context_type in (HANGING_BLOCK, CONTINUED_BLOCK)
+                and tokens.start_col(next_idx) in valid_offsets):
+            self._current_line.add_block_warning(next_idx, state, valid_offsets)
+        elif tokens.start_col(next_idx) not in valid_offsets:
+            self._add_continuation_message(state, valid_offsets, tokens, next_idx)
+
+    def _add_continuation_message(self, state, offsets, tokens, position):
+        readable_type, readable_position = _CONTINUATION_MSG_PARTS[state.context_type]
+        hint_line = _get_indent_hint_line(offsets, tokens.start_col(position))
+        self.add_message(
+            'bad-continuation',
+            line=tokens.start_line(position),
+            args=(readable_type, readable_position, tokens.line(position), hint_line))
+
+    @check_messages('multiple-statements')
     def visit_default(self, node):
         """check the node line number and check it if not yet done"""
         if not node.is_statement:
@@ -279,16 +835,19 @@
         if prev_sibl is not None:
             prev_line = prev_sibl.fromlineno
         else:
-            prev_line = node.parent.statement().fromlineno
+            # The line on which a finally: occurs in a try/finally
+            # is not directly represented in the AST. We infer it
+            # by taking the last line of the body and adding 1, which
+            # should be the line of finally:
+            if (isinstance(node.parent, nodes.TryFinally)
+                    and node in node.parent.finalbody):
+                prev_line = node.parent.body[0].tolineno + 1
+            else:
+                prev_line = node.parent.statement().fromlineno
         line = node.fromlineno
         assert line, node
         if prev_line == line and self._visited_lines.get(line) != 2:
-            # py2.5 try: except: finally:
-            if not (isinstance(node, nodes.TryExcept)
-                    and isinstance(node.parent, nodes.TryFinally)
-                    and node.fromlineno == node.parent.fromlineno):
-                self.add_message('C0321', node=node)
-                self._visited_lines[line] = 2
+            self._check_multi_statement_line(node, line)
             return
         if line in self._visited_lines:
             return
@@ -304,25 +863,49 @@
                 lines.append(self._lines[line].rstrip())
             except KeyError:
                 lines.append('')
-        try:
-            msg_def = check_line('\n'.join(lines))
-            if msg_def:
-                self.add_message(msg_def[0], node=node, args=msg_def[1])
-        except KeyError:
-            # FIXME: internal error !
-            pass
 
-    @check_messages('W0333')
+    def _check_multi_statement_line(self, node, line):
+        """Check for lines containing multiple statements."""
+        # Do not warn about multiple nested context managers
+        # in with statements.
+        if isinstance(node, nodes.With):
+            return
+        # For try... except... finally..., the two nodes
+        # appear to be on the same line due to how the AST is built.
+        if (isinstance(node, nodes.TryExcept) and
+                isinstance(node.parent, nodes.TryFinally)):
+            return
+        if (isinstance(node.parent, nodes.If) and not node.parent.orelse
+                and self.config.single_line_if_stmt):
+            return
+        self.add_message('multiple-statements', node=node)
+        self._visited_lines[line] = 2
+
+    @check_messages('backtick')
     def visit_backquote(self, node):
-        self.add_message('W0333', node=node)
+        self.add_message('backtick', node=node)
 
     def check_lines(self, lines, i):
         """check lines have less than a maximum number of characters
         """
         max_chars = self.config.max_line_length
-        for line in lines.splitlines():
-            if len(line) > max_chars:
-                self.add_message('C0301', line=i, args=(len(line), max_chars))
+        ignore_long_line = self.config.ignore_long_lines
+
+        for line in lines.splitlines(True):
+            if not line.endswith('\n'):
+                self.add_message('missing-final-newline', line=i)
+            else:
+                stripped_line = line.rstrip()
+                if line[len(stripped_line):] not in ('\n', '\r\n'):
+                    self.add_message('trailing-whitespace', line=i)
+                # Don't count excess whitespace in the line length.
+                line = stripped_line
+            mobj = OPTION_RGX.search(line)
+            if mobj and mobj.group(1).split('=', 1)[0].strip() == 'disable':
+                line = line.split('#')[0].rstrip()
+
+            if len(line) > max_chars and not ignore_long_line.search(line):
+                self.add_message('line-too-long', line=i, args=(len(line), max_chars))
             i += 1
 
     def check_indent_level(self, string, expected, line_num):
@@ -343,15 +926,15 @@
                     args = ('tab', 'space')
                 else:
                     args = ('space', 'tab')
-                self.add_message('W0312', args=args, line=line_num)
+                self.add_message('mixed-indentation', args=args, line=line_num)
                 return level
             suppl += string[0]
-            string = string [1:]
+            string = string[1:]
         if level != expected or suppl:
             i_type = 'spaces'
             if indent[0] == '\t':
                 i_type = 'tabs'
-            self.add_message('W0311', line=line_num,
+            self.add_message('bad-indentation', line=line_num,
                              args=(level * unit_size + len(suppl), i_type,
                                    expected * unit_size))
 
diff --git a/third_party/pylint/checkers/imports.py b/third_party/pylint/checkers/imports.py
index 7e6a4f8..5964a26 100644
--- a/third_party/pylint/checkers/imports.py
+++ b/third_party/pylint/checkers/imports.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -12,55 +12,51 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """imports checkers for Python code"""
 
+import sys
+
 from logilab.common.graph import get_cycles, DotBackend
-from logilab.common.modutils import is_standard_module
 from logilab.common.ureports import VerbatimText, Paragraph
 
-from logilab import astng
-from logilab.astng import are_exclusive
+import astroid
+from astroid import are_exclusive
+from astroid.modutils import get_module_part, is_standard_module
 
-from pylint.interfaces import IASTNGChecker
-from pylint.checkers import BaseChecker, EmptyReport
+from pylint.interfaces import IAstroidChecker
+from pylint.utils import EmptyReport
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import check_messages
 
 
 def get_first_import(node, context, name, base, level):
     """return the node where [base.]<name> is imported or None if not found
     """
+    fullname = '%s.%s' % (base, name) if base else name
+
     first = None
     found = False
-    for first in context.values():
-        if isinstance(first, astng.Import):
-            if name in [iname[0] for iname in first.names]:
+    for first in context.body:
+        if first is node:
+            continue
+        if first.scope() is node.scope() and first.fromlineno > node.fromlineno:
+            continue
+        if isinstance(first, astroid.Import):
+            if any(fullname == iname[0] for iname in first.names):
                 found = True
                 break
-        elif isinstance(first, astng.From):
-            if base == first.modname and level == first.level and \
-                   name in [iname[0] for iname in first.names]:
+        elif isinstance(first, astroid.From):
+            if level == first.level and any(
+                    fullname == '%s.%s' % (first.modname, iname[0])
+                    for iname in first.names):
                 found = True
                 break
-    if found and first is not node and not are_exclusive(first, node):
+    if found and not are_exclusive(first, node):
         return first
 
 # utilities to represents import dependencies as tree and dot graph ###########
 
-def filter_dependencies_info(dep_info, package_dir, mode='external'):
-    """filter external or internal dependencies from dep_info (return a
-    new dictionary containing the filtered modules only)
-    """
-    if mode == 'external':
-        filter_func = lambda x: not is_standard_module(x, (package_dir,))
-    else:
-        assert mode == 'internal'
-        filter_func = lambda x: is_standard_module(x, (package_dir,))
-    result = {}
-    for importee, importers in dep_info.items():
-        if filter_func(importee):
-            result[importee] = importers
-    return result
-
 def make_tree_defs(mod_files_list):
     """get a list of 2-uple (module, list_of_files_which_import_this_module),
     it will return a dictionary to represent this as a tree
@@ -86,7 +82,7 @@
             lines.append('%s %s' % (mod, files))
             sub_indent_str = '  '
         else:
-            lines.append('%s\-%s %s' % (indent_str, mod, files))
+            lines.append(r'%s\-%s %s' % (indent_str, mod, files))
             if i == len(nodes)-1:
                 sub_indent_str = '%s  ' % indent_str
             else:
@@ -100,16 +96,16 @@
     """write dependencies as a dot (graphviz) file
     """
     done = {}
-    printer = DotBackend(filename[:-4], rankdir = "LR")
+    printer = DotBackend(filename[:-4], rankdir='LR')
     printer.emit('URL="." node[shape="box"]')
-    for modname, dependencies in dep_info.items():
+    for modname, dependencies in sorted(dep_info.iteritems()):
         done[modname] = 1
         printer.emit_node(modname)
         for modname in dependencies:
             if modname not in done:
                 done[modname] = 1
                 printer.emit_node(modname)
-    for depmodname, dependencies in dep_info.items():
+    for depmodname, dependencies in sorted(dep_info.iteritems()):
         for modname in dependencies:
             printer.emit_edge(modname, depmodname)
     printer.generate(filename)
@@ -128,26 +124,36 @@
 
 MSGS = {
     'F0401': ('Unable to import %s',
+              'import-error',
               'Used when pylint has been unable to import a module.'),
     'R0401': ('Cyclic import (%s)',
+              'cyclic-import',
               'Used when a cyclic import between two or more modules is \
               detected.'),
 
     'W0401': ('Wildcard import %s',
+              'wildcard-import',
               'Used when `from module import *` is detected.'),
     'W0402': ('Uses of a deprecated module %r',
+              'deprecated-module',
               'Used a module marked as deprecated is imported.'),
     'W0403': ('Relative import %r, should be %r',
-              'Used when an import relative to the package directory is \
-              detected.'),
+              'relative-import',
+              'Used when an import relative to the package directory is '
+              'detected.',
+              {'maxversion': (3, 0)}),
     'W0404': ('Reimport %r (imported line %s)',
+              'reimported',
               'Used when a module is reimported multiple times.'),
     'W0406': ('Module import itself',
+              'import-self',
               'Used when a module is importing itself.'),
 
     'W0410': ('__future__ import is not the first non docstring statement',
+              'misplaced-future',
               'Python 2.5 and greater require __future__ import to be the \
-              first non docstring statement in the module.'),
+              first non docstring statement in the module.',
+              {'maxversion': (3, 0)}),
     }
 
 class ImportsChecker(BaseChecker):
@@ -158,43 +164,45 @@
     * uses of deprecated modules
     """
 
-    __implements__ = IASTNGChecker
+    __implements__ = IAstroidChecker
 
     name = 'imports'
     msgs = MSGS
     priority = -2
 
+    if sys.version_info < (3,):
+        deprecated_modules = ('regsub', 'TERMIOS', 'Bastion', 'rexec')
+    else:
+        deprecated_modules = ('stringprep', 'optparse')
     options = (('deprecated-modules',
-                {'default' : ('regsub', 'string', 'TERMIOS',
-                              'Bastion', 'rexec'),
+                {'default' : deprecated_modules,
                  'type' : 'csv',
                  'metavar' : '<modules>',
                  'help' : 'Deprecated modules which should not be used, \
 separated by a comma'}
-                ),
+               ),
                ('import-graph',
                 {'default' : '',
                  'type' : 'string',
                  'metavar' : '<file.dot>',
                  'help' : 'Create a graph of every (i.e. internal and \
 external) dependencies in the given file (report RP0402 must not be disabled)'}
-                ),
+               ),
                ('ext-import-graph',
                 {'default' : '',
                  'type' : 'string',
                  'metavar' : '<file.dot>',
                  'help' : 'Create a graph of external dependencies in the \
 given file (report RP0402 must not be disabled)'}
-                ),
+               ),
                ('int-import-graph',
                 {'default' : '',
                  'type' : 'string',
                  'metavar' : '<file.dot>',
                  'help' : 'Create a graph of internal dependencies in the \
 given file (report RP0402 must not be disabled)'}
-                ),
-
-               )
+               ),
+              )
 
     def __init__(self, linter=None):
         BaseChecker.__init__(self, linter)
@@ -205,7 +213,7 @@
                          self.report_external_dependencies),
                         ('RP0402', 'Modules dependencies graph',
                          self.report_dependencies_graph),
-                        )
+                       )
 
     def open(self):
         """called before visiting project (i.e set of modules)"""
@@ -217,9 +225,9 @@
     def close(self):
         """called before visiting project (i.e set of modules)"""
         # don't try to compute cycles if the associated message is disabled
-        if self.linter.is_message_enabled('R0401'):
+        if self.linter.is_message_enabled('cyclic-import'):
             for cycle in get_cycles(self.import_graph):
-                self.add_message('R0401', args=' -> '.join(cycle))
+                self.add_message('cyclic-import', args=' -> '.join(cycle))
 
     def visit_import(self, node):
         """triggered when an import statement is seen"""
@@ -233,7 +241,9 @@
             self._check_deprecated_module(node, name)
             self._check_reimport(node, name)
 
-
+    # TODO This appears to be the list of all messages of the checker...
+    # @check_messages('W0410', 'W0401', 'W0403', 'W0402', 'W0404', 'W0406', 'F0401')
+    @check_messages(*(MSGS.keys()))
     def visit_from(self, node):
         """triggered when a from statement is seen"""
         basename = node.modname
@@ -242,10 +252,13 @@
             prev = node.previous_sibling()
             if prev:
                 # consecutive future statements are possible
-                if not (isinstance(prev, astng.From)
-                       and prev.modname == '__future__'):
-                    self.add_message('W0410', node=node)
+                if not (isinstance(prev, astroid.From)
+                        and prev.modname == '__future__'):
+                    self.add_message('misplaced-future', node=node)
             return
+        for name, _ in node.names:
+            if name == '*':
+                self.add_message('wildcard-import', args=basename, node=node)
         modnode = node.root()
         importedmodnode = self.get_imported_module(modnode, node, basename)
         if importedmodnode is None:
@@ -253,28 +266,26 @@
         self._check_relative_import(modnode, node, importedmodnode, basename)
         self._check_deprecated_module(node, basename)
         for name, _ in node.names:
-            if name == '*':
-                self.add_message('W0401', args=basename, node=node)
-                continue
-            self._add_imported_module(node, '%s.%s' % (importedmodnode.name, name))
-            self._check_reimport(node, name, basename, node.level)
+            if name != '*':
+                self._add_imported_module(node, '%s.%s' % (importedmodnode.name, name))
+                self._check_reimport(node, name, basename, node.level)
 
     def get_imported_module(self, modnode, importnode, modname):
         try:
             return importnode.do_import_module(modname)
-        except astng.InferenceError, ex:
+        except astroid.InferenceError, ex:
             if str(ex) != modname:
                 args = '%r (%s)' % (modname, ex)
             else:
                 args = repr(modname)
-            self.add_message("F0401", args=args, node=importnode)
+            self.add_message("import-error", args=args, node=importnode)
 
     def _check_relative_import(self, modnode, importnode, importedmodnode,
                                importedasname):
         """check relative import. node is either an Import or From node, modname
         the imported module name.
         """
-        if 'W0403' not in self.active_msgs:
+        if not self.linter.is_message_enabled('relative-import'):
             return
         if importedmodnode.file is None:
             return False # built-in module
@@ -284,52 +295,55 @@
             return False
         if importedmodnode.name != importedasname:
             # this must be a relative import...
-            self.add_message('W0403', args=(importedasname, importedmodnode.name),
+            self.add_message('relative-import', args=(importedasname, importedmodnode.name),
                              node=importnode)
 
     def _add_imported_module(self, node, importedmodname):
         """notify an imported module, used to analyze dependencies"""
+        try:
+            importedmodname = get_module_part(importedmodname)
+        except ImportError:
+            pass
         context_name = node.root().name
         if context_name == importedmodname:
             # module importing itself !
-            self.add_message('W0406', node=node)
+            self.add_message('import-self', node=node)
         elif not is_standard_module(importedmodname):
             # handle dependencies
             importedmodnames = self.stats['dependencies'].setdefault(
                 importedmodname, set())
             if not context_name in importedmodnames:
                 importedmodnames.add(context_name)
-            if is_standard_module( importedmodname, (self.package_dir(),) ):
-                # update import graph
-                mgraph = self.import_graph.setdefault(context_name, set())
-                if not importedmodname in mgraph:
-                    mgraph.add(importedmodname)
+            # update import graph
+            mgraph = self.import_graph.setdefault(context_name, set())
+            if not importedmodname in mgraph:
+                mgraph.add(importedmodname)
 
     def _check_deprecated_module(self, node, mod_path):
         """check if the module is deprecated"""
         for mod_name in self.config.deprecated_modules:
             if mod_path == mod_name or mod_path.startswith(mod_name + '.'):
-                self.add_message('W0402', node=node, args=mod_path)
+                self.add_message('deprecated-module', node=node, args=mod_path)
 
-    def _check_reimport(self, node, name, basename=None, level=0):
+    def _check_reimport(self, node, name, basename=None, level=None):
         """check if the import is necessary (i.e. not already done)"""
-        if 'W0404' not in self.active_msgs:
+        if not self.linter.is_message_enabled('reimported'):
             return
         frame = node.frame()
         root = node.root()
         contexts = [(frame, level)]
         if root is not frame:
-            contexts.append((root, 0))
+            contexts.append((root, None))
         for context, level in contexts:
             first = get_first_import(node, context, name, basename, level)
             if first is not None:
-                self.add_message('W0404', node=node,
+                self.add_message('reimported', node=node,
                                  args=(name, first.fromlineno))
 
 
     def report_external_dependencies(self, sect, _, dummy):
         """return a verbatim layout for displaying dependencies"""
-        dep_info = make_tree_defs(self._external_dependencies_info().items())
+        dep_info = make_tree_defs(self._external_dependencies_info().iteritems())
         if not dep_info:
             raise EmptyReport()
         tree_str = repr_tree_defs(dep_info)
@@ -359,8 +373,11 @@
         cache them
         """
         if self.__ext_dep_info is None:
-            self.__ext_dep_info = filter_dependencies_info(
-                self.stats['dependencies'], self.package_dir(), 'external')
+            package = self.linter.current_name
+            self.__ext_dep_info = result = {}
+            for importee, importers in self.stats['dependencies'].iteritems():
+                if not importee.startswith(package):
+                    result[importee] = importers
         return self.__ext_dep_info
 
     def _internal_dependencies_info(self):
@@ -368,8 +385,11 @@
         cache them
         """
         if self.__int_dep_info is None:
-            self.__int_dep_info = filter_dependencies_info(
-                self.stats['dependencies'], self.package_dir(), 'internal')
+            package = self.linter.current_name
+            self.__int_dep_info = result = {}
+            for importee, importers in self.stats['dependencies'].iteritems():
+                if importee.startswith(package):
+                    result[importee] = importers
         return self.__int_dep_info
 
 
diff --git a/third_party/pylint/checkers/logging.py b/third_party/pylint/checkers/logging.py
index 89899b6..d82d74b 100644
--- a/third_party/pylint/checkers/logging.py
+++ b/third_party/pylint/checkers/logging.py
@@ -10,36 +10,41 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """checker for use of Python logging
 """
 
-from logilab import astng
+import astroid
 from pylint import checkers
 from pylint import interfaces
 from pylint.checkers import utils
-
+from pylint.checkers.utils import check_messages
 
 MSGS = {
     'W1201': ('Specify string format arguments as logging function parameters',
-             'Used when a logging statement has a call form of '
-             '"logging.<logging method>(format_string % (format_args...))". '
-             'Such calls should leave string interpolation to the logging '
-             'method itself and be written '
-             '"logging.<logging method>(format_string, format_args...)" '
-             'so that the program may avoid incurring the cost of the '
-             'interpolation in those cases in which no message will be '
-             'logged. For more, see '
-             'http://www.python.org/dev/peps/pep-0282/.'),
+              'logging-not-lazy',
+              'Used when a logging statement has a call form of '
+              '"logging.<logging method>(format_string % (format_args...))". '
+              'Such calls should leave string interpolation to the logging '
+              'method itself and be written '
+              '"logging.<logging method>(format_string, format_args...)" '
+              'so that the program may avoid incurring the cost of the '
+              'interpolation in those cases in which no message will be '
+              'logged. For more, see '
+              'http://www.python.org/dev/peps/pep-0282/.'),
     'E1200': ('Unsupported logging format character %r (%#02x) at index %d',
+              'logging-unsupported-format',
               'Used when an unsupported format character is used in a logging\
               statement format string.'),
     'E1201': ('Logging format string ends in middle of conversion specifier',
+              'logging-format-truncated',
               'Used when a logging statement format string terminates before\
               the end of a conversion specifier.'),
     'E1205': ('Too many arguments for logging format string',
+              'logging-too-many-args',
               'Used when a logging format string is given too few arguments.'),
     'E1206': ('Not enough arguments for logging format string',
+              'logging-too-few-args',
               'Used when a logging format string is given too many arguments'),
     }
 
@@ -52,60 +57,101 @@
 class LoggingChecker(checkers.BaseChecker):
     """Checks use of the logging module."""
 
-    __implements__ = interfaces.IASTNGChecker
+    __implements__ = interfaces.IAstroidChecker
     name = 'logging'
     msgs = MSGS
 
+    options = (('logging-modules',
+                {'default' : ('logging',),
+                 'type' : 'csv',
+                 'metavar' : '<comma separated list>',
+                 'help' : 'Logging modules to check that the string format '
+                          'arguments are in logging function parameter format'}
+               ),
+              )
+
     def visit_module(self, unused_node):
         """Clears any state left in this checker from last module checked."""
         # The code being checked can just as easily "import logging as foo",
         # so it is necessary to process the imports and store in this field
         # what name the logging module is actually given.
-        self._logging_name = None
+        self._logging_names = set()
+        logging_mods = self.config.logging_modules
+
+        self._logging_modules = set(logging_mods)
+        self._from_imports = {}
+        for logging_mod in logging_mods:
+            parts = logging_mod.rsplit('.', 1)
+            if len(parts) > 1:
+                self._from_imports[parts[0]] = parts[1]
+
+    def visit_from(self, node):
+        """Checks to see if a module uses a non-Python logging module."""
+        try:
+            logging_name = self._from_imports[node.modname]
+            for module, as_name in node.names:
+                if module == logging_name:
+                    self._logging_names.add(as_name or module)
+        except KeyError:
+            pass
 
     def visit_import(self, node):
         """Checks to see if this module uses Python's built-in logging."""
         for module, as_name in node.names:
-            if module == 'logging':
-                if as_name:
-                    self._logging_name = as_name
-                else:
-                    self._logging_name = 'logging'
+            if module in self._logging_modules:
+                self._logging_names.add(as_name or module)
 
+    @check_messages(*(MSGS.keys()))
     def visit_callfunc(self, node):
-        """Checks calls to (simple forms of) logging methods."""
-        if (not isinstance(node.func, astng.Getattr)
-            or not isinstance(node.func.expr, astng.Name)
-            or node.func.expr.name != self._logging_name):
-            return
-        self._check_convenience_methods(node)
-        self._check_log_methods(node)
+        """Checks calls to logging methods."""
+        def is_logging_name():
+            return (isinstance(node.func, astroid.Getattr) and
+                    isinstance(node.func.expr, astroid.Name) and
+                    node.func.expr.name in self._logging_names)
 
-    def _check_convenience_methods(self, node):
-        """Checks calls to logging convenience methods (like logging.warn)."""
-        if node.func.attrname not in CHECKED_CONVENIENCE_FUNCTIONS:
-            return
-        if node.starargs or node.kwargs or not node.args:
-            # Either no args, star args, or double-star args. Beyond the
-            # scope of this checker.
-            return
-        if isinstance(node.args[0], astng.BinOp) and node.args[0].op == '%':
-            self.add_message('W1201', node=node)
-        elif isinstance(node.args[0], astng.Const):
-            self._check_format_string(node, 0)
+        def is_logger_class():
+            try:
+                for inferred in node.func.infer():
+                    if isinstance(inferred, astroid.BoundMethod):
+                        parent = inferred._proxied.parent
+                        if (isinstance(parent, astroid.Class) and
+                                (parent.qname() == 'logging.Logger' or
+                                 any(ancestor.qname() == 'logging.Logger'
+                                     for ancestor in parent.ancestors()))):
+                            return True, inferred._proxied.name
+            except astroid.exceptions.InferenceError:
+                pass
+            return False, None
 
-    def _check_log_methods(self, node):
+        if is_logging_name():
+            name = node.func.attrname
+        else:
+            result, name = is_logger_class()
+            if not result:
+                return
+        self._check_log_method(node, name)
+
+    def _check_log_method(self, node, name):
         """Checks calls to logging.log(level, format, *format_args)."""
-        if node.func.attrname != 'log':
+        if name == 'log':
+            if node.starargs or node.kwargs or len(node.args) < 2:
+                # Either a malformed call, star args, or double-star args. Beyond
+                # the scope of this checker.
+                return
+            format_pos = 1
+        elif name in CHECKED_CONVENIENCE_FUNCTIONS:
+            if node.starargs or node.kwargs or not node.args:
+                # Either no args, star args, or double-star args. Beyond the
+                # scope of this checker.
+                return
+            format_pos = 0
+        else:
             return
-        if node.starargs or node.kwargs or len(node.args) < 2:
-            # Either a malformed call, star args, or double-star args. Beyond
-            # the scope of this checker.
-            return
-        if isinstance(node.args[1], astng.BinOp) and node.args[1].op == '%':
-            self.add_message('W1201', node=node)
-        elif isinstance(node.args[1], astng.Const):
-            self._check_format_string(node, 1)
+
+        if isinstance(node.args[format_pos], astroid.BinOp) and node.args[format_pos].op == '%':
+            self.add_message('logging-not-lazy', node=node)
+        elif isinstance(node.args[format_pos], astroid.Const):
+            self._check_format_string(node, format_pos)
 
     def _check_format_string(self, node, format_arg):
         """Checks that format string tokens match the supplied arguments.
@@ -114,7 +160,7 @@
           node: AST node to be checked.
           format_arg: Index of the format string in the node arguments.
         """
-        num_args = self._count_supplied_tokens(node.args[format_arg + 1:])
+        num_args = _count_supplied_tokens(node.args[format_arg + 1:])
         if not num_args:
             # If no args were supplied, then all format strings are valid -
             # don't check any further.
@@ -132,32 +178,34 @@
                     # Keyword checking on logging strings is complicated by
                     # special keywords - out of scope.
                     return
-            except utils.UnsupportedFormatCharacter, e:
-                c = format_string[e.index]
-                self.add_message('E1200', node=node, args=(c, ord(c), e.index))
+            except utils.UnsupportedFormatCharacter, ex:
+                char = format_string[ex.index]
+                self.add_message('logging-unsupported-format', node=node,
+                                 args=(char, ord(char), ex.index))
                 return
             except utils.IncompleteFormatString:
-                self.add_message('E1201', node=node)
+                self.add_message('logging-format-truncated', node=node)
                 return
         if num_args > required_num_args:
-            self.add_message('E1205', node=node)
+            self.add_message('logging-too-many-args', node=node)
         elif num_args < required_num_args:
-            self.add_message('E1206', node=node)
+            self.add_message('logging-too-few-args', node=node)
 
-    def _count_supplied_tokens(self, args):
-        """Counts the number of tokens in an args list.
 
-        The Python log functions allow for special keyword arguments: func,
-        exc_info and extra. To handle these cases correctly, we only count
-        arguments that aren't keywords.
+def _count_supplied_tokens(args):
+    """Counts the number of tokens in an args list.
 
-        Args:
-          args: List of AST nodes that are arguments for a log format string.
+    The Python log functions allow for special keyword arguments: func,
+    exc_info and extra. To handle these cases correctly, we only count
+    arguments that aren't keywords.
 
-        Returns:
-          Number of AST nodes that aren't keywords.
-        """
-        return sum(1 for arg in args if not isinstance(arg, astng.Keyword))
+    Args:
+      args: List of AST nodes that are arguments for a log format string.
+
+    Returns:
+      Number of AST nodes that aren't keywords.
+    """
+    return sum(1 for arg in args if not isinstance(arg, astroid.Keyword))
 
 
 def register(linter):
diff --git a/third_party/pylint/checkers/misc.py b/third_party/pylint/checkers/misc.py
index 7f09d40..b27b86a 100644
--- a/third_party/pylint/checkers/misc.py
+++ b/third_party/pylint/checkers/misc.py
@@ -10,14 +10,14 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """ Copyright (c) 2000-2010 LOGILAB S.A. (Paris, FRANCE).
  http://www.logilab.fr/ -- mailto:[email protected]
 
 Check source code is ascii only or has an encoding declaration (PEP 263)
 """
 
-import re, sys
+import re
 
 from pylint.interfaces import IRawChecker
 from pylint.checkers import BaseChecker
@@ -25,13 +25,21 @@
 
 MSGS = {
     'W0511': ('%s',
+              'fixme',
               'Used when a warning note as FIXME or XXX is detected.'),
-    }
+    'W0512': ('Cannot decode using encoding "%s", unexpected byte at position %d',
+              'invalid-encoded-data',
+              'Used when a source line cannot be decoded using the specified '
+              'source file encoding.',
+              {'maxversion': (3, 0)}),
+}
+
 
 class EncodingChecker(BaseChecker):
+
     """checks for:
     * warning notes in the code like FIXME, XXX
-    * PEP 263: source code with non ascii character but no encoding declaration
+    * encoding issues.
     """
     __implements__ = IRawChecker
 
@@ -40,36 +48,55 @@
     msgs = MSGS
 
     options = (('notes',
-                {'type' : 'csv', 'metavar' : '<comma separated values>',
-                 'default' : ('FIXME', 'XXX', 'TODO'),
-                 'help' : 'List of note tags to take in consideration, \
-separated by a comma.'
-                 }),
-               )
+                {'type': 'csv', 'metavar': '<comma separated values>',
+                 'default': ('FIXME', 'XXX', 'TODO'),
+                 'help': ('List of note tags to take in consideration, '
+                          'separated by a comma.')}),)
 
-    def __init__(self, linter=None):
-        BaseChecker.__init__(self, linter)
+    def _check_note(self, notes, lineno, line):
+        # First, simply check if the notes are in the line at all. This is an
+        # optimisation to prevent using the regular expression on every line,
+        # but rather only on lines which may actually contain one of the notes.
+        # This prevents a pathological problem with lines that are hundreds
+        # of thousands of characters long.
+        for note in self.config.notes:
+            if note in line:
+                break
+        else:
+            return
 
-    def process_module(self, node):
-        """inspect the source file to found encoding problem or fixmes like
+        match = notes.search(line)
+        if not match:
+            return
+        self.add_message('fixme', args=line[match.start(1):-1], line=lineno)
+
+    def _check_encoding(self, lineno, line, file_encoding):
+        try:
+            return unicode(line, file_encoding)
+        except UnicodeDecodeError, ex:
+            self.add_message('invalid-encoded-data', line=lineno,
+                             args=(file_encoding, ex.args[2]))
+
+    def process_module(self, module):
+        """inspect the source file to find encoding problem or fixmes like
         notes
         """
-        stream = node.file_stream
-        stream.seek(0) # XXX may be removed with astng > 0.23
-        # warning notes in the code
-        notes = []
-        for note in self.config.notes:
-            notes.append(re.compile(note))
-        linenum = 1
-        for line in stream.readlines():
-            for note in notes:
-                match = note.search(line)
-                if match:
-                    self.add_message('W0511', args=line[match.start():-1],
-                                     line=linenum)
-                    break
-            linenum += 1
+        stream = module.file_stream
+        stream.seek(0)  # XXX may be removed with astroid > 0.23
+        if self.config.notes:
+            notes = re.compile(
+                r'.*?#\s*(%s)(:*\s*.+)' % "|".join(self.config.notes))
+        else:
+            notes = None
+        if module.file_encoding:
+            encoding = module.file_encoding
+        else:
+            encoding = 'ascii'
 
+        for lineno, line in enumerate(stream):
+            line = self._check_encoding(lineno + 1, line, encoding)
+            if line is not None and notes:
+                self._check_note(notes, lineno + 1, line)
 
 
 def register(linter):
diff --git a/third_party/pylint/checkers/newstyle.py b/third_party/pylint/checkers/newstyle.py
index 7bb146d..cc8f640 100644
--- a/third_party/pylint/checkers/newstyle.py
+++ b/third_party/pylint/checkers/newstyle.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2005-2006 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2005-2014 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -12,39 +12,57 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """check for new / old style related problems
 """
+import sys
 
-from logilab import astng
+import astroid
 
-from pylint.interfaces import IASTNGChecker
+from pylint.interfaces import IAstroidChecker
 from pylint.checkers import BaseChecker
 from pylint.checkers.utils import check_messages
 
 MSGS = {
     'E1001': ('Use of __slots__ on an old style class',
-              'Used when an old style class uses the __slots__ attribute.'),
+              'slots-on-old-class',
+              'Used when an old style class uses the __slots__ attribute.',
+              {'maxversion': (3, 0)}),
     'E1002': ('Use of super on an old style class',
-              'Used when an old style class uses the super builtin.'),
-    'E1003': ('Bad first argument %r given to super class',
+              'super-on-old-class',
+              'Used when an old style class uses the super builtin.',
+              {'maxversion': (3, 0)}),
+    'E1003': ('Bad first argument %r given to super()',
+              'bad-super-call',
               'Used when another argument than the current class is given as \
               first argument of the super builtin.'),
+    'E1004': ('Missing argument to super()',
+              'missing-super-argument',
+              'Used when the super builtin didn\'t receive an \
+               argument.',
+              {'maxversion': (3, 0)}),
     'W1001': ('Use of "property" on an old style class',
+              'property-on-old-class',
               'Used when PyLint detect the use of the builtin "property" \
               on an old style class while this is relying on new style \
-              classes features'),
+              classes features.',
+              {'maxversion': (3, 0)}),
+    'C1001': ('Old-style class defined.',
+              'old-style-class',
+              'Used when a class is defined that does not inherit from another'
+              'class and does not inherit explicitly from "object".',
+              {'maxversion': (3, 0)})
     }
 
 
 class NewStyleConflictChecker(BaseChecker):
     """checks for usage of new style capabilities on old style classes and
-    other new/old styles conflicts problems                                    
-    * use of property, __slots__, super                                        
-    * "super" usage                                                            
+    other new/old styles conflicts problems
+    * use of property, __slots__, super
+    * "super" usage
     """
-    
-    __implements__ = (IASTNGChecker,)
+
+    __implements__ = (IAstroidChecker,)
 
     # configuration section name
     name = 'newstyle'
@@ -54,53 +72,78 @@
     # configuration options
     options = ()
 
-    @check_messages('E1001')
+    @check_messages('slots-on-old-class', 'old-style-class')
     def visit_class(self, node):
         """check __slots__ usage
-        """        
+        """
         if '__slots__' in node and not node.newstyle:
-            self.add_message('E1001', node=node)
+            self.add_message('slots-on-old-class', node=node)
+        # The node type could be class, exception, metaclass, or
+        # interface.  Presumably, the non-class-type nodes would always
+        # have an explicit base class anyway.
+        if not node.bases and node.type == 'class':
+            self.add_message('old-style-class', node=node)
 
-    @check_messages('W1001')
+    @check_messages('property-on-old-class')
     def visit_callfunc(self, node):
         """check property usage"""
         parent = node.parent.frame()
-        if (isinstance(parent, astng.Class) and
-            not parent.newstyle and
-            isinstance(node.func, astng.Name)):
+        if (isinstance(parent, astroid.Class) and
+                not parent.newstyle and
+                isinstance(node.func, astroid.Name)):
             name = node.func.name
             if name == 'property':
-                self.add_message('W1001', node=node)
+                self.add_message('property-on-old-class', node=node)
 
-    @check_messages('E1002', 'E1003')
+    @check_messages('super-on-old-class', 'bad-super-call', 'missing-super-argument')
     def visit_function(self, node):
         """check use of super"""
         # ignore actual functions or method within a new style class
         if not node.is_method():
             return
         klass = node.parent.frame()
-        for stmt in node.nodes_of_class(astng.CallFunc):
+        for stmt in node.nodes_of_class(astroid.CallFunc):
             expr = stmt.func
-            if not isinstance(expr, astng.Getattr):
+            if not isinstance(expr, astroid.Getattr):
                 continue
             call = expr.expr
             # skip the test if using super
-            if isinstance(call, astng.CallFunc) and \
-               isinstance(call.func, astng.Name) and \
+            if isinstance(call, astroid.CallFunc) and \
+               isinstance(call.func, astroid.Name) and \
                call.func.name == 'super':
                 if not klass.newstyle:
                     # super should not be used on an old style class
-                    self.add_message('E1002', node=node)
+                    self.add_message('super-on-old-class', node=node)
                 else:
                     # super first arg should be the class
+                    if not call.args and sys.version_info[0] == 3:
+                        # unless Python 3
+                        continue
+
                     try:
                         supcls = (call.args and call.args[0].infer().next()
                                   or None)
-                    except astng.InferenceError:
+                    except astroid.InferenceError:
                         continue
+
+                    if supcls is None:
+                        self.add_message('missing-super-argument', node=call)
+                        continue
+
                     if klass is not supcls:
-                        supcls = getattr(supcls, 'name', supcls)
-                        self.add_message('E1003', node=node, args=supcls)
+                        name = None
+                        # if supcls is not YES, then supcls was infered
+                        # and use its name. Otherwise, try to look
+                        # for call.args[0].name
+                        if supcls is not astroid.YES:
+                            name = supcls.name
+                        else:
+                            if hasattr(call.args[0], 'name'):
+                                name = call.args[0].name
+                        if name is not None:
+                            self.add_message('bad-super-call',
+                                             node=call,
+                                             args=(name, ))
 
 
 def register(linter):
diff --git a/third_party/pylint/checkers/raw_metrics.py b/third_party/pylint/checkers/raw_metrics.py
index 872ca7b..71fecf6 100644
--- a/third_party/pylint/checkers/raw_metrics.py
+++ b/third_party/pylint/checkers/raw_metrics.py
@@ -1,3 +1,6 @@
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
+# http://www.logilab.fr/ -- mailto:[email protected]
+#
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
 # Foundation; either version 2 of the License, or (at your option) any later
@@ -9,7 +12,7 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """ Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
  http://www.logilab.fr/ -- mailto:[email protected]
 
@@ -24,8 +27,9 @@
 
 from logilab.common.ureports import Table
 
-from pylint.interfaces import IRawChecker
-from pylint.checkers import BaseRawChecker, EmptyReport
+from pylint.interfaces import ITokenChecker
+from pylint.utils import EmptyReport
+from pylint.checkers import BaseTokenChecker
 from pylint.reporters import diff_string
 
 def report_raw_stats(sect, stats, old_stats):
@@ -50,28 +54,28 @@
     sect.append(Table(children=lines, cols=5, rheaders=1))
 
 
-class RawMetricsChecker(BaseRawChecker):
-    """does not check anything but gives some raw metrics :                    
-    * total number of lines                                                    
-    * total number of code lines                                               
-    * total number of docstring lines                                          
-    * total number of comments lines                                           
-    * total number of empty lines                                              
+class RawMetricsChecker(BaseTokenChecker):
+    """does not check anything but gives some raw metrics :
+    * total number of lines
+    * total number of code lines
+    * total number of docstring lines
+    * total number of comments lines
+    * total number of empty lines
     """
 
-    __implements__ = (IRawChecker,)
+    __implements__ = (ITokenChecker,)
 
     # configuration section name
     name = 'metrics'
     # configuration options
-    options = ( )
+    options = ()
     # messages
     msgs = {}
     # reports
-    reports = ( ('RP0701', 'Raw metrics', report_raw_stats), )
+    reports = (('RP0701', 'Raw metrics', report_raw_stats),)
 
     def __init__(self, linter):
-        BaseRawChecker.__init__(self, linter)
+        BaseTokenChecker.__init__(self, linter)
         self.stats = None
 
     def open(self):
diff --git a/third_party/pylint/checkers/similar.py b/third_party/pylint/checkers/similar.py
index 1e38ed6..e5c9300 100644
--- a/third_party/pylint/checkers/similar.py
+++ b/third_party/pylint/checkers/similar.py
@@ -1,5 +1,5 @@
 # pylint: disable=W0622
-# Copyright (c) 2004-2006 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2004-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -13,11 +13,9 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """a similarities / code duplication command line tool and pylint checker
 """
-from __future__ import generators
-
 import sys
 from itertools import izip
 
@@ -27,23 +25,32 @@
 from pylint.checkers import BaseChecker, table_lines_from_stats
 
 
-class Similar:
+class Similar(object):
     """finds copy-pasted lines of code in a project"""
 
     def __init__(self, min_lines=4, ignore_comments=False,
-                 ignore_docstrings=False):
+                 ignore_docstrings=False, ignore_imports=False):
         self.min_lines = min_lines
         self.ignore_comments = ignore_comments
         self.ignore_docstrings = ignore_docstrings
+        self.ignore_imports = ignore_imports
         self.linesets = []
 
-    def append_stream(self, streamid, stream):
+    def append_stream(self, streamid, stream, encoding=None):
         """append a file to search for similarities"""
-        stream.seek(0) # XXX may be removed with astng > 0.23
-        self.linesets.append(LineSet(streamid,
-                                     stream.readlines(),
-                                     self.ignore_comments,
-                                     self.ignore_docstrings))
+        stream.seek(0) # XXX may be removed with astroid > 0.23
+        if encoding is None:
+            readlines = stream.readlines
+        else:
+            readlines = lambda: [line.decode(encoding) for line in stream]
+        try:
+            self.linesets.append(LineSet(streamid,
+                                         readlines(),
+                                         self.ignore_comments,
+                                         self.ignore_docstrings,
+                                         self.ignore_imports))
+        except UnicodeDecodeError:
+            pass
 
     def run(self):
         """start looking for similarities and display results on stdout"""
@@ -56,15 +63,15 @@
             duplicate = no_duplicates.setdefault(num, [])
             for couples in duplicate:
                 if (lineset1, idx1) in couples or (lineset2, idx2) in couples:
-                    couples.add( (lineset1, idx1) )
-                    couples.add( (lineset2, idx2) )
+                    couples.add((lineset1, idx1))
+                    couples.add((lineset2, idx2))
                     break
             else:
-                duplicate.append( set([(lineset1, idx1), (lineset2, idx2)]) )
+                duplicate.append(set([(lineset1, idx1), (lineset2, idx2)]))
         sims = []
         for num, ensembles in no_duplicates.iteritems():
             for couples in ensembles:
-                sims.append( (num, couples) )
+                sims.append((num, couples))
         sims.sort()
         sims.reverse()
         return sims
@@ -80,7 +87,7 @@
                 print "==%s:%s" % (lineset.name, idx)
             # pylint: disable=W0631
             for line in lineset._real_lines[idx:idx+num]:
-                print "  ", line,
+                print "  ", line.rstrip()
             nb_lignes_dupliquees += num * (len(couples)-1)
         nb_total_lignes = sum([len(lineset) for lineset in self.linesets])
         print "TOTAL lines=%s duplicates=%s percent=%.2f" \
@@ -97,10 +104,10 @@
         while index1 < len(lineset1):
             skip = 1
             num = 0
-            for index2 in find( lineset1[index1] ):
+            for index2 in find(lineset1[index1]):
                 non_blank = 0
                 for num, ((_, line1), (_, line2)) in enumerate(
-                    izip(lines1(index1), lines2(index2))):
+                        izip(lines1(index1), lines2(index2))):
                     if line1 != line2:
                         if non_blank > min_lines:
                             yield num, lineset1, index1, lineset2, index2
@@ -125,7 +132,11 @@
                 for sim in self._find_common(lineset, lineset2):
                     yield sim
 
-def stripped_lines(lines, ignore_comments, ignore_docstrings):
+def stripped_lines(lines, ignore_comments, ignore_docstrings, ignore_imports):
+    """return lines with leading/trailing whitespace and any ignored code
+    features removed
+    """
+
     strippedlines = []
     docstring = None
     for line in lines:
@@ -139,6 +150,9 @@
                 if line.endswith(docstring):
                     docstring = None
                 line = ''
+        if ignore_imports:
+            if line.startswith("import ") or line.startswith("from "):
+                line = ''
         if ignore_comments:
             # XXX should use regex in checkers/format to avoid cutting
             # at a "#" in a string
@@ -146,14 +160,16 @@
         strippedlines.append(line)
     return strippedlines
 
-class LineSet:
+
+class LineSet(object):
     """Holds and indexes all the lines of a single source file"""
     def __init__(self, name, lines, ignore_comments=False,
-                 ignore_docstrings=False):
+                 ignore_docstrings=False, ignore_imports=False):
         self.name = name
         self._real_lines = lines
         self._stripped_lines = stripped_lines(lines, ignore_comments,
-                                              ignore_docstrings)
+                                              ignore_docstrings,
+                                              ignore_imports)
         self._index = self._mk_index()
 
     def __str__(self):
@@ -194,11 +210,12 @@
         index = {}
         for line_no, line in enumerate(self._stripped_lines):
             if line:
-                index.setdefault(line, []).append( line_no )
+                index.setdefault(line, []).append(line_no)
         return index
 
 
 MSGS = {'R0801': ('Similar lines in %s files\n%s',
+                  'duplicate-code',
                   'Indicates that a set of similar lines has been detected \
                   among multiple file. This usually means that the code should \
                   be refactored to avoid this duplication.')}
@@ -232,14 +249,18 @@
                ('ignore-comments',
                 {'default' : True, 'type' : 'yn', 'metavar' : '<y or n>',
                  'help': 'Ignore comments when computing similarities.'}
-                ),
+               ),
                ('ignore-docstrings',
                 {'default' : True, 'type' : 'yn', 'metavar' : '<y or n>',
                  'help': 'Ignore docstrings when computing similarities.'}
-                ),
-               )
+               ),
+               ('ignore-imports',
+                {'default' : False, 'type' : 'yn', 'metavar' : '<y or n>',
+                 'help': 'Ignore imports when computing similarities.'}
+               ),
+              )
     # reports
-    reports = ( ('R0801', 'Duplication', report_similarities), ) # XXX actually a Refactoring message
+    reports = (('RP0801', 'Duplication', report_similarities),)
 
     def __init__(self, linter=None):
         BaseChecker.__init__(self, linter)
@@ -259,6 +280,8 @@
             self.ignore_comments = self.config.ignore_comments
         elif optname == 'ignore-docstrings':
             self.ignore_docstrings = self.config.ignore_docstrings
+        elif optname == 'ignore-imports':
+            self.ignore_imports = self.config.ignore_imports
 
     def open(self):
         """init the checkers: reset linesets and statistics information"""
@@ -273,7 +296,7 @@
 
         stream must implement the readlines method
         """
-        self.append_stream(self.linter.current_name, node.file_stream)
+        self.append_stream(self.linter.current_name, node.file_stream, node.file_encoding)
 
     def close(self):
         """compute and display similarities on closing (i.e. end of parsing)"""
@@ -303,18 +326,21 @@
     print "finds copy pasted blocks in a set of files"
     print
     print 'Usage: symilar [-d|--duplicates min_duplicated_lines] \
-[-i|--ignore-comments] file1...'
+[-i|--ignore-comments] [--ignore-docstrings] [--ignore-imports] file1...'
     sys.exit(status)
 
-def run(argv=None):
+def Run(argv=None):
     """standalone command line access point"""
     if argv is None:
         argv = sys.argv[1:]
     from getopt import getopt
     s_opts = 'hdi'
-    l_opts = ('help', 'duplicates=', 'ignore-comments')
+    l_opts = ('help', 'duplicates=', 'ignore-comments', 'ignore-imports',
+              'ignore-docstrings')
     min_lines = 4
     ignore_comments = False
+    ignore_docstrings = False
+    ignore_imports = False
     opts, args = getopt(argv, s_opts, l_opts)
     for opt, val in opts:
         if opt in ('-d', '--duplicates'):
@@ -323,12 +349,17 @@
             usage()
         elif opt in ('-i', '--ignore-comments'):
             ignore_comments = True
+        elif opt in ('--ignore-docstrings',):
+            ignore_docstrings = True
+        elif opt in ('--ignore-imports',):
+            ignore_imports = True
     if not args:
         usage(1)
-    sim = Similar(min_lines, ignore_comments)
+    sim = Similar(min_lines, ignore_comments, ignore_docstrings, ignore_imports)
     for filename in args:
         sim.append_stream(filename, open(filename))
     sim.run()
+    sys.exit(0)
 
 if __name__ == '__main__':
-    run()
+    Run()
diff --git a/third_party/pylint/checkers/stdlib.py b/third_party/pylint/checkers/stdlib.py
new file mode 100644
index 0000000..9913e99
--- /dev/null
+++ b/third_party/pylint/checkers/stdlib.py
@@ -0,0 +1,70 @@
+# Copyright 2012 Google Inc.
+#
+# http://www.logilab.fr/ -- mailto:[email protected]
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+"""Checkers for various standard library functions."""
+
+import re
+import sys
+
+import astroid
+
+from pylint.interfaces import IAstroidChecker
+from pylint.checkers import BaseChecker
+from pylint.checkers import utils
+
+_VALID_OPEN_MODE_REGEX = re.compile(r'^(r?U|[rwa]\+?b?)$')
+
+if sys.version_info >= (3, 0):
+    OPEN_MODULE = '_io'
+else:
+    OPEN_MODULE = '__builtin__'
+
+class OpenModeChecker(BaseChecker):
+    __implements__ = (IAstroidChecker,)
+    name = 'open_mode'
+
+    msgs = {
+        'W1501': ('"%s" is not a valid mode for open.',
+                  'bad-open-mode',
+                  'Python supports: r, w, a modes with b, +, and U options. '
+                  'See http://docs.python.org/2/library/functions.html#open'),
+        }
+
+    @utils.check_messages('bad-open-mode')
+    def visit_callfunc(self, node):
+        """Visit a CallFunc node."""
+        if hasattr(node, 'func'):
+            infer = utils.safe_infer(node.func)
+            if infer and infer.root().name == OPEN_MODULE:
+                if getattr(node.func, 'name', None) in ('open', 'file'):
+                    self._check_open_mode(node)
+
+    def _check_open_mode(self, node):
+        """Check that the mode argument of an open or file call is valid."""
+        try:
+            mode_arg = utils.get_argument_from_call(node, position=1, keyword='mode')
+            if mode_arg:
+                mode_arg = utils.safe_infer(mode_arg)
+                if (isinstance(mode_arg, astroid.Const)
+                        and not _VALID_OPEN_MODE_REGEX.match(mode_arg.value)):
+                    self.add_message('bad-open-mode', node=node,
+                                     args=(mode_arg.value))
+        except (utils.NoSuchArgumentError, TypeError):
+            pass
+
+def register(linter):
+    """required method to auto register this checker """
+    linter.register_checker(OpenModeChecker(linter))
+
diff --git a/third_party/pylint/checkers/string_format.py b/third_party/pylint/checkers/string_format.py
deleted file mode 100644
index c420a60..0000000
--- a/third_party/pylint/checkers/string_format.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Copyright (c) 2009-2010 Arista Networks, Inc. - James Lingard
-# Copyright (c) 2004-2010 LOGILAB S.A. (Paris, FRANCE).
-# http://www.logilab.fr/ -- mailto:[email protected]
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-
-
-"""Checker for string formatting operations.
-"""
-
-import string
-from logilab import astng
-from pylint.interfaces import IASTNGChecker
-from pylint.checkers import BaseChecker
-from pylint.checkers import utils
-
-
-MSGS = {
-    'E1300': ("Unsupported format character %r (%#02x) at index %d",
-              "Used when a unsupported format character is used in a format\
-              string."),
-    'E1301': ("Format string ends in middle of conversion specifier",
-              "Used when a format string terminates before the end of a \
-              conversion specifier."),
-    'E1302': ("Mixing named and unnamed conversion specifiers in format string",
-              "Used when a format string contains both named (e.g. '%(foo)d') \
-              and unnamed (e.g. '%d') conversion specifiers.  This is also \
-              used when a named conversion specifier contains * for the \
-              minimum field width and/or precision."),
-    'E1303': ("Expected mapping for format string, not %s",
-              "Used when a format string that uses named conversion specifiers \
-              is used with an argument that is not a mapping."),
-    'W1300': ("Format string dictionary key should be a string, not %s",
-              "Used when a format string that uses named conversion specifiers \
-              is used with a dictionary whose keys are not all strings."),
-    'W1301': ("Unused key %r in format string dictionary",
-              "Used when a format string that uses named conversion specifiers \
-              is used with a dictionary that conWtains keys not required by the \
-              format string."),
-    'E1304': ("Missing key %r in format string dictionary",
-              "Used when a format string that uses named conversion specifiers \
-              is used with a dictionary that doesn't contain all the keys \
-              required by the format string."),
-    'E1305': ("Too many arguments for format string",
-              "Used when a format string that uses unnamed conversion \
-              specifiers is given too few arguments."),
-    'E1306': ("Not enough arguments for format string",
-              "Used when a format string that uses unnamed conversion \
-              specifiers is given too many arguments"),
-    }
-
-OTHER_NODES = (astng.Const, astng.List, astng.Backquote,
-               astng.Lambda, astng.Function,
-               astng.ListComp, astng.SetComp, astng.GenExpr)
-
-class StringFormatChecker(BaseChecker):
-    """Checks string formatting operations to ensure that the format string
-    is valid and the arguments match the format string.
-    """
-
-    __implements__ = (IASTNGChecker,)
-    name = 'string_format'
-    msgs = MSGS
-
-    def visit_binop(self, node):
-        if node.op != '%':
-            return
-        left = node.left
-        args = node.right
-
-        if not (isinstance(left, astng.Const)
-            and isinstance(left.value, basestring)):
-            return
-        format_string = left.value
-        try:
-            required_keys, required_num_args = \
-                utils.parse_format_string(format_string)
-        except utils.UnsupportedFormatCharacter, e:
-            c = format_string[e.index]
-            self.add_message('E1300', node=node, args=(c, ord(c), e.index))
-            return
-        except utils.IncompleteFormatString:
-            self.add_message('E1301', node=node)
-            return
-        if required_keys and required_num_args:
-            # The format string uses both named and unnamed format
-            # specifiers.
-            self.add_message('E1302', node=node)
-        elif required_keys:
-            # The format string uses only named format specifiers.
-            # Check that the RHS of the % operator is a mapping object
-            # that contains precisely the set of keys required by the
-            # format string.
-            if isinstance(args, astng.Dict):
-                keys = set()
-                unknown_keys = False
-                for k, v in args.items:
-                    if isinstance(k, astng.Const):
-                        key = k.value
-                        if isinstance(key, basestring):
-                            keys.add(key)
-                        else:
-                            self.add_message('W1300', node=node, args=key)
-                    else:
-                        # One of the keys was something other than a
-                        # constant.  Since we can't tell what it is,
-                        # supress checks for missing keys in the
-                        # dictionary.
-                        unknown_keys = True
-                if not unknown_keys:
-                    for key in required_keys:
-                        if key not in keys:
-                            self.add_message('E1304', node=node, args=key)
-                for key in keys:
-                    if key not in required_keys:
-                        self.add_message('W1301', node=node, args=key)
-            elif isinstance(args, OTHER_NODES + (astng.Tuple,)):
-                type_name = type(args).__name__
-                self.add_message('E1303', node=node, args=type_name)
-            # else:
-                # The RHS of the format specifier is a name or
-                # expression.  It may be a mapping object, so
-                # there's nothing we can check.
-        else:
-            # The format string uses only unnamed format specifiers.
-            # Check that the number of arguments passed to the RHS of
-            # the % operator matches the number required by the format
-            # string.
-            if isinstance(args, astng.Tuple):
-                num_args = len(args.elts)
-            elif isinstance(args, OTHER_NODES + (astng.Dict, astng.DictComp)):
-                num_args = 1
-            else:
-                # The RHS of the format specifier is a name or
-                # expression.  It could be a tuple of unknown size, so
-                # there's nothing we can check.
-                num_args = None
-            if num_args is not None:
-                if num_args > required_num_args:
-                    self.add_message('E1305', node=node)
-                elif num_args < required_num_args:
-                    self.add_message('E1306', node=node)
-
-
-def register(linter):
-    """required method to auto register this checker """
-    linter.register_checker(StringFormatChecker(linter))
diff --git a/third_party/pylint/checkers/strings.py b/third_party/pylint/checkers/strings.py
new file mode 100644
index 0000000..0eda6a2
--- /dev/null
+++ b/third_party/pylint/checkers/strings.py
@@ -0,0 +1,619 @@
+# Copyright (c) 2009-2010 Arista Networks, Inc. - James Lingard
+# Copyright (c) 2004-2013 LOGILAB S.A. (Paris, FRANCE).
+# Copyright 2012 Google Inc.
+#
+# http://www.logilab.fr/ -- mailto:[email protected]
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+"""Checker for string formatting operations.
+"""
+
+import sys
+import tokenize
+import string
+try:
+    import numbers
+except ImportError:
+    numbers = None
+
+import astroid
+
+from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker
+from pylint.checkers import BaseChecker, BaseTokenChecker
+from pylint.checkers import utils
+from pylint.checkers.utils import check_messages
+
+_PY3K = sys.version_info[:2] >= (3, 0)
+_PY27 = sys.version_info[:2] == (2, 7)
+
+MSGS = {
+    'E1300': ("Unsupported format character %r (%#02x) at index %d",
+              "bad-format-character",
+              "Used when a unsupported format character is used in a format\
+              string."),
+    'E1301': ("Format string ends in middle of conversion specifier",
+              "truncated-format-string",
+              "Used when a format string terminates before the end of a \
+              conversion specifier."),
+    'E1302': ("Mixing named and unnamed conversion specifiers in format string",
+              "mixed-format-string",
+              "Used when a format string contains both named (e.g. '%(foo)d') \
+              and unnamed (e.g. '%d') conversion specifiers.  This is also \
+              used when a named conversion specifier contains * for the \
+              minimum field width and/or precision."),
+    'E1303': ("Expected mapping for format string, not %s",
+              "format-needs-mapping",
+              "Used when a format string that uses named conversion specifiers \
+              is used with an argument that is not a mapping."),
+    'W1300': ("Format string dictionary key should be a string, not %s",
+              "bad-format-string-key",
+              "Used when a format string that uses named conversion specifiers \
+              is used with a dictionary whose keys are not all strings."),
+    'W1301': ("Unused key %r in format string dictionary",
+              "unused-format-string-key",
+              "Used when a format string that uses named conversion specifiers \
+              is used with a dictionary that conWtains keys not required by the \
+              format string."),
+    'E1304': ("Missing key %r in format string dictionary",
+              "missing-format-string-key",
+              "Used when a format string that uses named conversion specifiers \
+              is used with a dictionary that doesn't contain all the keys \
+              required by the format string."),
+    'E1305': ("Too many arguments for format string",
+              "too-many-format-args",
+              "Used when a format string that uses unnamed conversion \
+              specifiers is given too many arguments."),
+    'E1306': ("Not enough arguments for format string",
+              "too-few-format-args",
+              "Used when a format string that uses unnamed conversion \
+              specifiers is given too few arguments"),
+
+    'W1302': ("Invalid format string",
+              "bad-format-string",
+              "Used when a PEP 3101 format string is invalid.",
+              {'minversion': (2, 7)}),
+    'W1303': ("Missing keyword argument %r for format string",
+              "missing-format-argument-key",
+              "Used when a PEP 3101 format string that uses named fields "
+              "doesn't receive one or more required keywords.",
+              {'minversion': (2, 7)}),
+    'W1304': ("Unused format argument %r",
+              "unused-format-string-argument",
+              "Used when a PEP 3101 format string that uses named "
+              "fields is used with an argument that "
+              "is not required by the format string.",
+              {'minversion': (2, 7)}),
+    'W1305': ("Format string contains both automatic field numbering "
+              "and manual field specification",
+              "format-combined-specification",
+              "Usen when a PEP 3101 format string contains both automatic "
+              "field numbering (e.g. '{}') and manual field "
+              "specification (e.g. '{0}').",
+              {'minversion': (2, 7)}),
+    'W1306': ("Missing format attribute %r in format specifier %r",
+              "missing-format-attribute",
+              "Used when a PEP 3101 format string uses an "
+              "attribute specifier ({0.length}), but the argument "
+              "passed for formatting doesn't have that attribute.",
+              {'minversion': (2, 7)}),
+    'W1307': ("Using invalid lookup key %r in format specifier %r",
+              "invalid-format-index",
+              "Used when a PEP 3101 format string uses a lookup specifier "
+              "({a[1]}), but the argument passed for formatting "
+              "doesn't contain or doesn't have that key as an attribute.",
+              {'minversion': (2, 7)})
+    }
+
+OTHER_NODES = (astroid.Const, astroid.List, astroid.Backquote,
+               astroid.Lambda, astroid.Function,
+               astroid.ListComp, astroid.SetComp, astroid.GenExpr)
+
+if _PY3K:
+    import _string
+
+    def split_format_field_names(format_string):
+        return _string.formatter_field_name_split(format_string)
+else:
+    def _field_iterator_convertor(iterator):
+        for is_attr, key in iterator:
+            if isinstance(key, numbers.Number):
+                yield is_attr, int(key)
+            else:
+                yield is_attr, key
+
+    def split_format_field_names(format_string):
+        keyname, fielditerator = format_string._formatter_field_name_split()
+        # it will return longs, instead of ints, which will complicate
+        # the output
+        return keyname, _field_iterator_convertor(fielditerator)
+
+
+def collect_string_fields(format_string):
+    """ Given a format string, return an iterator
+    of all the valid format fields. It handles nested fields
+    as well.
+    """
+
+    formatter = string.Formatter()
+    parseiterator = formatter.parse(format_string)
+    try:
+        for result in parseiterator:
+            if all(item is None for item in result[1:]):
+                # not a replacement format
+                continue
+            name = result[1]
+            nested = result[2]
+            yield name
+            if nested:
+                for field in collect_string_fields(nested):
+                    yield field
+    except ValueError:
+        # probably the format string is invalid
+        # should we check the argument of the ValueError?
+        raise utils.IncompleteFormatString(format_string)
+
+def parse_format_method_string(format_string):
+    """
+    Parses a PEP 3101 format string, returning a tuple of
+    (keys, num_args, manual_pos_arg),
+    where keys is the set of mapping keys in the format string, num_args
+    is the number of arguments required by the format string and
+    manual_pos_arg is the number of arguments passed with the position.
+    """
+    keys = []
+    num_args = 0
+    manual_pos_arg = set()
+    for name in collect_string_fields(format_string):
+        if name and str(name).isdigit():
+            manual_pos_arg.add(str(name))
+        elif name:
+            keyname, fielditerator = split_format_field_names(name)
+            if isinstance(keyname, numbers.Number):
+                # In Python 2 it will return long which will lead
+                # to different output between 2 and 3
+                keyname = int(keyname)
+            keys.append((keyname, list(fielditerator)))
+        else:
+            num_args += 1
+    return keys, num_args, len(manual_pos_arg)
+
+def get_args(callfunc):
+    """ Get the arguments from the given `CallFunc` node.
+    Return a tuple, where the first element is the
+    number of positional arguments and the second element
+    is the keyword arguments in a dict.
+    """
+    positional = 0
+    named = {}
+
+    for arg in callfunc.args:
+        if isinstance(arg, astroid.Keyword):
+            named[arg.arg] = utils.safe_infer(arg.value)
+        else:
+            positional += 1
+    return positional, named
+
+def get_access_path(key, parts):
+    """ Given a list of format specifiers, returns
+    the final access path (e.g. a.b.c[0][1]).
+    """
+    path = []
+    for is_attribute, specifier in parts:
+        if is_attribute:
+            path.append(".{}".format(specifier))
+        else:
+            path.append("[{!r}]".format(specifier))
+    return str(key) + "".join(path)
+
+
+class StringFormatChecker(BaseChecker):
+    """Checks string formatting operations to ensure that the format string
+    is valid and the arguments match the format string.
+    """
+
+    __implements__ = (IAstroidChecker,)
+    name = 'string'
+    msgs = MSGS
+
+    @check_messages(*(MSGS.keys()))
+    def visit_binop(self, node):
+        if node.op != '%':
+            return
+        left = node.left
+        args = node.right
+
+        if not (isinstance(left, astroid.Const)
+                and isinstance(left.value, basestring)):
+            return
+        format_string = left.value
+        try:
+            required_keys, required_num_args = \
+                utils.parse_format_string(format_string)
+        except utils.UnsupportedFormatCharacter, e:
+            c = format_string[e.index]
+            self.add_message('bad-format-character',
+                             node=node, args=(c, ord(c), e.index))
+            return
+        except utils.IncompleteFormatString:
+            self.add_message('truncated-format-string', node=node)
+            return
+        if required_keys and required_num_args:
+            # The format string uses both named and unnamed format
+            # specifiers.
+            self.add_message('mixed-format-string', node=node)
+        elif required_keys:
+            # The format string uses only named format specifiers.
+            # Check that the RHS of the % operator is a mapping object
+            # that contains precisely the set of keys required by the
+            # format string.
+            if isinstance(args, astroid.Dict):
+                keys = set()
+                unknown_keys = False
+                for k, _ in args.items:
+                    if isinstance(k, astroid.Const):
+                        key = k.value
+                        if isinstance(key, basestring):
+                            keys.add(key)
+                        else:
+                            self.add_message('bad-format-string-key',
+                                             node=node, args=key)
+                    else:
+                        # One of the keys was something other than a
+                        # constant.  Since we can't tell what it is,
+                        # supress checks for missing keys in the
+                        # dictionary.
+                        unknown_keys = True
+                if not unknown_keys:
+                    for key in required_keys:
+                        if key not in keys:
+                            self.add_message('missing-format-string-key',
+                                             node=node, args=key)
+                for key in keys:
+                    if key not in required_keys:
+                        self.add_message('unused-format-string-key',
+                                         node=node, args=key)
+            elif isinstance(args, OTHER_NODES + (astroid.Tuple,)):
+                type_name = type(args).__name__
+                self.add_message('format-needs-mapping',
+                                 node=node, args=type_name)
+            # else:
+                # The RHS of the format specifier is a name or
+                # expression.  It may be a mapping object, so
+                # there's nothing we can check.
+        else:
+            # The format string uses only unnamed format specifiers.
+            # Check that the number of arguments passed to the RHS of
+            # the % operator matches the number required by the format
+            # string.
+            if isinstance(args, astroid.Tuple):
+                num_args = len(args.elts)
+            elif isinstance(args, OTHER_NODES + (astroid.Dict, astroid.DictComp)):
+                num_args = 1
+            else:
+                # The RHS of the format specifier is a name or
+                # expression.  It could be a tuple of unknown size, so
+                # there's nothing we can check.
+                num_args = None
+            if num_args is not None:
+                if num_args > required_num_args:
+                    self.add_message('too-many-format-args', node=node)
+                elif num_args < required_num_args:
+                    self.add_message('too-few-format-args', node=node)
+
+
+class StringMethodsChecker(BaseChecker):
+    __implements__ = (IAstroidChecker,)
+    name = 'string'
+    msgs = {
+        'E1310': ("Suspicious argument in %s.%s call",
+                  "bad-str-strip-call",
+                  "The argument to a str.{l,r,}strip call contains a"
+                  " duplicate character, "),
+        }
+
+    @check_messages(*(MSGS.keys()))
+    def visit_callfunc(self, node):
+        func = utils.safe_infer(node.func)
+        if (isinstance(func, astroid.BoundMethod)
+                and isinstance(func.bound, astroid.Instance)
+                and func.bound.name in ('str', 'unicode', 'bytes')):
+            if func.name in ('strip', 'lstrip', 'rstrip') and node.args:
+                arg = utils.safe_infer(node.args[0])
+                if not isinstance(arg, astroid.Const):
+                    return
+                if len(arg.value) != len(set(arg.value)):
+                    self.add_message('bad-str-strip-call', node=node,
+                                     args=(func.bound.name, func.name))
+            elif func.name == 'format':
+                if _PY27 or _PY3K:
+                    self._check_new_format(node, func)
+
+    def _check_new_format(self, node, func):
+        """ Check the new string formatting. """
+        # TODO: skip (for now) format nodes which don't have
+        #       an explicit string on the left side of the format operation.
+        #       We do this because our inference engine can't properly handle
+        #       redefinitions of the original string.
+        #       For more details, see issue 287.
+        if not isinstance(node.func.expr, astroid.Const):
+            return
+        try:
+            strnode = func.bound.infer().next()
+        except astroid.InferenceError:
+            return
+        if not isinstance(strnode, astroid.Const):
+            return
+        if node.starargs or node.kwargs:
+            # TODO: Don't complicate the logic, skip these for now.
+            return
+        try:
+            positional, named = get_args(node)
+        except astroid.InferenceError:
+            return
+        try:
+            fields, num_args, manual_pos = parse_format_method_string(strnode.value)
+        except utils.IncompleteFormatString:
+            self.add_message('bad-format-string', node=node)
+            return
+
+        manual_fields = set(field[0] for field in fields
+                            if isinstance(field[0], numbers.Number))
+        named_fields = set(field[0] for field in fields
+                           if isinstance(field[0], basestring))
+        if num_args and manual_pos:
+            self.add_message('format-combined-specification',
+                             node=node)
+            return
+
+        check_args = False
+        # Consider "{[0]} {[1]}" as num_args.
+        num_args += sum(1 for field in named_fields
+                        if field == '')
+        if named_fields:
+            for field in named_fields:
+                if field not in named and field:
+                    self.add_message('missing-format-argument-key',
+                                     node=node,
+                                     args=(field, ))
+            for field in named:
+                if field not in named_fields:
+                    self.add_message('unused-format-string-argument',
+                                     node=node,
+                                     args=(field, ))
+            # num_args can be 0 if manual_pos is not.
+            num_args = num_args or manual_pos
+            if positional or num_args:
+                empty = any(True for field in named_fields
+                            if field == '')
+                if named or empty:
+                    # Verify the required number of positional arguments
+                    # only if the .format got at least one keyword argument.
+                    # This means that the format strings accepts both
+                    # positional and named fields and we should warn
+                    # when one of the them is missing or is extra.
+                    check_args = True
+        else:
+            check_args = True
+        if check_args:
+            # num_args can be 0 if manual_pos is not.
+            num_args = num_args or manual_pos
+            if positional > num_args:
+                # We can have two possibilities:
+                # * "{0} {1}".format(a, b)
+                # * "{} {} {}".format(a, b, c, d)
+                # We can check the manual keys for the first one.
+                if len(manual_fields) != positional:
+                    self.add_message('too-many-format-args', node=node)
+            elif positional < num_args:
+                self.add_message('too-few-format-args', node=node)
+
+        self._check_new_format_specifiers(node, fields, named)
+
+    def _check_new_format_specifiers(self, node, fields, named):
+        """
+        Check attribute and index access in the format
+        string ("{0.a}" and "{0[a]}").
+        """
+        for key, specifiers in fields:
+            # Obtain the argument. If it can't be obtained
+            # or infered, skip this check.
+            if key == '':
+                # {[0]} will have an unnamed argument, defaulting
+                # to 0. It will not be present in `named`, so use the value
+                # 0 for it.
+                key = 0
+            if isinstance(key, numbers.Number):
+                try:
+                    argname = utils.get_argument_from_call(node, key)
+                except utils.NoSuchArgumentError:
+                    continue
+            else:
+                if key not in named:
+                    continue
+                argname = named[key]
+            if argname in (astroid.YES, None):
+                continue
+            try:
+                argument = argname.infer().next()
+            except astroid.InferenceError:
+                continue
+            if not specifiers or argument is astroid.YES:
+                # No need to check this key if it doesn't
+                # use attribute / item access
+                continue
+            if argument.parent and isinstance(argument.parent, astroid.Arguments):
+                # Check to see if our argument is kwarg or vararg,
+                # and skip the check for this argument if so, because when inferring,
+                # astroid will return empty objects (dicts and tuples) and
+                # that can lead to false positives.
+                if argname.name in (argument.parent.kwarg, argument.parent.vararg):
+                    continue
+            previous = argument
+            parsed = []
+            for is_attribute, specifier in specifiers:
+                if previous is astroid.YES:
+                    break
+                parsed.append((is_attribute, specifier))
+                if is_attribute:
+                    try:
+                        previous = previous.getattr(specifier)[0]
+                    except astroid.NotFoundError:
+                        if (hasattr(previous, 'has_dynamic_getattr') and
+                                previous.has_dynamic_getattr()):
+                            # Don't warn if the object has a custom __getattr__
+                            break
+                        path = get_access_path(key, parsed)
+                        self.add_message('missing-format-attribute',
+                                         args=(specifier, path),
+                                         node=node)
+                        break
+                else:
+                    warn_error = False
+                    if hasattr(previous, 'getitem'):
+                        try:
+                            previous = previous.getitem(specifier)
+                        except (IndexError, TypeError):
+                            warn_error = True
+                    else:
+                        try:
+                            # Lookup __getitem__ in the current node,
+                            # but skip further checks, because we can't
+                            # retrieve the looked object
+                            previous.getattr('__getitem__')
+                            break
+                        except astroid.NotFoundError:
+                            warn_error = True
+                    if warn_error:
+                        path = get_access_path(key, parsed)
+                        self.add_message('invalid-format-index',
+                                         args=(specifier, path),
+                                         node=node)
+                        break
+
+                try:
+                    previous = previous.infer().next()
+                except astroid.InferenceError:
+                    # can't check further if we can't infer it
+                    break
+
+
+
+class StringConstantChecker(BaseTokenChecker):
+    """Check string literals"""
+    __implements__ = (ITokenChecker, IRawChecker)
+    name = 'string_constant'
+    msgs = {
+        'W1401': ('Anomalous backslash in string: \'%s\'. '
+                  'String constant might be missing an r prefix.',
+                  'anomalous-backslash-in-string',
+                  'Used when a backslash is in a literal string but not as an '
+                  'escape.'),
+        'W1402': ('Anomalous Unicode escape in byte string: \'%s\'. '
+                  'String constant might be missing an r or u prefix.',
+                  'anomalous-unicode-escape-in-string',
+                  'Used when an escape like \\u is encountered in a byte '
+                  'string where it has no effect.'),
+        }
+
+    # Characters that have a special meaning after a backslash in either
+    # Unicode or byte strings.
+    ESCAPE_CHARACTERS = 'abfnrtvx\n\r\t\\\'\"01234567'
+
+    # TODO(mbp): Octal characters are quite an edge case today; people may
+    # prefer a separate warning where they occur.  \0 should be allowed.
+
+    # Characters that have a special meaning after a backslash but only in
+    # Unicode strings.
+    UNICODE_ESCAPE_CHARACTERS = 'uUN'
+
+    def process_module(self, module):
+        self._unicode_literals = 'unicode_literals' in module.future_imports
+
+    def process_tokens(self, tokens):
+        for (tok_type, token, (start_row, start_col), _, _) in tokens:
+            if tok_type == tokenize.STRING:
+                # 'token' is the whole un-parsed token; we can look at the start
+                # of it to see whether it's a raw or unicode string etc.
+                self.process_string_token(token, start_row, start_col)
+
+    def process_string_token(self, token, start_row, start_col):
+        for i, c in enumerate(token):
+            if c in '\'\"':
+                quote_char = c
+                break
+        prefix = token[:i].lower() #  markers like u, b, r.
+        after_prefix = token[i:]
+        if after_prefix[:3] == after_prefix[-3:] == 3 * quote_char:
+            string_body = after_prefix[3:-3]
+        else:
+            string_body = after_prefix[1:-1]  # Chop off quotes
+        # No special checks on raw strings at the moment.
+        if 'r' not in prefix:
+            self.process_non_raw_string_token(prefix, string_body,
+                                              start_row, start_col)
+
+    def process_non_raw_string_token(self, prefix, string_body, start_row,
+                                     start_col):
+        """check for bad escapes in a non-raw string.
+
+        prefix: lowercase string of eg 'ur' string prefix markers.
+        string_body: the un-parsed body of the string, not including the quote
+        marks.
+        start_row: integer line number in the source.
+        start_col: integer column number in the source.
+        """
+        # Walk through the string; if we see a backslash then escape the next
+        # character, and skip over it.  If we see a non-escaped character,
+        # alert, and continue.
+        #
+        # Accept a backslash when it escapes a backslash, or a quote, or
+        # end-of-line, or one of the letters that introduce a special escape
+        # sequence <http://docs.python.org/reference/lexical_analysis.html>
+        #
+        # TODO(mbp): Maybe give a separate warning about the rarely-used
+        # \a \b \v \f?
+        #
+        # TODO(mbp): We could give the column of the problem character, but
+        # add_message doesn't seem to have a way to pass it through at present.
+        i = 0
+        while True:
+            i = string_body.find('\\', i)
+            if i == -1:
+                break
+            # There must be a next character; having a backslash at the end
+            # of the string would be a SyntaxError.
+            next_char = string_body[i+1]
+            match = string_body[i:i+2]
+            if next_char in self.UNICODE_ESCAPE_CHARACTERS:
+                if 'u' in prefix:
+                    pass
+                elif (_PY3K or self._unicode_literals) and 'b' not in prefix:
+                    pass  # unicode by default
+                else:
+                    self.add_message('anomalous-unicode-escape-in-string',
+                                     line=start_row, args=(match, ))
+            elif next_char not in self.ESCAPE_CHARACTERS:
+                self.add_message('anomalous-backslash-in-string',
+                                 line=start_row, args=(match, ))
+            # Whether it was a valid escape or not, backslash followed by
+            # another character can always be consumed whole: the second
+            # character can never be the start of a new backslash escape.
+            i += 2
+
+
+
+def register(linter):
+    """required method to auto register this checker """
+    linter.register_checker(StringFormatChecker(linter))
+    linter.register_checker(StringMethodsChecker(linter))
+    linter.register_checker(StringConstantChecker(linter))
diff --git a/third_party/pylint/checkers/typecheck.py b/third_party/pylint/checkers/typecheck.py
index b0a591f..fb8f3c6 100644
--- a/third_party/pylint/checkers/typecheck.py
+++ b/third_party/pylint/checkers/typecheck.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2006-2010 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2006-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -12,59 +12,128 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-"""try to find more bugs in the code using astng inference capabilities
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+"""try to find more bugs in the code using astroid inference capabilities
 """
 
 import re
 import shlex
 
-from logilab import astng
-from logilab.astng import InferenceError, NotFoundError, YES, Instance
+import astroid
+from astroid import InferenceError, NotFoundError, YES, Instance
+from astroid.bases import BUILTINS
 
-from pylint.interfaces import IASTNGChecker
+from pylint.interfaces import IAstroidChecker
 from pylint.checkers import BaseChecker
 from pylint.checkers.utils import safe_infer, is_super, check_messages
 
 MSGS = {
     'E1101': ('%s %r has no %r member',
+              'no-member',
               'Used when a variable is accessed for an unexistent member.'),
     'E1102': ('%s is not callable',
+              'not-callable',
               'Used when an object being called has been inferred to a non \
               callable object'),
     'E1103': ('%s %r has no %r member (but some types could not be inferred)',
+              'maybe-no-member',
               'Used when a variable is accessed for an unexistent member, but \
-              astng was not able to interpret all possible types of this \
+              astroid was not able to interpret all possible types of this \
               variable.'),
     'E1111': ('Assigning to function call which doesn\'t return',
+              'assignment-from-no-return',
               'Used when an assignment is done on a function call but the \
               inferred function doesn\'t return anything.'),
     'W1111': ('Assigning to function call which only returns None',
+              'assignment-from-none',
               'Used when an assignment is done on a function call but the \
               inferred function returns nothing but None.'),
 
-    'E1120': ('No value passed for parameter %s in function call',
+    'E1120': ('No value for argument %s in %s call',
+              'no-value-for-parameter',
               'Used when a function call passes too few arguments.'),
-    'E1121': ('Too many positional arguments for function call',
+    'E1121': ('Too many positional arguments for %s call',
+              'too-many-function-args',
               'Used when a function call passes too many positional \
               arguments.'),
-    'E1122': ('Duplicate keyword argument %r in function call',
+    'E1122': ('Duplicate keyword argument %r in %s call',
+              'duplicate-keyword-arg',
               'Used when a function call passes the same keyword argument \
-              multiple times.'),
-    'E1123': ('Passing unexpected keyword argument %r in function call',
+              multiple times.',
+              {'maxversion': (2, 6)}),
+    'E1123': ('Unexpected keyword argument %r in %s call',
+              'unexpected-keyword-arg',
               'Used when a function call passes a keyword argument that \
               doesn\'t correspond to one of the function\'s parameter names.'),
-    'E1124': ('Multiple values passed for parameter %r in function call',
+    'E1124': ('Argument %r passed by position and keyword in %s call',
+              'redundant-keyword-arg',
               'Used when a function call would result in assigning multiple \
               values to a function parameter, one value from a positional \
               argument and one from a keyword argument.'),
+    'E1125': ('Missing mandatory keyword argument %r in %s call',
+              'missing-kwoa',
+              ('Used when a function call does not pass a mandatory'
+               ' keyword-only argument.'),
+              {'minversion': (3, 0)}),
+    'E1126': ('Sequence index is not an int, slice, or instance with __index__',
+              'invalid-sequence-index',
+              'Used when a sequence type is indexed with an invalid type. '
+              'Valid types are ints, slices, and objects with an __index__ '
+              'method.'),
+    'E1127': ('Slice index is not an int, None, or instance with __index__',
+              'invalid-slice-index',
+              'Used when a slice index is not an integer, None, or an object \
+               with an __index__ method.'),
     }
 
+# builtin sequence types in Python 2 and 3.
+SEQUENCE_TYPES = set(['str', 'unicode', 'list', 'tuple', 'bytearray',
+                      'xrange', 'range', 'bytes', 'memoryview'])
+
+def _determine_callable(callable_obj):
+    # Ordering is important, since BoundMethod is a subclass of UnboundMethod,
+    # and Function inherits Lambda.
+    if isinstance(callable_obj, astroid.BoundMethod):
+        # Bound methods have an extra implicit 'self' argument.
+        return callable_obj, 1, callable_obj.type
+    elif isinstance(callable_obj, astroid.UnboundMethod):
+        return callable_obj, 0, 'unbound method'
+    elif isinstance(callable_obj, astroid.Function):
+        return callable_obj, 0, callable_obj.type
+    elif isinstance(callable_obj, astroid.Lambda):
+        return callable_obj, 0, 'lambda'
+    elif isinstance(callable_obj, astroid.Class):
+        # Class instantiation, lookup __new__ instead.
+        # If we only find object.__new__, we can safely check __init__
+        # instead.
+        try:
+            # Use the last definition of __new__.
+            new = callable_obj.local_attr('__new__')[-1]
+        except astroid.NotFoundError:
+            new = None
+
+        if not new or new.parent.scope().name == 'object':
+            try:
+                # Use the last definition of __init__.
+                callable_obj = callable_obj.local_attr('__init__')[-1]
+            except astroid.NotFoundError:
+                # do nothing, covered by no-init.
+                raise ValueError
+        else:
+            callable_obj = new
+
+        if not isinstance(callable_obj, astroid.Function):
+            raise ValueError
+        # both have an extra implicit 'cls'/'self' argument.
+        return callable_obj, 1, 'constructor'
+    else:
+        raise ValueError
+
 class TypeChecker(BaseChecker):
     """try to find bugs in the code using type inference
     """
 
-    __implements__ = (IASTNGChecker,)
+    __implements__ = (IAstroidChecker,)
 
     # configuration section name
     name = 'typecheck'
@@ -77,31 +146,38 @@
                  'help' : 'Tells whether missing members accessed in mixin \
 class should be ignored. A mixin class is detected if its name ends with \
 "mixin" (case insensitive).'}
-                ),
-
+               ),
+               ('ignored-modules',
+                {'default': (),
+                 'type': 'csv',
+                 'metavar': '<module names>',
+                 'help': 'List of module names for which member attributes \
+should not be checked (useful for modules/projects where namespaces are \
+manipulated during runtime and thus existing member attributes cannot be \
+deduced by static analysis'},
+               ),
                ('ignored-classes',
                 {'default' : ('SQLObject',),
                  'type' : 'csv',
                  'metavar' : '<members names>',
                  'help' : 'List of classes names for which member attributes \
 should not be checked (useful for classes with attributes dynamically set).'}
-                 ),
+               ),
 
                ('zope',
                 {'default' : False, 'type' : 'yn', 'metavar': '<y_or_n>',
                  'help' : 'When zope mode is activated, add a predefined set \
 of Zope acquired attributes to generated-members.'}
-                ),
+               ),
                ('generated-members',
-                {'default' : (
-        'REQUEST', 'acl_users', 'aq_parent'),
+                {'default' : ('REQUEST', 'acl_users', 'aq_parent'),
                  'type' : 'string',
                  'metavar' : '<members names>',
                  'help' : 'List of members which are set dynamically and \
 missed by pylint inference system, and so shouldn\'t trigger E0201 when \
 accessed. Python regular expressions are accepted.'}
-                ),
-        )
+               ),
+              )
 
     def open(self):
         # do this in open since config not fully initialized in __init__
@@ -110,13 +186,13 @@
             self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent'))
 
     def visit_assattr(self, node):
-        if isinstance(node.ass_type(), astng.AugAssign):
+        if isinstance(node.ass_type(), astroid.AugAssign):
             self.visit_getattr(node)
 
     def visit_delattr(self, node):
         self.visit_getattr(node)
 
-    @check_messages('E1101', 'E1103')
+    @check_messages('no-member', 'maybe-no-member')
     def visit_getattr(self, node):
         """check that the accessed attribute exists
 
@@ -132,6 +208,7 @@
         if isinstance(self.config.generated_members, str):
             gen = shlex.shlex(self.config.generated_members)
             gen.whitespace += ','
+            gen.wordchars += '[]-+'
             self.config.generated_members = tuple(tok.strip('"') for tok in gen)
         for pattern in self.config.generated_members:
             # attribute is marked as generated, stop here
@@ -151,7 +228,7 @@
                 inference_failure = True
                 continue
             # skip None anyway
-            if isinstance(owner, astng.Const) and owner.value is None:
+            if isinstance(owner, astroid.Const) and owner.value is None:
                 continue
             # XXX "super" / metaclass call
             if is_super(owner) or getattr(owner, 'type', None) == 'metaclass':
@@ -163,19 +240,19 @@
                 continue
             try:
                 if not [n for n in owner.getattr(node.attrname)
-                        if not isinstance(n.statement(), astng.AugAssign)]:
+                        if not isinstance(n.statement(), astroid.AugAssign)]:
                     missingattr.add((owner, name))
                     continue
             except AttributeError:
                 # XXX method / function
                 continue
             except NotFoundError:
-                if isinstance(owner, astng.Function) and owner.decorators:
-                   continue
+                if isinstance(owner, astroid.Function) and owner.decorators:
+                    continue
                 if isinstance(owner, Instance) and owner.has_dynamic_getattr():
                     continue
-                # explicit skipping of optparse'Values class
-                if owner.name == 'Values' and owner.root().name == 'optparse':
+                # explicit skipping of module member access
+                if owner.root().name in self.config.ignored_modules:
                     continue
                 missingattr.add((owner, name))
                 continue
@@ -194,55 +271,122 @@
                     continue
                 done.add(actual)
                 if inference_failure:
-                    msgid = 'E1103'
+                    msgid = 'maybe-no-member'
                 else:
-                    msgid = 'E1101'
+                    msgid = 'no-member'
                 self.add_message(msgid, node=node,
                                  args=(owner.display_type(), name,
                                        node.attrname))
 
-
+    @check_messages('assignment-from-no-return', 'assignment-from-none')
     def visit_assign(self, node):
         """check that if assigning to a function call, the function is
         possibly returning something valuable
         """
-        if not isinstance(node.value, astng.CallFunc):
+        if not isinstance(node.value, astroid.CallFunc):
             return
         function_node = safe_infer(node.value.func)
         # skip class, generator and incomplete function definition
-        if not (isinstance(function_node, astng.Function) and
+        if not (isinstance(function_node, astroid.Function) and
                 function_node.root().fully_defined()):
             return
         if function_node.is_generator() \
                or function_node.is_abstract(pass_is_abstract=False):
             return
-        returns = list(function_node.nodes_of_class(astng.Return,
-                                                    skip_klass=astng.Function))
+        returns = list(function_node.nodes_of_class(astroid.Return,
+                                                    skip_klass=astroid.Function))
         if len(returns) == 0:
-            self.add_message('E1111', node=node)
+            self.add_message('assignment-from-no-return', node=node)
         else:
             for rnode in returns:
-                if not (isinstance(rnode.value, astng.Const)
-                        and rnode.value.value is None):
+                if not (isinstance(rnode.value, astroid.Const)
+                        and rnode.value.value is None
+                        or rnode.value is None):
                     break
             else:
-                self.add_message('W1111', node=node)
+                self.add_message('assignment-from-none', node=node)
 
+    def _check_uninferable_callfunc(self, node):
+        """
+        Check that the given uninferable CallFunc node does not
+        call an actual function.
+        """
+        if not isinstance(node.func, astroid.Getattr):
+            return
+
+        # Look for properties. First, obtain
+        # the lhs of the Getattr node and search the attribute
+        # there. If that attribute is a property or a subclass of properties,
+        # then most likely it's not callable.
+
+        # TODO: since astroid doesn't understand descriptors very well
+        # we will not handle them here, right now.
+
+        expr = node.func.expr
+        klass = safe_infer(expr)
+        if (klass is None or klass is astroid.YES or
+                not isinstance(klass, astroid.Instance)):
+            return
+
+        try:
+            attrs = klass._proxied.getattr(node.func.attrname)
+        except astroid.NotFoundError:
+            return
+
+        stop_checking = False
+        for attr in attrs:
+            if attr is astroid.YES:
+                continue
+            if stop_checking:
+                break
+            if not isinstance(attr, astroid.Function):
+                continue
+
+            # Decorated, see if it is decorated with a property
+            if not attr.decorators:
+                continue
+            for decorator in attr.decorators.nodes:
+                if not isinstance(decorator, astroid.Name):
+                    continue
+                try:
+                    for infered in decorator.infer():
+                        property_like = False
+                        if isinstance(infered, astroid.Class):
+                            if (infered.root().name == BUILTINS and
+                                    infered.name == 'property'):
+                                property_like = True
+                            else:
+                                for ancestor in infered.ancestors():
+                                    if (ancestor.name == 'property' and
+                                            ancestor.root().name == BUILTINS):
+                                        property_like = True
+                                        break
+                            if property_like:
+                                self.add_message('not-callable', node=node,
+                                                 args=node.func.as_string())
+                                stop_checking = True
+                                break
+                except InferenceError:
+                    pass
+                if stop_checking:
+                    break
+
+    @check_messages(*(list(MSGS.keys())))
     def visit_callfunc(self, node):
         """check that called functions/methods are inferred to callable objects,
         and that the arguments passed to the function match the parameters in
         the inferred function's definition
         """
-
         # Build the set of keyword arguments, checking for duplicate keywords,
         # and count the positional arguments.
         keyword_args = set()
         num_positional_args = 0
         for arg in node.args:
-            if isinstance(arg, astng.Keyword):
+            if isinstance(arg, astroid.Keyword):
                 keyword = arg.arg
                 if keyword in keyword_args:
-                    self.add_message('E1122', node=node, args=keyword)
+                    self.add_message('duplicate-keyword-arg', node=node,
+                                     args=(keyword, 'function'))
                 keyword_args.add(keyword)
             else:
                 num_positional_args += 1
@@ -250,31 +394,23 @@
         called = safe_infer(node.func)
         # only function, generator and object defining __call__ are allowed
         if called is not None and not called.callable():
-            self.add_message('E1102', node=node, args=node.func.as_string())
+            self.add_message('not-callable', node=node,
+                             args=node.func.as_string())
 
-        # Note that BoundMethod is a subclass of UnboundMethod (huh?), so must
-        # come first in this 'if..else'.
-        if isinstance(called, astng.BoundMethod):
-            # Bound methods have an extra implicit 'self' argument.
-            num_positional_args += 1
-        elif isinstance(called, astng.UnboundMethod):
-            if called.decorators is not None:
-                for d in called.decorators.nodes:
-                    if isinstance(d, astng.Name) and (d.name == 'classmethod'):
-                        # Class methods have an extra implicit 'cls' argument.
-                        num_positional_args += 1
-                        break
-        elif (isinstance(called, astng.Function) or
-              isinstance(called, astng.Lambda)):
-            pass
-        else:
+        self._check_uninferable_callfunc(node)
+
+        try:
+            called, implicit_args, callable_name = _determine_callable(called)
+        except ValueError:
+            # Any error occurred during determining the function type, most of
+            # those errors are handled by different warnings.
             return
-
+        num_positional_args += implicit_args
         if called.args.args is None:
             # Built-in functions have no argument information.
             return
 
-        if len( called.argnames() ) != len( set( called.argnames() ) ):
+        if len(called.argnames()) != len(set(called.argnames())):
             # Duplicate parameter name (see E9801).  We can't really make sense
             # of the function call in this case, so just return.
             return
@@ -284,15 +420,15 @@
         parameters = []
         parameter_name_to_index = {}
         for i, arg in enumerate(called.args.args):
-            if isinstance(arg, astng.Tuple):
+            if isinstance(arg, astroid.Tuple):
                 name = None
                 # Don't store any parameter names within the tuple, since those
                 # are not assignable from keyword arguments.
             else:
-                if isinstance(arg, astng.Keyword):
+                if isinstance(arg, astroid.Keyword):
                     name = arg.arg
                 else:
-                    assert isinstance(arg, astng.AssName)
+                    assert isinstance(arg, astroid.AssName)
                     # This occurs with:
                     #    def f( (a), (b) ): pass
                     name = arg.name
@@ -303,6 +439,15 @@
                 defval = None
             parameters.append([(name, defval), False])
 
+        kwparams = {}
+        for i, arg in enumerate(called.args.kwonlyargs):
+            if isinstance(arg, astroid.Keyword):
+                name = arg.arg
+            else:
+                assert isinstance(arg, astroid.AssName)
+                name = arg.name
+            kwparams[name] = [called.args.kw_defaults[i], False]
+
         # Match the supplied arguments against the function parameters.
 
         # 1. Match the positional arguments.
@@ -315,7 +460,8 @@
                 break
             else:
                 # Too many positional arguments.
-                self.add_message('E1121', node=node)
+                self.add_message('too-many-function-args',
+                                 node=node, args=(callable_name,))
                 break
 
         # 2. Match the keyword arguments.
@@ -324,15 +470,24 @@
                 i = parameter_name_to_index[keyword]
                 if parameters[i][1]:
                     # Duplicate definition of function parameter.
-                    self.add_message('E1124', node=node, args=keyword)
+                    self.add_message('redundant-keyword-arg',
+                                     node=node, args=(keyword, callable_name))
                 else:
                     parameters[i][1] = True
+            elif keyword in kwparams:
+                if kwparams[keyword][1]:  # XXX is that even possible?
+                    # Duplicate definition of function parameter.
+                    self.add_message('redundant-keyword-arg', node=node,
+                                     args=(keyword, callable_name))
+                else:
+                    kwparams[keyword][1] = True
             elif called.args.kwarg is not None:
                 # The keyword argument gets assigned to the **kwargs parameter.
                 pass
             else:
                 # Unexpected keyword argument.
-                self.add_message('E1123', node=node, args=keyword)
+                self.add_message('unexpected-keyword-arg', node=node,
+                                 args=(keyword, callable_name))
 
         # 3. Match the *args, if any.  Note that Python actually processes
         #    *args _before_ any keyword arguments, but we wait until after
@@ -366,10 +521,133 @@
         for [(name, defval), assigned] in parameters:
             if (defval is None) and not assigned:
                 if name is None:
-                    display = '<tuple>'
+                    display_name = '<tuple>'
                 else:
                     display_name = repr(name)
-                self.add_message('E1120', node=node, args=display_name)
+                self.add_message('no-value-for-parameter', node=node,
+                                 args=(display_name, callable_name))
+
+        for name in kwparams:
+            defval, assigned = kwparams[name]
+            if defval is None and not assigned:
+                self.add_message('missing-kwoa', node=node,
+                                 args=(name, callable_name))
+
+    @check_messages('invalid-sequence-index')
+    def visit_extslice(self, node):
+        # Check extended slice objects as if they were used as a sequence
+        # index to check if the object being sliced can support them
+        return self.visit_index(node)
+
+    @check_messages('invalid-sequence-index')
+    def visit_index(self, node):
+        if not node.parent or not hasattr(node.parent, "value"):
+            return
+
+        # Look for index operations where the parent is a sequence type.
+        # If the types can be determined, only allow indices to be int,
+        # slice or instances with __index__.
+
+        parent_type = safe_infer(node.parent.value)
+
+        if not isinstance(parent_type, (astroid.Class, astroid.Instance)):
+            return
+
+        # Determine what method on the parent this index will use
+        # The parent of this node will be a Subscript, and the parent of that
+        # node determines if the Subscript is a get, set, or delete operation.
+        operation = node.parent.parent
+        if isinstance(operation, astroid.Assign):
+            methodname = '__setitem__'
+        elif isinstance(operation, astroid.Delete):
+            methodname = '__delitem__'
+        else:
+            methodname = '__getitem__'
+
+        # Check if this instance's __getitem__, __setitem__, or __delitem__, as
+        # appropriate to the statement, is implemented in a builtin sequence
+        # type. This way we catch subclasses of sequence types but skip classes
+        # that override __getitem__ and which may allow non-integer indices.
+        try:
+            methods = parent_type.getattr(methodname)
+            if methods is astroid.YES:
+                return
+            itemmethod = methods[0]
+        except (astroid.NotFoundError, IndexError):
+            return
+
+        if not isinstance(itemmethod, astroid.Function):
+            return
+
+        if itemmethod.root().name != BUILTINS:
+            return
+
+        if not itemmethod.parent:
+            return
+
+        if itemmethod.parent.name not in SEQUENCE_TYPES:
+            return
+
+        # For ExtSlice objects coming from visit_extslice, no further
+        # inference is necessary, since if we got this far the ExtSlice
+        # is an error.
+        if isinstance(node, astroid.ExtSlice):
+            index_type = node
+        else:
+            index_type = safe_infer(node)
+
+        if index_type is None or index_type is astroid.YES:
+            return
+
+        # Constants must be of type int
+        if isinstance(index_type, astroid.Const):
+            if isinstance(index_type.value, int):
+                return
+        # Instance values must be int, slice, or have an __index__ method
+        elif isinstance(index_type, astroid.Instance):
+            if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'):
+                return
+
+            try:
+                index_type.getattr('__index__')
+                return
+            except astroid.NotFoundError:
+                pass
+
+        # Anything else is an error
+        self.add_message('invalid-sequence-index', node=node)
+
+    @check_messages('invalid-slice-index')
+    def visit_slice(self, node):
+        # Check the type of each part of the slice
+        for index in (node.lower, node.upper, node.step):
+            if index is None:
+                continue
+
+            index_type = safe_infer(index)
+
+            if index_type is None or index_type is astroid.YES:
+                continue
+
+            # Constants must of type int or None
+            if isinstance(index_type, astroid.Const):
+                if isinstance(index_type.value, (int, type(None))):
+                    continue
+            # Instance values must be of type int, None or an object
+            # with __index__
+            elif isinstance(index_type, astroid.Instance):
+                if index_type.pytype() in (BUILTINS + '.int',
+                                           BUILTINS + '.NoneType'):
+                    continue
+
+                try:
+                    index_type.getattr('__index__')
+                    return
+                except astroid.NotFoundError:
+                    pass
+
+            # Anything else is an error
+            self.add_message('invalid-slice-index', node=node)
 
 def register(linter):
     """required method to auto register this checker """
diff --git a/third_party/pylint/checkers/utils.py b/third_party/pylint/checkers/utils.py
index 2883c43..d88d620 100644
--- a/third_party/pylint/checkers/utils.py
+++ b/third_party/pylint/checkers/utils.py
@@ -1,6 +1,6 @@
 # pylint: disable=W0611
 #
-# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -14,20 +14,43 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """some functions that may be useful for various checkers
 """
 
+import re
+import sys
 import string
-from logilab import astng
-from logilab.common.compat import builtins
-BUILTINS_NAME = builtins.__name__
 
-COMP_NODE_TYPES = astng.ListComp, astng.SetComp, astng.DictComp, astng.GenExpr
+import astroid
+from astroid import scoped_nodes
+from logilab.common.compat import builtins
+
+BUILTINS_NAME = builtins.__name__
+COMP_NODE_TYPES = astroid.ListComp, astroid.SetComp, astroid.DictComp, astroid.GenExpr
+PY3K = sys.version_info[0] == 3
+
+
+class NoSuchArgumentError(Exception):
+    pass
 
 def is_inside_except(node):
-    """Returns true if node is directly inside an exception handler"""
-    return isinstance(node.parent, astng.ExceptHandler)
+    """Returns true if node is inside the name of an except handler."""
+    current = node
+    while current and not isinstance(current.parent, astroid.ExceptHandler):
+        current = current.parent
+
+    return current and current is current.parent.name
+
+
+def get_all_elements(node):
+    """Recursively returns all atoms in nested lists and tuples."""
+    if isinstance(node, (astroid.Tuple, astroid.List)):
+        for child in node.elts:
+            for e in get_all_elements(child):
+                yield e
+    else:
+        yield node
 
 
 def clobber_in_except(node):
@@ -37,18 +60,18 @@
     Returns (True, args for W0623) if assignment clobbers an existing variable,
     (False, None) otherwise.
     """
-    if isinstance(node, astng.AssAttr):
-        return (True, (node.attrname, 'object %r' % (node.expr.name,)))
-    elif node is not None:
+    if isinstance(node, astroid.AssAttr):
+        return (True, (node.attrname, 'object %r' % (node.expr.as_string(),)))
+    elif isinstance(node, astroid.AssName):
         name = node.name
         if is_builtin(name):
             return (True, (name, 'builtins'))
         else:
-            scope, stmts = node.lookup(name)
-            if (stmts and 
-                not isinstance(stmts[0].ass_type(), 
-                               (astng.Assign, astng.AugAssign, astng.ExceptHandler))):
-                return (True, (name, 'outer scope (line %i)' % (stmts[0].lineno,)))
+            stmts = node.lookup(name)[1]
+            if (stmts and not isinstance(stmts[0].ass_type(),
+                                         (astroid.Assign, astroid.AugAssign,
+                                          astroid.ExceptHandler))):
+                return (True, (name, 'outer scope (line %s)' % stmts[0].fromlineno))
     return (False, None)
 
 
@@ -60,11 +83,13 @@
     try:
         inferit = node.infer()
         value = inferit.next()
-    except astng.InferenceError:
+    except astroid.InferenceError:
         return
     try:
         inferit.next()
         return # None if there is ambiguity on the inferred node
+    except astroid.InferenceError:
+        return # there is some kind of ambiguity
     except StopIteration:
         return value
 
@@ -79,24 +104,28 @@
 def is_error(node):
     """return true if the function does nothing but raising an exception"""
     for child_node in node.get_children():
-        if isinstance(child_node, astng.Raise):
+        if isinstance(child_node, astroid.Raise):
             return True
         return False
 
 def is_raising(body):
     """return true if the given statement node raise an exception"""
     for node in body:
-        if isinstance(node, astng.Raise):
+        if isinstance(node, astroid.Raise):
             return True
     return False
 
 def is_empty(body):
     """return true if the given node does nothing but 'pass'"""
-    return len(body) == 1 and isinstance(body[0], astng.Pass)
+    return len(body) == 1 and isinstance(body[0], astroid.Pass)
 
-builtins =  __builtins__.copy()
+builtins = builtins.__dict__.copy()
 SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__')
 
+def is_builtin_object(node):
+    """Returns True if the given node is an object from the __builtin__ module."""
+    return node and node.root().name == BUILTINS_NAME
+
 def is_builtin(name): # was is_native_builtin
     """return true if <name> could be considered as a builtin defined by python
     """
@@ -115,35 +144,42 @@
     _node = var_node.parent
     while _node:
         if isinstance(_node, COMP_NODE_TYPES):
-            for ass_node in _node.nodes_of_class(astng.AssName):
+            for ass_node in _node.nodes_of_class(astroid.AssName):
                 if ass_node.name == varname:
                     return True
-        elif isinstance(_node, astng.For):
-            for ass_node in _node.target.nodes_of_class(astng.AssName):
+        elif isinstance(_node, astroid.For):
+            for ass_node in _node.target.nodes_of_class(astroid.AssName):
                 if ass_node.name == varname:
                     return True
-        elif isinstance(_node, astng.With):
-            if _node.vars is None:
-                # quickfix : case in which 'with' is used without 'as'
-                return False
-            if _node.vars.name == varname:
-                return True
-        elif isinstance(_node, (astng.Lambda, astng.Function)):
+        elif isinstance(_node, astroid.With):
+            for expr, vars in _node.items:
+                if expr.parent_of(var_node):
+                    break
+                if (vars and
+                        isinstance(vars, astroid.AssName) and
+                        vars.name == varname):
+                    return True
+        elif isinstance(_node, (astroid.Lambda, astroid.Function)):
             if _node.args.is_argument(varname):
                 return True
             if getattr(_node, 'name', None) == varname:
                 return True
             break
+        elif isinstance(_node, astroid.ExceptHandler):
+            if isinstance(_node.name, astroid.AssName):
+                ass_node = _node.name
+                if ass_node.name == varname:
+                    return True
         _node = _node.parent
     # possibly multiple statements on the same line using semi colon separator
     stmt = var_node.statement()
     _node = stmt.previous_sibling()
     lineno = stmt.fromlineno
     while _node and _node.fromlineno == lineno:
-        for ass_node in _node.nodes_of_class(astng.AssName):
+        for ass_node in _node.nodes_of_class(astroid.AssName):
             if ass_node.name == varname:
                 return True
-        for imp_node in _node.nodes_of_class( (astng.From, astng.Import)):
+        for imp_node in _node.nodes_of_class((astroid.From, astroid.Import)):
             if varname in [name[1] or name[0] for name in imp_node.names]:
                 return True
         _node = _node.previous_sibling()
@@ -154,9 +190,9 @@
     value
     """
     parent = node.scope()
-    if isinstance(parent, astng.Function):
+    if isinstance(parent, astroid.Function):
         for default_node in parent.args.defaults:
-            for default_name_node in default_node.nodes_of_class(astng.Name):
+            for default_name_node in default_node.nodes_of_class(astroid.Name):
                 if default_name_node is node:
                     return True
     return False
@@ -165,15 +201,18 @@
     """return true if the name is used in function decorator"""
     parent = node.parent
     while parent is not None:
-        if isinstance(parent, astng.Decorators):
+        if isinstance(parent, astroid.Decorators):
             return True
-        if parent.is_statement or isinstance(parent, astng.Lambda):
+        if (parent.is_statement or
+                isinstance(parent, astroid.Lambda) or
+                isinstance(parent, (scoped_nodes.ComprehensionScope,
+                                    scoped_nodes.ListComp))):
             break
         parent = parent.parent
     return False
 
 def is_ancestor_name(frame, node):
-    """return True if `frame` is a astng.Class node with `node` in the
+    """return True if `frame` is a astroid.Class node with `node` in the
     subtree of its bases attribute
     """
     try:
@@ -181,23 +220,23 @@
     except AttributeError:
         return False
     for base in bases:
-        if node in base.nodes_of_class(astng.Name):
+        if node in base.nodes_of_class(astroid.Name):
             return True
     return False
 
 def assign_parent(node):
     """return the higher parent which is not an AssName, Tuple or List node
     """
-    while node and isinstance(node, (astng.AssName,
-                                     astng.Tuple,
-                                     astng.List)):
+    while node and isinstance(node, (astroid.AssName,
+                                     astroid.Tuple,
+                                     astroid.List)):
         node = node.parent
     return node
 
 def overrides_an_abstract_method(class_node, name):
     """return True if pnode is a parent of node"""
     for ancestor in class_node.ancestors():
-        if name in ancestor and isinstance(ancestor[name], astng.Function) and \
+        if name in ancestor and isinstance(ancestor[name], astroid.Function) and \
                ancestor[name].is_abstract(pass_is_abstract=False):
             return True
     return False
@@ -205,7 +244,7 @@
 def overrides_a_method(class_node, name):
     """return True if <name> is a method overridden from an ancestor"""
     for ancestor in class_node.ancestors():
-        if name in ancestor and isinstance(ancestor[name], astng.Function):
+        if name in ancestor and isinstance(ancestor[name], astroid.Function):
             return True
     return False
 
@@ -229,7 +268,7 @@
                  '__or__', '__ior__', '__ror__',
                  '__xor__', '__ixor__', '__rxor__',
                  # XXX To be continued
-                 ))
+                ))
 
 def check_messages(*messages):
     """decorator to store messages that are handled by a checker method"""
@@ -265,52 +304,118 @@
         return (i, format_string[i])
     i = 0
     while i < len(format_string):
-        c = format_string[i]
-        if c == '%':
-            i, c = next_char(i)
+        char = format_string[i]
+        if char == '%':
+            i, char = next_char(i)
             # Parse the mapping key (optional).
             key = None
-            if c == '(':
+            if char == '(':
                 depth = 1
-                i, c = next_char(i)
+                i, char = next_char(i)
                 key_start = i
                 while depth != 0:
-                    if c == '(':
+                    if char == '(':
                         depth += 1
-                    elif c == ')':
+                    elif char == ')':
                         depth -= 1
-                    i, c = next_char(i)
+                    i, char = next_char(i)
                 key_end = i - 1
                 key = format_string[key_start:key_end]
 
             # Parse the conversion flags (optional).
-            while c in '#0- +':
-                i, c = next_char(i)
+            while char in '#0- +':
+                i, char = next_char(i)
             # Parse the minimum field width (optional).
-            if c == '*':
+            if char == '*':
                 num_args += 1
-                i, c = next_char(i)
+                i, char = next_char(i)
             else:
-                while c in string.digits:
-                    i, c = next_char(i)
+                while char in string.digits:
+                    i, char = next_char(i)
             # Parse the precision (optional).
-            if c == '.':
-                i, c = next_char(i)
-                if c == '*':
+            if char == '.':
+                i, char = next_char(i)
+                if char == '*':
                     num_args += 1
-                    i, c = next_char(i)
+                    i, char = next_char(i)
                 else:
-                    while c in string.digits:
-                        i, c = next_char(i)
+                    while char in string.digits:
+                        i, char = next_char(i)
             # Parse the length modifier (optional).
-            if c in 'hlL':
-                i, c = next_char(i)
+            if char in 'hlL':
+                i, char = next_char(i)
             # Parse the conversion type (mandatory).
-            if c not in 'diouxXeEfFgGcrs%':
+            if PY3K:
+                flags = 'diouxXeEfFgGcrs%a'
+            else:
+                flags = 'diouxXeEfFgGcrs%'
+            if char not in flags:
                 raise UnsupportedFormatCharacter(i)
             if key:
                 keys.add(key)
-            elif c != '%':
+            elif char != '%':
                 num_args += 1
         i += 1
     return keys, num_args
+
+
+def is_attr_protected(attrname):
+    """return True if attribute name is protected (start with _ and some other
+    details), False otherwise.
+    """
+    return attrname[0] == '_' and not attrname == '_' and not (
+        attrname.startswith('__') and attrname.endswith('__'))
+
+def node_frame_class(node):
+    """return klass node for a method node (or a staticmethod or a
+    classmethod), return null otherwise
+    """
+    klass = node.frame()
+
+    while klass is not None and not isinstance(klass, astroid.Class):
+        if klass.parent is None:
+            klass = None
+        else:
+            klass = klass.parent.frame()
+
+    return klass
+
+def is_super_call(expr):
+    """return True if expression node is a function call and if function name
+    is super. Check before that you're in a method.
+    """
+    return (isinstance(expr, astroid.CallFunc) and
+            isinstance(expr.func, astroid.Name) and
+            expr.func.name == 'super')
+
+def is_attr_private(attrname):
+    """Check that attribute name is private (at least two leading underscores,
+    at most one trailing underscore)
+    """
+    regex = re.compile('^_{2,}.*[^_]+_?$')
+    return regex.match(attrname)
+
+def get_argument_from_call(callfunc_node, position=None, keyword=None):
+    """Returns the specified argument from a function call.
+
+    :param callfunc_node: Node representing a function call to check.
+    :param int position: position of the argument.
+    :param str keyword: the keyword of the argument.
+
+    :returns: The node representing the argument, None if the argument is not found.
+    :raises ValueError: if both position and keyword are None.
+    :raises NoSuchArgumentError: if no argument at the provided position or with
+    the provided keyword.
+    """
+    if position is None and keyword is None:
+        raise ValueError('Must specify at least one of: position or keyword.')
+    try:
+        if position is not None and not isinstance(callfunc_node.args[position], astroid.Keyword):
+            return callfunc_node.args[position]
+    except IndexError, error:
+        raise NoSuchArgumentError(error)
+    if keyword:
+        for arg in callfunc_node.args:
+            if isinstance(arg, astroid.Keyword) and arg.arg == keyword:
+                return arg.value
+    raise NoSuchArgumentError
diff --git a/third_party/pylint/checkers/variables.py b/third_party/pylint/checkers/variables.py
index 484a952..c75768b 100644
--- a/third_party/pylint/checkers/variables.py
+++ b/third_party/pylint/checkers/variables.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2003-2011 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -12,27 +12,32 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """variables checkers for Python code
 """
-
+import os
 import sys
 from copy import copy
 
-from logilab import astng
-from logilab.astng import are_exclusive, builtin_lookup, ASTNGBuildingException
+import astroid
+from astroid import are_exclusive, builtin_lookup, AstroidBuildingException
 
-from pylint.interfaces import IASTNGChecker
+from logilab.common.modutils import file_from_modpath
+
+from pylint.interfaces import IAstroidChecker
+from pylint.utils import get_global_option
 from pylint.checkers import BaseChecker
-from pylint.checkers.utils import (PYMETHODS, is_ancestor_name, is_builtin,
-     is_defined_before, is_error, is_func_default, is_func_decorator,
-     assign_parent, check_messages, is_inside_except, clobber_in_except)
+from pylint.checkers.utils import (
+    PYMETHODS, is_ancestor_name, is_builtin,
+    is_defined_before, is_error, is_func_default, is_func_decorator,
+    assign_parent, check_messages, is_inside_except, clobber_in_except,
+    get_all_elements)
 
 
 def in_for_else_branch(parent, stmt):
-  """Returns True if stmt in inside the else branch for a parent For stmt."""
-  return (isinstance(parent, astng.For) and
-          any(else_stmt.parent_of(stmt) for else_stmt in parent.orelse))
+    """Returns True if stmt in inside the else branch for a parent For stmt."""
+    return (isinstance(parent, astroid.For) and
+            any(else_stmt.parent_of(stmt) for else_stmt in parent.orelse))
 
 def overridden_method(klass, name):
     """get overridden method if any"""
@@ -44,58 +49,176 @@
         meth_node = parent[name]
     except KeyError:
         # We have found an ancestor defining <name> but it's not in the local
-        # dictionary. This may happen with astng built from living objects.
+        # dictionary. This may happen with astroid built from living objects.
         return None
-    if isinstance(meth_node, astng.Function):
+    if isinstance(meth_node, astroid.Function):
         return meth_node
     return None
 
+def _get_unpacking_extra_info(node, infered):
+    """return extra information to add to the message for unpacking-non-sequence
+    and unbalanced-tuple-unpacking errors
+    """
+    more = ''
+    infered_module = infered.root().name
+    if node.root().name == infered_module:
+        if node.lineno == infered.lineno:
+            more = ' %s' % infered.as_string()
+        elif infered.lineno:
+            more = ' defined at line %s' % infered.lineno
+    elif infered.lineno:
+        more = ' defined at line %s of %s' % (infered.lineno, infered_module)
+    return more
+
+def _detect_global_scope(node, frame, defframe):
+    """ Detect that the given frames shares a global
+    scope.
+
+    Two frames shares a global scope when neither
+    of them are hidden under a function scope, as well
+    as any of parent scope of them, until the root scope.
+    In this case, depending from something defined later on
+    will not work, because it is still undefined.
+
+    Example:
+        class A:
+            # B has the same global scope as `C`, leading to a NameError.
+            class B(C): ...
+        class C: ...
+
+    """
+    def_scope = scope = None
+    if frame and frame.parent:
+        scope = frame.parent.scope()
+    if defframe and defframe.parent:
+        def_scope = defframe.parent.scope()
+    if isinstance(frame, astroid.Function):
+        # If the parent of the current node is a
+        # function, then it can be under its scope
+        # (defined in, which doesn't concern us) or
+        # the `->` part of annotations. The same goes
+        # for annotations of function arguments, they'll have
+        # their parent the Arguments node.
+        if not isinstance(node.parent,
+                          (astroid.Function, astroid.Arguments)):
+            return False
+    elif any(not isinstance(f, (astroid.Class, astroid.Module))
+             for f in (frame, defframe)):
+        # Not interested in other frames, since they are already
+        # not in a global scope.
+        return False
+
+    break_scopes = []
+    for s in (scope, def_scope):
+        # Look for parent scopes. If there is anything different
+        # than a module or a class scope, then they frames don't
+        # share a global scope.
+        parent_scope = s
+        while parent_scope:
+            if not isinstance(parent_scope, (astroid.Class, astroid.Module)):
+                break_scopes.append(parent_scope)
+                break
+            if parent_scope.parent:
+                parent_scope = parent_scope.parent.scope()
+            else:
+                break
+    if break_scopes and len(set(break_scopes)) != 1:
+        # Store different scopes than expected.
+        # If the stored scopes are, in fact, the very same, then it means
+        # that the two frames (frame and defframe) shares the same scope,
+        # and we could apply our lineno analysis over them.
+        # For instance, this works when they are inside a function, the node
+        # that uses a definition and the definition itself.
+        return False
+    # At this point, we are certain that frame and defframe shares a scope
+    # and the definition of the first depends on the second.
+    return frame.lineno < defframe.lineno
+
 
 MSGS = {
     'E0601': ('Using variable %r before assignment',
+              'used-before-assignment',
               'Used when a local variable is accessed before it\'s \
               assignment.'),
     'E0602': ('Undefined variable %r',
+              'undefined-variable',
               'Used when an undefined variable is accessed.'),
+    'E0603': ('Undefined variable name %r in __all__',
+              'undefined-all-variable',
+              'Used when an undefined variable name is referenced in __all__.'),
+    'E0604': ('Invalid object %r in __all__, must contain only strings',
+              'invalid-all-object',
+              'Used when an invalid (non-string) object occurs in __all__.'),
     'E0611': ('No name %r in module %r',
+              'no-name-in-module',
               'Used when a name cannot be found in a module.'),
 
     'W0601': ('Global variable %r undefined at the module level',
+              'global-variable-undefined',
               'Used when a variable is defined through the "global" statement \
               but the variable is not defined in the module scope.'),
     'W0602': ('Using global for %r but no assignment is done',
+              'global-variable-not-assigned',
               'Used when a variable is defined through the "global" statement \
               but no assignment to this variable is done.'),
     'W0603': ('Using the global statement', # W0121
+              'global-statement',
               'Used when you use the "global" statement to update a global \
               variable. PyLint just try to discourage this \
               usage. That doesn\'t mean you can not use it !'),
     'W0604': ('Using the global statement at the module level', # W0103
+              'global-at-module-level',
               'Used when you use the "global" statement at the module level \
               since it has no effect'),
     'W0611': ('Unused import %s',
+              'unused-import',
               'Used when an imported module or variable is not used.'),
     'W0612': ('Unused variable %r',
+              'unused-variable',
               'Used when a variable is defined but not used.'),
     'W0613': ('Unused argument %r',
+              'unused-argument',
               'Used when a function or method argument is not used.'),
     'W0614': ('Unused import %s from wildcard import',
+              'unused-wildcard-import',
               'Used when an imported module or variable is not used from a \
               \'from X import *\' style import.'),
 
     'W0621': ('Redefining name %r from outer scope (line %s)',
+              'redefined-outer-name',
               'Used when a variable\'s name hide a name defined in the outer \
               scope.'),
     'W0622': ('Redefining built-in %r',
+              'redefined-builtin',
               'Used when a variable or function override a built-in.'),
     'W0623': ('Redefining name %r from %s in exception handler',
+              'redefine-in-handler',
               'Used when an exception handler assigns the exception \
                to an existing name'),
 
     'W0631': ('Using possibly undefined loop variable %r',
+              'undefined-loop-variable',
               'Used when an loop variable (i.e. defined by a for loop or \
               a list comprehension or a generator expression) is used outside \
               the loop.'),
+
+    'W0632': ('Possible unbalanced tuple unpacking with '
+              'sequence%s: '
+              'left side has %d label(s), right side has %d value(s)',
+              'unbalanced-tuple-unpacking',
+              'Used when there is an unbalanced tuple unpacking in assignment'),
+
+    'W0633': ('Attempting to unpack a non-sequence%s',
+              'unpacking-non-sequence',
+              'Used when something which is not '
+              'a sequence is used in an unpack assignment'),
+
+    'W0640': ('Cell variable %s defined in loop',
+              'cell-var-from-loop',
+              'A variable used in a closure is defined in a loop. '
+              'This will result in all closures using the same value for '
+              'the closed-over variable.'),
+
     }
 
 class VariablesChecker(BaseChecker):
@@ -104,67 +227,107 @@
     * undefined variables
     * redefinition of variable from builtins or from an outer scope
     * use of variable before assignment
+    * __all__ consistency
     """
 
-    __implements__ = IASTNGChecker
+    __implements__ = IAstroidChecker
 
     name = 'variables'
     msgs = MSGS
     priority = -1
-    options = (
-               ("init-import",
+    options = (("init-import",
                 {'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
                  'help' : 'Tells whether we should check for unused import in \
 __init__ files.'}),
                ("dummy-variables-rgx",
-                {'default': ('_|dummy'),
+                {'default': ('_$|dummy'),
                  'type' :'regexp', 'metavar' : '<regexp>',
-                 'help' : 'A regular expression matching the beginning of \
-                  the name of dummy variables (i.e. not used).'}),
+                 'help' : 'A regular expression matching the name of dummy \
+variables (i.e. expectedly not used).'}),
                ("additional-builtins",
                 {'default': (), 'type' : 'csv',
                  'metavar' : '<comma separated list>',
                  'help' : 'List of additional names supposed to be defined in \
 builtins. Remember that you should avoid to define new builtins when possible.'
-                 }),
-               )
+                }),
+              )
     def __init__(self, linter=None):
         BaseChecker.__init__(self, linter)
         self._to_consume = None
         self._checking_mod_attr = None
-        self._vars = None
 
     def visit_module(self, node):
         """visit module : update consumption analysis variable
         checks globals doesn't overrides builtins
         """
         self._to_consume = [(copy(node.locals), {}, 'module')]
-        self._vars = []
-        for name, stmts in node.locals.items():
+        for name, stmts in node.locals.iteritems():
             if is_builtin(name) and not is_inside_except(stmts[0]):
                 # do not print Redefining builtin for additional builtins
-                self.add_message('W0622', args=name, node=stmts[0])
+                self.add_message('redefined-builtin', args=name, node=stmts[0])
 
-    @check_messages('W0611', 'W0614')
+    @check_messages('unused-import', 'unused-wildcard-import', 'redefined-builtin', 'undefined-all-variable', 'invalid-all-object')
     def leave_module(self, node):
         """leave module: check globals
         """
         assert len(self._to_consume) == 1
         not_consumed = self._to_consume.pop()[0]
+        # attempt to check for __all__ if defined
+        if '__all__' in node.locals:
+            assigned = node.igetattr('__all__').next()
+            if assigned is not astroid.YES:
+                for elt in getattr(assigned, 'elts', ()):
+                    try:
+                        elt_name = elt.infer().next()
+                    except astroid.InferenceError:
+                        continue
+
+                    if not isinstance(elt_name, astroid.Const) \
+                             or not isinstance(elt_name.value, basestring):
+                        self.add_message('invalid-all-object', args=elt.as_string(), node=elt)
+                        continue
+                    elt_name = elt_name.value
+                    # If elt is in not_consumed, remove it from not_consumed
+                    if elt_name in not_consumed:
+                        del not_consumed[elt_name]
+                        continue
+                    if elt_name not in node.locals:
+                        if not node.package:
+                            self.add_message('undefined-all-variable',
+                                             args=elt_name,
+                                             node=elt)
+                        else:
+                            basename = os.path.splitext(node.file)[0]
+                            if os.path.basename(basename) == '__init__':
+                                name = node.name + "." + elt_name
+                                try:
+                                    file_from_modpath(name.split("."))
+                                except ImportError:
+                                    self.add_message('undefined-all-variable',
+                                                     args=elt_name,
+                                                     node=elt)
+                                except SyntaxError:
+                                    # don't yield an syntax-error warning,
+                                    # because it will be later yielded
+                                    # when the file will be checked
+                                    pass
         # don't check unused imports in __init__ files
         if not self.config.init_import and node.package:
             return
-        for name, stmts in not_consumed.items():
+        for name, stmts in not_consumed.iteritems():
+            if any(isinstance(stmt, astroid.AssName)
+                   and isinstance(stmt.ass_type(), astroid.AugAssign)
+                   for stmt in stmts):
+                continue
             stmt = stmts[0]
-            if isinstance(stmt, astng.Import):
-                self.add_message('W0611', args=name, node=stmt)
-            elif isinstance(stmt, astng.From) and stmt.modname != '__future__':
+            if isinstance(stmt, astroid.Import):
+                self.add_message('unused-import', args=name, node=stmt)
+            elif isinstance(stmt, astroid.From) and stmt.modname != '__future__':
                 if stmt.names[0][0] == '*':
-                    self.add_message('W0614', args=name, node=stmt)
+                    self.add_message('unused-wildcard-import', args=name, node=stmt)
                 else:
-                    self.add_message('W0611', args=name, node=stmt)
+                    self.add_message('unused-import', args=name, node=stmt)
         del self._to_consume
-        del self._vars
 
     def visit_class(self, node):
         """visit class: update consumption analysis variable
@@ -225,25 +388,27 @@
         """visit function: update consumption analysis variable and check locals
         """
         self._to_consume.append((copy(node.locals), {}, 'function'))
-        self._vars.append({})
-        if not set(('W0621', 'W0622')) & self.active_msgs:
+        if not (self.linter.is_message_enabled('redefined-outer-name') or
+                self.linter.is_message_enabled('redefined-builtin')):
             return
         globs = node.root().globals
         for name, stmt in node.items():
             if is_inside_except(stmt):
                 continue
-            if name in globs and not isinstance(stmt, astng.Global):
-                line = globs[name][0].lineno
-                self.add_message('W0621', args=(name, line), node=stmt)
+            if name in globs and not isinstance(stmt, astroid.Global):
+                line = globs[name][0].fromlineno
+                dummy_rgx = self.config.dummy_variables_rgx
+                if not dummy_rgx.match(name):
+                    self.add_message('redefined-outer-name', args=(name, line), node=stmt)
             elif is_builtin(name):
                 # do not print Redefining builtin for additional builtins
-                self.add_message('W0622', args=name, node=stmt)
+                self.add_message('redefined-builtin', args=name, node=stmt)
 
     def leave_function(self, node):
         """leave function: check function's locals are consumed"""
         not_consumed = self._to_consume.pop()[0]
-        self._vars.pop(0)
-        if not set(('W0612', 'W0613')) & self.active_msgs:
+        if not (self.linter.is_message_enabled('unused-variable') or
+                self.linter.is_message_enabled('unused-argument')):
             return
         # don't check arguments of function which are only raising an exception
         if is_error(node):
@@ -256,6 +421,13 @@
         authorized_rgx = self.config.dummy_variables_rgx
         called_overridden = False
         argnames = node.argnames()
+        global_names = set()
+        nonlocal_names = set()
+        for global_stmt in node.nodes_of_class(astroid.Global):
+            global_names.update(set(global_stmt.names))
+        for nonlocal_stmt in node.nodes_of_class(astroid.Nonlocal):
+            nonlocal_names.update(set(nonlocal_stmt.names))
+
         for name, stmts in not_consumed.iteritems():
             # ignore some special names specified by user configuration
             if authorized_rgx.match(name):
@@ -263,8 +435,25 @@
             # ignore names imported by the global statement
             # FIXME: should only ignore them if it's assigned latter
             stmt = stmts[0]
-            if isinstance(stmt, astng.Global):
+            if isinstance(stmt, astroid.Global):
                 continue
+            if isinstance(stmt, (astroid.Import, astroid.From)):
+                # Detect imports, assigned to global statements.
+                if global_names:
+                    skip = False
+                    for import_name, import_alias in stmt.names:
+                        # If the import uses an alias, check only that.
+                        # Otherwise, check only the import name.
+                        if import_alias:
+                            if import_alias in global_names:
+                                skip = True
+                                break
+                        elif import_name in global_names:
+                            skip = True
+                            break
+                    if skip:
+                        continue
+
             # care about functions with unknown argument (builtins)
             if name in argnames:
                 if is_method:
@@ -282,23 +471,27 @@
                 # don't check callback arguments XXX should be configurable
                 if node.name.startswith('cb_') or node.name.endswith('_cb'):
                     continue
-                self.add_message('W0613', args=name, node=stmt)
+                self.add_message('unused-argument', args=name, node=stmt)
             else:
-                self.add_message('W0612', args=name, node=stmt)
+                if stmt.parent and isinstance(stmt.parent, astroid.Assign):
+                    if name in nonlocal_names:
+                        continue
+                self.add_message('unused-variable', args=name, node=stmt)
 
-    @check_messages('W0601', 'W0602', 'W0603', 'W0604', 'W0622')
+    @check_messages('global-variable-undefined', 'global-variable-not-assigned', 'global-statement',
+                    'global-at-module-level', 'redefined-builtin')
     def visit_global(self, node):
         """check names imported exists in the global scope"""
         frame = node.frame()
-        if isinstance(frame, astng.Module):
-            self.add_message('W0604', node=node)
+        if isinstance(frame, astroid.Module):
+            self.add_message('global-at-module-level', node=node)
             return
         module = frame.root()
         default_message = True
         for name in node.names:
             try:
                 assign_nodes = module.getattr(name)
-            except astng.NotFoundError:
+            except astroid.NotFoundError:
                 # unassigned global, skip
                 assign_nodes = []
             for anode in assign_nodes:
@@ -311,23 +504,69 @@
                     break
             else:
                 # global but no assignment
-                self.add_message('W0602', args=name, node=node)
+                # Detect imports in the current frame, with the required
+                # name. Such imports can be considered assignments.
+                imports = frame.nodes_of_class((astroid.Import, astroid.From))
+                for import_node in imports:
+                    found = False
+                    for import_name, import_alias in import_node.names:
+                        # If the import uses an alias, check only that.
+                        # Otherwise, check only the import name.
+                        if import_alias:
+                            if import_alias == name:
+                                found = True
+                                break
+                        elif import_name and import_name == name:
+                            found = True
+                            break
+                    if found:
+                        break
+                else:
+                    self.add_message('global-variable-not-assigned',
+                                     args=name, node=node)
                 default_message = False
             if not assign_nodes:
                 continue
             for anode in assign_nodes:
                 if anode.parent is None:
-                    self.add_message('W0622', args=name, node=node)
+                    self.add_message('redefined-builtin', args=name, node=node)
                     break
                 if anode.frame() is module:
                     # module level assignment
                     break
             else:
                 # global undefined at the module scope
-                self.add_message('W0601', args=name, node=node)
+                self.add_message('global-variable-undefined', args=name, node=node)
                 default_message = False
         if default_message:
-            self.add_message('W0603', node=node)
+            self.add_message('global-statement', node=node)
+
+    def _check_late_binding_closure(self, node, assignment_node, scope_type):
+        def _is_direct_lambda_call():
+            return (isinstance(node_scope.parent, astroid.CallFunc)
+                    and node_scope.parent.func is node_scope)
+
+        node_scope = node.scope()
+        if not isinstance(node_scope, (astroid.Lambda, astroid.Function)):
+            return
+        if isinstance(node.parent, astroid.Arguments):
+            return
+
+        if isinstance(assignment_node, astroid.Comprehension):
+            if assignment_node.parent.parent_of(node.scope()):
+                self.add_message('cell-var-from-loop', node=node, args=node.name)
+        else:
+            assign_scope = assignment_node.scope()
+            maybe_for = assignment_node
+            while not isinstance(maybe_for, astroid.For):
+                if maybe_for is assign_scope:
+                    break
+                maybe_for = maybe_for.parent
+            else:
+                if (maybe_for.parent_of(node_scope)
+                        and not _is_direct_lambda_call()
+                        and not isinstance(node_scope.statement(), astroid.Return)):
+                    self.add_message('cell-var-from-loop', node=node, args=node.name)
 
     def _loopvar_name(self, node, name):
         # filter variables according to node's scope
@@ -337,7 +576,7 @@
         #astmts = [stmt for stmt in node.lookup(name)[1]
         #          if hasattr(stmt, 'ass_type')] and
         #          not stmt.statement().parent_of(node)]
-        if 'W0631' not in self.active_msgs:
+        if not self.linter.is_message_enabled('undefined-loop-variable'):
             return
         astmts = [stmt for stmt in node.lookup(name)[1]
                   if hasattr(stmt, 'ass_type')]
@@ -355,35 +594,38 @@
             _astmts = astmts[:1]
         for i, stmt in enumerate(astmts[1:]):
             if (astmts[i].statement().parent_of(stmt)
-                and not in_for_else_branch(astmts[i].statement(), stmt)):
+                    and not in_for_else_branch(astmts[i].statement(), stmt)):
                 continue
             _astmts.append(stmt)
         astmts = _astmts
         if len(astmts) == 1:
             ass = astmts[0].ass_type()
-            if isinstance(ass, (astng.For, astng.Comprehension, astng.GenExpr)) \
+            if isinstance(ass, (astroid.For, astroid.Comprehension, astroid.GenExpr)) \
                    and not ass.statement() is node.statement():
-                self.add_message('W0631', args=name, node=node)
+                self.add_message('undefined-loop-variable', args=name, node=node)
 
+    @check_messages('redefine-in-handler')
     def visit_excepthandler(self, node):
-        clobbering, args = clobber_in_except(node.name)
-        if clobbering:
-            self.add_message('W0623', args=args, node=node)
+        for name in get_all_elements(node.name):
+            clobbering, args = clobber_in_except(name)
+            if clobbering:
+                self.add_message('redefine-in-handler', args=args, node=name)
 
     def visit_assname(self, node):
-        if isinstance(node.ass_type(), astng.AugAssign):
+        if isinstance(node.ass_type(), astroid.AugAssign):
             self.visit_name(node)
 
     def visit_delname(self, node):
         self.visit_name(node)
 
+    @check_messages(*(MSGS.keys()))
     def visit_name(self, node):
         """check that a name is defined if the current scope and doesn't
         redefine a built-in
         """
         stmt = node.statement()
         if stmt.fromlineno is None:
-            # name node from a astng built from live code, skip
+            # name node from a astroid built from live code, skip
             assert not stmt.root().file.endswith('.py')
             return
         name = node.name
@@ -392,7 +634,7 @@
         # a decorator, then start from the parent frame of the function instead
         # of the function frame - and thus open an inner class scope
         if (is_func_default(node) or is_func_decorator(node)
-            or is_ancestor_name(frame, node)):
+                or is_ancestor_name(frame, node)):
             start_index = len(self._to_consume) - 2
         else:
             start_index = len(self._to_consume) - 1
@@ -406,12 +648,21 @@
             # names. The only exception is when the starting scope is a
             # comprehension and its direct outer scope is a class
             if scope_type == 'class' and i != start_index and not (
-                base_scope_type == 'comprehension' and i == start_index-1):
-                # XXX find a way to handle class scope in a smoother way
-                continue
+                    base_scope_type == 'comprehension' and i == start_index-1):
+                # Detect if we are in a local class scope, as an assignment.
+                # For example, the following is fair game.
+                # class A:
+                #    b = 1
+                #    c = lambda b=b: b * b
+                class_assignment = (isinstance(frame, astroid.Class) and
+                                    name in frame.locals)
+                if not class_assignment:
+                    continue
             # the name has already been consumed, only check it's not a loop
             # variable used outside the loop
             if name in consumed:
+                defnode = assign_parent(consumed[name][0])
+                self._check_late_binding_closure(node, defnode, scope_type)
                 self._loopvar_name(node, name)
                 break
             # mark the name as consumed if it's defined in this scope
@@ -423,11 +674,12 @@
             # checks for use before assignment
             defnode = assign_parent(to_consume[name][0])
             if defnode is not None:
+                self._check_late_binding_closure(node, defnode, scope_type)
                 defstmt = defnode.statement()
                 defframe = defstmt.frame()
                 maybee0601 = True
                 if not frame is defframe:
-                    maybee0601 = False
+                    maybee0601 = _detect_global_scope(node, frame, defframe)
                 elif defframe.parent is None:
                     # we are at the module level, check the name is not
                     # defined in builtins
@@ -438,49 +690,82 @@
                     # defined in global or builtin scope
                     if defframe.root().lookup(name)[1]:
                         maybee0601 = False
+                    else:
+                        # check if we have a nonlocal
+                        if name in defframe.locals:
+                            maybee0601 = not any(isinstance(child, astroid.Nonlocal)
+                                                 and name in child.names
+                                                 for child in defframe.get_children())
+                if (self._to_consume[-1][-1] == 'lambda' and
+                        isinstance(frame, astroid.Class)
+                        and name in frame.locals):
+                    maybee0601 = True
+                else:
+                    maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno
+
                 if (maybee0601
-                    and stmt.fromlineno <= defstmt.fromlineno
-                    and not is_defined_before(node)
-                    and not are_exclusive(stmt, defstmt, ('NameError', 'Exception', 'BaseException'))):
-                    if defstmt is stmt and isinstance(node, (astng.DelName,
-                                                             astng.AssName)):
-                        self.add_message('E0602', args=name, node=node)
+                        and not is_defined_before(node)
+                        and not are_exclusive(stmt, defstmt, ('NameError',
+                                                              'Exception',
+                                                              'BaseException'))):
+                    if defstmt is stmt and isinstance(node, (astroid.DelName,
+                                                             astroid.AssName)):
+                        self.add_message('undefined-variable', args=name, node=node)
                     elif self._to_consume[-1][-1] != 'lambda':
-                        # E0601 may *not* occurs in lambda scope
-                        self.add_message('E0601', args=name, node=node)
-            if not isinstance(node, astng.AssName): # Aug AssName
-                del to_consume[name]
-            else:
+                        # E0601 may *not* occurs in lambda scope.
+                        self.add_message('used-before-assignment', args=name, node=node)
+                    elif self._to_consume[-1][-1] == 'lambda':
+                        # E0601 can occur in class-level scope in lambdas, as in
+                        # the following example:
+                        #   class A:
+                        #      x = lambda attr: f + attr
+                        #      f = 42
+                        if isinstance(frame, astroid.Class) and name in frame.locals:
+                            if isinstance(node.parent, astroid.Arguments):
+                                # Doing the following is fine:
+                                #   class A:
+                                #      x = 42
+                                #      y = lambda attr=x: attr
+                                if stmt.fromlineno <= defstmt.fromlineno:
+                                    self.add_message('used-before-assignment',
+                                                     args=name, node=node)
+                            else:
+                                self.add_message('undefined-variable',
+                                                 args=name, node=node)
+
+            if isinstance(node, astroid.AssName): # Aug AssName
                 del consumed[name]
+            else:
+                del to_consume[name]
             # check it's not a loop variable used outside the loop
             self._loopvar_name(node, name)
             break
         else:
             # we have not found the name, if it isn't a builtin, that's an
             # undefined name !
-            if not (name in astng.Module.scope_attrs or is_builtin(name)
+            if not (name in astroid.Module.scope_attrs or is_builtin(name)
                     or name in self.config.additional_builtins):
-                self.add_message('E0602', args=name, node=node)
+                self.add_message('undefined-variable', args=name, node=node)
 
-    @check_messages('E0611')
+    @check_messages('no-name-in-module')
     def visit_import(self, node):
         """check modules attribute accesses"""
         for name, _ in node.names:
             parts = name.split('.')
             try:
                 module = node.infer_name_module(parts[0]).next()
-            except astng.ResolveError:
+            except astroid.ResolveError:
                 continue
             self._check_module_attrs(node, module, parts[1:])
 
-    @check_messages('E0611')
+    @check_messages('no-name-in-module')
     def visit_from(self, node):
         """check modules attribute accesses"""
         name_parts = node.modname.split('.')
         level = getattr(node, 'level', None)
         try:
             module = node.root().import_module(name_parts[0], level=level)
-        except ASTNGBuildingException:
+        except AstroidBuildingException:
             return
         except Exception, exc:
             print 'Unhandled exception in VariablesChecker:', exc
@@ -493,12 +778,63 @@
                 continue
             self._check_module_attrs(node, module, name.split('.'))
 
+    @check_messages('unbalanced-tuple-unpacking', 'unpacking-non-sequence')
+    def visit_assign(self, node):
+        """Check unbalanced tuple unpacking for assignments
+        and unpacking non-sequences.
+        """
+        if not isinstance(node.targets[0], (astroid.Tuple, astroid.List)):
+            return
+
+        targets = node.targets[0].itered()
+        try:
+            for infered in node.value.infer():
+                self._check_unpacking(infered, node, targets)
+        except astroid.InferenceError:
+            return
+
+    def _check_unpacking(self, infered, node, targets):
+        """ Check for unbalanced tuple unpacking
+        and unpacking non sequences.
+        """
+        if infered is astroid.YES:
+            return
+        if isinstance(infered, (astroid.Tuple, astroid.List)):
+            # attempt to check unpacking is properly balanced
+            values = infered.itered()
+            if len(targets) != len(values):
+                # Check if we have starred nodes.
+                if any(isinstance(target, astroid.Starred)
+                       for target in targets):
+                    return
+                self.add_message('unbalanced-tuple-unpacking', node=node,
+                                 args=(_get_unpacking_extra_info(node, infered),
+                                       len(targets),
+                                       len(values)))
+        # attempt to check unpacking may be possible (ie RHS is iterable)
+        elif isinstance(infered, astroid.Instance):
+            for meth in ('__iter__', '__getitem__'):
+                try:
+                    infered.getattr(meth)
+                    break
+                except astroid.NotFoundError:
+                    continue
+            else:
+                self.add_message('unpacking-non-sequence', node=node,
+                                 args=(_get_unpacking_extra_info(node, infered),))
+        else:
+            self.add_message('unpacking-non-sequence', node=node,
+                             args=(_get_unpacking_extra_info(node, infered),))
+
+
     def _check_module_attrs(self, node, module, module_names):
         """check that module_names (list of string) are accessible through the
         given module
         if the latest access name corresponds to a module, return it
         """
-        assert isinstance(module, astng.Module), module
+        assert isinstance(module, astroid.Module), module
+        ignored_modules = get_global_option(self, 'ignored-modules',
+                                            default=[])
         while module_names:
             name = module_names.pop(0)
             if name == '__dict__':
@@ -506,21 +842,24 @@
                 break
             try:
                 module = module.getattr(name)[0].infer().next()
-                if module is astng.YES:
+                if module is astroid.YES:
                     return None
-            except astng.NotFoundError:
-                self.add_message('E0611', args=(name, module.name), node=node)
+            except astroid.NotFoundError:
+                if module.name in ignored_modules:
+                    return None
+                self.add_message('no-name-in-module',
+                                 args=(name, module.name), node=node)
                 return None
-            except astng.InferenceError:
+            except astroid.InferenceError:
                 return None
         if module_names:
             # FIXME: other message if name is not the latest part of
             # module_names ?
             modname = module and module.name or '__dict__'
-            self.add_message('E0611', node=node,
+            self.add_message('no-name-in-module', node=node,
                              args=('.'.join(module_names), modname))
             return None
-        if isinstance(module, astng.Module):
+        if isinstance(module, astroid.Module):
             return module
         return None
 
@@ -540,6 +879,57 @@
         # do not check for not used locals here
         self._to_consume.pop()
 
+    def leave_module(self, node):
+        """ Update consumption analysis variable
+        for metaclasses.
+        """
+        module_locals = self._to_consume[0][0]
+        module_imports = self._to_consume[0][1]
+        consumed = {}
+
+        for klass in node.nodes_of_class(astroid.Class):
+            found = metaclass = name = None
+            if not klass._metaclass:
+                # Skip if this class doesn't use
+                # explictly a metaclass, but inherits it from ancestors
+                continue
+
+            metaclass = klass.metaclass()
+
+            # Look the name in the already found locals.
+            # If it's not found there, look in the module locals
+            # and in the imported modules.
+            if isinstance(klass._metaclass, astroid.Name):
+                name = klass._metaclass.name
+            elif metaclass:
+                # if it uses a `metaclass=module.Class`
+                name = metaclass.root().name
+
+            if name:
+                found = consumed.setdefault(
+                    name, module_locals.get(name, module_imports.get(name)))
+
+            if found is None and not metaclass:
+                name = None
+                if isinstance(klass._metaclass, astroid.Name):
+                    name = klass._metaclass.name
+                elif isinstance(klass._metaclass, astroid.Getattr):
+                    name = klass._metaclass.as_string()
+
+                if name is not None:
+                    if not (name in astroid.Module.scope_attrs or
+                            is_builtin(name) or
+                            name in self.config.additional_builtins or
+                            name in node.locals):
+                        self.add_message('undefined-variable',
+                                         node=klass,
+                                         args=(name, ))
+        # Pop the consumed items, in order to
+        # avoid having unused-import false positives
+        for name in consumed:
+            module_locals.pop(name, None)
+        super(VariablesChecker3k, self).leave_module(node)
+
 if sys.version_info >= (3, 0):
     VariablesChecker = VariablesChecker3k
 
diff --git a/third_party/pylint/config.py b/third_party/pylint/config.py
index 60b51ee..c346a0e 100644
--- a/third_party/pylint/config.py
+++ b/third_party/pylint/config.py
@@ -1,3 +1,4 @@
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
 # Foundation; either version 2 of the License, or (at your option) any later
@@ -9,14 +10,13 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2003-2006 LOGILAB S.A. (Paris, FRANCE).
- http://www.logilab.fr/ -- mailto:[email protected]
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+"""utilities for Pylint configuration :
 
-  utilities for PyLint configuration :
-   _ pylintrc
-   _ pylint.d (PYLINT_HOME)
+* pylintrc
+* pylint.d (PYLINTHOME)
 """
+from __future__ import with_statement
 
 import pickle
 import os
@@ -34,12 +34,6 @@
     PYLINT_HOME = ".pylint.d"
 else:
     PYLINT_HOME = join(USER_HOME, '.pylint.d')
-        
-if not exists(PYLINT_HOME):
-    try:
-        os.mkdir(PYLINT_HOME)
-    except OSError:
-        print >> sys.stderr, 'Unable to create directory %s' % PYLINT_HOME
 
 def get_pdata_path(base_name, recurs):
     """return the path of the file which should contain old search data for the
@@ -47,29 +41,36 @@
     """
     base_name = base_name.replace(os.sep, '_')
     return join(PYLINT_HOME, "%s%s%s"%(base_name, recurs, '.stats'))
-    
+
 def load_results(base):
     """try to unpickle and return data from file if it exists and is not
     corrupted
-    
+
     return an empty dictionary if it doesn't exists
     """
-    data_file = get_pdata_path(base, 1)        
+    data_file = get_pdata_path(base, 1)
     try:
-        return pickle.load(open(data_file))
+        with open(data_file, _PICK_LOAD) as stream:
+            return pickle.load(stream)
     except:
         return {}
 
 if sys.version_info < (3, 0):
-    _PICK_MOD = 'w'
+    _PICK_DUMP, _PICK_LOAD = 'w', 'r'
 else:
-    _PICK_MOD = 'wb'
+    _PICK_DUMP, _PICK_LOAD = 'wb', 'rb'
 
 def save_results(results, base):
     """pickle results"""
+    if not exists(PYLINT_HOME):
+        try:
+            os.mkdir(PYLINT_HOME)
+        except OSError:
+            print >> sys.stderr, 'Unable to create directory %s' % PYLINT_HOME
     data_file = get_pdata_path(base, 1)
     try:
-        pickle.dump(results, open(data_file, _PICK_MOD))
+        with open(data_file, _PICK_DUMP) as stream:
+            pickle.dump(results, stream)
     except (IOError, OSError), ex:
         print >> sys.stderr, 'Unable to create file %s: %s' % (data_file, ex)
 
@@ -96,6 +97,8 @@
             pylintrc = ".pylintrc"
         else:
             pylintrc = join(user_home, '.pylintrc')
+            if not isfile(pylintrc):
+                pylintrc = join(user_home, '.config', 'pylintrc')
     if not isfile(pylintrc):
         if isfile('/etc/pylintrc'):
             pylintrc = '/etc/pylintrc'
@@ -106,14 +109,14 @@
 PYLINTRC = find_pylintrc()
 
 ENV_HELP = '''
-The following environment variables are used :                                 
-    * PYLINTHOME                                                               
-    path to the directory where data of persistent run will be stored. If not
-found, it defaults to ~/.pylint.d/ or .pylint.d (in the current working
-directory).
-    * PYLINTRC                                                                 
-    path to the configuration file. If not found, it will use the first        
-existent file in ~/.pylintrc, /etc/pylintrc.
+The following environment variables are used:                                   
+    * PYLINTHOME                                                                
+    Path to the directory where the persistent for the run will be stored. If 
+not found, it defaults to ~/.pylint.d/ or .pylint.d (in the current working 
+directory).                                                                     
+    * PYLINTRC                                                                  
+    Path to the configuration file. See the documentation for the method used
+to search for configuration file.
 ''' % globals()
 
 # evaluation messages #########################################################
diff --git a/third_party/pylint/epylint.py b/third_party/pylint/epylint.py
index f6b16e7..beae481 100644
--- a/third_party/pylint/epylint.py
+++ b/third_party/pylint/epylint.py
@@ -1,5 +1,19 @@
-#!/usr/bin/env python
 # -*- coding: utf-8; mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=python:et:sw=4:ts=4:sts=4
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
+# http://www.logilab.fr/ -- mailto:[email protected]
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """Emacs and Flymake compatible Pylint.
 
 This script is for integration with emacs and is compatible with flymake mode.
@@ -15,7 +29,8 @@
         a/b/x.py
         a/c/y.py
 
-   - Then if y.py imports x as "from a.b import x" the following produces pylint errors
+   - Then if y.py imports x as "from a.b import x" the following produces pylint
+     errors
 
        cd a/c; pylint y.py
 
@@ -27,75 +42,75 @@
      we are checking we need to go out of it to avoid these false positives.
 
 
-You may also use py_run to run pylint with desired options and get back (or not) its output.
+You may also use py_run to run pylint with desired options and get back (or not)
+its output.
 """
 
-import sys, os, re
+import sys, os
+import os.path as osp
 from subprocess import Popen, PIPE
 
+def _get_env():
+    '''Extracts the environment PYTHONPATH and appends the current sys.path to
+    those.'''
+    env = dict(os.environ)
+    env['PYTHONPATH'] = os.pathsep.join(sys.path)
+    return env
 
-def lint(filename):
+def lint(filename, options=None):
     """Pylint the given file.
 
-    When run from emacs we will be in the directory of a file, and passed its filename.
-    If this file is part of a package and is trying to import other modules from within
-    its own package or another package rooted in a directory below it, pylint will classify
-    it as a failed import.
+    When run from emacs we will be in the directory of a file, and passed its
+    filename.  If this file is part of a package and is trying to import other
+    modules from within its own package or another package rooted in a directory
+    below it, pylint will classify it as a failed import.
 
-    To get around this, we traverse down the directory tree to find the root of the package this
-    module is in.  We then invoke pylint from this directory.
+    To get around this, we traverse down the directory tree to find the root of
+    the package this module is in.  We then invoke pylint from this directory.
 
-    Finally, we must correct the filenames in the output generated by pylint so Emacs doesn't
-    become confused (it will expect just the original filename, while pylint may extend it with
-    extra directories if we've traversed down the tree)
+    Finally, we must correct the filenames in the output generated by pylint so
+    Emacs doesn't become confused (it will expect just the original filename,
+    while pylint may extend it with extra directories if we've traversed down
+    the tree)
     """
     # traverse downwards until we are out of a python package
-    fullPath = os.path.abspath(filename)
-    parentPath, childPath = os.path.dirname(fullPath), os.path.basename(fullPath)
+    full_path = osp.abspath(filename)
+    parent_path = osp.dirname(full_path)
+    child_path = osp.basename(full_path)
 
-    while parentPath != "/" and os.path.exists(os.path.join(parentPath, '__init__.py')):
-        childPath = os.path.join(os.path.basename(parentPath), childPath)
-        parentPath = os.path.dirname(parentPath)
+    while parent_path != "/" and osp.exists(osp.join(parent_path, '__init__.py')):
+        child_path = osp.join(osp.basename(parent_path), child_path)
+        parent_path = osp.dirname(parent_path)
 
     # Start pylint
-    process = Popen('pylint -f parseable -r n --disable=C,R,I "%s"' %
-                    childPath, shell=True, stdout=PIPE, stderr=PIPE,
-                    cwd=parentPath)
-    p = process.stdout
+    # Ensure we use the python and pylint associated with the running epylint
+    from pylint import lint as lint_mod
+    lint_path = lint_mod.__file__
+    options = options or ['--disable=C,R,I']
+    cmd = [sys.executable, lint_path] + options + [
+        '--msg-template', '{path}:{line}: {category} ({msg_id}, {symbol}, {obj}) {msg}',
+        '-r', 'n', child_path]
+    process = Popen(cmd, stdout=PIPE, cwd=parent_path, env=_get_env(),
+                    universal_newlines=True)
 
-    # The parseable line format is '%(path)s:%(line)s: [%(sigle)s%(obj)s] %(msg)s'
-    # NOTE: This would be cleaner if we added an Emacs reporter to pylint.reporters.text ..
-    regex = re.compile(r"\[(?P<type>[WE])(?P<remainder>.*?)\]")
-
-    def _replacement(mObj):
-        "Alter to include 'Error' or 'Warning'"
-        if mObj.group("type") == "W":
-            replacement = "Warning"
-        else:
-            replacement = "Error"
-        # replace as "Warning (W0511, funcName): Warning Text"
-        return "%s (%s%s):" % (replacement, mObj.group("type"), mObj.group("remainder"))
-
-    for line in p:
+    for line in process.stdout:
         # remove pylintrc warning
         if line.startswith("No config file found"):
             continue
-        line = regex.sub(_replacement, line, 1)
+
         # modify the file name thats output to reverse the path traversal we made
         parts = line.split(":")
-        if parts and parts[0] == childPath:
+        if parts and parts[0] == child_path:
             line = ":".join([filename] + parts[1:])
         print line,
 
-    p.close()
-
-def Run():
-    lint(sys.argv[1])
+    process.wait()
+    return process.returncode
 
 
 def py_run(command_options='', return_std=False, stdout=None, stderr=None,
            script='epylint'):
-    """Run pylint from python (needs Python >= 2.4).
+    """Run pylint from python
 
     ``command_options`` is a string containing ``pylint`` command line options;
     ``return_std`` (boolean) indicates return of created standart output
@@ -137,13 +152,24 @@
         else:
             stderr = sys.stderr
     # Call pylint in a subprocess
-    p = Popen(command_line, shell=True, stdout=stdout, stderr=stderr)
+    p = Popen(command_line, shell=True, stdout=stdout, stderr=stderr,
+              env=_get_env(), universal_newlines=True)
     p.wait()
     # Return standart output and error
     if return_std:
         return (p.stdout, p.stderr)
 
 
-if __name__ == '__main__':
-    lint(sys.argv[1])
+def Run():
+    if len(sys.argv) == 1:
+        print "Usage: %s <filename> [options]" % sys.argv[0]
+        sys.exit(1)
+    elif not osp.exists(sys.argv[1]):
+        print "%s does not exist" % sys.argv[1]
+        sys.exit(1)
+    else:
+        sys.exit(lint(sys.argv[1], sys.argv[2:]))
 
+
+if __name__ == '__main__':
+    Run()
diff --git a/third_party/pylint/gui.py b/third_party/pylint/gui.py
index 2d8e81e..fcc84e0 100644
--- a/third_party/pylint/gui.py
+++ b/third_party/pylint/gui.py
@@ -1,3 +1,18 @@
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
+# http://www.logilab.fr/ -- mailto:[email protected]
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """Tkinker gui for pylint"""
 
 import os
@@ -8,7 +23,7 @@
 from Tkinter import (Tk, Frame, Listbox, Entry, Label, Button, Scrollbar,
                      Checkbutton, Radiobutton, IntVar, StringVar)
 from Tkinter import (TOP, LEFT, RIGHT, BOTTOM, END, X, Y, BOTH, SUNKEN, W,
-                     HORIZONTAL, DISABLED, NORMAL, W, E)
+                     HORIZONTAL, DISABLED, NORMAL, W)
 from tkFileDialog import askopenfilename, askdirectory
 
 import pylint.lint
@@ -21,7 +36,15 @@
           '(W)':'black', '(E)':'darkred',
           '(F)':'red'}
 
-class BasicStream:
+
+def convert_to_string(msg):
+    """make a string representation of a message"""
+    module_object = msg.module
+    if msg.obj:
+        module_object += ".%s" % msg.obj
+    return "(%s) %s [%d]: %s" % (msg.C, module_object, msg.line, msg.msg)
+
+class BasicStream(object):
     '''
     used in gui reporter instead of writing to stdout, it is written to
     this stream and saved in contents
@@ -33,34 +56,37 @@
         self.contents = []
         self.outdict = {}
         self.currout = None
-        self.nextTitle = None
+        self.next_title = None
 
     def write(self, text):
         """write text to the stream"""
         if re.match('^--+$', text.strip()) or re.match('^==+$', text.strip()):
             if self.currout:
-                self.outdict[self.currout].remove(self.nextTitle)
+                self.outdict[self.currout].remove(self.next_title)
                 self.outdict[self.currout].pop()
-            self.currout = self.nextTitle
+            self.currout = self.next_title
             self.outdict[self.currout] = ['']
 
         if text.strip():
-            self.nextTitle = text.strip()
+            self.next_title = text.strip()
 
-        if text.startswith('\n'):
+        if text.startswith(os.linesep):
             self.contents.append('')
-            if self.currout: self.outdict[self.currout].append('')
-        self.contents[-1] += text.strip('\n')
-        if self.currout: self.outdict[self.currout][-1] += text.strip('\n')
-        if text.endswith('\n') and text.strip():
+            if self.currout:
+                self.outdict[self.currout].append('')
+        self.contents[-1] += text.strip(os.linesep)
+        if self.currout:
+            self.outdict[self.currout][-1] += text.strip(os.linesep)
+        if text.endswith(os.linesep) and text.strip():
             self.contents.append('')
-            if self.currout: self.outdict[self.currout].append('')
+            if self.currout:
+                self.outdict[self.currout].append('')
 
     def fix_contents(self):
         """finalize what the contents of the dict should look like before output"""
         for item in self.outdict:
-            numEmpty = self.outdict[item].count('')
-            for i in range(numEmpty):
+            num_empty = self.outdict[item].count('')
+            for _ in xrange(num_empty):
                 self.outdict[item].remove('')
             if self.outdict[item]:
                 self.outdict[item].pop(0)
@@ -79,10 +105,10 @@
         self.contents = []
         self.outdict = {}
         self.currout = None
-        self.nextTitle = None
+        self.next_title = None
 
 
-class LintGui:
+class LintGui(object):
     """Build and control a window to interact with pylint"""
 
     def __init__(self, root=None):
@@ -94,12 +120,13 @@
         #message queue for output from reporter
         self.msg_queue = Queue.Queue()
         self.msgs = []
+        self.visible_msgs = []
         self.filenames = []
         self.rating = StringVar()
         self.tabs = {}
         self.report_stream = BasicStream(self)
         #gui objects
-        self.lbMessages = None
+        self.lb_messages = None
         self.showhistory = None
         self.results = None
         self.btnRun = None
@@ -136,28 +163,34 @@
         msg_frame.pack(side=TOP, fill=BOTH, expand=True)
         btn_frame.pack(side=TOP, fill=X)
 
+        # Binding F5 application-wide to run lint
+        self.root.bind('<F5>', self.run_lint)
+
         #Message ListBox
         rightscrollbar = Scrollbar(msg_frame)
         rightscrollbar.pack(side=RIGHT, fill=Y)
         bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL)
         bottomscrollbar.pack(side=BOTTOM, fill=X)
-        self.lbMessages = Listbox(msg_frame,
-                  yscrollcommand=rightscrollbar.set,
-                  xscrollcommand=bottomscrollbar.set,
-                  bg="white")
-        self.lbMessages.pack(expand=True, fill=BOTH)
-        rightscrollbar.config(command=self.lbMessages.yview)
-        bottomscrollbar.config(command=self.lbMessages.xview)
+        self.lb_messages = Listbox(
+            msg_frame,
+            yscrollcommand=rightscrollbar.set,
+            xscrollcommand=bottomscrollbar.set,
+            bg="white")
+        self.lb_messages.bind("<Double-Button-1>", self.show_sourcefile)
+        self.lb_messages.pack(expand=True, fill=BOTH)
+        rightscrollbar.config(command=self.lb_messages.yview)
+        bottomscrollbar.config(command=self.lb_messages.xview)
 
         #History ListBoxes
         rightscrollbar2 = Scrollbar(history_frame)
         rightscrollbar2.pack(side=RIGHT, fill=Y)
         bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL)
         bottomscrollbar2.pack(side=BOTTOM, fill=X)
-        self.showhistory = Listbox(history_frame,
-                    yscrollcommand=rightscrollbar2.set,
-                    xscrollcommand=bottomscrollbar2.set,
-                    bg="white")
+        self.showhistory = Listbox(
+            history_frame,
+            yscrollcommand=rightscrollbar2.set,
+            xscrollcommand=bottomscrollbar2.set,
+            bg="white")
         self.showhistory.pack(expand=True, fill=BOTH)
         rightscrollbar2.config(command=self.showhistory.yview)
         bottomscrollbar2.config(command=self.showhistory.xview)
@@ -168,36 +201,37 @@
         self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W)
         self.status.pack(side=BOTTOM, fill=X)
 
-        #labels
-        self.lblRatingLabel = Label(rating_frame, text='Rating:')
-        self.lblRatingLabel.pack(side=LEFT)
-        self.lblRating = Label(rating_frame, textvariable=self.rating)
-        self.lblRating.pack(side=LEFT)
+        #labelbl_ratingls
+        lbl_rating_label = Label(rating_frame, text='Rating:')
+        lbl_rating_label.pack(side=LEFT)
+        lbl_rating = Label(rating_frame, textvariable=self.rating)
+        lbl_rating.pack(side=LEFT)
         Label(mid_frame, text='Recently Used:').pack(side=LEFT)
         Label(top_frame, text='Module or package').pack(side=LEFT)
 
         #file textbox
-        self.txtModule = Entry(top_frame, background='white')
-        self.txtModule.bind('<Return>', self.run_lint)
-        self.txtModule.pack(side=LEFT, expand=True, fill=X)
+        self.txt_module = Entry(top_frame, background='white')
+        self.txt_module.bind('<Return>', self.run_lint)
+        self.txt_module.pack(side=LEFT, expand=True, fill=X)
 
         #results box
         rightscrollbar = Scrollbar(res_frame)
         rightscrollbar.pack(side=RIGHT, fill=Y)
         bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL)
         bottomscrollbar.pack(side=BOTTOM, fill=X)
-        self.results = Listbox(res_frame,
-                  yscrollcommand=rightscrollbar.set,
-                  xscrollcommand=bottomscrollbar.set,
-                  bg="white", font="Courier")
+        self.results = Listbox(
+            res_frame,
+            yscrollcommand=rightscrollbar.set,
+            xscrollcommand=bottomscrollbar.set,
+            bg="white", font="Courier")
         self.results.pack(expand=True, fill=BOTH, side=BOTTOM)
         rightscrollbar.config(command=self.results.yview)
         bottomscrollbar.config(command=self.results.xview)
 
         #buttons
         Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT)
-        Button(top_frame, text='Open Package', 
-               command=(lambda : self.file_open(package=True))).pack(side=LEFT)
+        Button(top_frame, text='Open Package',
+               command=(lambda: self.file_open(package=True))).pack(side=LEFT)
 
         self.btnRun = Button(top_frame, text='Run', command=self.run_lint)
         self.btnRun.pack(side=LEFT)
@@ -238,42 +272,53 @@
         #check boxes
         self.box = StringVar()
         # XXX should be generated
-        report = Radiobutton(radio_frame, text="Report", variable=self.box,
-                             value="Report", command=self.refresh_results_window)
-        rawMet = Radiobutton(radio_frame, text="Raw metrics", variable=self.box,
-                             value="Raw metrics", command=self.refresh_results_window)
-        dup = Radiobutton(radio_frame, text="Duplication", variable=self.box,
-                          value="Duplication", command=self.refresh_results_window)
-        ext = Radiobutton(radio_frame, text="External dependencies",
-                          variable=self.box, value="External dependencies",
-                          command=self.refresh_results_window)
-        stat = Radiobutton(radio_frame, text="Statistics by type",
-                           variable=self.box, value="Statistics by type",
-                           command=self.refresh_results_window)
-        msgCat = Radiobutton(radio_frame, text="Messages by category",
-                             variable=self.box, value="Messages by category",
-                             command=self.refresh_results_window)
-        msg = Radiobutton(radio_frame, text="Messages", variable=self.box,
-                            value="Messages", command=self.refresh_results_window)
+        report = Radiobutton(
+            radio_frame, text="Report", variable=self.box,
+            value="Report", command=self.refresh_results_window)
+        raw_met = Radiobutton(
+            radio_frame, text="Raw metrics", variable=self.box,
+            value="Raw metrics", command=self.refresh_results_window)
+        dup = Radiobutton(
+            radio_frame, text="Duplication", variable=self.box,
+            value="Duplication", command=self.refresh_results_window)
+        ext = Radiobutton(
+            radio_frame, text="External dependencies",
+            variable=self.box, value="External dependencies",
+            command=self.refresh_results_window)
+        stat = Radiobutton(
+            radio_frame, text="Statistics by type",
+            variable=self.box, value="Statistics by type",
+            command=self.refresh_results_window)
+        msg_cat = Radiobutton(
+            radio_frame, text="Messages by category",
+            variable=self.box, value="Messages by category",
+            command=self.refresh_results_window)
+        msg = Radiobutton(
+            radio_frame, text="Messages", variable=self.box,
+            value="Messages", command=self.refresh_results_window)
+        source_file = Radiobutton(
+            radio_frame, text="Source File", variable=self.box,
+            value="Source File", command=self.refresh_results_window)
         report.select()
         report.grid(column=0, row=0, sticky=W)
-        rawMet.grid(column=1, row=0, sticky=W)
+        raw_met.grid(column=1, row=0, sticky=W)
         dup.grid(column=2, row=0, sticky=W)
-        msg.grid(column=3, row=0, sticky=E)
+        msg.grid(column=3, row=0, sticky=W)
         stat.grid(column=0, row=1, sticky=W)
-        msgCat.grid(column=1, row=1, sticky=W)
-        ext.grid(column=2, row=1, columnspan=2, sticky=W)
+        msg_cat.grid(column=1, row=1, sticky=W)
+        ext.grid(column=2, row=1, sticky=W)
+        source_file.grid(column=3, row=1, sticky=W)
 
         #dictionary for check boxes and associated error term
         self.msg_type_dict = {
-            'I' : lambda : self.information_box.get() == 1,
-            'C' : lambda : self.convention_box.get() == 1,
-            'R' : lambda : self.refactor_box.get() == 1,
-            'E' : lambda : self.error_box.get() == 1,
-            'W' : lambda : self.warning_box.get() == 1,
-            'F' : lambda : self.fatal_box.get() == 1
+            'I': lambda: self.information_box.get() == 1,
+            'C': lambda: self.convention_box.get() == 1,
+            'R': lambda: self.refactor_box.get() == 1,
+            'E': lambda: self.error_box.get() == 1,
+            'W': lambda: self.warning_box.get() == 1,
+            'F': lambda: self.fatal_box.get() == 1
         }
-        self.txtModule.focus_set()
+        self.txt_module.focus_set()
 
 
     def select_recent_file(self, event):
@@ -284,19 +329,21 @@
         selected = self.showhistory.curselection()
         item = self.showhistory.get(selected)
         #update module
-        self.txtModule.delete(0, END)
-        self.txtModule.insert(0, item)
+        self.txt_module.delete(0, END)
+        self.txt_module.insert(0, item)
 
     def refresh_msg_window(self):
         """refresh the message window with current output"""
         #clear the window
-        self.lbMessages.delete(0, END)
+        self.lb_messages.delete(0, END)
+        self.visible_msgs = []
         for msg in self.msgs:
-            if (self.msg_type_dict.get(msg[0])()):
-                msg_str = self.convert_to_string(msg)
-                self.lbMessages.insert(END, msg_str)
+            if self.msg_type_dict.get(msg.C)():
+                self.visible_msgs.append(msg)
+                msg_str = convert_to_string(msg)
+                self.lb_messages.insert(END, msg_str)
                 fg_color = COLORS.get(msg_str[:3], 'black')
-                self.lbMessages.itemconfigure(END, fg=fg_color)
+                self.lb_messages.itemconfigure(END, fg=fg_color)
 
     def refresh_results_window(self):
         """refresh the results window with current output"""
@@ -308,13 +355,6 @@
         except:
             pass
 
-    def convert_to_string(self, msg):
-        """make a string representation of a message"""
-        if (msg[2] != ""):
-            return "(" + msg[0] + ") " + msg[1] + "." + msg[2] + " [" + msg[3] + "]: " + msg[4]
-        else:
-            return "(" + msg[0] + ") " + msg[1] + " [" + msg[3] + "]: " + msg[4]
-
     def process_incoming(self):
         """process the incoming messages from running pylint"""
         while self.msg_queue.qsize():
@@ -328,11 +368,12 @@
                 self.msgs.append(msg)
 
                 #displaying msg if message type is selected in check box
-                if (self.msg_type_dict.get(msg[0])()):
-                    msg_str = self.convert_to_string(msg)
-                    self.lbMessages.insert(END, msg_str)
+                if self.msg_type_dict.get(msg.C)():
+                    self.visible_msgs.append(msg)
+                    msg_str = convert_to_string(msg)
+                    self.lb_messages.insert(END, msg_str)
                     fg_color = COLORS.get(msg_str[:3], 'black')
-                    self.lbMessages.itemconfigure(END, fg=fg_color)
+                    self.lb_messages.itemconfigure(END, fg=fg_color)
 
             except Queue.Empty:
                 pass
@@ -361,20 +402,22 @@
     def file_open(self, package=False, _=None):
         """launch a file browser"""
         if not package:
-            filename = askopenfilename(parent=self.root, filetypes=[('pythonfiles', '*.py'),
-                                                    ('allfiles', '*')], title='Select Module')
+            filename = askopenfilename(parent=self.root,
+                                       filetypes=[('pythonfiles', '*.py'),
+                                                  ('allfiles', '*')],
+                                       title='Select Module')
         else:
             filename = askdirectory(title="Select A Folder", mustexist=1)
 
         if filename == ():
             return
 
-        self.txtModule.delete(0, END)
-        self.txtModule.insert(0, filename)
+        self.txt_module.delete(0, END)
+        self.txt_module.insert(0, filename)
 
     def update_filenames(self):
         """update the list of recent filenames"""
-        filename = self.txtModule.get()
+        filename = self.txt_module.get()
         if not filename:
             filename = os.getcwd()
         if filename+'\n' in self.filenames:
@@ -407,13 +450,14 @@
         self.update_filenames()
         self.root.configure(cursor='watch')
         self.reporter = GUIReporter(self, output=self.report_stream)
-        module = self.txtModule.get()
+        module = self.txt_module.get()
         if not module:
             module = os.getcwd()
 
         #cleaning up msgs and windows
         self.msgs = []
-        self.lbMessages.delete(0, END)
+        self.visible_msgs = []
+        self.lb_messages.delete(0, END)
         self.tabs = {}
         self.results.delete(0, END)
         self.btnRun.config(state=DISABLED)
@@ -432,11 +476,27 @@
 
         self.root.configure(cursor='')
 
+    def show_sourcefile(self, event=None):
+        selected = self.lb_messages.curselection()
+        if not selected:
+            return
+
+        msg = self.visible_msgs[int(selected[0])]
+        scroll = msg.line - 3
+        if scroll < 0:
+            scroll = 0
+
+        self.tabs["Source File"] = open(msg.path, "r").readlines()
+        self.box.set("Source File")
+        self.refresh_results_window()
+        self.results.yview(scroll)
+        self.results.select_set(msg.line - 1)
+
 
 def lint_thread(module, reporter, gui):
     """thread for pylint"""
     gui.status.text = "processing module(s)"
-    lint_obj = pylint.lint.Run(args=[module], reporter=reporter, exit=False)
+    pylint.lint.Run(args=[module], reporter=reporter, exit=False)
     gui.msg_queue.put("DONE")
 
 
@@ -444,9 +504,10 @@
     """launch pylint gui from args"""
     if args:
         print 'USAGE: pylint-gui\n launch a simple pylint gui using Tk'
-        return
+        sys.exit(1)
     gui = LintGui()
     gui.mainloop()
+    sys.exit(0)
 
 if __name__ == '__main__':
     Run(sys.argv[1:])
diff --git a/third_party/pylint/interfaces.py b/third_party/pylint/interfaces.py
index 3d7bdad..50f2c83 100644
--- a/third_party/pylint/interfaces.py
+++ b/third_party/pylint/interfaces.py
@@ -9,14 +9,8 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-""" Copyright (c) 2002-2003 LOGILAB S.A. (Paris, FRANCE).
- http://www.logilab.fr/ -- mailto:[email protected]
-
-Interfaces for PyLint objects
-"""
-
-__revision__ = "$Id: interfaces.py,v 1.9 2004-04-24 12:14:53 syt Exp $"
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+"""Interfaces for PyLint objects"""
 
 from logilab.common.interface import Interface
 
@@ -32,53 +26,33 @@
     def close(self):
         """called after visiting project (i.e set of modules)"""
 
-##     def open_module(self):
-##         """called before visiting a module"""
-
-##     def close_module(self):
-##         """called after visiting a module"""
-
 
 class IRawChecker(IChecker):
     """interface for checker which need to parse the raw file
     """
 
-    def process_module(self, astng):
+    def process_module(self, astroid):
         """ process a module
 
-        the module's content is accessible via astng.file_stream
+        the module's content is accessible via astroid.file_stream
         """
 
 
-class IASTNGChecker(IChecker):
+class ITokenChecker(IChecker):
+    """Interface for checkers that need access to the token list."""
+    def process_tokens(self, tokens):
+        """Process a module.
+
+        tokens is a list of all source code tokens in the file.
+        """
+
+
+class IAstroidChecker(IChecker):
     """ interface for checker which prefers receive events according to
     statement type
     """
 
 
-class ILinter(Interface):
-    """interface for the linter class
-
-    the linter class will generate events to its registered checkers.
-    Each checker may interact with the linter instance using this API
-    """
-
-    def register_checker(self, checker):
-        """register a new checker class
-
-        checker is a class implementing IrawChecker or / and IASTNGChecker
-        """
-
-    def add_message(self, msg_id, line=None, node=None, args=None):
-        """add the message corresponding to the given id.
-
-        If provided, msg is expanded using args
-
-        astng checkers should provide the node argument,
-        raw checkers should provide the line argument.
-        """
-
-
 class IReporter(Interface):
     """ reporter collect messages and display results encapsulated in a layout
     """
@@ -95,4 +69,4 @@
         """
 
 
-__all__ = ('IRawChecker', 'IStatable', 'ILinter', 'IReporter')
+__all__ = ('IRawChecker', 'IAstroidChecker', 'ITokenChecker', 'IReporter')
diff --git a/third_party/pylint/lint.py b/third_party/pylint/lint.py
index 48ebdf2..5be37f3 100644
--- a/third_party/pylint/lint.py
+++ b/third_party/pylint/lint.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2003-2010 Sylvain Thenault ([email protected]).
-# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -13,10 +12,10 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """ %prog [options] module_or_package
 
-  Check that a module satisfy a coding standard (and more !).
+  Check that a module satisfies a coding standard (and more !).
 
     %prog --help
 
@@ -28,111 +27,148 @@
 """
 
 # import this first to avoid builtin namespace pollution
-from pylint.checkers import utils
+from pylint.checkers import utils #pylint: disable=unused-import
 
 import sys
 import os
-import re
 import tokenize
+from operator import attrgetter
 from warnings import warn
 
 from logilab.common.configuration import UnsupportedAction, OptionsManagerMixIn
 from logilab.common.optik_ext import check_csv
-from logilab.common.modutils import load_module_from_name
 from logilab.common.interface import implements
-from logilab.common.textutils import splitstrip
+from logilab.common.textutils import splitstrip, unquote
 from logilab.common.ureports import Table, Text, Section
 from logilab.common.__pkginfo__ import version as common_version
 
-from logilab.astng import MANAGER, nodes, ASTNGBuildingException
-from logilab.astng.__pkginfo__ import version as astng_version
+from astroid import MANAGER, AstroidBuildingException
+from astroid.__pkginfo__ import version as astroid_version
+from astroid.modutils import load_module_from_name, get_module_part
 
-from pylint.utils import PyLintASTWalker, UnknownMessage, MessagesHandlerMixIn,\
-     ReportsHandlerMixIn, MSG_TYPES, expand_modules
-from pylint.interfaces import ILinter, IRawChecker, IASTNGChecker
-from pylint.checkers import BaseRawChecker, EmptyReport, \
-     table_lines_from_stats
-from pylint.reporters.text import TextReporter, ParseableTextReporter, \
-     VSTextReporter, ColorizedTextReporter
-from pylint.reporters.html import HTMLReporter
+from pylint.utils import (
+    MSG_TYPES, OPTION_RGX,
+    PyLintASTWalker, UnknownMessage, MessagesHandlerMixIn, ReportsHandlerMixIn,
+    MessagesStore, FileState, EmptyReport,
+    expand_modules, tokenize_module)
+from pylint.interfaces import IRawChecker, ITokenChecker, IAstroidChecker
+from pylint.checkers import (BaseTokenChecker,
+                             table_lines_from_stats,
+                             initialize as checkers_initialize)
+from pylint.reporters import initialize as reporters_initialize
 from pylint import config
 
 from pylint.__pkginfo__ import version
 
 
-OPTION_RGX = re.compile('\s*#*\s*pylint:(.*)')
-REPORTER_OPT_MAP = {'text': TextReporter,
-                    'parseable': ParseableTextReporter,
-                    'msvs': VSTextReporter,
-                    'colorized': ColorizedTextReporter,
-                    'html': HTMLReporter,}
+
+def _get_python_path(filepath):
+    dirname = os.path.dirname(os.path.realpath(
+        os.path.expanduser(filepath)))
+    while True:
+        if not os.path.exists(os.path.join(dirname, "__init__.py")):
+            return dirname
+        old_dirname = dirname
+        dirname = os.path.dirname(dirname)
+        if old_dirname == dirname:
+            return os.getcwd()
 
 
 # Python Linter class #########################################################
 
 MSGS = {
     'F0001': ('%s',
+              'fatal',
               'Used when an error occurred preventing the analysis of a \
               module (unable to find it for instance).'),
     'F0002': ('%s: %s',
-              'Used when an unexpected error occurred while building the ASTNG \
-              representation. This is usually accompanied by a traceback. \
-              Please report such errors !'),
+              'astroid-error',
+              'Used when an unexpected error occurred while building the '
+              'Astroid  representation. This is usually accompanied by a '
+              'traceback. Please report such errors !'),
     'F0003': ('ignored builtin module %s',
-              'Used to indicate that the user asked to analyze a builtin module\
-              which has been skipped.'),
-    'F0004': ('unexpected inferred value %s',
-              'Used to indicate that some value of an unexpected type has been \
-              inferred.'),
+              'ignored-builtin-module',
+              'Used to indicate that the user asked to analyze a builtin '
+              'module which has been skipped.'),
     'F0010': ('error while code parsing: %s',
-              'Used when an exception occured while building the ASTNG \
-               representation which could be handled by astng.'),
-
+              'parse-error',
+              'Used when an exception occured while building the Astroid '
+              'representation which could be handled by astroid.'),
 
     'I0001': ('Unable to run raw checkers on built-in module %s',
-              'Used to inform that a built-in module has not been checked \
-              using the raw checkers.'),
+              'raw-checker-failed',
+              'Used to inform that a built-in module has not been checked '
+              'using the raw checkers.'),
 
     'I0010': ('Unable to consider inline option %r',
-              'Used when an inline option is either badly formatted or can\'t \
-              be used inside modules.'),
+              'bad-inline-option',
+              'Used when an inline option is either badly formatted or can\'t '
+              'be used inside modules.'),
 
-    'I0011': ('Locally disabling %s',
-              'Used when an inline option disables a message or a messages \
-              category.'),
-    'I0012': ('Locally enabling %s',
-              'Used when an inline option enables a message or a messages \
-              category.'),
+    'I0011': ('Locally disabling %s (%s)',
+              'locally-disabled',
+              'Used when an inline option disables a message or a messages '
+              'category.'),
+    'I0012': ('Locally enabling %s (%s)',
+              'locally-enabled',
+              'Used when an inline option enables a message or a messages '
+              'category.'),
     'I0013': ('Ignoring entire file',
+              'file-ignored',
               'Used to inform that the file will not be checked'),
-
+    'I0020': ('Suppressed %s (from line %d)',
+              'suppressed-message',
+              'A message was triggered on a line, but suppressed explicitly '
+              'by a disable= comment in the file. This message is not '
+              'generated for messages that are ignored due to configuration '
+              'settings.'),
+    'I0021': ('Useless suppression of %s',
+              'useless-suppression',
+              'Reported when a message is explicitly disabled for a line or '
+              'a block of code, but never triggered.'),
+    'I0022': ('Pragma "%s" is deprecated, use "%s" instead',
+              'deprecated-pragma',
+              'Some inline pylint options have been renamed or reworked, '
+              'only the most recent form should be used. '
+              'NOTE:skip-all is only available with pylint >= 0.26',
+              {'old_names': [('I0014', 'deprecated-disable-all')]}),
 
     'E0001': ('%s',
+              'syntax-error',
               'Used when a syntax error is raised for a module.'),
 
     'E0011': ('Unrecognized file option %r',
+              'unrecognized-inline-option',
               'Used when an unknown inline option is encountered.'),
     'E0012': ('Bad option value %r',
+              'bad-option-value',
               'Used when a bad value for an inline option is encountered.'),
     }
 
 
+def _deprecated_option(shortname, opt_type):
+    def _warn_deprecated(option, optname, *args):
+        sys.stderr.write('Warning: option %s is deprecated and ignored.\n' % (optname,))
+    return {'short': shortname, 'help': 'DEPRECATED', 'hide': True,
+            'type': opt_type, 'action': 'callback', 'callback': _warn_deprecated}
+
+
 class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
-               BaseRawChecker):
+               BaseTokenChecker):
     """lint Python modules using external checkers.
 
     This is the main checker controlling the other ones and the reports
-    generation. It is itself both a raw checker and an astng checker in order
+    generation. It is itself both a raw checker and an astroid checker in order
     to:
     * handle message activation / deactivation at the module level
     * handle some basic but necessary stats'data (number of classes, methods...)
 
     IDE plugins developpers: you may have to call
-    `logilab.astng.builder.MANAGER.astng_cache.clear()` accross run if you want
+    `astroid.builder.MANAGER.astroid_cache.clear()` accross run if you want
     to ensure the latest code version is actually checked.
     """
 
-    __implements__ = (ILinter, IRawChecker)
+    __implements__ = (ITokenChecker,)
 
     name = 'master'
     priority = 0
@@ -145,8 +181,8 @@
         return (('ignore',
                  {'type' : 'csv', 'metavar' : '<file>[,<file>...]',
                   'dest' : 'black_list', 'default' : ('CVS',),
-                  'help' : 'Add files or directories to the blacklist. \
-They should be base names, not paths.'}),
+                  'help' : 'Add files or directories to the blacklist. '
+                           'They should be base names, not paths.'}),
                 ('persistent',
                  {'default': True, 'type' : 'yn', 'metavar' : '<y_or_n>',
                   'level': 1,
@@ -155,71 +191,90 @@
                 ('load-plugins',
                  {'type' : 'csv', 'metavar' : '<modules>', 'default' : (),
                   'level': 1,
-                  'help' : 'List of plugins (as comma separated values of \
-python modules names) to load, usually to register additional checkers.'}),
+                  'help' : 'List of plugins (as comma separated values of '
+                           'python modules names) to load, usually to register '
+                           'additional checkers.'}),
 
                 ('output-format',
-                 {'default': 'text', 'type': 'choice', 'metavar' : '<format>',
-                  'choices': REPORTER_OPT_MAP.keys(),
+                 {'default': 'text', 'type': 'string', 'metavar' : '<format>',
                   'short': 'f',
                   'group': 'Reports',
-                  'help' : 'Set the output format. Available formats are text,\
-                 parseable, colorized, msvs (visual studio) and html'}),
-
-                ('include-ids',
-                 {'type' : 'yn', 'metavar' : '<y_or_n>', 'default' : 0,
-                  'short': 'i',
-                  'group': 'Reports',
-                  'help' : 'Include message\'s id in output'}),
+                  'help' : 'Set the output format. Available formats are text,'
+                           ' parseable, colorized, msvs (visual studio) and html. You '
+                           'can also give a reporter class, eg mypackage.mymodule.'
+                           'MyReporterClass.'}),
 
                 ('files-output',
                  {'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
                   'group': 'Reports', 'level': 1,
-                  'help' : 'Put messages in a separate file for each module / \
-package specified on the command line instead of printing them on stdout. \
-Reports (if any) will be written in a file name "pylint_global.[txt|html]".'}),
+                  'help' : 'Put messages in a separate file for each module / '
+                           'package specified on the command line instead of printing '
+                           'them on stdout. Reports (if any) will be written in a file '
+                           'name "pylint_global.[txt|html]".'}),
 
                 ('reports',
                  {'default': 1, 'type' : 'yn', 'metavar' : '<y_or_n>',
                   'short': 'r',
                   'group': 'Reports',
-                  'help' : 'Tells whether to display a full report or only the\
- messages'}),
+                  'help' : 'Tells whether to display a full report or only the '
+                           'messages'}),
 
                 ('evaluation',
                  {'type' : 'string', 'metavar' : '<python_expression>',
                   'group': 'Reports', 'level': 1,
-                  'default': '10.0 - ((float(5 * error + warning + refactor + \
-convention) / statement) * 10)',
-                  'help' : 'Python expression which should return a note less \
-than 10 (10 is the highest note). You have access to the variables errors \
-warning, statement which respectively contain the number of errors / warnings\
- messages and the total number of statements analyzed. This is used by the \
- global evaluation report (RP0004).'}),
+                  'default': '10.0 - ((float(5 * error + warning + refactor + '
+                             'convention) / statement) * 10)',
+                  'help' : 'Python expression which should return a note less '
+                           'than 10 (10 is the highest note). You have access '
+                           'to the variables errors warning, statement which '
+                           'respectively contain the number of errors / '
+                           'warnings messages and the total number of '
+                           'statements analyzed. This is used by the global '
+                           'evaluation report (RP0004).'}),
 
                 ('comment',
                  {'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
                   'group': 'Reports', 'level': 1,
-                  'help' : 'Add a comment according to your evaluation note. \
-This is used by the global evaluation report (RP0004).'}),
+                  'help' : 'Add a comment according to your evaluation note. '
+                           'This is used by the global evaluation report (RP0004).'}),
 
                 ('enable',
                  {'type' : 'csv', 'metavar': '<msg ids>',
                   'short': 'e',
                   'group': 'Messages control',
                   'help' : 'Enable the message, report, category or checker with the '
-                  'given id(s). You can either give multiple identifier '
-                  'separated by comma (,) or put this option multiple time.'}),
+                           'given id(s). You can either give multiple identifier '
+                           'separated by comma (,) or put this option multiple time. '
+                           'See also the "--disable" option for examples. '}),
 
                 ('disable',
                  {'type' : 'csv', 'metavar': '<msg ids>',
                   'short': 'd',
                   'group': 'Messages control',
                   'help' : 'Disable the message, report, category or checker '
-                  'with the given id(s). You can either give multiple identifier'
-                  ' separated by comma (,) or put this option multiple time '
-                  '(only on the command line, not in the configuration file '
-                  'where it should appear only once).'}),
+                           'with the given id(s). You can either give multiple identifiers'
+                           ' separated by comma (,) or put this option multiple times '
+                           '(only on the command line, not in the configuration file '
+                           'where it should appear only once).'
+                           'You can also use "--disable=all" to disable everything first '
+                           'and then reenable specific checks. For example, if you want '
+                           'to run only the similarities checker, you can use '
+                           '"--disable=all --enable=similarities". '
+                           'If you want to run only the classes checker, but have no '
+                           'Warning level messages displayed, use'
+                           '"--disable=all --enable=classes --disable=W"'}),
+
+                ('msg-template',
+                 {'type' : 'string', 'metavar': '<template>',
+                  'group': 'Reports',
+                  'help' : ('Template used to display messages. '
+                            'This is a python new-style format string '
+                            'used to format the message information. '
+                            'See doc for all details')
+                 }),
+
+                ('include-ids', _deprecated_option('i', 'yn')),
+                ('symbols', _deprecated_option('s', 'yn')),
                )
 
     option_groups = (
@@ -231,13 +286,15 @@
                  pylintrc=None):
         # some stuff has to be done before ancestors initialization...
         #
-        # checkers / reporter / astng manager
+        # messages store / checkers / reporter / astroid manager
+        self.msgs_store = MessagesStore()
         self.reporter = None
+        self._reporter_name = None
+        self._reporters = {}
         self._checkers = {}
         self._ignore_file = False
         # visit variables
-        self.base_name = None
-        self.base_file = None
+        self.file_state = FileState()
         self.current_name = None
         self.current_file = None
         self.stats = None
@@ -249,14 +306,14 @@
             'disable': self.disable}
         self._bw_options_methods = {'disable-msg': self.disable,
                                     'enable-msg': self.enable}
-        full_version = '%%prog %s, \nastng %s, common %s\nPython %s' % (
-            version, astng_version, common_version, sys.version)
+        full_version = '%%prog %s, \nastroid %s, common %s\nPython %s' % (
+            version, astroid_version, common_version, sys.version)
         OptionsManagerMixIn.__init__(self, usage=__doc__,
                                      version=full_version,
                                      config_file=pylintrc or config.PYLINTRC)
         MessagesHandlerMixIn.__init__(self)
         ReportsHandlerMixIn.__init__(self)
-        BaseRawChecker.__init__(self)
+        BaseTokenChecker.__init__(self)
         # provided reports
         self.reports = (('RP0001', 'Messages by category',
                          report_total_messages_stats),
@@ -266,15 +323,31 @@
                          report_messages_stats),
                         ('RP0004', 'Global evaluation',
                          self.report_evaluation),
-                        )
+                       )
         self.register_checker(self)
-        self._dynamic_plugins = []
+        self._dynamic_plugins = set()
         self.load_provider_defaults()
-        self.set_reporter(reporter or TextReporter(sys.stdout))
+        if reporter:
+            self.set_reporter(reporter)
 
     def load_default_plugins(self):
-        from pylint import checkers
-        checkers.initialize(self)
+        checkers_initialize(self)
+        reporters_initialize(self)
+        # Make sure to load the default reporter, because
+        # the option has been set before the plugins had been loaded.
+        if not self.reporter:
+            self._load_reporter()
+
+    def prepare_import_path(self, args):
+        """Prepare sys.path for running the linter checks."""
+        if len(args) == 1:
+            sys.path.insert(0, _get_python_path(args[0]))
+        else:
+            sys.path.insert(0, os.getcwd())
+
+    def cleanup_import_path(self):
+        """Revert any changes made to sys.path in prepare_import_path."""
+        sys.path.pop(0)
 
     def load_plugin_modules(self, modnames):
         """take a list of module names which are pylint plugins and load
@@ -283,10 +356,21 @@
         for modname in modnames:
             if modname in self._dynamic_plugins:
                 continue
-            self._dynamic_plugins.append(modname)
+            self._dynamic_plugins.add(modname)
             module = load_module_from_name(modname)
             module.register(self)
 
+    def _load_reporter(self):
+        name = self._reporter_name.lower()
+        if name in self._reporters:
+            self.set_reporter(self._reporters[name]())
+        else:
+            qname = self._reporter_name
+            module = load_module_from_name(get_module_part(qname))
+            class_name = qname.split('.')[-1]
+            reporter_class = getattr(module, class_name)
+            self.set_reporter(reporter_class())
+
     def set_reporter(self, reporter):
         """set the reporter used to display messages and reports"""
         self.reporter = reporter
@@ -296,7 +380,8 @@
         """overridden from configuration.OptionsProviderMixin to handle some
         special options
         """
-        if optname in self._options_methods or optname in self._bw_options_methods:
+        if optname in self._options_methods or \
+                optname in self._bw_options_methods:
             if value:
                 try:
                     meth = self._options_methods[optname]
@@ -306,24 +391,31 @@
                         optname, optname.split('-')[0]), DeprecationWarning)
                 value = check_csv(None, optname, value)
                 if isinstance(value, (list, tuple)):
-                    for _id in value :
-                        meth(_id)
-                else :
+                    for _id in value:
+                        meth(_id, ignore_unknown=True)
+                else:
                     meth(value)
         elif optname == 'output-format':
-            self.set_reporter(REPORTER_OPT_MAP[value.lower()]())
+            self._reporter_name = value
+            # If the reporters are already available, load
+            # the reporter class.
+            if self._reporters:
+                self._load_reporter()
         try:
-            BaseRawChecker.set_option(self, optname, value, action, optdict)
+            BaseTokenChecker.set_option(self, optname, value, action, optdict)
         except UnsupportedAction:
             print >> sys.stderr, 'option %s can\'t be read from config file' % \
                   optname
 
+    def register_reporter(self, reporter_class):
+        self._reporters[reporter_class.name] = reporter_class
+
     # checkers manipulation methods ############################################
 
     def register_checker(self, checker):
         """register a new checker
 
-        checker is an object implementing IRawChecker or / and IASTNGChecker
+        checker is an object implementing IRawChecker or / and IAstroidChecker
         """
         assert checker.priority <= 0, 'checker priority can\'t be >= 0'
         self._checkers.setdefault(checker.name, []).append(checker)
@@ -331,11 +423,11 @@
             self.register_report(r_id, r_title, r_cb, checker)
         self.register_options_provider(checker)
         if hasattr(checker, 'msgs'):
-            self.register_messages(checker)
+            self.msgs_store.register_messages(checker)
         checker.load_defaults()
 
     def disable_noerror_messages(self):
-        for msgcat, msgids in self._msgs_by_category.iteritems():
+        for msgcat, msgids in self.msgs_store._msgs_by_category.iteritems():
             if msgcat == 'E':
                 for msgid in msgids:
                     self.enable(msgid)
@@ -344,10 +436,10 @@
                     self.disable(msgid)
 
     def disable_reporters(self):
-       """disable all reporters"""
-       for reporters in self._reports.values():
-           for report_id, _title, _cb in reporters:
-               self.disable_report(report_id)
+        """disable all reporters"""
+        for reporters in self._reports.itervalues():
+            for report_id, _title, _cb in reporters:
+                self.disable_report(report_id)
 
     def error_mode(self):
         """error mode: enable only errors; no reports, no persistent"""
@@ -364,22 +456,24 @@
         """process tokens from the current module to search for module/block
         level options
         """
-        comment = tokenize.COMMENT
-        newline = tokenize.NEWLINE
-        for (tok_type, _, start, _, line) in tokens:
-            if tok_type not in (comment, newline):
+        for (tok_type, content, start, _, _) in tokens:
+            if tok_type != tokenize.COMMENT:
                 continue
-            match = OPTION_RGX.search(line)
+            match = OPTION_RGX.search(content)
             if match is None:
                 continue
-            if match.group(1).strip() == "disable-all":
-                self.add_message('I0013', line=start[0])
+            if match.group(1).strip() == "disable-all" or \
+                    match.group(1).strip() == 'skip-file':
+                if match.group(1).strip() == "disable-all":
+                    self.add_message('deprecated-pragma', line=start[0],
+                                     args=('disable-all', 'skip-file'))
+                self.add_message('file-ignored', line=start[0])
                 self._ignore_file = True
                 return
             try:
                 opt, value = match.group(1).split('=', 1)
             except ValueError:
-                self.add_message('I0010', args=match.group(1).strip(),
+                self.add_message('bad-inline-option', args=match.group(1).strip(),
                                  line=start[0])
                 continue
             opt = opt.strip()
@@ -388,67 +482,27 @@
                     meth = self._options_methods[opt]
                 except KeyError:
                     meth = self._bw_options_methods[opt]
-                    warn('%s is deprecated, replace it by %s (%s, line %s)' % (
-                        opt, opt.split('-')[0], self.current_file, line),
-                         DeprecationWarning)
+                    # found a "(dis|en)able-msg" pragma deprecated suppresssion
+                    self.add_message('deprecated-pragma', line=start[0], args=(opt, opt.replace('-msg', '')))
                 for msgid in splitstrip(value):
                     try:
+                        if (opt, msgid) == ('disable', 'all'):
+                            self.add_message('deprecated-pragma', line=start[0], args=('disable=all', 'skip-file'))
+                            self.add_message('file-ignored', line=start[0])
+                            self._ignore_file = True
+                            return
                         meth(msgid, 'module', start[0])
                     except UnknownMessage:
-                        self.add_message('E0012', args=msgid, line=start[0])
+                        self.add_message('bad-option-value', args=msgid, line=start[0])
             else:
-                self.add_message('E0011', args=opt, line=start[0])
-
-    def collect_block_lines(self, node, msg_state):
-        """walk ast to collect block level options line numbers"""
-        # recurse on children (depth first)
-        for child in node.get_children():
-            self.collect_block_lines(child, msg_state)
-        first = node.fromlineno
-        last = node.tolineno
-        # first child line number used to distinguish between disable
-        # which are the first child of scoped node with those defined later.
-        # For instance in the code below:
-        #
-        # 1.   def meth8(self):
-        # 2.        """test late disabling"""
-        # 3.        # pylint: disable=E1102
-        # 4.        print self.blip
-        # 5.        # pylint: disable=E1101
-        # 6.        print self.bla
-        #
-        # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6
-        #
-        # this is necessary to disable locally messages applying to class /
-        # function using their fromlineno
-        if isinstance(node, (nodes.Module, nodes.Class, nodes.Function)) and node.body:
-            firstchildlineno = node.body[0].fromlineno
-        else:
-            firstchildlineno = last
-        for msgid, lines in msg_state.iteritems():
-            for lineno, state in lines.items():
-                if first <= lineno <= last:
-                    if lineno > firstchildlineno:
-                        state = True
-                    # set state for all lines for this block
-                    first, last = node.block_range(lineno)
-                    for line in xrange(first, last+1):
-                        # do not override existing entries
-                        if not line in self._module_msgs_state.get(msgid, ()):
-                            if line in lines: # state change in the same block
-                                state = lines[line]
-                            try:
-                                self._module_msgs_state[msgid][line] = state
-                            except KeyError:
-                                self._module_msgs_state[msgid] = {line: state}
-                    del lines[lineno]
+                self.add_message('unrecognized-inline-option', args=opt, line=start[0])
 
 
     # code checking methods ###################################################
 
     def get_checkers(self):
         """return all available checkers as a list"""
-        return [self] + [c for checkers in self._checkers.values()
+        return [self] + [c for checkers in self._checkers.itervalues()
                          for c in checkers if c is not self]
 
     def prepare_checkers(self):
@@ -458,48 +512,80 @@
         # get needed checkers
         neededcheckers = [self]
         for checker in self.get_checkers()[1:]:
+            # fatal errors should not trigger enable / disabling a checker
             messages = set(msg for msg in checker.msgs
-                           if self.is_message_enabled(msg))
+                           if msg[0] != 'F' and self.is_message_enabled(msg))
             if (messages or
-                any(self.report_is_enabled(r[0]) for r in checker.reports)):
+                    any(self.report_is_enabled(r[0]) for r in checker.reports)):
                 neededcheckers.append(checker)
-                checker.active_msgs = messages
+        # Sort checkers by priority
+        neededcheckers = sorted(neededcheckers, key=attrgetter('priority'),
+                                reverse=True)
         return neededcheckers
 
+    def should_analyze_file(self, modname, path): # pylint: disable=unused-argument
+        """Returns whether or not a module should be checked.
+
+        This implementation returns True for all python source file, indicating
+        that all files should be linted.
+
+        Subclasses may override this method to indicate that modules satisfying
+        certain conditions should not be linted.
+
+        :param str modname: The name of the module to be checked.
+        :param str path: The full path to the source code of the module.
+        :returns: True if the module should be checked.
+        :rtype: bool
+        """
+        return path.endswith('.py')
+
     def check(self, files_or_modules):
         """main checking entry: check a list of files or modules from their
         name.
         """
-        self.reporter.include_ids = self.config.include_ids
+        # initialize msgs_state now that all messages have been registered into
+        # the store
+        for msg in self.msgs_store.messages:
+            if not msg.may_be_emitted():
+                self._msgs_state[msg.msgid] = False
+
         if not isinstance(files_or_modules, (list, tuple)):
             files_or_modules = (files_or_modules,)
         walker = PyLintASTWalker(self)
         checkers = self.prepare_checkers()
-        rawcheckers = [c for c in checkers if implements(c, IRawChecker)
-                       and c is not self]
+        tokencheckers = [c for c in checkers if implements(c, ITokenChecker)
+                         and c is not self]
+        rawcheckers = [c for c in checkers if implements(c, IRawChecker)]
         # notify global begin
         for checker in checkers:
             checker.open()
-            if implements(checker, IASTNGChecker):
+            if implements(checker, IAstroidChecker):
                 walker.add_checker(checker)
         # build ast and check modules or packages
         for descr in self.expand_files(files_or_modules):
             modname, filepath = descr['name'], descr['path']
-            self.set_current_module(modname, filepath)
-            # get the module representation
-            astng = self.get_astng(filepath, modname)
-            if astng is None:
+            if not descr['isarg'] and not self.should_analyze_file(modname, filepath):
                 continue
-            self.base_name = descr['basename']
-            self.base_file = descr['basepath']
             if self.config.files_output:
                 reportfile = 'pylint_%s.%s' % (modname, self.reporter.extension)
                 self.reporter.set_output(open(reportfile, 'w'))
+            self.set_current_module(modname, filepath)
+            # get the module representation
+            astroid = self.get_ast(filepath, modname)
+            if astroid is None:
+                continue
+            # XXX to be correct we need to keep module_msgs_state for every
+            # analyzed module (the problem stands with localized messages which
+            # are only detected in the .close step)
+            self.file_state = FileState(descr['basename'])
             self._ignore_file = False
             # fix the current file (if the source file was not available or
             # if it's actually a c extension)
-            self.current_file = astng.file
-            self.check_astng_module(astng, walker, rawcheckers)
+            self.current_file = astroid.file # pylint: disable=maybe-no-member
+            self.check_astroid_module(astroid, walker, rawcheckers, tokencheckers)
+            # warn about spurious inline messages handling
+            for msgid, line, args in self.file_state.iter_spurious_suppression_messages(self.msgs_store):
+                self.add_message(msgid, line, None, args)
         # notify global end
         self.set_current_module('')
         self.stats['statement'] = walker.nbstatements
@@ -515,7 +601,7 @@
             message = modname = error["mod"]
             key = error["key"]
             self.set_current_module(modname)
-            if key == "F0001":
+            if key == "fatal":
                 message = str(error["ex"]).replace(os.getcwd() + os.sep, '')
             self.add_message(key, args=message)
         return result
@@ -526,62 +612,64 @@
         """
         if not modname and filepath is None:
             return
+        self.reporter.on_set_current_module(modname, filepath)
         self.current_name = modname
         self.current_file = filepath or modname
         self.stats['by_module'][modname] = {}
         self.stats['by_module'][modname]['statement'] = 0
-        for msg_cat in MSG_TYPES.values():
+        for msg_cat in MSG_TYPES.itervalues():
             self.stats['by_module'][modname][msg_cat] = 0
-        # XXX hack, to be correct we need to keep module_msgs_state
-        # for every analyzed module (the problem stands with localized
-        # messages which are only detected in the .close step)
-        if modname:
-            self._module_msgs_state = {}
-            self._module_msg_cats_state = {}
 
-    def get_astng(self, filepath, modname):
-        """return a astng representation for a module"""
+    def get_ast(self, filepath, modname):
+        """return a ast(roid) representation for a module"""
         try:
-            return MANAGER.astng_from_file(filepath, modname, source=True)
+            return MANAGER.ast_from_file(filepath, modname, source=True)
         except SyntaxError, ex:
-            self.add_message('E0001', line=ex.lineno, args=ex.msg)
-        except ASTNGBuildingException, ex:
-            self.add_message('F0010', args=ex)
+            self.add_message('syntax-error', line=ex.lineno, args=ex.msg)
+        except AstroidBuildingException, ex:
+            self.add_message('parse-error', args=ex)
         except Exception, ex:
             import traceback
             traceback.print_exc()
-            self.add_message('F0002', args=(ex.__class__, ex))
+            self.add_message('astroid-error', args=(ex.__class__, ex))
 
-    def check_astng_module(self, astng, walker, rawcheckers):
-        """check a module from its astng representation, real work"""
+    def check_astroid_module(self, astroid, walker, rawcheckers, tokencheckers):
+        """check a module from its astroid representation, real work"""
         # call raw checkers if possible
-        if not astng.pure_python:
-            self.add_message('I0001', args=astng.name)
+        try:
+            tokens = tokenize_module(astroid)
+        except tokenize.TokenError, ex:
+            self.add_message('syntax-error', line=ex.args[1][0], args=ex.args[0])
+            return
+
+        if not astroid.pure_python:
+            self.add_message('raw-checker-failed', args=astroid.name)
         else:
-            #assert astng.file.endswith('.py')
-            # invoke IRawChecker interface on self to fetch module/block
+            #assert astroid.file.endswith('.py')
+            # invoke ITokenChecker interface on self to fetch module/block
             # level options
-            self.process_module(astng)
+            self.process_tokens(tokens)
             if self._ignore_file:
                 return False
             # walk ast to collect line numbers
-            orig_state = self._module_msgs_state.copy()
-            self._module_msgs_state = {}
-            self.collect_block_lines(astng, orig_state)
+            self.file_state.collect_block_lines(self.msgs_store, astroid)
+            # run raw and tokens checkers
             for checker in rawcheckers:
-                checker.process_module(astng)
-        # generate events to astng checkers
-        walker.walk(astng)
+                checker.process_module(astroid)
+            for checker in tokencheckers:
+                checker.process_tokens(tokens)
+        # generate events to astroid checkers
+        walker.walk(astroid)
         return True
 
-    # IASTNGChecker interface #################################################
+    # IAstroidChecker interface #################################################
 
     def open(self):
         """initialize counters"""
-        self.stats = { 'by_module' : {},
-                       'by_msg' : {},
-                       }
-        for msg_cat in MSG_TYPES.values():
+        self.stats = {'by_module' : {},
+                      'by_msg' : {},
+                     }
+        for msg_cat in MSG_TYPES.itervalues():
             self.stats[msg_cat] = 0
 
     def close(self):
@@ -589,20 +677,29 @@
 
         if persistent run, pickle results for later comparison
         """
-        if self.base_name is not None:
-            # load old results if any
-            old_stats = config.load_results(self.base_name)
+        if self.file_state.base_name is not None:
+            # load previous results if any
+            previous_stats = config.load_results(self.file_state.base_name)
+            # XXX code below needs refactoring to be more reporter agnostic
+            self.reporter.on_close(self.stats, previous_stats)
             if self.config.reports:
-                self.make_reports(self.stats, old_stats)
-            elif self.config.output_format == 'html':
-                self.reporter.display_results(Section())
+                sect = self.make_reports(self.stats, previous_stats)
+                if self.config.files_output:
+                    filename = 'pylint_global.' + self.reporter.extension
+                    self.reporter.set_output(open(filename, 'w'))
+            else:
+                sect = Section()
+            if self.config.reports or self.config.output_format == 'html':
+                self.reporter.display_results(sect)
             # save results if persistent run
             if self.config.persistent:
-                config.save_results(self.stats, self.base_name)
+                config.save_results(self.stats, self.file_state.base_name)
+        else:
+            self.reporter.on_close(self.stats, {})
 
     # specific reports ########################################################
 
-    def report_evaluation(self, sect, stats, old_stats):
+    def report_evaluation(self, sect, stats, previous_stats):
         """make the global evaluation report"""
         # check with at least check 1 statements (usually 0 when there is a
         # syntax error preventing pylint from further processing)
@@ -617,18 +714,19 @@
         else:
             stats['global_note'] = note
             msg = 'Your code has been rated at %.2f/10' % note
-            if 'global_note' in old_stats:
-                msg += ' (previous run: %.2f/10)' % old_stats['global_note']
+            pnote = previous_stats.get('global_note')
+            if pnote is not None:
+                msg += ' (previous run: %.2f/10, %+.2f)' % (pnote, note - pnote)
             if self.config.comment:
                 msg = '%s\n%s' % (msg, config.get_note_message(note))
         sect.append(Text(msg))
 
 # some reporting functions ####################################################
 
-def report_total_messages_stats(sect, stats, old_stats):
+def report_total_messages_stats(sect, stats, previous_stats):
     """make total errors / warnings report"""
     lines = ['type', 'number', 'previous', 'difference']
-    lines += table_lines_from_stats(stats, old_stats,
+    lines += table_lines_from_stats(stats, previous_stats,
                                     ('convention', 'refactor',
                                      'warning', 'error'))
     sect.append(Table(children=lines, cols=4, rheaders=1))
@@ -638,8 +736,8 @@
     if not stats['by_msg']:
         # don't print this report when we didn't detected any errors
         raise EmptyReport()
-    in_order = sorted([(value, msg_id) 
-                       for msg_id, value in stats['by_msg'].items()
+    in_order = sorted([(value, msg_id)
+                       for msg_id, value in stats['by_msg'].iteritems()
                        if not msg_id.startswith('I')])
     in_order.reverse()
     lines = ('message id', 'occurrences')
@@ -655,7 +753,7 @@
     by_mod = {}
     for m_type in ('fatal', 'error', 'warning', 'refactor', 'convention'):
         total = stats[m_type]
-        for module in stats['by_module'].keys():
+        for module in stats['by_module'].iterkeys():
             mod_total = stats['by_module'][module][m_type]
             if total == 0:
                 percent = 0
@@ -663,7 +761,7 @@
                 percent = float((mod_total)*100) / total
             by_mod.setdefault(module, {})[m_type] = percent
     sorted_result = []
-    for module, mod_info in by_mod.items():
+    for module, mod_info in by_mod.iteritems():
         sorted_result.append((mod_info['error'],
                               mod_info['warning'],
                               mod_info['refactor'],
@@ -686,11 +784,10 @@
 # utilities ###################################################################
 
 # this may help to import modules using gettext
+# XXX syt, actually needed since we don't import code?
 
-try:
-    __builtins__._ = str
-except AttributeError:
-    __builtins__['_'] = str
+from logilab.common.compat import builtins
+builtins._ = str
 
 
 class ArgumentPreprocessingError(Exception):
@@ -714,19 +811,24 @@
                 option, val = arg[2:], None
             try:
                 cb, takearg = search_for[option]
+            except KeyError:
+                i += 1
+            else:
                 del args[i]
                 if takearg and val is None:
                     if i >= len(args) or args[i].startswith('-'):
-                        raise ArgumentPreprocessingError(arg)
+                        msg = 'Option %s expects a value' % option
+                        raise ArgumentPreprocessingError(msg)
                     val = args[i]
                     del args[i]
+                elif not takearg and val is not None:
+                    msg = "Option %s doesn't expects a value" % option
+                    raise ArgumentPreprocessingError(msg)
                 cb(option, val)
-            except KeyError:
-                i += 1
         else:
             i += 1
 
-class Run:
+class Run(object):
     """helper class to use as main for pylint :
 
     run(*sys.argv[1:])
@@ -743,11 +845,12 @@
         try:
             preprocess_options(args, {
                 # option: (callback, takearg)
+                'init-hook':   (cb_init_hook, True),
                 'rcfile':       (self.cb_set_rcfile, True),
                 'load-plugins': (self.cb_add_plugins, True),
                 })
-        except ArgumentPreprocessingError, e:
-            print >> sys.stderr, 'Argument %s expects a value.' % (e.args[0],)
+        except ArgumentPreprocessingError, ex:
+            print >> sys.stderr, ex
             sys.exit(32)
 
         self.linter = linter = self.LinterClass((
@@ -757,17 +860,18 @@
               'help' : 'Specify a configuration file.'}),
 
             ('init-hook',
-             {'action' : 'callback', 'type' : 'string', 'metavar': '<code>',
-              'callback' : cb_init_hook, 'level': 1,
-              'help' : 'Python code to execute, usually for sys.path \
-manipulation such as pygtk.require().'}),
+             {'action' : 'callback', 'callback' : lambda *args: 1,
+              'type' : 'string', 'metavar': '<code>',
+              'level': 1,
+              'help' : 'Python code to execute, usually for sys.path '
+                       'manipulation such as pygtk.require().'}),
 
             ('help-msg',
              {'action' : 'callback', 'type' : 'string', 'metavar': '<msg-id>',
               'callback' : self.cb_help_message,
               'group': 'Commands',
-              'help' : '''Display a help message for the given message id and \
-exit. The value may be a comma separated list of message ids.'''}),
+              'help' : 'Display a help message for the given message id and '
+                       'exit. The value may be a comma separated list of message ids.'}),
 
             ('list-msgs',
              {'action' : 'callback', 'metavar': '<msg-id>',
@@ -784,66 +888,73 @@
             ('generate-rcfile',
              {'action' : 'callback', 'callback' : self.cb_generate_config,
               'group': 'Commands',
-              'help' : '''Generate a sample configuration file according to \
-the current configuration. You can put other options before this one to get \
-them in the generated configuration.'''}),
+              'help' : 'Generate a sample configuration file according to '
+                       'the current configuration. You can put other options '
+                       'before this one to get them in the generated '
+                       'configuration.'}),
 
             ('generate-man',
              {'action' : 'callback', 'callback' : self.cb_generate_manpage,
               'group': 'Commands',
-              'help' : "Generate pylint's man page.",'hide': True}),
+              'help' : "Generate pylint's man page.", 'hide': True}),
 
             ('errors-only',
              {'action' : 'callback', 'callback' : self.cb_error_mode,
               'short': 'E',
-              'help' : '''In error mode, checkers without error messages are \
-disabled and for others, only the ERROR messages are displayed, and no reports \
-are done by default'''}),
+              'help' : 'In error mode, checkers without error messages are '
+                       'disabled and for others, only the ERROR messages are '
+                       'displayed, and no reports are done by default'''}),
 
             ('profile',
              {'type' : 'yn', 'metavar' : '<y_or_n>',
               'default': False, 'hide': True,
               'help' : 'Profiled execution.'}),
 
-            ), option_groups=self.option_groups,
-               reporter=reporter, pylintrc=self._rcfile)
+            ), option_groups=self.option_groups, pylintrc=self._rcfile)
         # register standard checkers
         linter.load_default_plugins()
         # load command line plugins
         linter.load_plugin_modules(self._plugins)
         # add some help section
         linter.add_help_section('Environment variables', config.ENV_HELP, level=1)
-        linter.add_help_section('Output', '''
-Using the default text output, the message format is :                          
-                                                                                
-        MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE                                
-                                                                                
-There are 5 kind of message types :                                             
-    * (C) convention, for programming standard violation                        
-    * (R) refactor, for bad code smell                                          
-    * (W) warning, for python specific problems                                 
-    * (E) error, for probable bugs in the code                                  
-    * (F) fatal, if an error occurred which prevented pylint from doing further
-processing.
-        ''', level=1)
-        linter.add_help_section('Output status code', '''
-Pylint should leave with following status code:                                 
-    * 0 if everything went fine                                                 
-    * 1 if a fatal message was issued                                           
-    * 2 if an error message was issued                                          
-    * 4 if a warning message was issued                                         
-    * 8 if a refactor message was issued                                        
-    * 16 if a convention message was issued                                     
-    * 32 on usage error                                                         
-                                                                                
-status 1 to 16 will be bit-ORed so you can know which different categories has
-been issued by analysing pylint output status code
-        ''', level=1)
+        # pylint: disable=bad-continuation
+        linter.add_help_section('Output',
+'Using the default text output, the message format is :                          \n'
+'                                                                                \n'
+'        MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE                                \n'
+'                                                                                \n'
+'There are 5 kind of message types :                                             \n'
+'    * (C) convention, for programming standard violation                        \n'
+'    * (R) refactor, for bad code smell                                          \n'
+'    * (W) warning, for python specific problems                                 \n'
+'    * (E) error, for probable bugs in the code                                  \n'
+'    * (F) fatal, if an error occurred which prevented pylint from doing further\n'
+'processing.\n'
+                                , level=1)
+        linter.add_help_section('Output status code',
+'Pylint should leave with following status code:                                 \n'
+'    * 0 if everything went fine                                                 \n'
+'    * 1 if a fatal message was issued                                           \n'
+'    * 2 if an error message was issued                                          \n'
+'    * 4 if a warning message was issued                                         \n'
+'    * 8 if a refactor message was issued                                        \n'
+'    * 16 if a convention message was issued                                     \n'
+'    * 32 on usage error                                                         \n'
+'                                                                                \n'
+'status 1 to 16 will be bit-ORed so you can know which different categories has\n'
+'been issued by analysing pylint output status code\n',
+                                level=1)
         # read configuration
-        linter.disable('W0704')
+        linter.disable('pointless-except')
+        linter.disable('suppressed-message')
+        linter.disable('useless-suppression')
         linter.read_config_file()
-        # is there some additional plugins in the file configuration, in
         config_parser = linter.cfgfile_parser
+        # run init hook, if present, before loading plugins
+        if config_parser.has_option('MASTER', 'init-hook'):
+            cb_init_hook('init-hook',
+                         unquote(config_parser.get('MASTER', 'init-hook')))
+        # is there some additional plugins in the file configuration, in
         if config_parser.has_option('MASTER', 'load-plugins'):
             plugins = splitstrip(config_parser.get('MASTER', 'load-plugins'))
             linter.load_plugin_modules(plugins)
@@ -866,27 +977,28 @@
             sys.exit(32)
         # insert current working directory to the python path to have a correct
         # behaviour
-        sys.path.insert(0, os.getcwd())
+        linter.prepare_import_path(args)
         if self.linter.config.profile:
             print >> sys.stderr, '** profiled run'
             import cProfile, pstats
-            cProfile.runctx('linter.check(%r)' % args, globals(), locals(), 'stones.prof' )
+            cProfile.runctx('linter.check(%r)' % args, globals(), locals(),
+                            'stones.prof')
             data = pstats.Stats('stones.prof')
             data.strip_dirs()
             data.sort_stats('time', 'calls')
             data.print_stats(30)
         else:
             linter.check(args)
-        sys.path.pop(0)
+        linter.cleanup_import_path()
         if exit:
             sys.exit(self.linter.msg_status)
 
     def cb_set_rcfile(self, name, value):
-        """callback for option preprocessing (i.e. before optik parsing)"""
+        """callback for option preprocessing (i.e. before option parsing)"""
         self._rcfile = value
 
     def cb_add_plugins(self, name, value):
-        """callback for option preprocessing (i.e. before optik parsing)"""
+        """callback for option preprocessing (i.e. before option parsing)"""
         self._plugins.extend(splitstrip(value))
 
     def cb_error_mode(self, *args, **kwargs):
@@ -912,7 +1024,7 @@
 
     def cb_help_message(self, option, optname, value, parser):
         """optik callback for printing some help about a particular message"""
-        self.linter.help_message(splitstrip(value))
+        self.linter.msgs_store.help_message(splitstrip(value))
         sys.exit(0)
 
     def cb_full_documentation(self, option, optname, value, parser):
@@ -922,10 +1034,10 @@
 
     def cb_list_messages(self, option, optname, value, parser): # FIXME
         """optik callback for printing available messages"""
-        self.linter.list_messages()
+        self.linter.msgs_store.list_messages()
         sys.exit(0)
 
-def cb_init_hook(option, optname, value, parser):
+def cb_init_hook(optname, value):
     """exec arbitrary code to set sys.path for instance"""
     exec value
 
diff --git a/third_party/pylint/pyreverse/diadefslib.py b/third_party/pylint/pyreverse/diadefslib.py
index 68ca68c..46d0f19 100644
--- a/third_party/pylint/pyreverse/diadefslib.py
+++ b/third_party/pylint/pyreverse/diadefslib.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2000-2010 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2000-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -12,22 +12,24 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """handle diagram generation options for class diagram or default diagrams
 """
 
 from logilab.common.compat import builtins
-BUILTINS_NAME = builtins.__name__
-from logilab import astng
-from logilab.astng.utils import LocalsVisitor
+
+import astroid
+from astroid.utils import LocalsVisitor
 
 from pylint.pyreverse.diagrams import PackageDiagram, ClassDiagram
 
+BUILTINS_NAME = builtins.__name__
+
 # diagram generators ##########################################################
 
-class DiaDefGenerator:
-    """handle diagram generation options
-    """
+class DiaDefGenerator(object):
+    """handle diagram generation options"""
+
     def __init__(self, linker, handler):
         """common Diagram Handler initialization"""
         self.config = handler.config
@@ -39,7 +41,7 @@
         """get title for objects"""
         title = node.name
         if self.module_names:
-            title =  '%s.%s' % (node.root().name, title)
+            title = '%s.%s' % (node.root().name, title)
         return title
 
     def _set_option(self, option):
@@ -100,9 +102,9 @@
         for ass_nodes in klass_node.instance_attrs_type.values() + \
                          klass_node.locals_type.values():
             for ass_node in ass_nodes:
-                if isinstance(ass_node, astng.Instance):
+                if isinstance(ass_node, astroid.Instance):
                     ass_node = ass_node._proxied
-                if not (isinstance(ass_node, astng.Class) 
+                if not (isinstance(ass_node, astroid.Class)
                         and self.show_node(ass_node)):
                     continue
                 yield ass_node
@@ -132,7 +134,7 @@
         LocalsVisitor.__init__(self)
 
     def visit_project(self, node):
-        """visit an astng.Project node
+        """visit an astroid.Project node
 
         create a diagram definition for packages
         """
@@ -144,7 +146,7 @@
         self.classdiagram = ClassDiagram('classes %s' % node.name, mode)
 
     def leave_project(self, node):
-        """leave the astng.Project node
+        """leave the astroid.Project node
 
         return the generated diagram definition
         """
@@ -153,7 +155,7 @@
         return self.classdiagram,
 
     def visit_module(self, node):
-        """visit an astng.Module node
+        """visit an astroid.Module node
 
         add this class to the package diagram definition
         """
@@ -162,7 +164,7 @@
             self.pkgdiagram.add_object(node.name, node)
 
     def visit_class(self, node):
-        """visit an astng.Class node
+        """visit an astroid.Class node
 
         add this class to the class diagram definition
         """
@@ -170,7 +172,7 @@
         self.extract_classes(node, anc_level, ass_level)
 
     def visit_from(self, node):
-        """visit astng.From  and catch modules for package diagram
+        """visit astroid.From  and catch modules for package diagram
         """
         if self.pkgdiagram:
             self.pkgdiagram.add_from_depend(node, node.modname)
@@ -204,7 +206,7 @@
 
 # diagram handler #############################################################
 
-class DiadefsHandler:
+class DiadefsHandler(object):
     """handle diagram definitions :
 
     get it from user (i.e. xml files) or generate them
@@ -215,8 +217,8 @@
 
     def get_diadefs(self, project, linker):
         """get the diagrams configuration data
-        :param linker: astng.inspector.Linker(IdGeneratorMixIn, LocalsVisitor)
-        :param project: astng.manager.Project        
+        :param linker: astroid.inspector.Linker(IdGeneratorMixIn, LocalsVisitor)
+        :param project: astroid.manager.Project
         """
 
         #  read and interpret diagram definitions (Diadefs)
diff --git a/third_party/pylint/pyreverse/diagrams.py b/third_party/pylint/pyreverse/diagrams.py
index 23d23ef..28cc500 100644
--- a/third_party/pylint/pyreverse/diagrams.py
+++ b/third_party/pylint/pyreverse/diagrams.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2004-2010 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2004-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -12,24 +12,16 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """diagram objects
 """
 
-from logilab import astng
+import astroid
 from pylint.pyreverse.utils import is_interface, FilterMixIn
 
-def set_counter(value):
-    """Figure counter (re)set"""
-    Figure._UID_COUNT = value
-    
-class Figure:
+class Figure(object):
     """base class for counter handling"""
-    _UID_COUNT = 0
-    def __init__(self):
-        Figure._UID_COUNT += 1
-        self.fig_id = Figure._UID_COUNT
-        
+
 class Relationship(Figure):
     """a relation ship from an object in the diagram to another
     """
@@ -39,10 +31,10 @@
         self.to_object = to_object
         self.type = relation_type
         self.name = name
-        
-    
+
+
 class DiagramEntity(Figure):
-    """a diagram object, i.e. a label associated to an astng node
+    """a diagram object, i.e. a label associated to an astroid node
     """
     def __init__(self, title='No name', node=None):
         Figure.__init__(self)
@@ -62,7 +54,12 @@
         self._nodes = {}
         self.depends = []
 
-    def add_relationship(self, from_object, to_object, 
+    def get_relationships(self, role):
+        # sorted to get predictable (hence testable) results
+        return sorted(self.relationships.get(role, ()),
+                      key=lambda x: (x.from_object.fig_id, x.to_object.fig_id))
+
+    def add_relationship(self, from_object, to_object,
                          relation_type, name=None):
         """create a relation ship
         """
@@ -88,12 +85,15 @@
             if names:
                 node_name = "%s : %s" % (node_name, ", ".join(names))
             attrs.append(node_name)
-        return attrs
+        return sorted(attrs)
 
     def get_methods(self, node):
         """return visible methods"""
-        return [m for m in node.values()
-                if isinstance(m, astng.Function) and self.show_attr(m.name)]
+        methods = [
+            m for m in node.values()
+            if isinstance(m, astroid.Function) and self.show_attr(m.name)
+        ]
+        return sorted(methods, key=lambda n: n.name)
 
     def add_object(self, title, node):
         """create a diagram object
@@ -107,9 +107,9 @@
         """return class names if needed in diagram"""
         names = []
         for ass_node in nodes:
-            if isinstance(ass_node, astng.Instance):
+            if isinstance(ass_node, astroid.Instance):
                 ass_node = ass_node._proxied
-            if isinstance(ass_node, astng.Class) \
+            if isinstance(ass_node, astroid.Class) \
                 and hasattr(ass_node, "name") and not self.has_node(ass_node):
                 if ass_node.name not in names:
                     ass_name = ass_node.name
@@ -125,15 +125,15 @@
         """return true if the given node is included in the diagram
         """
         return node in self._nodes
-        
+
     def object_from_node(self, node):
         """return the diagram object mapped to node
         """
         return self._nodes[node]
-            
+
     def classes(self):
         """return all class nodes in the diagram"""
-        return [o for o in self.objects if isinstance(o.node, astng.Class)]
+        return [o for o in self.objects if isinstance(o.node, astroid.Class)]
 
     def classe(self, name):
         """return a class by its name, raise KeyError if not found
@@ -142,7 +142,7 @@
             if klass.node.name == name:
                 return klass
         raise KeyError(name)
-    
+
     def extract_relationships(self):
         """extract relation ships between nodes in the diagram
         """
@@ -173,9 +173,9 @@
             for name, values in node.instance_attrs_type.items() + \
                                 node.locals_type.items():
                 for value in values:
-                    if value is astng.YES:
+                    if value is astroid.YES:
                         continue
-                    if isinstance( value, astng.Instance):
+                    if isinstance(value, astroid.Instance):
                         value = value._proxied
                     try:
                         ass_obj = self.object_from_node(value)
@@ -188,10 +188,10 @@
     """package diagram handling
     """
     TYPE = 'package'
-    
+
     def modules(self):
         """return all module nodes in the diagram"""
-        return [o for o in self.objects if isinstance(o.node, astng.Module)]
+        return [o for o in self.objects if isinstance(o.node, astroid.Module)]
 
     def module(self, name):
         """return a module by its name, raise KeyError if not found
@@ -216,12 +216,12 @@
             if mod_name == "%s.%s" % (package.rsplit('.', 1)[0], name):
                 return mod
         raise KeyError(name)
-        
+
     def add_from_depend(self, node, from_module):
         """add dependencies created by from-imports
         """
         mod_name = node.root().name
-        obj = self.module( mod_name )
+        obj = self.module(mod_name)
         if from_module not in obj.node.depends:
             obj.node.depends.append(from_module)
 
diff --git a/third_party/pylint/pyreverse/main.py b/third_party/pylint/pyreverse/main.py
index da80bd6..d4991b5 100644
--- a/third_party/pylint/pyreverse/main.py
+++ b/third_party/pylint/pyreverse/main.py
@@ -1,4 +1,4 @@
-# # Copyright (c) 2000-2010 LOGILAB S.A. (Paris, FRANCE).
+# # Copyright (c) 2000-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -12,27 +12,27 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """
   %prog [options] <packages>
 
-  create UML diagrams for classes and modules in <packages> 
+  create UML diagrams for classes and modules in <packages>
 """
 
 import sys, os
 from logilab.common.configuration import ConfigurationMixIn
-from logilab.astng.manager import ASTNGManager
-from logilab.astng.inspector import Linker
+from astroid.manager import AstroidManager
+from astroid.inspector import Linker
 
 from pylint.pyreverse.diadefslib import DiadefsHandler
 from pylint.pyreverse import writer
 from pylint.pyreverse.utils import insert_default_options
 
 OPTIONS = (
-("filter-mode",
-    dict(short='f', default='PUB_ONLY', dest='mode', type='string',
-    action='store', metavar='<mode>', 
-    help="""filter attributes and functions according to
+    ("filter-mode",
+     dict(short='f', default='PUB_ONLY', dest='mode', type='string',
+          action='store', metavar='<mode>',
+          help="""filter attributes and functions according to
     <mode>. Correct modes are :
                             'PUB_ONLY' filter all non public attributes
                                 [DEFAULT], equivalent to PRIVATE+SPECIAL_A
@@ -42,49 +42,48 @@
                             'OTHER' filter protected and private
                                 attributes""")),
 
-("class",
-dict(short='c', action="append", metavar="<class>", dest="classes", default=[],
-    help="create a class diagram with all classes related to <class>;\
+    ("class",
+     dict(short='c', action="append", metavar="<class>", dest="classes", default=[],
+          help="create a class diagram with all classes related to <class>;\
  this uses by default the options -ASmy")),
 
-("show-ancestors",
-dict(short="a",  action="store", metavar='<ancestor>', type='int',
-    help='show <ancestor> generations of ancestor classes not in <projects>')),
-("all-ancestors",
-dict(short="A", default=None,
-    help="show all ancestors off all classes in <projects>") ),
-("show-associated",
-dict(short='s', action="store", metavar='<ass_level>', type='int',
-    help='show <ass_level> levels of associated classes not in <projects>')),
-("all-associated",
-dict(short='S', default=None,
-    help='show recursively all associated off all associated classes')),
+    ("show-ancestors",
+     dict(short="a", action="store", metavar='<ancestor>', type='int',
+          help='show <ancestor> generations of ancestor classes not in <projects>')),
+    ("all-ancestors",
+     dict(short="A", default=None,
+          help="show all ancestors off all classes in <projects>")),
+    ("show-associated",
+     dict(short='s', action="store", metavar='<ass_level>', type='int',
+          help='show <ass_level> levels of associated classes not in <projects>')),
+    ("all-associated",
+     dict(short='S', default=None,
+          help='show recursively all associated off all associated classes')),
+    ("show-builtin",
+     dict(short="b", action="store_true", default=False,
+          help='include builtin objects in representation of classes')),
 
-("show-builtin",
-dict(short="b", action="store_true", default=False,
-    help='include builtin objects in representation of classes')),
-
-("module-names",
-dict(short="m", default=None, type='yn', metavar='[yn]',
-    help='include module name in representation of classes')),
-# TODO : generate dependencies like in pylint
-#("package-dependencies",
-#dict(short="M", action="store", metavar='<package_depth>', type='int',
-    #help='show <package_depth> module dependencies beyond modules in \
-#<projects> (for the package diagram)')),
-("only-classnames",
-dict(short='k', action="store_true", default=False,
-    help="don't show attributes and methods in the class boxes; \
+    ("module-names",
+     dict(short="m", default=None, type='yn', metavar='[yn]',
+          help='include module name in representation of classes')),
+    # TODO : generate dependencies like in pylint
+    # ("package-dependencies",
+    # dict(short="M", action="store", metavar='<package_depth>', type='int',
+    #     help='show <package_depth> module dependencies beyond modules in \
+    # <projects> (for the package diagram)')),
+    ("only-classnames",
+     dict(short='k', action="store_true", default=False,
+          help="don't show attributes and methods in the class boxes; \
 this disables -f values")),
-("output", dict(short="o", dest="output_format", action="store",
-                 default="dot", metavar="<format>",
-                help="create a *.<format> output file if format available.")),
+    ("output", dict(short="o", dest="output_format", action="store",
+                    default="dot", metavar="<format>",
+                    help="create a *.<format> output file if format available.")),
 )
 # FIXME : quiet mode
-#( ('quiet', 
+#( ('quiet',
                 #dict(help='run quietly', action='store_true', short='q')), )
 
-class PyreverseCommand(ConfigurationMixIn):
+class Run(ConfigurationMixIn):
     """base class providing common behaviour for pyreverse commands"""
 
     options = OPTIONS
@@ -92,16 +91,16 @@
     def __init__(self, args):
         ConfigurationMixIn.__init__(self, usage=__doc__)
         insert_default_options()
-        self.manager = ASTNGManager()
+        self.manager = AstroidManager()
         self.register_options_provider(self.manager)
         args = self.load_command_line_configuration()
-        self.run(args)
+        sys.exit(self.run(args))
 
     def run(self, args):
         """checking arguments and run project"""
         if not args:
             print self.help()
-            return
+            return 1
         # insert current working directory to the python path to recognize
         # dependencies to local modules even if cwd is not in the PYTHONPATH
         sys.path.insert(0, os.getcwd())
@@ -117,13 +116,8 @@
             writer.VCGWriter(self.config).write(diadefs)
         else:
             writer.DotWriter(self.config).write(diadefs)
+        return 0
 
 
-class Run:
-    """pyreverse main class"""
-    def __init__(self, args):
-        """run pyreverse"""
-        PyreverseCommand(args)
-
 if __name__ == '__main__':
     Run(sys.argv[1:])
diff --git a/third_party/pylint/pyreverse/utils.py b/third_party/pylint/pyreverse/utils.py
index ea8b67c..3d12d41 100644
--- a/third_party/pylint/pyreverse/utils.py
+++ b/third_party/pylint/pyreverse/utils.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2002-2010 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2002-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -12,7 +12,7 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """
 generic classes/functions for pyreverse core/extensions
 """
@@ -50,7 +50,7 @@
 
 
 
-# astng utilities ###########################################################
+# astroid utilities ###########################################################
 
 SPECIAL = re.compile('^__[A-Za-z0-9]+[A-Za-z0-9_]*__$')
 PRIVATE = re.compile('^__[_A-Za-z0-9]*[A-Za-z0-9]+_?$')
@@ -106,10 +106,10 @@
     'SPECIAL'   : _SPECIAL,
     'OTHER'     : _PROTECTED + _PRIVATE,
 }
-VIS_MOD = {'special': _SPECIAL, 'protected': _PROTECTED, \
-            'private': _PRIVATE, 'public': 0 }
+VIS_MOD = {'special': _SPECIAL, 'protected': _PROTECTED,
+           'private': _PRIVATE, 'public': 0}
 
-class FilterMixIn:
+class FilterMixIn(object):
     """filter nodes according to a mode and nodes' visibility
     """
     def __init__(self, mode):
@@ -127,5 +127,5 @@
         """return true if the node should be treated
         """
         visibility = get_visibility(getattr(node, 'name', node))
-        return not (self.__mode & VIS_MOD[visibility] )
+        return not (self.__mode & VIS_MOD[visibility])
 
diff --git a/third_party/pylint/pyreverse/writer.py b/third_party/pylint/pyreverse/writer.py
index 6dbfc26..8628a8c 100644
--- a/third_party/pylint/pyreverse/writer.py
+++ b/third_party/pylint/pyreverse/writer.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Copyright (c) 2008-2010 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2008-2013 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -13,17 +13,15 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-"""
-Utilities for creating VCG and Dot diagrams.
-"""
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+"""Utilities for creating VCG and Dot diagrams"""
 
 from logilab.common.vcgutils import VCGPrinter
 from logilab.common.graph import DotBackend
 
 from pylint.pyreverse.utils import is_exception
 
-class DiagramWriter:
+class DiagramWriter(object):
     """base class for writing project diagrams
     """
     def __init__(self, config, styles):
@@ -46,30 +44,33 @@
 
     def write_packages(self, diagram):
         """write a package diagram"""
-        for obj in diagram.modules():
-            label = self.get_title(obj)
-            self.printer.emit_node(obj.fig_id, label=label, shape='box')
+        # sorted to get predictable (hence testable) results
+        for i, obj in enumerate(sorted(diagram.modules(), key=lambda x: x.title)):
+            self.printer.emit_node(i, label=self.get_title(obj), shape='box')
+            obj.fig_id = i
         # package dependencies
-        for rel in diagram.relationships.get('depends', ()):
+        for rel in diagram.get_relationships('depends'):
             self.printer.emit_edge(rel.from_object.fig_id, rel.to_object.fig_id,
-                              **self.pkg_edges)
+                                   **self.pkg_edges)
 
     def write_classes(self, diagram):
         """write a class diagram"""
-        for obj in diagram.objects:
-            self.printer.emit_node(obj.fig_id, **self.get_values(obj) )
+        # sorted to get predictable (hence testable) results
+        for i, obj in enumerate(sorted(diagram.objects, key=lambda x: x.title)):
+            self.printer.emit_node(i, **self.get_values(obj))
+            obj.fig_id = i
         # inheritance links
-        for rel in diagram.relationships.get('specialization', ()):
+        for rel in diagram.get_relationships('specialization'):
             self.printer.emit_edge(rel.from_object.fig_id, rel.to_object.fig_id,
-                              **self.inh_edges)
+                                   **self.inh_edges)
         # implementation links
-        for rel in diagram.relationships.get('implements', ()):
+        for rel in diagram.get_relationships('implements'):
             self.printer.emit_edge(rel.from_object.fig_id, rel.to_object.fig_id,
-                              **self.imp_edges)
+                                   **self.imp_edges)
         # generate associations
-        for rel in diagram.relationships.get('association', ()):
+        for rel in diagram.get_relationships('association'):
             self.printer.emit_edge(rel.from_object.fig_id, rel.to_object.fig_id,
-                              label=rel.name, **self.ass_edges)
+                                   label=rel.name, **self.ass_edges)
 
     def set_printer(self, file_name, basename):
         """set printer"""
@@ -93,11 +94,12 @@
     """
 
     def __init__(self, config):
-        styles = [dict(arrowtail='none', arrowhead="open"), 
-                  dict(arrowtail = "none", arrowhead='empty'), 
-                  dict(arrowtail="node", arrowhead='empty', style='dashed'),
+        styles = [dict(arrowtail='none', arrowhead="open"),
+                  dict(arrowtail='none', arrowhead='empty'),
+                  dict(arrowtail='node', arrowhead='empty', style='dashed'),
                   dict(fontcolor='green', arrowtail='none',
-                       arrowhead='diamond', style='solid') ]
+                       arrowhead='diamond', style='solid'),
+                 ]
         DiagramWriter.__init__(self, config, styles)
 
     def set_printer(self, file_name, basename):
@@ -113,20 +115,20 @@
 
     def get_values(self, obj):
         """get label and shape for classes.
-        
+
         The label contains all attributes and methods
         """
-        label =  obj.title
+        label = obj.title
         if obj.shape == 'interface':
-            label = "«interface»\\n%s" % label
+            label = u'«interface»\\n%s' % label
         if not self.config.only_classnames:
-            label = "%s|%s\l|" % (label,  r"\l".join(obj.attrs) )
+            label = r'%s|%s\l|' % (label, r'\l'.join(obj.attrs))
             for func in obj.methods:
                 label = r'%s%s()\l' % (label, func.name)
             label = '{%s}' % label
         if is_exception(obj.node):
-            return dict(fontcolor="red", label=label, shape="record")
-        return dict(label=label, shape="record")
+            return dict(fontcolor='red', label=label, shape='record')
+        return dict(label=label, shape='record')
 
     def close_graph(self):
         """print the dot graph into <file_name>"""
@@ -139,12 +141,13 @@
     def __init__(self, config):
         styles = [dict(arrowstyle='solid', backarrowstyle='none',
                        backarrowsize=0),
-                  dict(arrowstyle='solid', backarrowstyle='none', 
+                  dict(arrowstyle='solid', backarrowstyle='none',
                        backarrowsize=10),
                   dict(arrowstyle='solid', backarrowstyle='none',
                        linestyle='dotted', backarrowsize=10),
                   dict(arrowstyle='solid', backarrowstyle='none',
-                       textcolor='green') ]
+                       textcolor='green'),
+                 ]
         DiagramWriter.__init__(self, config, styles)
 
     def set_printer(self, file_name, basename):
@@ -163,7 +166,7 @@
 
     def get_values(self, obj):
         """get label and shape for classes.
-        
+
         The label contains all attributes and methods
         """
         if is_exception(obj.node):
@@ -179,7 +182,7 @@
             methods = [func.name for func in obj.methods]
             # box width for UML like diagram
             maxlen = max(len(name) for name in [obj.title] + methods + attrs)
-            line =  "_" * (maxlen + 2)
+            line = '_' * (maxlen + 2)
             label = r'%s\n\f%s' % (label, line)
             for attr in attrs:
                 label = r'%s\n\f08%s' % (label, attr)
diff --git a/third_party/pylint/reporters/__init__.py b/third_party/pylint/reporters/__init__.py
index 0582a6f..12d193f 100644
--- a/third_party/pylint/reporters/__init__.py
+++ b/third_party/pylint/reporters/__init__.py
@@ -1,3 +1,4 @@
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
 # Foundation; either version 2 of the License, or (at your option) any later
@@ -9,17 +10,28 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-"""utilities methods and classes for reporters
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+"""utilities methods and classes for reporters"""
 
-Copyright (c) 2000-2003 LOGILAB S.A. (Paris, FRANCE).
-http://www.logilab.fr/ -- mailto:[email protected]
-"""
+import sys
+import locale
+import os
 
-import sys, locale
+from pylint.utils import MSG_TYPES
+
+from pylint import utils
 
 CMPS = ['=', '-', '+']
 
+# py3k has no more cmp builtin
+if sys.version_info >= (3, 0):
+    def cmp(a, b):
+        return (a > b) - (a < b)
+
+if sys.version_info < (2, 6):
+    import stringformat
+    stringformat.init(True)
+
 def diff_string(old, new):
     """given a old and new int value, return a string representing the
     difference
@@ -29,21 +41,51 @@
     return diff_str
 
 
-class EmptyReport(Exception):
-    """raised when a report is empty and so should not be displayed"""
+class Message(object):
+    """This class represent a message to be issued by the reporters"""
 
-class BaseReporter:
-    """base class for reporters"""
+    def __init__(self, reporter, msg_id, location, msg):
+        self.msg_id = msg_id
+        self.abspath, self.module, self.obj, self.line, self.column = location
+        self.path = self.abspath.replace(reporter.path_strip_prefix, '')
+        self.msg = msg
+        self.C = msg_id[0]
+        self.category = MSG_TYPES[msg_id[0]]
+        self.symbol = reporter.linter.msgs_store.check_message_id(msg_id).symbol
+
+    def format(self, template):
+        """Format the message according to the given template.
+
+        The template format is the one of the format method :
+        cf. http://docs.python.org/2/library/string.html#formatstrings
+        """
+        return template.format(**(self.__dict__))
+
+
+class BaseReporter(object):
+    """base class for reporters
+
+    symbols: show short symbolic names for messages.
+    """
 
     extension = ''
 
     def __init__(self, output=None):
         self.linter = None
-        self.include_ids = None
+        # self.include_ids = None # Deprecated
+        # self.symbols = None # Deprecated
         self.section = 0
         self.out = None
         self.out_encoding = None
+        self.encode = None
         self.set_output(output)
+        # Build the path prefix to strip to get relative paths
+        self.path_strip_prefix = os.getcwd() + os.sep
+
+    def add_message(self, msg_id, location, msg):
+        """Client API to send a message"""
+        # Shall we store the message objects somewhere, do some validity checking ?
+        raise NotImplementedError
 
     def set_output(self, output=None):
         """set output stream"""
@@ -59,7 +101,10 @@
             encoding = (getattr(self.out, 'encoding', None) or
                         locale.getdefaultlocale()[1] or
                         sys.getdefaultencoding())
-            return string.encode(encoding)
+            # errors=replace, we don't want to crash when attempting to show
+            # source code line that can't be encoded with the current locale
+            # settings
+            return string.encode(encoding, 'replace')
         self.encode = encode
 
     def writeln(self, string=''):
@@ -69,7 +114,7 @@
     def display_results(self, layout):
         """display results encapsulated in the layout tree"""
         self.section = 0
-        if self.include_ids and hasattr(layout, 'report_id'):
+        if hasattr(layout, 'report_id'):
             layout.children[0].children[0].data += ' (%s)' % layout.report_id
         self._display(layout)
 
@@ -77,3 +122,17 @@
         """display the layout"""
         raise NotImplementedError()
 
+    # Event callbacks
+
+    def on_set_current_module(self, module, filepath):
+        """starting analyzis of a module"""
+        pass
+
+    def on_close(self, stats, previous_stats):
+        """global end of analyzis"""
+        pass
+
+
+def initialize(linter):
+    """initialize linter with reporters in this package """
+    utils.register_plugins(linter, __path__[0])
diff --git a/third_party/pylint/reporters/guireporter.py b/third_party/pylint/reporters/guireporter.py
index 13914ba..331eb17 100644
--- a/third_party/pylint/reporters/guireporter.py
+++ b/third_party/pylint/reporters/guireporter.py
@@ -3,7 +3,7 @@
 import sys
 
 from pylint.interfaces import IReporter
-from pylint.reporters import BaseReporter
+from pylint.reporters import BaseReporter, Message
 from logilab.common.ureports import TextWriter
 
 
@@ -16,20 +16,12 @@
     def __init__(self, gui, output=sys.stdout):
         """init"""
         BaseReporter.__init__(self, output)
-        self.msgs = []
         self.gui = gui
 
     def add_message(self, msg_id, location, msg):
         """manage message of different type and in the context of path"""
-        module, obj, line = location[1:]
-        if self.include_ids:
-            sigle = msg_id
-        else:
-            sigle = msg_id[0]
-
-        full_msg = [sigle, module, obj, str(line), msg]
-        self.msgs += [[sigle, module, obj, str(line)]]
-        self.gui.msg_queue.put(full_msg)
+        message = Message(self, msg_id, location, msg)
+        self.gui.msg_queue.put(message)
 
     def _display(self, layout):
         """launch layouts display"""
diff --git a/third_party/pylint/reporters/html.py b/third_party/pylint/reporters/html.py
index 56efcd6..71d46eb 100644
--- a/third_party/pylint/reporters/html.py
+++ b/third_party/pylint/reporters/html.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2003-2006 Sylvain Thenault ([email protected]).
-# Copyright (c) 2003-2011 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
 # Foundation; either version 2 of the License, or (at your option) any later
@@ -11,7 +10,7 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """HTML reporter"""
 
 import sys
@@ -20,13 +19,14 @@
 from logilab.common.ureports import HTMLWriter, Section, Table
 
 from pylint.interfaces import IReporter
-from pylint.reporters import BaseReporter
+from pylint.reporters import BaseReporter, Message
 
 
 class HTMLReporter(BaseReporter):
     """report messages and layouts in HTML"""
 
     __implements__ = IReporter
+    name = 'html'
     extension = 'html'
 
     def __init__(self, output=sys.stdout):
@@ -35,12 +35,9 @@
 
     def add_message(self, msg_id, location, msg):
         """manage message of different type and in the context of path"""
-        module, obj, line, col_offset = location[1:]
-        if self.include_ids:
-            sigle = msg_id
-        else:
-            sigle = msg_id[0]
-        self.msgs += [sigle, module, obj, str(line), str(col_offset), escape(msg)]
+        msg = Message(self, msg_id, location, msg)
+        self.msgs += (msg.category, msg.module, msg.obj,
+                      str(msg.line), str(msg.column), escape(msg.msg))
 
     def set_output(self, output=None):
         """set output stream
@@ -67,3 +64,7 @@
             self.msgs = []
         HTMLWriter().format(layout, self.out)
 
+
+def register(linter):
+    """Register the reporter classes with the linter."""
+    linter.register_reporter(HTMLReporter)
diff --git a/third_party/pylint/reporters/text.py b/third_party/pylint/reporters/text.py
index 032df6b..04245f7 100644
--- a/third_party/pylint/reporters/text.py
+++ b/third_party/pylint/reporters/text.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2003-2007 Sylvain Thenault ([email protected]).
-# Copyright (c) 2003-2011 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
 # Foundation; either version 2 of the License, or (at your option) any later
@@ -11,56 +10,54 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """Plain text reporters:
 
 :text: the default one grouping messages by module
-:parseable:
-  standard parseable output with full module path on each message (for
-  editor integration)
 :colorized: an ANSI colorized text reporter
-
 """
 
-import os
-import sys
+import warnings
 
 from logilab.common.ureports import TextWriter
 from logilab.common.textutils import colorize_ansi
 
 from pylint.interfaces import IReporter
-from pylint.reporters import BaseReporter
+from pylint.reporters import BaseReporter, Message
 
 TITLE_UNDERLINES = ['', '=', '-', '.']
 
 
 class TextReporter(BaseReporter):
-    """reports messages and layouts in plain text
-    """
+    """reports messages and layouts in plain text"""
 
     __implements__ = IReporter
+    name = 'text'
     extension = 'txt'
+    line_format = '{C}:{line:3d},{column:2d}: {msg} ({symbol})'
 
-    def __init__(self, output=sys.stdout):
+    def __init__(self, output=None):
         BaseReporter.__init__(self, output)
-        self._modules = {}
+        self._modules = set()
+        self._template = None
+
+    def on_set_current_module(self, module, filepath):
+        self._template = unicode(self.linter.config.msg_template or self.line_format)
+
+    def write_message(self, msg):
+        """Convenience method to write a formated message with class default template"""
+        self.writeln(msg.format(self._template))
 
     def add_message(self, msg_id, location, msg):
         """manage message of different type and in the context of path"""
-        path, module, obj, line, col_offset = location
-        if module not in self._modules:
-            if module:
-                self.writeln('************* Module %s' % (path if path else module))
-                self._modules[module] = 1
+        m = Message(self, msg_id, location, msg)
+        if m.module not in self._modules:
+            if m.module:
+                self.writeln('************* Module %s' % m.module)
+                self._modules.add(m.module)
             else:
-                self.writeln('************* %s' % module)
-        if obj:
-            obj = ':%s' % obj
-        if self.include_ids:
-            sigle = msg_id
-        else:
-            sigle = msg_id[0]
-        self.writeln('%s:%3s,%s%s: %s' % (sigle, line, col_offset, obj, msg))
+                self.writeln('************* ')
+        self.write_message(m)
 
     def _display(self, layout):
         """launch layouts display"""
@@ -74,35 +71,25 @@
 
     <filename>:<linenum>:<msg>
     """
-    line_format = '%(path)s:%(line)s: [%(sigle)s%(obj)s] %(msg)s'
+    name = 'parseable'
+    line_format = '{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}'
 
-    def __init__(self, output=sys.stdout, relative=True):
+    def __init__(self, output=None):
+        warnings.warn('%s output format is deprecated. This is equivalent '
+                      'to --msg-template=%s' % (self.name, self.line_format))
         TextReporter.__init__(self, output)
-        if relative:
-            self._prefix = os.getcwd() + os.sep
-        else:
-            self._prefix = ''
 
-    def add_message(self, msg_id, location, msg):
-        """manage message of different type and in the context of path"""
-        path, _, obj, line, _ = location
-        if obj:
-            obj = ', %s' % obj
-        if self.include_ids:
-            sigle = msg_id
-        else:
-            sigle = msg_id[0]
-        if self._prefix:
-            path = path.replace(self._prefix, '')
-        self.writeln(self.line_format % locals())
 
 class VSTextReporter(ParseableTextReporter):
     """Visual studio text reporter"""
-    line_format = '%(path)s(%(line)s): [%(sigle)s%(obj)s] %(msg)s'
+    name = 'msvs'
+    line_format = '{path}({line}): [{msg_id}({symbol}){obj}] {msg}'
+
 
 class ColorizedTextReporter(TextReporter):
     """Simple TextReporter that colorizes text output"""
 
+    name = 'colorized'
     COLOR_MAPPING = {
         "I" : ("green", None),
         'C' : (None, "bold"),
@@ -113,12 +100,11 @@
         'S' : ("yellow", "inverse"), # S stands for module Separator
     }
 
-    def __init__(self, output=sys.stdout, color_mapping = None):
+    def __init__(self, output=None, color_mapping=None):
         TextReporter.__init__(self, output)
         self.color_mapping = color_mapping or \
                              dict(ColorizedTextReporter.COLOR_MAPPING)
 
-
     def _get_decoration(self, msg_id):
         """Returns the tuple color, style associated with msg_id as defined
         in self.color_mapping
@@ -132,24 +118,26 @@
         """manage message of different types, and colorize output
         using ansi escape codes
         """
-        module, obj, line, _ = location[1:]
-        if module not in self._modules:
+        msg = Message(self, msg_id, location, msg)
+        if msg.module not in self._modules:
             color, style = self._get_decoration('S')
-            if module:
-                modsep = colorize_ansi('************* Module %s' % module,
+            if msg.module:
+                modsep = colorize_ansi('************* Module %s' % msg.module,
                                        color, style)
             else:
-                modsep = colorize_ansi('************* %s' % module,
+                modsep = colorize_ansi('************* %s' % msg.module,
                                        color, style)
             self.writeln(modsep)
-            self._modules[module] = 1
-        if obj:
-            obj = ':%s' % obj
-        if self.include_ids:
-            sigle = msg_id
-        else:
-            sigle = msg_id[0]
-        color, style = self._get_decoration(sigle)
-        msg = colorize_ansi(msg, color, style)
-        sigle = colorize_ansi(sigle, color, style)
-        self.writeln('%s:%3s%s: %s' % (sigle, line, obj, msg))
+            self._modules.add(msg.module)
+        color, style = self._get_decoration(msg.C)
+        for attr in ('msg', 'symbol', 'category', 'C'):
+            setattr(msg, attr, colorize_ansi(getattr(msg, attr), color, style))
+        self.write_message(msg)
+
+
+def register(linter):
+    """Register the reporter classes with the linter."""
+    linter.register_reporter(TextReporter)
+    linter.register_reporter(ParseableTextReporter)
+    linter.register_reporter(VSTextReporter)
+    linter.register_reporter(ColorizedTextReporter)
diff --git a/third_party/pylint/testutils.py b/third_party/pylint/testutils.py
new file mode 100644
index 0000000..1dd083f
--- /dev/null
+++ b/third_party/pylint/testutils.py
@@ -0,0 +1,386 @@
+# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
+# http://www.logilab.fr/ -- mailto:[email protected]
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+"""functional/non regression tests for pylint"""
+from __future__ import with_statement
+
+import collections
+import contextlib
+import functools
+import sys
+import re
+
+from glob import glob
+from os import linesep
+from os.path import abspath, basename, dirname, isdir, join, splitext
+from cStringIO import StringIO
+
+from logilab.common import testlib
+
+from pylint import checkers
+from pylint.utils import PyLintASTWalker
+from pylint.reporters import BaseReporter
+from pylint.interfaces import IReporter
+from pylint.lint import PyLinter
+
+
+# Utils
+
+SYS_VERS_STR = '%d%d%d' % sys.version_info[:3]
+TITLE_UNDERLINES = ['', '=', '-', '.']
+PREFIX = abspath(dirname(__file__))
+PY3K = sys.version_info[0] == 3
+
+def fix_path():
+    sys.path.insert(0, PREFIX)
+
+def get_tests_info(input_dir, msg_dir, prefix, suffix):
+    """get python input examples and output messages
+
+    We use following conventions for input files and messages:
+    for different inputs:
+        test for python  >= x.y    ->  input   =  <name>_pyxy.py
+        test for python  <  x.y    ->  input   =  <name>_py_xy.py
+    for one input and different messages:
+        message for python >=  x.y ->  message =  <name>_pyxy.txt
+        lower versions             ->  message with highest num
+    """
+    result = []
+    for fname in glob(join(input_dir, prefix + '*' + suffix)):
+        infile = basename(fname)
+        fbase = splitext(infile)[0]
+        # filter input files :
+        pyrestr = fbase.rsplit('_py', 1)[-1] # like _26 or 26
+        if pyrestr.isdigit(): # '24', '25'...
+            if SYS_VERS_STR < pyrestr:
+                continue
+        if pyrestr.startswith('_') and  pyrestr[1:].isdigit():
+            # skip test for higher python versions
+            if SYS_VERS_STR >= pyrestr[1:]:
+                continue
+        messages = glob(join(msg_dir, fbase + '*.txt'))
+        # the last one will be without ext, i.e. for all or upper versions:
+        if messages:
+            for outfile in sorted(messages, reverse=True):
+                py_rest = outfile.rsplit('_py', 1)[-1][:-4]
+                if py_rest.isdigit() and SYS_VERS_STR >= py_rest:
+                    break
+        else:
+            # This will provide an error message indicating the missing filename.
+            outfile = join(msg_dir, fbase + '.txt')
+        result.append((infile, outfile))
+    return result
+
+
+class TestReporter(BaseReporter):
+    """reporter storing plain text messages"""
+
+    __implements____ = IReporter
+
+    def __init__(self):
+        self.message_ids = {}
+        self.reset()
+
+    def reset(self):
+        self.out = StringIO()
+        self.messages = []
+
+    def add_message(self, msg_id, location, msg):
+        """manage message of different type and in the context of path """
+        _, _, obj, line, _ = location
+        self.message_ids[msg_id] = 1
+        if obj:
+            obj = ':%s' % obj
+        sigle = msg_id[0]
+        if PY3K and linesep != '\n':
+            # 2to3 writes os.linesep instead of using
+            # the previosly used line separators
+            msg = msg.replace('\r\n', '\n')
+        self.messages.append('%s:%3s%s: %s' % (sigle, line, obj, msg))
+
+    def finalize(self):
+        self.messages.sort()
+        for msg in self.messages:
+            print >> self.out, msg
+        result = self.out.getvalue()
+        self.reset()
+        return result
+
+    def display_results(self, layout):
+        """ignore layouts"""
+
+
+if sys.version_info < (2, 6):
+    class Message(tuple):
+        def __new__(cls, msg_id, line=None, node=None, args=None):
+            return tuple.__new__(cls, (msg_id, line, node, args))
+
+        @property
+        def msg_id(self):
+            return self[0]
+        @property
+        def line(self):
+            return self[1]
+        @property
+        def node(self):
+            return self[2]
+        @property
+        def args(self):
+            return self[3]
+
+
+else:
+    class Message(collections.namedtuple('Message',
+                                         ['msg_id', 'line', 'node', 'args'])):
+        def __new__(cls, msg_id, line=None, node=None, args=None):
+            return tuple.__new__(cls, (msg_id, line, node, args))
+
+
+class UnittestLinter(object):
+    """A fake linter class to capture checker messages."""
+
+    def __init__(self):
+        self._messages = []
+        self.stats = {}
+
+    def release_messages(self):
+        try:
+            return self._messages
+        finally:
+            self._messages = []
+
+    def add_message(self, msg_id, line=None, node=None, args=None):
+        self._messages.append(Message(msg_id, line, node, args))
+
+    def is_message_enabled(self, *unused_args):
+        return True
+
+    def add_stats(self, **kwargs):
+        for name, value in kwargs.iteritems():
+            self.stats[name] = value
+        return self.stats
+
+    @property
+    def options_providers(self):
+        return linter.options_providers
+
+def set_config(**kwargs):
+    """Decorator for setting config values on a checker."""
+    def _Wrapper(fun):
+        @functools.wraps(fun)
+        def _Forward(self):
+            for key, value in kwargs.iteritems():
+                setattr(self.checker.config, key, value)
+            if isinstance(self, CheckerTestCase):
+                # reopen checker in case, it may be interested in configuration change
+                self.checker.open()
+            fun(self)
+
+        return _Forward
+    return _Wrapper
+
+
+class CheckerTestCase(testlib.TestCase):
+    """A base testcase class for unittesting individual checker classes."""
+    CHECKER_CLASS = None
+    CONFIG = {}
+
+    def setUp(self):
+        self.linter = UnittestLinter()
+        self.checker = self.CHECKER_CLASS(self.linter) # pylint: disable=not-callable
+        for key, value in self.CONFIG.iteritems():
+            setattr(self.checker.config, key, value)
+        self.checker.open()
+
+    @contextlib.contextmanager
+    def assertNoMessages(self):
+        """Assert that no messages are added by the given method."""
+        with self.assertAddsMessages():
+            yield
+
+    @contextlib.contextmanager
+    def assertAddsMessages(self, *messages):
+        """Assert that exactly the given method adds the given messages.
+
+        The list of messages must exactly match *all* the messages added by the
+        method. Additionally, we check to see whether the args in each message can
+        actually be substituted into the message string.
+        """
+        yield
+        got = self.linter.release_messages()
+        msg = ('Expected messages did not match actual.\n'
+               'Expected:\n%s\nGot:\n%s' % ('\n'.join(repr(m) for m in messages),
+                                            '\n'.join(repr(m) for m in got)))
+        self.assertEqual(list(messages), got, msg)
+
+    def walk(self, node):
+        """recursive walk on the given node"""
+        walker = PyLintASTWalker(linter)
+        walker.add_checker(self.checker)
+        walker.walk(node)
+
+
+# Init
+test_reporter = TestReporter()
+linter = PyLinter()
+linter.set_reporter(test_reporter)
+linter.config.persistent = 0
+checkers.initialize(linter)
+linter.global_set_option('required-attributes', ('__revision__',))
+
+if linesep != '\n':
+    LINE_RGX = re.compile(linesep)
+    def ulines(string):
+        return LINE_RGX.sub('\n', string)
+else:
+    def ulines(string):
+        return string
+
+INFO_TEST_RGX = re.compile(r'^func_i\d\d\d\d$')
+
+def exception_str(self, ex):
+    """function used to replace default __str__ method of exception instances"""
+    return 'in %s\n:: %s' % (ex.file, ', '.join(ex.args))
+
+# Test classes
+
+class LintTestUsingModule(testlib.TestCase):
+    INPUT_DIR = None
+    DEFAULT_PACKAGE = 'input'
+    package = DEFAULT_PACKAGE
+    linter = linter
+    module = None
+    depends = None
+    output = None
+    _TEST_TYPE = 'module'
+
+    def shortDescription(self):
+        values = {'mode' : self._TEST_TYPE,
+                  'input': self.module,
+                  'pkg':   self.package,
+                  'cls':   self.__class__.__name__}
+
+        if self.package == self.DEFAULT_PACKAGE:
+            msg = '%(mode)s test of input file "%(input)s" (%(cls)s)'
+        else:
+            msg = '%(mode)s test of input file "%(input)s" in "%(pkg)s" (%(cls)s)'
+        return msg % values
+
+    def test_functionality(self):
+        tocheck = [self.package+'.'+self.module]
+        if self.depends:
+            tocheck += [self.package+'.%s' % name.replace('.py', '')
+                        for name, _ in self.depends]
+        self._test(tocheck)
+
+    def _check_result(self, got):
+        self.assertMultiLineEqual(self._get_expected().strip()+'\n',
+                                  got.strip()+'\n')
+
+    def _test(self, tocheck):
+        if INFO_TEST_RGX.match(self.module):
+            self.linter.enable('I')
+        else:
+            self.linter.disable('I')
+        try:
+            self.linter.check(tocheck)
+        except Exception, ex:
+            # need finalization to restore a correct state
+            self.linter.reporter.finalize()
+            ex.file = tocheck
+            print ex
+            ex.__str__ = exception_str
+            raise
+        self._check_result(self.linter.reporter.finalize())
+
+    def _has_output(self):
+        return not self.module.startswith('func_noerror_')
+
+    def _get_expected(self):
+        if self._has_output() and self.output:
+            with open(self.output, 'U') as fobj:
+                return fobj.read().strip() + '\n'
+        else:
+            return ''
+
+class LintTestUsingFile(LintTestUsingModule):
+
+    _TEST_TYPE = 'file'
+
+    def test_functionality(self):
+        importable = join(self.INPUT_DIR, self.module)
+        # python also prefers packages over simple modules.
+        if not isdir(importable):
+            importable += '.py'
+        tocheck = [importable]
+        if self.depends:
+            tocheck += [join(self.INPUT_DIR, name) for name, _file in self.depends]
+        self._test(tocheck)
+
+class LintTestUpdate(LintTestUsingModule):
+
+    _TEST_TYPE = 'update'
+
+    def _check_result(self, got):
+        if self._has_output():
+            try:
+                expected = self._get_expected()
+            except IOError:
+                expected = ''
+            if got != expected:
+                with open(self.output, 'w') as fobj:
+                    fobj.write(got)
+
+# Callback
+
+def cb_test_gen(base_class):
+    def call(input_dir, msg_dir, module_file, messages_file, dependencies):
+        class LintTC(base_class):
+            module = module_file.replace('.py', '')
+            output = messages_file
+            depends = dependencies or None
+            tags = testlib.Tags(('generated', 'pylint_input_%s' % module))
+            INPUT_DIR = input_dir
+            MSG_DIR = msg_dir
+        return LintTC
+    return call
+
+# Main function
+
+def make_tests(input_dir, msg_dir, filter_rgx, callbacks):
+    """generate tests classes from test info
+
+    return the list of generated test classes
+    """
+    if filter_rgx:
+        is_to_run = re.compile(filter_rgx).search
+    else:
+        is_to_run = lambda x: 1
+    tests = []
+    for module_file, messages_file in (
+            get_tests_info(input_dir, msg_dir, 'func_', '')
+    ):
+        if not is_to_run(module_file):
+            continue
+        base = module_file.replace('func_', '').replace('.py', '')
+
+        dependencies = get_tests_info(input_dir, msg_dir, base, '.py')
+
+        for callback in callbacks:
+            test = callback(input_dir, msg_dir, module_file, messages_file,
+                            dependencies)
+            if test:
+                tests.append(test)
+    return tests
diff --git a/third_party/pylint/utils.py b/third_party/pylint/utils.py
index 9c8e8f4..b476dc1 100644
--- a/third_party/pylint/utils.py
+++ b/third_party/pylint/utils.py
@@ -1,5 +1,4 @@
-# Copyright (c) 2003-2010 Sylvain Thenault ([email protected]).
-# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
 # http://www.logilab.fr/ -- mailto:[email protected]
 #
 # This program is free software; you can redistribute it and/or modify it under
@@ -13,29 +12,36 @@
 #
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """some various utilities and helper classes, most of them used in the
 main pylint class
 """
 
+import re
 import sys
-from os import linesep
+import tokenize
+import os
+from warnings import warn
 from os.path import dirname, basename, splitext, exists, isdir, join, normpath
 
-from logilab.common.modutils import modpath_from_file, get_module_files, \
-                                    file_from_modpath
+from logilab.common.interface import implements
 from logilab.common.textutils import normalize_text
 from logilab.common.configuration import rest_format_section
 from logilab.common.ureports import Section
 
-from logilab.astng import nodes, Module
+from astroid import nodes, Module
+from astroid.modutils import modpath_from_file, get_module_files, \
+    file_from_modpath, load_module_from_file
 
-from pylint.checkers import EmptyReport
+from pylint.interfaces import IRawChecker, ITokenChecker
 
 
 class UnknownMessage(Exception):
     """raised when a unregistered message id is encountered"""
 
+class EmptyReport(Exception):
+    """raised when a report is empty and so should not be displayed"""
+
 
 MSG_TYPES = {
     'I' : 'info',
@@ -57,17 +63,18 @@
     }
 
 _MSG_ORDER = 'EWRCIF'
+MSG_STATE_SCOPE_CONFIG = 0
+MSG_STATE_SCOPE_MODULE = 1
 
-def sort_msgs(msgids):
-    """sort message identifiers according to their category first"""
-    msgs = {}
-    for msg in msgids:
-        msgs.setdefault(msg[0], []).append(msg)
-    result = []
-    for m_id in _MSG_ORDER:
-        if m_id in msgs:
-            result.extend( sorted(msgs[m_id]) )
-    return result
+OPTION_RGX = re.compile(r'\s*#.*\bpylint:(.*)')
+
+# The line/node distinction does not apply to fatal errors and reports.
+_SCOPE_EXEMPT = 'FR'
+
+class WarningScope(object):
+    LINE = 'line-based-msg'
+    NODE = 'node-based-msg'
+
 
 def get_module_and_frameid(node):
     """return the module name and the frame id in the module"""
@@ -92,161 +99,228 @@
     return MSG_TYPES_LONG.get(id)
 
 
-class Message:
-    def __init__(self, checker, msgid, msg, descr):
+def tokenize_module(module):
+    stream = module.file_stream
+    stream.seek(0)
+    readline = stream.readline
+    if sys.version_info < (3, 0):
+        if module.file_encoding is not None:
+            readline = lambda: stream.readline().decode(module.file_encoding,
+                                                        'replace')
+        return list(tokenize.generate_tokens(readline))
+    return list(tokenize.tokenize(readline))
+
+def build_message_def(checker, msgid, msg_tuple):
+    if implements(checker, (IRawChecker, ITokenChecker)):
+        default_scope = WarningScope.LINE
+    else:
+        default_scope = WarningScope.NODE
+    options = {}
+    if len(msg_tuple) > 3:
+        (msg, symbol, descr, options) = msg_tuple
+    elif len(msg_tuple) > 2:
+        (msg, symbol, descr) = msg_tuple[:3]
+    else:
+        # messages should have a symbol, but for backward compatibility
+        # they may not.
+        (msg, descr) = msg_tuple
+        warn("[pylint 0.26] description of message %s doesn't include "
+             "a symbolic name" % msgid, DeprecationWarning)
+        symbol = None
+    options.setdefault('scope', default_scope)
+    return MessageDefinition(checker, msgid, msg, descr, symbol, **options)
+
+
+class MessageDefinition(object):
+    def __init__(self, checker, msgid, msg, descr, symbol, scope,
+                 minversion=None, maxversion=None, old_names=None):
+        self.checker = checker
         assert len(msgid) == 5, 'Invalid message id %s' % msgid
         assert msgid[0] in MSG_TYPES, \
                'Bad message type %s in %r' % (msgid[0], msgid)
         self.msgid = msgid
         self.msg = msg
         self.descr = descr
-        self.checker = checker
+        self.symbol = symbol
+        self.scope = scope
+        self.minversion = minversion
+        self.maxversion = maxversion
+        self.old_names = old_names or []
 
-class MessagesHandlerMixIn:
+    def may_be_emitted(self):
+        """return True if message may be emitted using the current interpreter"""
+        if self.minversion is not None and self.minversion > sys.version_info:
+            return False
+        if self.maxversion is not None and self.maxversion <= sys.version_info:
+            return False
+        return True
+
+    def format_help(self, checkerref=False):
+        """return the help string for the given message id"""
+        desc = self.descr
+        if checkerref:
+            desc += ' This message belongs to the %s checker.' % \
+                   self.checker.name
+        title = self.msg
+        if self.symbol:
+            msgid = '%s (%s)' % (self.symbol, self.msgid)
+        else:
+            msgid = self.msgid
+        if self.minversion or self.maxversion:
+            restr = []
+            if self.minversion:
+                restr.append('< %s' % '.'.join([str(n) for n in self.minversion]))
+            if self.maxversion:
+                restr.append('>= %s' % '.'.join([str(n) for n in self.maxversion]))
+            restr = ' or '.join(restr)
+            if checkerref:
+                desc += " It can't be emitted when using Python %s." % restr
+            else:
+                desc += " This message can't be emitted when using Python %s." % restr
+        desc = normalize_text(' '.join(desc.split()), indent='  ')
+        if title != '%s':
+            title = title.splitlines()[0]
+            return ':%s: *%s*\n%s' % (msgid, title, desc)
+        return ':%s:\n%s' % (msgid, desc)
+
+
+class MessagesHandlerMixIn(object):
     """a mix-in class containing all the messages related methods for the main
     lint class
     """
 
     def __init__(self):
-        # dictionary of registered messages
-        self._messages = {}
         self._msgs_state = {}
-        self._module_msgs_state = {} # None
-        self._msgs_by_category = {}
         self.msg_status = 0
 
-    def register_messages(self, checker):
-        """register a dictionary of messages
-
-        Keys are message ids, values are a 2-uple with the message type and the
-        message itself
-
-        message ids should be a string of len 4, where the two first characters
-        are the checker id and the two last the message id in this checker
-        """
-        msgs_dict = checker.msgs
-        chkid = None
-        for msgid, (msg, msgdescr) in msgs_dict.items():
-            # avoid duplicate / malformed ids
-            assert msgid not in self._messages, \
-                   'Message id %r is already defined' % msgid
-            assert chkid is None or chkid == msgid[1:3], \
-                   'Inconsistent checker part in message id %r' % msgid
-            chkid = msgid[1:3]
-            self._messages[msgid] = Message(checker, msgid, msg, msgdescr)
-            self._msgs_by_category.setdefault(msgid[0], []).append(msgid)
-
-    def get_message_help(self, msgid, checkerref=False):
-        """return the help string for the given message id"""
-        msg = self.check_message_id(msgid)
-        desc = normalize_text(' '.join(msg.descr.split()), indent='  ')
-        if checkerref:
-            desc += ' This message belongs to the %s checker.' % \
-                   msg.checker.name
-        title = msg.msg
-        if title != '%s':
-            title = title.splitlines()[0]
-            return ':%s: *%s*\n%s' % (msg.msgid, title, desc)
-        return ':%s:\n%s' % (msg.msgid, desc)
-
-    def disable(self, msgid, scope='package', line=None):
+    def disable(self, msgid, scope='package', line=None, ignore_unknown=False):
         """don't output message of the given id"""
         assert scope in ('package', 'module')
+        # handle disable=all by disabling all categories
+        if msgid == 'all':
+            for msgid in MSG_TYPES:
+                self.disable(msgid, scope, line)
+            return
         # msgid is a category?
         catid = category_id(msgid)
         if catid is not None:
-            for msgid in self._msgs_by_category.get(catid):
-                self.disable(msgid, scope, line)
+            for _msgid in self.msgs_store._msgs_by_category.get(catid):
+                self.disable(_msgid, scope, line)
             return
         # msgid is a checker name?
         if msgid.lower() in self._checkers:
+            msgs_store = self.msgs_store
             for checker in self._checkers[msgid.lower()]:
-                for msgid in checker.msgs:
-                    self.disable(msgid, scope, line)
+                for _msgid in checker.msgs:
+                    if _msgid in msgs_store._alternative_names:
+                        self.disable(_msgid, scope, line)
             return
         # msgid is report id?
         if msgid.lower().startswith('rp'):
             self.disable_report(msgid)
             return
-        # msgid is a msgid.
-        msg = self.check_message_id(msgid)
+
+        try:
+            # msgid is a symbolic or numeric msgid.
+            msg = self.msgs_store.check_message_id(msgid)
+        except UnknownMessage:
+            if ignore_unknown:
+                return
+            raise
+
         if scope == 'module':
-            assert line > 0
-            try:
-                self._module_msgs_state[msg.msgid][line] = False
-            except KeyError:
-                self._module_msgs_state[msg.msgid] = {line: False}
-                if msgid != 'I0011':
-                    self.add_message('I0011', line=line, args=msg.msgid)
+            self.file_state.set_msg_status(msg, line, False)
+            if msg.symbol != 'locally-disabled':
+                self.add_message('locally-disabled', line=line,
+                                 args=(msg.symbol, msg.msgid))
 
         else:
             msgs = self._msgs_state
             msgs[msg.msgid] = False
             # sync configuration object
-            self.config.disable_msg = [mid for mid, val in msgs.items()
+            self.config.disable_msg = [mid for mid, val in msgs.iteritems()
                                        if not val]
 
-    def enable(self, msgid, scope='package', line=None):
+    def enable(self, msgid, scope='package', line=None, ignore_unknown=False):
         """reenable message of the given id"""
         assert scope in ('package', 'module')
         catid = category_id(msgid)
         # msgid is a category?
         if catid is not None:
-            for msgid in self._msgs_by_category.get(catid):
+            for msgid in self.msgs_store._msgs_by_category.get(catid):
                 self.enable(msgid, scope, line)
             return
         # msgid is a checker name?
         if msgid.lower() in self._checkers:
             for checker in self._checkers[msgid.lower()]:
-                for msgid in checker.msgs:
-                    self.enable(msgid, scope, line)
+                for msgid_ in checker.msgs:
+                    self.enable(msgid_, scope, line)
             return
         # msgid is report id?
         if msgid.lower().startswith('rp'):
             self.enable_report(msgid)
             return
-        # msgid is a msgid.
-        msg = self.check_message_id(msgid)
+
+        try:
+            # msgid is a symbolic or numeric msgid.
+            msg = self.msgs_store.check_message_id(msgid)
+        except UnknownMessage:
+            if ignore_unknown:
+                return
+            raise
+
         if scope == 'module':
-            assert line > 0
-            try:
-                self._module_msgs_state[msg.msgid][line] = True
-            except KeyError:
-                self._module_msgs_state[msg.msgid] = {line: True}
-                self.add_message('I0012', line=line, args=msg.msgid)
+            self.file_state.set_msg_status(msg, line, True)
+            self.add_message('locally-enabled', line=line, args=(msg.symbol, msg.msgid))
         else:
             msgs = self._msgs_state
             msgs[msg.msgid] = True
             # sync configuration object
-            self.config.enable = [mid for mid, val in msgs.items() if val]
+            self.config.enable = [mid for mid, val in msgs.iteritems() if val]
 
-    def check_message_id(self, msgid):
-        """raise UnknownMessage if the message id is not defined"""
-        msgid = msgid.upper()
-        try:
-            return self._messages[msgid]
-        except KeyError:
-            raise UnknownMessage('No such message id %s' % msgid)
-
-    def is_message_enabled(self, msgid, line=None):
+    def is_message_enabled(self, msg_descr, line=None):
         """return true if the message associated to the given message id is
         enabled
+
+        msgid may be either a numeric or symbolic message id.
         """
+        try:
+            msgid = self.msgs_store.check_message_id(msg_descr).msgid
+        except UnknownMessage:
+            # The linter checks for messages that are not registered
+            # due to version mismatch, just treat them as message IDs
+            # for now.
+            msgid = msg_descr
         if line is None:
             return self._msgs_state.get(msgid, True)
         try:
-            return self._module_msgs_state[msgid][line]
-        except (KeyError, TypeError):
+            return self.file_state._module_msgs_state[msgid][line]
+        except KeyError:
             return self._msgs_state.get(msgid, True)
 
-    def add_message(self, msgid, line=None, node=None, args=None):
-        """add the message corresponding to the given id.
+    def add_message(self, msg_descr, line=None, node=None, args=None):
+        """Adds a message given by ID or name.
 
-        If provided, msg is expanded using args
+        If provided, the message string is expanded using args
 
-        astng checkers should provide the node argument, raw checkers should
-        provide the line argument.
+        AST checkers should must the node argument (but may optionally
+        provide line if the line number is different), raw and token checkers
+        must provide the line argument.
         """
+        msg_info = self.msgs_store.check_message_id(msg_descr)
+        msgid = msg_info.msgid
+        # backward compatibility, message may not have a symbol
+        symbol = msg_info.symbol or msgid
+        # Fatal messages and reports are special, the node/scope distinction
+        # does not apply to them.
+        if msgid[0] not in _SCOPE_EXEMPT:
+            if msg_info.scope == WarningScope.LINE:
+                assert node is None and line is not None, (
+                    'Message %s must only provide line, got line=%s, node=%s' % (msgid, line, node))
+            elif msg_info.scope == WarningScope.NODE:
+                # Node-based warnings may provide an override line.
+                assert node is not None, 'Message %s must provide Node, got None'
+
         if line is None and node is not None:
             line = node.fromlineno
         if hasattr(node, 'col_offset'):
@@ -255,6 +329,7 @@
             col_offset = None
         # should this message be displayed
         if not self.is_message_enabled(msgid, line):
+            self.file_state.handle_ignored_message(msgid, line, node, args)
             return
         # update stats
         msg_cat = MSG_TYPES[msgid[0]]
@@ -262,11 +337,11 @@
         self.stats[msg_cat] += 1
         self.stats['by_module'][self.current_name][msg_cat] += 1
         try:
-            self.stats['by_msg'][msgid] += 1
+            self.stats['by_msg'][symbol] += 1
         except KeyError:
-            self.stats['by_msg'][msgid] = 1
-        msg = self._messages[msgid].msg
+            self.stats['by_msg'][symbol] = 1
         # expand message ?
+        msg = msg_info.msg
         if args:
             msg %= args
         # get module and object
@@ -279,17 +354,6 @@
         # add the message
         self.reporter.add_message(msgid, (path, module, obj, line or 1, col_offset or 0), msg)
 
-    def help_message(self, msgids):
-        """display help messages for the given message identifiers"""
-        for msgid in msgids:
-            try:
-                print self.get_message_help(msgid, True)
-                print
-            except UnknownMessage, ex:
-                print ex
-                print
-                continue
-
     def print_full_documentation(self):
         """output a full documentation in ReST format"""
         by_checker = {}
@@ -317,7 +381,7 @@
                     by_checker[checker.name] = [list(checker.options_and_values()),
                                                 dict(checker.msgs),
                                                 list(checker.reports)]
-        for checker, (options, msgs, reports) in by_checker.items():
+        for checker, (options, msgs, reports) in by_checker.iteritems():
             prefix = ''
             title = '%s checker' % checker
             print title
@@ -333,8 +397,10 @@
                 title = ('%smessages' % prefix).capitalize()
                 print title
                 print '~' * len(title)
-                for msgid in sort_msgs(msgs.keys()):
-                    print self.get_message_help(msgid, False)
+                for msgid, msg in sorted(msgs.iteritems(),
+                                         key=lambda (k, v): (_MSG_ORDER.index(k[0]), k)):
+                    msg = build_message_def(checker, msgid, msg)
+                    print msg.format_help(checkerref=False)
                 print
             if reports:
                 title = ('%sreports' % prefix).capitalize()
@@ -345,19 +411,228 @@
                 print
             print
 
+
+class FileState(object):
+    """Hold internal state specific to the currently analyzed file"""
+
+    def __init__(self, modname=None):
+        self.base_name = modname
+        self._module_msgs_state = {}
+        self._raw_module_msgs_state = {}
+        self._ignored_msgs = {}
+        self._suppression_mapping = {}
+
+    def collect_block_lines(self, msgs_store, module_node):
+        """Walk the AST to collect block level options line numbers."""
+        for msg, lines in self._module_msgs_state.iteritems():
+            self._raw_module_msgs_state[msg] = lines.copy()
+        orig_state = self._module_msgs_state.copy()
+        self._module_msgs_state = {}
+        self._suppression_mapping = {}
+        self._collect_block_lines(msgs_store, module_node, orig_state)
+
+    def _collect_block_lines(self, msgs_store, node, msg_state):
+        """Recursivly walk (depth first) AST to collect block level options line
+        numbers.
+        """
+        for child in node.get_children():
+            self._collect_block_lines(msgs_store, child, msg_state)
+        first = node.fromlineno
+        last = node.tolineno
+        # first child line number used to distinguish between disable
+        # which are the first child of scoped node with those defined later.
+        # For instance in the code below:
+        #
+        # 1.   def meth8(self):
+        # 2.        """test late disabling"""
+        # 3.        # pylint: disable=E1102
+        # 4.        print self.blip
+        # 5.        # pylint: disable=E1101
+        # 6.        print self.bla
+        #
+        # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6
+        #
+        # this is necessary to disable locally messages applying to class /
+        # function using their fromlineno
+        if isinstance(node, (nodes.Module, nodes.Class, nodes.Function)) and node.body:
+            firstchildlineno = node.body[0].fromlineno
+        else:
+            firstchildlineno = last
+        for msgid, lines in msg_state.iteritems():
+            for lineno, state in lines.items():
+                original_lineno = lineno
+                if first <= lineno <= last:
+                    # Set state for all lines for this block, if the
+                    # warning is applied to nodes.
+                    if  msgs_store.check_message_id(msgid).scope == WarningScope.NODE:
+                        if lineno > firstchildlineno:
+                            state = True
+                        first_, last_ = node.block_range(lineno)
+                    else:
+                        first_ = lineno
+                        last_ = last
+                    for line in xrange(first_, last_+1):
+                        # do not override existing entries
+                        if not line in self._module_msgs_state.get(msgid, ()):
+                            if line in lines: # state change in the same block
+                                state = lines[line]
+                                original_lineno = line
+                            if not state:
+                                self._suppression_mapping[(msgid, line)] = original_lineno
+                            try:
+                                self._module_msgs_state[msgid][line] = state
+                            except KeyError:
+                                self._module_msgs_state[msgid] = {line: state}
+                    del lines[lineno]
+
+    def set_msg_status(self, msg, line, status):
+        """Set status (enabled/disable) for a given message at a given line"""
+        assert line > 0
+        try:
+            self._module_msgs_state[msg.msgid][line] = status
+        except KeyError:
+            self._module_msgs_state[msg.msgid] = {line: status}
+
+    def handle_ignored_message(self, msgid, line, node, args):
+        """Report an ignored message.
+
+        state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG,
+        depending on whether the message was disabled locally in the module,
+        or globally. The other arguments are the same as for add_message.
+        """
+        state_scope = self._message_state_scope(msgid, line)
+        if state_scope == MSG_STATE_SCOPE_MODULE:
+            try:
+                orig_line = self._suppression_mapping[(msgid, line)]
+                self._ignored_msgs.setdefault((msgid, orig_line), set()).add(line)
+            except KeyError:
+                pass
+
+    def _message_state_scope(self, msgid, line=None):
+        """Returns the scope at which a message was enabled/disabled."""
+        try:
+            if line in self._module_msgs_state[msgid]:
+                return MSG_STATE_SCOPE_MODULE
+        except KeyError:
+            return MSG_STATE_SCOPE_CONFIG
+
+    def iter_spurious_suppression_messages(self, msgs_store):
+        for warning, lines in self._raw_module_msgs_state.iteritems():
+            for line, enable in lines.iteritems():
+                if not enable and (warning, line) not in self._ignored_msgs:
+                    yield 'useless-suppression', line, \
+                        (msgs_store.get_msg_display_string(warning),)
+        # don't use iteritems here, _ignored_msgs may be modified by add_message
+        for (warning, from_), lines in self._ignored_msgs.items():
+            for line in lines:
+                yield 'suppressed-message', line, \
+                    (msgs_store.get_msg_display_string(warning), from_)
+
+
+class MessagesStore(object):
+    """The messages store knows information about every possible message but has
+    no particular state during analysis.
+    """
+
+    def __init__(self):
+        # Primary registry for all active messages (i.e. all messages
+        # that can be emitted by pylint for the underlying Python
+        # version). It contains the 1:1 mapping from symbolic names
+        # to message definition objects.
+        self._messages = {}
+        # Maps alternative names (numeric IDs, deprecated names) to
+        # message definitions. May contain several names for each definition
+        # object.
+        self._alternative_names = {}
+        self._msgs_by_category = {}
+
+    @property
+    def messages(self):
+        """The list of all active messages."""
+        return self._messages.itervalues()
+
+    def add_renamed_message(self, old_id, old_symbol, new_symbol):
+        """Register the old ID and symbol for a warning that was renamed.
+
+        This allows users to keep using the old ID/symbol in suppressions.
+        """
+        msg = self.check_message_id(new_symbol)
+        msg.old_names.append((old_id, old_symbol))
+        self._alternative_names[old_id] = msg
+        self._alternative_names[old_symbol] = msg
+
+    def register_messages(self, checker):
+        """register a dictionary of messages
+
+        Keys are message ids, values are a 2-uple with the message type and the
+        message itself
+
+        message ids should be a string of len 4, where the two first characters
+        are the checker id and the two last the message id in this checker
+        """
+        chkid = None
+        for msgid, msg_tuple in checker.msgs.iteritems():
+            msg = build_message_def(checker, msgid, msg_tuple)
+            assert msg.symbol not in self._messages, \
+                    'Message symbol %r is already defined' % msg.symbol
+            # avoid duplicate / malformed ids
+            assert msg.msgid not in self._alternative_names, \
+                   'Message id %r is already defined' % msgid
+            assert chkid is None or chkid == msg.msgid[1:3], \
+                   'Inconsistent checker part in message id %r' % msgid
+            chkid = msg.msgid[1:3]
+            self._messages[msg.symbol] = msg
+            self._alternative_names[msg.msgid] = msg
+            for old_id, old_symbol in msg.old_names:
+                self._alternative_names[old_id] = msg
+                self._alternative_names[old_symbol] = msg
+            self._msgs_by_category.setdefault(msg.msgid[0], []).append(msg.msgid)
+
+    def check_message_id(self, msgid):
+        """returns the Message object for this message.
+
+        msgid may be either a numeric or symbolic id.
+
+        Raises UnknownMessage if the message id is not defined.
+        """
+        if msgid[1:].isdigit():
+            msgid = msgid.upper()
+        for source in (self._alternative_names, self._messages):
+            try:
+                return source[msgid]
+            except KeyError:
+                pass
+        raise UnknownMessage('No such message id %s' % msgid)
+
+    def get_msg_display_string(self, msgid):
+        """Generates a user-consumable representation of a message.
+
+        Can be just the message ID or the ID and the symbol.
+        """
+        return repr(self.check_message_id(msgid).symbol)
+
+    def help_message(self, msgids):
+        """display help messages for the given message identifiers"""
+        for msgid in msgids:
+            try:
+                print self.check_message_id(msgid).format_help(checkerref=True)
+                print
+            except UnknownMessage, ex:
+                print ex
+                print
+                continue
+
     def list_messages(self):
         """output full messages list documentation in ReST format"""
-        msgids = []
-        for checker in self.get_checkers():
-            for msgid in checker.msgs.keys():
-                msgids.append(msgid)
-        msgids.sort()
-        for msgid in msgids:
-            print self.get_message_help(msgid, False)
+        msgs = sorted(self._messages.itervalues(), key=lambda msg: msg.msgid)
+        for msg in msgs:
+            if not msg.may_be_emitted():
+                continue
+            print msg.format_help(checkerref=False)
         print
 
 
-class ReportsHandlerMixIn:
+class ReportsHandlerMixIn(object):
     """a mix-in class containing all the reports and stats manipulation
     related methods for the main lint class
     """
@@ -374,7 +649,7 @@
         checker is the checker defining the report
         """
         reportid = reportid.upper()
-        self._reports.setdefault(checker, []).append( (reportid, r_title, r_cb) )
+        self._reports.setdefault(checker, []).append((reportid, r_title, r_cb))
 
     def enable_report(self, reportid):
         """disable the report of the given id"""
@@ -394,9 +669,6 @@
 
     def make_reports(self, stats, old_stats):
         """render registered reports"""
-        if self.config.files_output:
-            filename = 'pylint_global.' + self.reporter.extension
-            self.reporter.set_output(open(filename, 'w'))
         sect = Section('Report',
                        '%s statements analysed.'% (self.stats['statement']))
         for checker in self._reports:
@@ -410,13 +682,13 @@
                     continue
                 report_sect.report_id = reportid
                 sect.append(report_sect)
-        self.reporter.display_results(sect)
+        return sect
 
     def add_stats(self, **kwargs):
         """add some stats entries to the statistic dictionary
         raise an AssertionError if there is a key conflict
         """
-        for key, value in kwargs.items():
+        for key, value in kwargs.iteritems():
             if key[-1] == '_':
                 key = key[:-1]
             assert key not in self.stats
@@ -447,24 +719,25 @@
             try:
                 filepath = file_from_modpath(modname.split('.'))
                 if filepath is None:
-                    errors.append( {'key' : 'F0003', 'mod': modname} )
+                    errors.append({'key' : 'ignored-builtin-module', 'mod': modname})
                     continue
             except (ImportError, SyntaxError), ex:
                 # FIXME p3k : the SyntaxError is a Python bug and should be
                 # removed as soon as possible http://bugs.python.org/issue10588
-                errors.append( {'key': 'F0001', 'mod': modname, 'ex': ex} )
+                errors.append({'key': 'fatal', 'mod': modname, 'ex': ex})
                 continue
         filepath = normpath(filepath)
-        result.append( {'path': filepath, 'name': modname,
-                        'basepath': filepath, 'basename': modname} )
+        result.append({'path': filepath, 'name': modname, 'isarg': True,
+                       'basepath': filepath, 'basename': modname})
         if not (modname.endswith('.__init__') or modname == '__init__') \
                 and '__init__.py' in filepath:
             for subfilepath in get_module_files(dirname(filepath), black_list):
                 if filepath == subfilepath:
                     continue
                 submodname = '.'.join(modpath_from_file(subfilepath))
-                result.append( {'path': subfilepath, 'name': submodname,
-                                'basepath': filepath, 'basename': modname} )
+                result.append({'path': subfilepath, 'name': submodname,
+                               'isarg': False,
+                               'basepath': filepath, 'basename': modname})
     return result, errors
 
 
@@ -477,6 +750,14 @@
         self.leave_events = {}
         self.linter = linter
 
+    def _is_method_enabled(self, method):
+        if not hasattr(method, 'checks_msgs'):
+            return True
+        for msg_desc in method.checks_msgs:
+            if self.linter.is_message_enabled(msg_desc):
+                return True
+        return False
+
     def add_checker(self, checker):
         """walk to the checker's dir and collect visit and leave methods"""
         # XXX : should be possible to merge needed_checkers and add_checker
@@ -484,7 +765,6 @@
         lcids = set()
         visits = self.visit_events
         leaves = self.leave_events
-        msgs = self.linter._msgs_state
         for member in dir(checker):
             cid = member[6:]
             if cid == 'default':
@@ -492,19 +772,15 @@
             if member.startswith('visit_'):
                 v_meth = getattr(checker, member)
                 # don't use visit_methods with no activated message:
-                if hasattr(v_meth, 'checks_msgs'):
-                    if not any(msgs.get(m, True) for m in v_meth.checks_msgs):
-                        continue
-                visits.setdefault(cid, []).append(v_meth)
-                vcids.add(cid)
+                if self._is_method_enabled(v_meth):
+                    visits.setdefault(cid, []).append(v_meth)
+                    vcids.add(cid)
             elif member.startswith('leave_'):
                 l_meth = getattr(checker, member)
                 # don't use leave_methods with no activated message:
-                if hasattr(l_meth, 'checks_msgs'):
-                    if not any(msgs.get(m, True) for m in l_meth.checks_msgs):
-                        continue
-                leaves.setdefault(cid, []).append(l_meth)
-                lcids.add(cid)
+                if self._is_method_enabled(l_meth):
+                    leaves.setdefault(cid, []).append(l_meth)
+                    lcids.add(cid)
         visit_default = getattr(checker, 'visit_default', None)
         if visit_default:
             for cls in nodes.ALL_NODE_CLASSES:
@@ -513,19 +789,65 @@
                     visits.setdefault(cid, []).append(visit_default)
         # for now we have no "leave_default" method in Pylint
 
-    def walk(self, astng):
-        """call visit events of astng checkers for the given node, recurse on
+    def walk(self, astroid):
+        """call visit events of astroid checkers for the given node, recurse on
         its children, then leave events.
         """
-        cid = astng.__class__.__name__.lower()
-        if astng.is_statement:
+        cid = astroid.__class__.__name__.lower()
+        if astroid.is_statement:
             self.nbstatements += 1
         # generate events for this node on each checker
         for cb in self.visit_events.get(cid, ()):
-            cb(astng)
+            cb(astroid)
         # recurse on children
-        for child in astng.get_children():
+        for child in astroid.get_children():
             self.walk(child)
         for cb in self.leave_events.get(cid, ()):
-            cb(astng)
+            cb(astroid)
 
+
+PY_EXTS = ('.py', '.pyc', '.pyo', '.pyw', '.so', '.dll')
+
+def register_plugins(linter, directory):
+    """load all module and package in the given directory, looking for a
+    'register' function in each one, used to register pylint checkers
+    """
+    imported = {}
+    for filename in os.listdir(directory):
+        base, extension = splitext(filename)
+        if base in imported or base == '__pycache__':
+            continue
+        if extension in PY_EXTS and base != '__init__' or (
+                not extension and isdir(join(directory, base))):
+            try:
+                module = load_module_from_file(join(directory, filename))
+            except ValueError:
+                # empty module name (usually emacs auto-save files)
+                continue
+            except ImportError, exc:
+                print >> sys.stderr, "Problem importing module %s: %s" % (filename, exc)
+            else:
+                if hasattr(module, 'register'):
+                    module.register(linter)
+                    imported[base] = 1
+
+def get_global_option(checker, option, default=None):
+    """ Retrieve an option defined by the given *checker* or
+    by all known option providers.
+
+    It will look in the list of all options providers
+    until the given *option* will be found.
+    If the option wasn't found, the *default* value will be returned.
+    """
+    # First, try in the given checker's config.
+    # After that, look in the options providers.
+
+    try:
+        return getattr(checker.config, option.replace("-", "_"))
+    except AttributeError:
+        pass
+    for provider in checker.linter.options_providers:
+        for options in provider.options:
+            if options[0] == option:
+                return getattr(provider.config, option.replace("-", "_"))
+    return default
diff --git a/third_party/six/LICENSE.txt b/third_party/six/LICENSE.txt
new file mode 100644
index 0000000..d76e024
--- /dev/null
+++ b/third_party/six/LICENSE.txt
@@ -0,0 +1,18 @@
+Copyright (c) 2010-2014 Benjamin Peterson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/third_party/six/README.chromium b/third_party/six/README.chromium
new file mode 100644
index 0000000..7573e6b
--- /dev/null
+++ b/third_party/six/README.chromium
@@ -0,0 +1,10 @@
+URL: https://pypi.python.org/pypi/six
+Version: 1.8.0
+License: MIT
+License File: LICENSE.txt
+
+Description:
+This directory contains the Python six module.
+
+Local Modifications:
+None
diff --git a/third_party/six/__init__.py b/third_party/six/__init__.py
new file mode 100644
index 0000000..21b0e80
--- /dev/null
+++ b/third_party/six/__init__.py
@@ -0,0 +1,762 @@
+"""Utilities for writing code that runs on Python 2 and 3"""
+
+# Copyright (c) 2010-2014 Benjamin Peterson
+#
+# 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.
+
+from __future__ import absolute_import
+
+import functools
+import operator
+import sys
+import types
+
+__author__ = "Benjamin Peterson <[email protected]>"
+__version__ = "1.8.0"
+
+
+# Useful for very coarse version differentiation.
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+    string_types = str,
+    integer_types = int,
+    class_types = type,
+    text_type = str
+    binary_type = bytes
+
+    MAXSIZE = sys.maxsize
+else:
+    string_types = basestring,
+    integer_types = (int, long)
+    class_types = (type, types.ClassType)
+    text_type = unicode
+    binary_type = str
+
+    if sys.platform.startswith("java"):
+        # Jython always uses 32 bits.
+        MAXSIZE = int((1 << 31) - 1)
+    else:
+        # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
+        class X(object):
+            def __len__(self):
+                return 1 << 31
+        try:
+            len(X())
+        except OverflowError:
+            # 32-bit
+            MAXSIZE = int((1 << 31) - 1)
+        else:
+            # 64-bit
+            MAXSIZE = int((1 << 63) - 1)
+        del X
+
+
+def _add_doc(func, doc):
+    """Add documentation to a function."""
+    func.__doc__ = doc
+
+
+def _import_module(name):
+    """Import module, returning the module after the last dot."""
+    __import__(name)
+    return sys.modules[name]
+
+
+class _LazyDescr(object):
+
+    def __init__(self, name):
+        self.name = name
+
+    def __get__(self, obj, tp):
+        result = self._resolve()
+        setattr(obj, self.name, result) # Invokes __set__.
+        # This is a bit ugly, but it avoids running this again.
+        delattr(obj.__class__, self.name)
+        return result
+
+
+class MovedModule(_LazyDescr):
+
+    def __init__(self, name, old, new=None):
+        super(MovedModule, self).__init__(name)
+        if PY3:
+            if new is None:
+                new = name
+            self.mod = new
+        else:
+            self.mod = old
+
+    def _resolve(self):
+        return _import_module(self.mod)
+
+    def __getattr__(self, attr):
+        _module = self._resolve()
+        value = getattr(_module, attr)
+        setattr(self, attr, value)
+        return value
+
+
+class _LazyModule(types.ModuleType):
+
+    def __init__(self, name):
+        super(_LazyModule, self).__init__(name)
+        self.__doc__ = self.__class__.__doc__
+
+    def __dir__(self):
+        attrs = ["__doc__", "__name__"]
+        attrs += [attr.name for attr in self._moved_attributes]
+        return attrs
+
+    # Subclasses should override this
+    _moved_attributes = []
+
+
+class MovedAttribute(_LazyDescr):
+
+    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
+        super(MovedAttribute, self).__init__(name)
+        if PY3:
+            if new_mod is None:
+                new_mod = name
+            self.mod = new_mod
+            if new_attr is None:
+                if old_attr is None:
+                    new_attr = name
+                else:
+                    new_attr = old_attr
+            self.attr = new_attr
+        else:
+            self.mod = old_mod
+            if old_attr is None:
+                old_attr = name
+            self.attr = old_attr
+
+    def _resolve(self):
+        module = _import_module(self.mod)
+        return getattr(module, self.attr)
+
+
+class _SixMetaPathImporter(object):
+    """
+    A meta path importer to import six.moves and its submodules.
+
+    This class implements a PEP302 finder and loader. It should be compatible
+    with Python 2.5 and all existing versions of Python3
+    """
+    def __init__(self, six_module_name):
+        self.name = six_module_name
+        self.known_modules = {}
+
+    def _add_module(self, mod, *fullnames):
+        for fullname in fullnames:
+            self.known_modules[self.name + "." + fullname] = mod
+
+    def _get_module(self, fullname):
+        return self.known_modules[self.name + "." + fullname]
+
+    def find_module(self, fullname, path=None):
+        if fullname in self.known_modules:
+            return self
+        return None
+
+    def __get_module(self, fullname):
+        try:
+            return self.known_modules[fullname]
+        except KeyError:
+            raise ImportError("This loader does not know module " + fullname)
+
+    def load_module(self, fullname):
+        try:
+            # in case of a reload
+            return sys.modules[fullname]
+        except KeyError:
+            pass
+        mod = self.__get_module(fullname)
+        if isinstance(mod, MovedModule):
+            mod = mod._resolve()
+        else:
+            mod.__loader__ = self
+        sys.modules[fullname] = mod
+        return mod
+
+    def is_package(self, fullname):
+        """
+        Return true, if the named module is a package.
+
+        We need this method to get correct spec objects with
+        Python 3.4 (see PEP451)
+        """
+        return hasattr(self.__get_module(fullname), "__path__")
+
+    def get_code(self, fullname):
+        """Return None
+
+        Required, if is_package is implemented"""
+        self.__get_module(fullname)  # eventually raises ImportError
+        return None
+    get_source = get_code  # same as get_code
+
+_importer = _SixMetaPathImporter(__name__)
+
+
+class _MovedItems(_LazyModule):
+    """Lazy loading of moved objects"""
+    __path__ = []  # mark as package
+
+
+_moved_attributes = [
+    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
+    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
+    MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
+    MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
+    MovedAttribute("intern", "__builtin__", "sys"),
+    MovedAttribute("map", "itertools", "builtins", "imap", "map"),
+    MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
+    MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
+    MovedAttribute("reduce", "__builtin__", "functools"),
+    MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
+    MovedAttribute("StringIO", "StringIO", "io"),
+    MovedAttribute("UserDict", "UserDict", "collections"),
+    MovedAttribute("UserList", "UserList", "collections"),
+    MovedAttribute("UserString", "UserString", "collections"),
+    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
+    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
+    MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
+
+    MovedModule("builtins", "__builtin__"),
+    MovedModule("configparser", "ConfigParser"),
+    MovedModule("copyreg", "copy_reg"),
+    MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
+    MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
+    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
+    MovedModule("http_cookies", "Cookie", "http.cookies"),
+    MovedModule("html_entities", "htmlentitydefs", "html.entities"),
+    MovedModule("html_parser", "HTMLParser", "html.parser"),
+    MovedModule("http_client", "httplib", "http.client"),
+    MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
+    MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
+    MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
+    MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
+    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
+    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
+    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
+    MovedModule("cPickle", "cPickle", "pickle"),
+    MovedModule("queue", "Queue"),
+    MovedModule("reprlib", "repr"),
+    MovedModule("socketserver", "SocketServer"),
+    MovedModule("_thread", "thread", "_thread"),
+    MovedModule("tkinter", "Tkinter"),
+    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
+    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
+    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
+    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
+    MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
+    MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
+    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
+    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
+    MovedModule("tkinter_colorchooser", "tkColorChooser",
+                "tkinter.colorchooser"),
+    MovedModule("tkinter_commondialog", "tkCommonDialog",
+                "tkinter.commondialog"),
+    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
+    MovedModule("tkinter_font", "tkFont", "tkinter.font"),
+    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
+    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
+                "tkinter.simpledialog"),
+    MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
+    MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
+    MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
+    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
+    MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
+    MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
+    MovedModule("winreg", "_winreg"),
+]
+for attr in _moved_attributes:
+    setattr(_MovedItems, attr.name, attr)
+    if isinstance(attr, MovedModule):
+        _importer._add_module(attr, "moves." + attr.name)
+del attr
+
+_MovedItems._moved_attributes = _moved_attributes
+
+moves = _MovedItems(__name__ + ".moves")
+_importer._add_module(moves, "moves")
+
+
+class Module_six_moves_urllib_parse(_LazyModule):
+    """Lazy loading of moved objects in six.moves.urllib_parse"""
+
+
+_urllib_parse_moved_attributes = [
+    MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
+    MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
+    MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
+    MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
+    MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
+    MovedAttribute("urljoin", "urlparse", "urllib.parse"),
+    MovedAttribute("urlparse", "urlparse", "urllib.parse"),
+    MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
+    MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
+    MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
+    MovedAttribute("quote", "urllib", "urllib.parse"),
+    MovedAttribute("quote_plus", "urllib", "urllib.parse"),
+    MovedAttribute("unquote", "urllib", "urllib.parse"),
+    MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
+    MovedAttribute("urlencode", "urllib", "urllib.parse"),
+    MovedAttribute("splitquery", "urllib", "urllib.parse"),
+    MovedAttribute("splittag", "urllib", "urllib.parse"),
+    MovedAttribute("splituser", "urllib", "urllib.parse"),
+    MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
+    MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
+    MovedAttribute("uses_params", "urlparse", "urllib.parse"),
+    MovedAttribute("uses_query", "urlparse", "urllib.parse"),
+    MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
+]
+for attr in _urllib_parse_moved_attributes:
+    setattr(Module_six_moves_urllib_parse, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
+                      "moves.urllib_parse", "moves.urllib.parse")
+
+
+class Module_six_moves_urllib_error(_LazyModule):
+    """Lazy loading of moved objects in six.moves.urllib_error"""
+
+
+_urllib_error_moved_attributes = [
+    MovedAttribute("URLError", "urllib2", "urllib.error"),
+    MovedAttribute("HTTPError", "urllib2", "urllib.error"),
+    MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
+]
+for attr in _urllib_error_moved_attributes:
+    setattr(Module_six_moves_urllib_error, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
+                      "moves.urllib_error", "moves.urllib.error")
+
+
+class Module_six_moves_urllib_request(_LazyModule):
+    """Lazy loading of moved objects in six.moves.urllib_request"""
+
+
+_urllib_request_moved_attributes = [
+    MovedAttribute("urlopen", "urllib2", "urllib.request"),
+    MovedAttribute("install_opener", "urllib2", "urllib.request"),
+    MovedAttribute("build_opener", "urllib2", "urllib.request"),
+    MovedAttribute("pathname2url", "urllib", "urllib.request"),
+    MovedAttribute("url2pathname", "urllib", "urllib.request"),
+    MovedAttribute("getproxies", "urllib", "urllib.request"),
+    MovedAttribute("Request", "urllib2", "urllib.request"),
+    MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
+    MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
+    MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
+    MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
+    MovedAttribute("FileHandler", "urllib2", "urllib.request"),
+    MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
+    MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
+    MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
+    MovedAttribute("urlretrieve", "urllib", "urllib.request"),
+    MovedAttribute("urlcleanup", "urllib", "urllib.request"),
+    MovedAttribute("URLopener", "urllib", "urllib.request"),
+    MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
+    MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
+]
+for attr in _urllib_request_moved_attributes:
+    setattr(Module_six_moves_urllib_request, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
+                      "moves.urllib_request", "moves.urllib.request")
+
+
+class Module_six_moves_urllib_response(_LazyModule):
+    """Lazy loading of moved objects in six.moves.urllib_response"""
+
+
+_urllib_response_moved_attributes = [
+    MovedAttribute("addbase", "urllib", "urllib.response"),
+    MovedAttribute("addclosehook", "urllib", "urllib.response"),
+    MovedAttribute("addinfo", "urllib", "urllib.response"),
+    MovedAttribute("addinfourl", "urllib", "urllib.response"),
+]
+for attr in _urllib_response_moved_attributes:
+    setattr(Module_six_moves_urllib_response, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
+                      "moves.urllib_response", "moves.urllib.response")
+
+
+class Module_six_moves_urllib_robotparser(_LazyModule):
+    """Lazy loading of moved objects in six.moves.urllib_robotparser"""
+
+
+_urllib_robotparser_moved_attributes = [
+    MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
+]
+for attr in _urllib_robotparser_moved_attributes:
+    setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
+                      "moves.urllib_robotparser", "moves.urllib.robotparser")
+
+
+class Module_six_moves_urllib(types.ModuleType):
+    """Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
+    __path__ = []  # mark as package
+    parse = _importer._get_module("moves.urllib_parse")
+    error = _importer._get_module("moves.urllib_error")
+    request = _importer._get_module("moves.urllib_request")
+    response = _importer._get_module("moves.urllib_response")
+    robotparser = _importer._get_module("moves.urllib_robotparser")
+
+    def __dir__(self):
+        return ['parse', 'error', 'request', 'response', 'robotparser']
+
+_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
+                      "moves.urllib")
+
+
+def add_move(move):
+    """Add an item to six.moves."""
+    setattr(_MovedItems, move.name, move)
+
+
+def remove_move(name):
+    """Remove item from six.moves."""
+    try:
+        delattr(_MovedItems, name)
+    except AttributeError:
+        try:
+            del moves.__dict__[name]
+        except KeyError:
+            raise AttributeError("no such move, %r" % (name,))
+
+
+if PY3:
+    _meth_func = "__func__"
+    _meth_self = "__self__"
+
+    _func_closure = "__closure__"
+    _func_code = "__code__"
+    _func_defaults = "__defaults__"
+    _func_globals = "__globals__"
+else:
+    _meth_func = "im_func"
+    _meth_self = "im_self"
+
+    _func_closure = "func_closure"
+    _func_code = "func_code"
+    _func_defaults = "func_defaults"
+    _func_globals = "func_globals"
+
+
+try:
+    advance_iterator = next
+except NameError:
+    def advance_iterator(it):
+        return it.next()
+next = advance_iterator
+
+
+try:
+    callable = callable
+except NameError:
+    def callable(obj):
+        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
+
+
+if PY3:
+    def get_unbound_function(unbound):
+        return unbound
+
+    create_bound_method = types.MethodType
+
+    Iterator = object
+else:
+    def get_unbound_function(unbound):
+        return unbound.im_func
+
+    def create_bound_method(func, obj):
+        return types.MethodType(func, obj, obj.__class__)
+
+    class Iterator(object):
+
+        def next(self):
+            return type(self).__next__(self)
+
+    callable = callable
+_add_doc(get_unbound_function,
+         """Get the function out of a possibly unbound function""")
+
+
+get_method_function = operator.attrgetter(_meth_func)
+get_method_self = operator.attrgetter(_meth_self)
+get_function_closure = operator.attrgetter(_func_closure)
+get_function_code = operator.attrgetter(_func_code)
+get_function_defaults = operator.attrgetter(_func_defaults)
+get_function_globals = operator.attrgetter(_func_globals)
+
+
+if PY3:
+    def iterkeys(d, **kw):
+        return iter(d.keys(**kw))
+
+    def itervalues(d, **kw):
+        return iter(d.values(**kw))
+
+    def iteritems(d, **kw):
+        return iter(d.items(**kw))
+
+    def iterlists(d, **kw):
+        return iter(d.lists(**kw))
+else:
+    def iterkeys(d, **kw):
+        return iter(d.iterkeys(**kw))
+
+    def itervalues(d, **kw):
+        return iter(d.itervalues(**kw))
+
+    def iteritems(d, **kw):
+        return iter(d.iteritems(**kw))
+
+    def iterlists(d, **kw):
+        return iter(d.iterlists(**kw))
+
+_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
+_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
+_add_doc(iteritems,
+         "Return an iterator over the (key, value) pairs of a dictionary.")
+_add_doc(iterlists,
+         "Return an iterator over the (key, [values]) pairs of a dictionary.")
+
+
+if PY3:
+    def b(s):
+        return s.encode("latin-1")
+    def u(s):
+        return s
+    unichr = chr
+    if sys.version_info[1] <= 1:
+        def int2byte(i):
+            return bytes((i,))
+    else:
+        # This is about 2x faster than the implementation above on 3.2+
+        int2byte = operator.methodcaller("to_bytes", 1, "big")
+    byte2int = operator.itemgetter(0)
+    indexbytes = operator.getitem
+    iterbytes = iter
+    import io
+    StringIO = io.StringIO
+    BytesIO = io.BytesIO
+else:
+    def b(s):
+        return s
+    # Workaround for standalone backslash
+    def u(s):
+        return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
+    unichr = unichr
+    int2byte = chr
+    def byte2int(bs):
+        return ord(bs[0])
+    def indexbytes(buf, i):
+        return ord(buf[i])
+    def iterbytes(buf):
+        return (ord(byte) for byte in buf)
+    import StringIO
+    StringIO = BytesIO = StringIO.StringIO
+_add_doc(b, """Byte literal""")
+_add_doc(u, """Text literal""")
+
+
+if PY3:
+    exec_ = getattr(moves.builtins, "exec")
+
+
+    def reraise(tp, value, tb=None):
+        if value is None:
+            value = tp()
+        if value.__traceback__ is not tb:
+            raise value.with_traceback(tb)
+        raise value
+
+else:
+    def exec_(_code_, _globs_=None, _locs_=None):
+        """Execute code in a namespace."""
+        if _globs_ is None:
+            frame = sys._getframe(1)
+            _globs_ = frame.f_globals
+            if _locs_ is None:
+                _locs_ = frame.f_locals
+            del frame
+        elif _locs_ is None:
+            _locs_ = _globs_
+        exec("""exec _code_ in _globs_, _locs_""")
+
+
+    exec_("""def reraise(tp, value, tb=None):
+    raise tp, value, tb
+""")
+
+
+print_ = getattr(moves.builtins, "print", None)
+if print_ is None:
+    def print_(*args, **kwargs):
+        """The new-style print function for Python 2.4 and 2.5."""
+        fp = kwargs.pop("file", sys.stdout)
+        if fp is None:
+            return
+        def write(data):
+            if not isinstance(data, basestring):
+                data = str(data)
+            # If the file has an encoding, encode unicode with it.
+            if (isinstance(fp, file) and
+                isinstance(data, unicode) and
+                fp.encoding is not None):
+                errors = getattr(fp, "errors", None)
+                if errors is None:
+                    errors = "strict"
+                data = data.encode(fp.encoding, errors)
+            fp.write(data)
+        want_unicode = False
+        sep = kwargs.pop("sep", None)
+        if sep is not None:
+            if isinstance(sep, unicode):
+                want_unicode = True
+            elif not isinstance(sep, str):
+                raise TypeError("sep must be None or a string")
+        end = kwargs.pop("end", None)
+        if end is not None:
+            if isinstance(end, unicode):
+                want_unicode = True
+            elif not isinstance(end, str):
+                raise TypeError("end must be None or a string")
+        if kwargs:
+            raise TypeError("invalid keyword arguments to print()")
+        if not want_unicode:
+            for arg in args:
+                if isinstance(arg, unicode):
+                    want_unicode = True
+                    break
+        if want_unicode:
+            newline = unicode("\n")
+            space = unicode(" ")
+        else:
+            newline = "\n"
+            space = " "
+        if sep is None:
+            sep = space
+        if end is None:
+            end = newline
+        for i, arg in enumerate(args):
+            if i:
+                write(sep)
+            write(arg)
+        write(end)
+
+_add_doc(reraise, """Reraise an exception.""")
+
+if sys.version_info[0:2] < (3, 4):
+    def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
+              updated=functools.WRAPPER_UPDATES):
+        def wrapper(f):
+            f = functools.wraps(wrapped)(f)
+            f.__wrapped__ = wrapped
+            return f
+        return wrapper
+else:
+    wraps = functools.wraps
+
+def with_metaclass(meta, *bases):
+    """Create a base class with a metaclass."""
+    # This requires a bit of explanation: the basic idea is to make a dummy
+    # metaclass for one level of class instantiation that replaces itself with
+    # the actual metaclass.
+    class metaclass(meta):
+        def __new__(cls, name, this_bases, d):
+            return meta(name, bases, d)
+    return type.__new__(metaclass, 'temporary_class', (), {})
+
+
+def add_metaclass(metaclass):
+    """Class decorator for creating a class with a metaclass."""
+    def wrapper(cls):
+        orig_vars = cls.__dict__.copy()
+        slots = orig_vars.get('__slots__')
+        if slots is not None:
+            if isinstance(slots, str):
+                slots = [slots]
+            for slots_var in slots:
+                orig_vars.pop(slots_var)
+        orig_vars.pop('__dict__', None)
+        orig_vars.pop('__weakref__', None)
+        return metaclass(cls.__name__, cls.__bases__, orig_vars)
+    return wrapper
+
+# Complete the moves implementation.
+# This code is at the end of this module to speed up module loading.
+# Turn this module into a package.
+__path__ = []  # required for PEP 302 and PEP 451
+__package__ = __name__  # see PEP 366 @ReservedAssignment
+if globals().get("__spec__") is not None:
+    __spec__.submodule_search_locations = []  # PEP 451 @UndefinedVariable
+# Remove other six meta path importers, since they cause problems. This can
+# happen if six is removed from sys.modules and then reloaded. (Setuptools does
+# this for some reason.)
+if sys.meta_path:
+    for i, importer in enumerate(sys.meta_path):
+        # Here's some real nastiness: Another "instance" of the six module might
+        # be floating around. Therefore, we can't use isinstance() to check for
+        # the six meta path importer, since the other six instance will have
+        # inserted an importer with different class.
+        if (type(importer).__name__ == "_SixMetaPathImporter" and
+            importer.name == __name__):
+            del sys.meta_path[i]
+            break
+    del i, importer
+# Finally, add the importer to the meta path import hook.
+sys.meta_path.append(_importer)