Commit 862370c6 authored by Mike Frysinger's avatar Mike Frysinger Committed by Commit Bot

pylint: reland upgrade to 1.5.6

** WARNING: please do not revert this CL without checking with vapier@ first ***

We disable new warnings that are triggered in depot_tools to pylintrc.
So the lint output before & after this CL are mostly unchanged.  The
repos checked: depot_tools, src, and build.

Also update its deps along the way:
- Update astroid to 1.4.9.
- Drop now unused logilab.common.
- Import lazy_object_proxy 1.3.1.
- Import wrapt 1.10.11.

This should address the issue where some Chromium bots don't have the
lazy_object_proxy/wrapt python modules installed.  These have optional
C modules that we don't compile either as they have Python fallbacks.

Bug: 863669
Change-Id: Iade0e6d89598e2c836ed647996f43db356c43cd0
Reviewed-on: https://chromium-review.googlesource.com/1145485
Commit-Queue: Ned Nguyen <nednguyen@google.com>
Reviewed-by: 's avatarRobbie Iannucci <iannucci@chromium.org>
Reviewed-by: 's avatarJohn Budorick <jbudorick@chromium.org>
parent 40764b07
...@@ -7,9 +7,6 @@ ...@@ -7,9 +7,6 @@
# pygtk.require(). # pygtk.require().
#init-hook= #init-hook=
# Profiled execution.
profile=no
# Add files or directories to the blacklist. They should be base names, not # Add files or directories to the blacklist. They should be base names, not
# paths. # paths.
ignore=CVS ignore=CVS
...@@ -93,31 +90,53 @@ disable= ...@@ -93,31 +90,53 @@ disable=
logging-not-lazy, logging-not-lazy,
bad-continuation, bad-continuation,
anomalous-backslash-in-string, anomalous-backslash-in-string,
assigning-non-slot,
bad-context-manager, bad-context-manager,
bad-indentation, bad-indentation,
bad-str-strip-call, bad-str-strip-call,
bad-super-call,
bad-whitespace, bad-whitespace,
cell-var-from-loop, cell-var-from-loop,
consider-using-enumerate,
deprecated-lambda, deprecated-lambda,
deprecated-method,
eval-used, eval-used,
function-redefined, function-redefined,
import-error, import-error,
locally-enabled, locally-enabled,
misplaced-comparison-constant,
misplaced-bare-raise,
missing-final-newline, missing-final-newline,
multiple-imports,
no-init, no-init,
no-name-in-module, no-name-in-module,
no-self-argument,
no-self-use, no-self-use,
not-an-iterable,
not-callable, not-callable,
old-style-class, old-style-class,
protected-access, protected-access,
redefined-variable-type,
simplifiable-if-statement,
singleton-comparison,
superfluous-parens, superfluous-parens,
super-on-old-class, super-on-old-class,
too-many-boolean-expressions,
too-many-function-args, too-many-function-args,
too-many-nested-blocks,
trailing-whitespace, trailing-whitespace,
undefined-variable,
ungrouped-imports,
unnecessary-semicolon, unnecessary-semicolon,
unneeded-not,
unpacking-non-sequence, unpacking-non-sequence,
unsubscriptable-object,
unsupported-membership-test,
unused-import, unused-import,
useless-else-on-loop useless-else-on-loop,
using-constant-test,
wrong-import-order,
wrong-import-position,
[REPORTS] [REPORTS]
...@@ -142,10 +161,6 @@ reports=no ...@@ -142,10 +161,6 @@ reports=no
# (RP0004). # (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Add a comment according to your evaluation note. This is used by the global
# evaluation report (RP0004).
comment=no
[VARIABLES] [VARIABLES]
...@@ -171,10 +186,6 @@ ignore-mixin-members=yes ...@@ -171,10 +186,6 @@ ignore-mixin-members=yes
# (useful for classes with attributes dynamically set). # (useful for classes with attributes dynamically set).
ignored-classes=SQLObject,twisted.internet.reactor,hashlib,google.appengine.api.memcache ignored-classes=SQLObject,twisted.internet.reactor,hashlib,google.appengine.api.memcache
# When zope mode is activated, add a predefined set of Zope acquired attributes
# to generated-members.
zope=no
# List of members which are set dynamically and missed by pylint inference # List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E0201 when accessed. Python regular # system, and so shouldn't trigger E0201 when accessed. Python regular
# expressions are accepted. # expressions are accepted.
...@@ -215,9 +226,6 @@ indent-string=' ' ...@@ -215,9 +226,6 @@ indent-string=' '
[BASIC] [BASIC]
# Required attributes for module, separated by a comma
required-attributes=
# List of builtins function names that should not be used, separated by a comma # List of builtins function names that should not be used, separated by a comma
bad-functions=map,filter,apply,input bad-functions=map,filter,apply,input
...@@ -296,10 +304,6 @@ max-public-methods=20 ...@@ -296,10 +304,6 @@ max-public-methods=20
[CLASSES] [CLASSES]
# 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.
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
# List of method names used to declare (i.e. assign) instance attributes. # List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp defining-attr-methods=__init__,__new__,setUp
......
URL: http://www.logilab.org/
Description: Description:
This directory contains the logilab-astng and logilab-common modules. See This directory contains modules that pylint needs.
the individual README.chromium files for details. See the individual README.chromium files for details.
Local Modifications: Local Modifications:
None None
URL: https://github.com/PyCQA/astroid URL: https://github.com/PyCQA/astroid
Version: 1.3.8 URL: https://pypi.org/project/astroid/#files
Version: 1.4.9
License: GPL License: GPL
License File: LICENSE.txt License File: LICENSE.txt
......
...@@ -58,13 +58,15 @@ from astroid import inference ...@@ -58,13 +58,15 @@ from astroid import inference
# more stuff available # more stuff available
from astroid import raw_building from astroid import raw_building
from astroid.bases import YES, Instance, BoundMethod, UnboundMethod from astroid.bases import Instance, BoundMethod, UnboundMethod
from astroid.node_classes import are_exclusive, unpack_infer from astroid.node_classes import are_exclusive, unpack_infer
from astroid.scoped_nodes import builtin_lookup from astroid.scoped_nodes import builtin_lookup
from astroid.builder import parse
from astroid.util import YES
# make a manager instance (borg) as well as Project and Package classes # make a manager instance (borg) as well as Project and Package classes
# accessible from astroid package # accessible from astroid package
from astroid.manager import AstroidManager, Project from astroid.manager import AstroidManager
MANAGER = AstroidManager() MANAGER = AstroidManager()
del AstroidManager del AstroidManager
...@@ -100,7 +102,7 @@ def inference_tip(infer_function): ...@@ -100,7 +102,7 @@ def inference_tip(infer_function):
.. sourcecode:: python .. sourcecode:: python
MANAGER.register_transform(CallFunc, inference_tip(infer_named_tuple), MANAGER.register_transform(Call, inference_tip(infer_named_tuple),
predicate) predicate)
""" """
def transform(node, infer_function=infer_function): def transform(node, infer_function=infer_function):
...@@ -112,8 +114,11 @@ def inference_tip(infer_function): ...@@ -112,8 +114,11 @@ def inference_tip(infer_function):
def register_module_extender(manager, module_name, get_extension_mod): def register_module_extender(manager, module_name, get_extension_mod):
def transform(node): def transform(node):
extension_module = get_extension_mod() extension_module = get_extension_mod()
for name, obj in extension_module.locals.items(): for name, objs in extension_module._locals.items():
node.locals[name] = obj node._locals[name] = objs
for obj in objs:
if obj.parent is extension_module:
obj.parent = node
manager.register_transform(Module, transform, lambda n: n.name == module_name) manager.register_transform(Module, transform, lambda n: n.name == module_name)
......
...@@ -20,17 +20,17 @@ distname = 'astroid' ...@@ -20,17 +20,17 @@ distname = 'astroid'
modname = 'astroid' modname = 'astroid'
numversion = (1, 3, 8) numversion = (1, 4, 9)
version = '.'.join([str(num) for num in numversion]) version = '.'.join([str(num) for num in numversion])
install_requires = ['logilab-common>=0.63.0', 'six'] install_requires = ['six', 'lazy_object_proxy', 'wrapt']
license = 'LGPL' license = 'LGPL'
author = 'Logilab' author = 'Python Code Quality Authority'
author_email = 'pylint-dev@lists.logilab.org' author_email = 'code-quality@python.org'
mailinglist = "mailto://%s" % author_email mailinglist = "mailto://%s" % author_email
web = 'http://bitbucket.org/logilab/astroid' web = 'https://github.com/PyCQA/astroid'
description = "A abstract syntax tree for Python with inference support." description = "A abstract syntax tree for Python with inference support."
......
# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# 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/>.
from astroid import bases
from astroid import context as contextmod
from astroid import exceptions
from astroid import nodes
from astroid import util
import six
class CallSite(object):
"""Class for understanding arguments passed into a call site
It needs a call context, which contains the arguments and the
keyword arguments that were passed into a given call site.
In order to infer what an argument represents, call
:meth:`infer_argument` with the corresponding function node
and the argument name.
"""
def __init__(self, callcontext):
args = callcontext.args
keywords = callcontext.keywords
self.duplicated_keywords = set()
self._unpacked_args = self._unpack_args(args)
self._unpacked_kwargs = self._unpack_keywords(keywords)
self.positional_arguments = [
arg for arg in self._unpacked_args
if arg is not util.YES
]
self.keyword_arguments = {
key: value for key, value in self._unpacked_kwargs.items()
if value is not util.YES
}
@classmethod
def from_call(cls, call_node):
"""Get a CallSite object from the given Call node."""
callcontext = contextmod.CallContext(call_node.args,
call_node.keywords)
return cls(callcontext)
def has_invalid_arguments(self):
"""Check if in the current CallSite were passed *invalid* arguments
This can mean multiple things. For instance, if an unpacking
of an invalid object was passed, then this method will return True.
Other cases can be when the arguments can't be inferred by astroid,
for example, by passing objects which aren't known statically.
"""
return len(self.positional_arguments) != len(self._unpacked_args)
def has_invalid_keywords(self):
"""Check if in the current CallSite were passed *invalid* keyword arguments
For instance, unpacking a dictionary with integer keys is invalid
(**{1:2}), because the keys must be strings, which will make this
method to return True. Other cases where this might return True if
objects which can't be inferred were passed.
"""
return len(self.keyword_arguments) != len(self._unpacked_kwargs)
def _unpack_keywords(self, keywords):
values = {}
context = contextmod.InferenceContext()
for name, value in keywords:
if name is None:
# Then it's an unpacking operation (**)
try:
inferred = next(value.infer(context=context))
except exceptions.InferenceError:
values[name] = util.YES
continue
if not isinstance(inferred, nodes.Dict):
# Not something we can work with.
values[name] = util.YES
continue
for dict_key, dict_value in inferred.items:
try:
dict_key = next(dict_key.infer(context=context))
except exceptions.InferenceError:
values[name] = util.YES
continue
if not isinstance(dict_key, nodes.Const):
values[name] = util.YES
continue
if not isinstance(dict_key.value, six.string_types):
values[name] = util.YES
continue
if dict_key.value in values:
# The name is already in the dictionary
values[dict_key.value] = util.YES
self.duplicated_keywords.add(dict_key.value)
continue
values[dict_key.value] = dict_value
else:
values[name] = value
return values
@staticmethod
def _unpack_args(args):
values = []
context = contextmod.InferenceContext()
for arg in args:
if isinstance(arg, nodes.Starred):
try:
inferred = next(arg.value.infer(context=context))
except exceptions.InferenceError:
values.append(util.YES)
continue
if inferred is util.YES:
values.append(util.YES)
continue
if not hasattr(inferred, 'elts'):
values.append(util.YES)
continue
values.extend(inferred.elts)
else:
values.append(arg)
return values
def infer_argument(self, funcnode, name, context):
"""infer a function argument value according to the call context"""
if name in self.duplicated_keywords:
raise exceptions.InferenceError(name)
# Look into the keywords first, maybe it's already there.
try:
return self.keyword_arguments[name].infer(context)
except KeyError:
pass
# Too many arguments given and no variable arguments.
if len(self.positional_arguments) > len(funcnode.args.args):
if not funcnode.args.vararg:
raise exceptions.InferenceError(name)
positional = self.positional_arguments[:len(funcnode.args.args)]
vararg = self.positional_arguments[len(funcnode.args.args):]
argindex = funcnode.args.find_argname(name)[0]
kwonlyargs = set(arg.name for arg in funcnode.args.kwonlyargs)
kwargs = {
key: value for key, value in self.keyword_arguments.items()
if key not in kwonlyargs
}
# If there are too few positionals compared to
# what the function expects to receive, check to see
# if the missing positional arguments were passed
# as keyword arguments and if so, place them into the
# positional args list.
if len(positional) < len(funcnode.args.args):
for func_arg in funcnode.args.args:
if func_arg.name in kwargs:
arg = kwargs.pop(func_arg.name)
positional.append(arg)
if argindex is not None:
# 2. first argument of instance/class method
if argindex == 0 and funcnode.type in ('method', 'classmethod'):
if context.boundnode is not None:
boundnode = context.boundnode
else:
# XXX can do better ?
boundnode = funcnode.parent.frame()
if funcnode.type == 'method':
if not isinstance(boundnode, bases.Instance):
boundnode = bases.Instance(boundnode)
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.positional_arguments[argindex].infer(context)
except IndexError:
pass
if funcnode.args.kwarg == name:
# It wants all the keywords that were passed into
# the call site.
if self.has_invalid_keywords():
raise exceptions.InferenceError
kwarg = nodes.Dict()
kwarg.lineno = funcnode.args.lineno
kwarg.col_offset = funcnode.args.col_offset
kwarg.parent = funcnode.args
items = [(nodes.const_factory(key), value)
for key, value in kwargs.items()]
kwarg.items = items
return iter((kwarg, ))
elif funcnode.args.vararg == name:
# It wants all the args that were passed into
# the call site.
if self.has_invalid_arguments():
raise exceptions.InferenceError
args = nodes.Tuple()
args.lineno = funcnode.args.lineno
args.col_offset = funcnode.args.col_offset
args.parent = funcnode.args
args.elts = vararg
return iter((args, ))
# Check if it's a default parameter.
try:
return funcnode.args.default_value(name).infer(context)
except exceptions.NoDefault:
pass
raise exceptions.InferenceError(name)
...@@ -22,9 +22,10 @@ ...@@ -22,9 +22,10 @@
* :func:`dump` function return an internal representation of nodes found * :func:`dump` function return an internal representation of nodes found
in the tree, useful for debugging or understanding the tree structure in the tree, useful for debugging or understanding the tree structure
""" """
import sys import sys
import six
INDENT = ' ' # 4 spaces ; keep indentation variable INDENT = ' ' # 4 spaces ; keep indentation variable
...@@ -89,9 +90,9 @@ class AsStringVisitor(object): ...@@ -89,9 +90,9 @@ class AsStringVisitor(object):
"""return an astroid.Function node as string""" """return an astroid.Function node as string"""
return node.format_args() return node.format_args()
def visit_assattr(self, node): def visit_assignattr(self, node):
"""return an astroid.AssAttr node as string""" """return an astroid.AssAttr node as string"""
return self.visit_getattr(node) return self.visit_attribute(node)
def visit_assert(self, node): def visit_assert(self, node):
"""return an astroid.Assert node as string""" """return an astroid.Assert node as string"""
...@@ -100,7 +101,7 @@ class AsStringVisitor(object): ...@@ -100,7 +101,7 @@ class AsStringVisitor(object):
node.fail.accept(self)) node.fail.accept(self))
return 'assert %s' % node.test.accept(self) return 'assert %s' % node.test.accept(self)
def visit_assname(self, node): def visit_assignname(self, node):
"""return an astroid.AssName node as string""" """return an astroid.AssName node as string"""
return node.name return node.name
...@@ -113,8 +114,8 @@ class AsStringVisitor(object): ...@@ -113,8 +114,8 @@ class AsStringVisitor(object):
"""return an astroid.AugAssign node as string""" """return an astroid.AugAssign node as string"""
return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self)) return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self))
def visit_backquote(self, node): def visit_repr(self, node):
"""return an astroid.Backquote node as string""" """return an astroid.Repr node as string"""
return '`%s`' % node.value.accept(self) return '`%s`' % node.value.accept(self)
def visit_binop(self, node): def visit_binop(self, node):
...@@ -130,18 +131,20 @@ class AsStringVisitor(object): ...@@ -130,18 +131,20 @@ class AsStringVisitor(object):
"""return an astroid.Break node as string""" """return an astroid.Break node as string"""
return 'break' return 'break'
def visit_callfunc(self, node): def visit_call(self, node):
"""return an astroid.CallFunc node as string""" """return an astroid.Call node as string"""
expr_str = node.func.accept(self) expr_str = node.func.accept(self)
args = [arg.accept(self) for arg in node.args] args = [arg.accept(self) for arg in node.args]
if node.starargs: if node.keywords:
args.append('*' + node.starargs.accept(self)) keywords = [kwarg.accept(self) for kwarg in node.keywords]
if node.kwargs: else:
args.append('**' + node.kwargs.accept(self)) keywords = []
args.extend(keywords)
return '%s(%s)' % (expr_str, ', '.join(args)) return '%s(%s)' % (expr_str, ', '.join(args))
def visit_class(self, node): def visit_classdef(self, node):
"""return an astroid.Class node as string""" """return an astroid.ClassDef node as string"""
decorate = node.decorators and node.decorators.accept(self) or '' decorate = node.decorators and node.decorators.accept(self) or ''
bases = ', '.join([n.accept(self) for n in node.bases]) bases = ', '.join([n.accept(self) for n in node.bases])
if sys.version_info[0] == 2: if sys.version_info[0] == 2:
...@@ -186,7 +189,7 @@ class AsStringVisitor(object): ...@@ -186,7 +189,7 @@ class AsStringVisitor(object):
def visit_delattr(self, node): def visit_delattr(self, node):
"""return an astroid.DelAttr node as string""" """return an astroid.DelAttr node as string"""
return self.visit_getattr(node) return self.visit_attribute(node)
def visit_delname(self, node): def visit_delname(self, node):
"""return an astroid.DelName node as string""" """return an astroid.DelName node as string"""
...@@ -198,16 +201,27 @@ class AsStringVisitor(object): ...@@ -198,16 +201,27 @@ class AsStringVisitor(object):
def visit_dict(self, node): def visit_dict(self, node):
"""return an astroid.Dict node as string""" """return an astroid.Dict node as string"""
return '{%s}' % ', '.join(['%s: %s' % (key.accept(self), return '{%s}' % ', '.join(self._visit_dict(node))
value.accept(self))
for key, value in node.items]) def _visit_dict(self, node):
for key, value in node.items:
key = key.accept(self)
value = value.accept(self)
if key == '**':
# It can only be a DictUnpack node.
yield key + value
else:
yield '%s: %s' % (key, value)
def visit_dictunpack(self, node):
return '**'
def visit_dictcomp(self, node): def visit_dictcomp(self, node):
"""return an astroid.DictComp node as string""" """return an astroid.DictComp node as string"""
return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self), return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self),
' '.join([n.accept(self) for n in node.generators])) ' '.join([n.accept(self) for n in node.generators]))
def visit_discard(self, node): def visit_expr(self, node):
"""return an astroid.Discard node as string""" """return an astroid.Discard node as string"""
return node.value.accept(self) return node.value.accept(self)
...@@ -258,24 +272,33 @@ class AsStringVisitor(object): ...@@ -258,24 +272,33 @@ class AsStringVisitor(object):
fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse)) fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse))
return fors return fors
def visit_from(self, node): def visit_importfrom(self, node):
"""return an astroid.From node as string""" """return an astroid.ImportFrom node as string"""
return 'from %s import %s' % ('.' * (node.level or 0) + node.modname, return 'from %s import %s' % ('.' * (node.level or 0) + node.modname,
_import_string(node.names)) _import_string(node.names))
def visit_function(self, node): def visit_functiondef(self, node):
"""return an astroid.Function node as string""" """return an astroid.Function node as string"""
decorate = node.decorators and node.decorators.accept(self) or '' decorate = node.decorators and node.decorators.accept(self) or ''
docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) 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), return_annotation = ''
docs, self._stmt_list(node.body)) if six.PY3 and node.returns:
return_annotation = '->' + node.returns.as_string()
def visit_genexpr(self, node): trailer = return_annotation + ":"
"""return an astroid.GenExpr node as string""" else:
trailer = ":"
def_format = "\n%sdef %s(%s)%s%s\n%s"
return def_format % (decorate, node.name,
node.args.accept(self),
trailer, docs,
self._stmt_list(node.body))
def visit_generatorexp(self, node):
"""return an astroid.GeneratorExp node as string"""
return '(%s %s)' % (node.elt.accept(self), return '(%s %s)' % (node.elt.accept(self),
' '.join([n.accept(self) for n in node.generators])) ' '.join([n.accept(self) for n in node.generators]))
def visit_getattr(self, node): def visit_attribute(self, node):
"""return an astroid.Getattr node as string""" """return an astroid.Getattr node as string"""
return '%s.%s' % (node.expr.accept(self), node.attrname) return '%s.%s' % (node.expr.accept(self), node.attrname)
...@@ -302,6 +325,8 @@ class AsStringVisitor(object): ...@@ -302,6 +325,8 @@ class AsStringVisitor(object):
def visit_keyword(self, node): def visit_keyword(self, node):
"""return an astroid.Keyword node as string""" """return an astroid.Keyword node as string"""
if node.arg is None:
return '**%s' % node.value.accept(self)
return '%s=%s' % (node.arg, node.value.accept(self)) return '%s=%s' % (node.arg, node.value.accept(self))
def visit_lambda(self, node): def visit_lambda(self, node):
...@@ -438,6 +463,22 @@ class AsStringVisitor(object): ...@@ -438,6 +463,22 @@ class AsStringVisitor(object):
else: else:
return "(%s)" % (expr,) return "(%s)" % (expr,)
def visit_starred(self, node):
"""return Starred node as string"""
return "*" + node.value.accept(self)
# These aren't for real AST nodes, but for inference objects.
def visit_frozenset(self, node):
return node.parent.accept(self)
def visit_super(self, node):
return node.parent.accept(self)
def visit_yes(self, node):
return "Uninferable"
class AsStringVisitor3k(AsStringVisitor): class AsStringVisitor3k(AsStringVisitor):
"""AsStringVisitor3k overwrites some AsStringVisitor methods""" """AsStringVisitor3k overwrites some AsStringVisitor methods"""
...@@ -466,10 +507,6 @@ class AsStringVisitor3k(AsStringVisitor): ...@@ -466,10 +507,6 @@ class AsStringVisitor3k(AsStringVisitor):
return 'raise %s' % node.exc.accept(self) return 'raise %s' % node.exc.accept(self)
return 'raise' return 'raise'
def visit_starred(self, node):
"""return Starred node as string"""
return "*" + node.value.accept(self)
def visit_yieldfrom(self, node): def visit_yieldfrom(self, node):
""" Return an astroid.YieldFrom node as string. """ """ Return an astroid.YieldFrom node as string. """
yi_val = node.value and (" " + node.value.accept(self)) or "" yi_val = node.value and (" " + node.value.accept(self)) or ""
...@@ -479,6 +516,19 @@ class AsStringVisitor3k(AsStringVisitor): ...@@ -479,6 +516,19 @@ class AsStringVisitor3k(AsStringVisitor):
else: else:
return "(%s)" % (expr,) return "(%s)" % (expr,)
def visit_asyncfunctiondef(self, node):
function = super(AsStringVisitor3k, self).visit_functiondef(node)
return 'async ' + function.strip()
def visit_await(self, node):
return 'await %s' % node.value.accept(self)
def visit_asyncwith(self, node):
return 'async %s' % self.visit_with(node)
def visit_asyncfor(self, node):
return 'async %s' % self.visit_for(node)
def _import_string(names): def _import_string(names):
"""return a list of (name, asname) formatted as a string""" """return a list of (name, asname) formatted as a string"""
...@@ -496,4 +546,3 @@ if sys.version_info >= (3, 0): ...@@ -496,4 +546,3 @@ if sys.version_info >= (3, 0):
# this visitor is stateless, thus it can be reused # this visitor is stateless, thus it can be reused
to_code = AsStringVisitor() to_code = AsStringVisitor()
This diff is collapsed.
...@@ -7,9 +7,11 @@ from textwrap import dedent ...@@ -7,9 +7,11 @@ from textwrap import dedent
import six import six
from astroid import (MANAGER, UseInferenceDefault, from astroid import (MANAGER, UseInferenceDefault,
inference_tip, YES, InferenceError, UnresolvableName) inference_tip, YES, InferenceError, UnresolvableName)
from astroid import arguments
from astroid import nodes from astroid import nodes
from astroid import objects
from astroid.builder import AstroidBuilder from astroid.builder import AstroidBuilder
from astroid import util
def _extend_str(class_node, rvalue): def _extend_str(class_node, rvalue):
"""function to extend builtin str/unicode class""" """function to extend builtin str/unicode class"""
...@@ -51,7 +53,7 @@ def _extend_str(class_node, rvalue): ...@@ -51,7 +53,7 @@ def _extend_str(class_node, rvalue):
def rstrip(self, chars=None): def rstrip(self, chars=None):
return {rvalue} return {rvalue}
def rjust(self, width, fillchar=None): def rjust(self, width, fillchar=None):
return {rvalue} return {rvalue}
def center(self, width, fillchar=None): def center(self, width, fillchar=None):
return {rvalue} return {rvalue}
def ljust(self, width, fillchar=None): def ljust(self, width, fillchar=None):
...@@ -60,7 +62,7 @@ def _extend_str(class_node, rvalue): ...@@ -60,7 +62,7 @@ def _extend_str(class_node, rvalue):
code = code.format(rvalue=rvalue) code = code.format(rvalue=rvalue)
fake = AstroidBuilder(MANAGER).string_build(code)['whatever'] fake = AstroidBuilder(MANAGER).string_build(code)['whatever']
for method in fake.mymethods(): for method in fake.mymethods():
class_node.locals[method.name] = [method] class_node._locals[method.name] = [method]
method.parent = class_node method.parent = class_node
def extend_builtins(class_transforms): def extend_builtins(class_transforms):
...@@ -86,12 +88,17 @@ def register_builtin_transform(transform, builtin_name): ...@@ -86,12 +88,17 @@ def register_builtin_transform(transform, builtin_name):
def _transform_wrapper(node, context=None): def _transform_wrapper(node, context=None):
result = transform(node, context=context) result = transform(node, context=context)
if result: if result:
result.parent = node if not result.parent:
# Let the transformation function determine
# the parent for its result. Otherwise,
# we set it to be the node we transformed from.
result.parent = node
result.lineno = node.lineno result.lineno = node.lineno
result.col_offset = node.col_offset result.col_offset = node.col_offset
return iter([result]) return iter([result])
MANAGER.register_transform(nodes.CallFunc, MANAGER.register_transform(nodes.Call,
inference_tip(_transform_wrapper), inference_tip(_transform_wrapper),
lambda n: (isinstance(n.func, nodes.Name) and lambda n: (isinstance(n.func, nodes.Name) and
n.func.name == builtin_name)) n.func.name == builtin_name))
...@@ -108,13 +115,13 @@ def _generic_inference(node, context, node_type, transform): ...@@ -108,13 +115,13 @@ def _generic_inference(node, context, node_type, transform):
transformed = transform(arg) transformed = transform(arg)
if not transformed: if not transformed:
try: try:
infered = next(arg.infer(context=context)) inferred = next(arg.infer(context=context))
except (InferenceError, StopIteration): except (InferenceError, StopIteration):
raise UseInferenceDefault() raise UseInferenceDefault()
if infered is YES: if inferred is util.YES:
raise UseInferenceDefault() raise UseInferenceDefault()
transformed = transform(infered) transformed = transform(inferred)
if not transformed or transformed is YES: if not transformed or transformed is util.YES:
raise UseInferenceDefault() raise UseInferenceDefault()
return transformed return transformed
...@@ -172,19 +179,25 @@ infer_set = partial( ...@@ -172,19 +179,25 @@ infer_set = partial(
iterables=(nodes.List, nodes.Tuple), iterables=(nodes.List, nodes.Tuple),
build_elts=set) build_elts=set)
infer_frozenset = partial(
_infer_builtin,
klass=objects.FrozenSet,
iterables=(nodes.List, nodes.Tuple, nodes.Set),
build_elts=frozenset)
def _get_elts(arg, context): def _get_elts(arg, context):
is_iterable = lambda n: isinstance(n, is_iterable = lambda n: isinstance(n,
(nodes.List, nodes.Tuple, nodes.Set)) (nodes.List, nodes.Tuple, nodes.Set))
try: try:
infered = next(arg.infer(context)) inferred = next(arg.infer(context))
except (InferenceError, UnresolvableName): except (InferenceError, UnresolvableName):
raise UseInferenceDefault() raise UseInferenceDefault()
if isinstance(infered, nodes.Dict): if isinstance(inferred, nodes.Dict):
items = infered.items items = inferred.items
elif is_iterable(infered): elif is_iterable(inferred):
items = [] items = []
for elt in infered.elts: for elt in inferred.elts:
# If an item is not a pair of two items, # If an item is not a pair of two items,
# then fallback to the default inference. # then fallback to the default inference.
# Also, take in consideration only hashable items, # Also, take in consideration only hashable items,
...@@ -213,24 +226,28 @@ def infer_dict(node, context=None): ...@@ -213,24 +226,28 @@ def infer_dict(node, context=None):
* dict(mapping, **kwargs) * dict(mapping, **kwargs)
* dict(**kwargs) * dict(**kwargs)
If a case can't be infered, we'll fallback to default inference. If a case can't be inferred, we'll fallback to default inference.
""" """
has_keywords = lambda args: all(isinstance(arg, nodes.Keyword) call = arguments.CallSite.from_call(node)
for arg in args) if call.has_invalid_arguments() or call.has_invalid_keywords():
if not node.args and not node.kwargs: raise UseInferenceDefault
args = call.positional_arguments
kwargs = list(call.keyword_arguments.items())
if not args and not kwargs:
# dict() # dict()
return nodes.Dict() return nodes.Dict()
elif has_keywords(node.args) and node.args: elif kwargs and not args:
# dict(a=1, b=2, c=4) # dict(a=1, b=2, c=4)
items = [(nodes.Const(arg.arg), arg.value) for arg in node.args] items = [(nodes.Const(key), value) for key, value in kwargs]
elif (len(node.args) >= 2 and elif len(args) == 1 and kwargs:
has_keywords(node.args[1:])):
# dict(some_iterable, b=2, c=4) # dict(some_iterable, b=2, c=4)
elts = _get_elts(node.args[0], context) elts = _get_elts(args[0], context)
keys = [(nodes.Const(arg.arg), arg.value) for arg in node.args[1:]] keys = [(nodes.Const(key), value) for key, value in kwargs]
items = elts + keys items = elts + keys
elif len(node.args) == 1: elif len(args) == 1:
items = _get_elts(node.args[0], context) items = _get_elts(args[0], context)
else: else:
raise UseInferenceDefault() raise UseInferenceDefault()
...@@ -238,8 +255,82 @@ def infer_dict(node, context=None): ...@@ -238,8 +255,82 @@ def infer_dict(node, context=None):
empty.items = items empty.items = items
return empty return empty
def _node_class(node):
klass = node.frame()
while klass is not None and not isinstance(klass, nodes.ClassDef):
if klass.parent is None:
klass = None
else:
klass = klass.parent.frame()
return klass
def infer_super(node, context=None):
"""Understand super calls.
There are some restrictions for what can be understood:
* unbounded super (one argument form) is not understood.
* if the super call is not inside a function (classmethod or method),
then the default inference will be used.
* if the super arguments can't be infered, the default inference
will be used.
"""
if len(node.args) == 1:
# Ignore unbounded super.
raise UseInferenceDefault
scope = node.scope()
if not isinstance(scope, nodes.FunctionDef):
# Ignore non-method uses of super.
raise UseInferenceDefault
if scope.type not in ('classmethod', 'method'):
# Not interested in staticmethods.
raise UseInferenceDefault
cls = _node_class(scope)
if not len(node.args):
mro_pointer = cls
# In we are in a classmethod, the interpreter will fill
# automatically the class as the second argument, not an instance.
if scope.type == 'classmethod':
mro_type = cls
else:
mro_type = cls.instantiate_class()
else:
# TODO(cpopa): support flow control (multiple inference values).
try:
mro_pointer = next(node.args[0].infer(context=context))
except InferenceError:
raise UseInferenceDefault
try:
mro_type = next(node.args[1].infer(context=context))
except InferenceError:
raise UseInferenceDefault
if mro_pointer is YES or mro_type is YES:
# No way we could understand this.
raise UseInferenceDefault
super_obj = objects.Super(mro_pointer=mro_pointer,
mro_type=mro_type,
self_class=cls,
scope=scope)
super_obj.parent = node
return iter([super_obj])
# Builtins inference # Builtins inference
MANAGER.register_transform(nodes.Call,
inference_tip(infer_super),
lambda n: (isinstance(n.func, nodes.Name) and
n.func.name == 'super'))
register_builtin_transform(infer_tuple, 'tuple') register_builtin_transform(infer_tuple, 'tuple')
register_builtin_transform(infer_set, 'set') register_builtin_transform(infer_set, 'set')
register_builtin_transform(infer_list, 'list') register_builtin_transform(infer_list, 'list')
register_builtin_transform(infer_dict, 'dict') register_builtin_transform(infer_dict, 'dict')
register_builtin_transform(infer_frozenset, 'frozenset')
"""Astroid hooks for dateutil"""
import textwrap
from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder
def dateutil_transform():
return AstroidBuilder(MANAGER).string_build(textwrap.dedent('''
import datetime
def parse(timestr, parserinfo=None, **kwargs):
return datetime.datetime()
'''))
register_module_extender(MANAGER, 'dateutil.parser', dateutil_transform)
...@@ -7,8 +7,9 @@ import inspect ...@@ -7,8 +7,9 @@ import inspect
import itertools import itertools
import sys import sys
import re import re
import warnings
from astroid import MANAGER, AstroidBuildingException from astroid import MANAGER, AstroidBuildingException, nodes
from astroid.builder import AstroidBuilder from astroid.builder import AstroidBuilder
...@@ -46,13 +47,13 @@ def _gi_build_stub(parent): ...@@ -46,13 +47,13 @@ def _gi_build_stub(parent):
elif (inspect.ismethod(obj) or elif (inspect.ismethod(obj) or
inspect.ismethoddescriptor(obj)): inspect.ismethoddescriptor(obj)):
methods[name] = obj methods[name] = obj
elif type(obj) in [int, str]:
constants[name] = obj
elif (str(obj).startswith("<flags") or elif (str(obj).startswith("<flags") or
str(obj).startswith("<enum ") or str(obj).startswith("<enum ") or
str(obj).startswith("<GType ") or str(obj).startswith("<GType ") or
inspect.isdatadescriptor(obj)): inspect.isdatadescriptor(obj)):
constants[name] = 0 constants[name] = 0
elif isinstance(obj, (int, str)):
constants[name] = obj
elif callable(obj): elif callable(obj):
# Fall back to a function for anything callable # Fall back to a function for anything callable
functions[name] = obj functions[name] = obj
...@@ -73,7 +74,7 @@ def _gi_build_stub(parent): ...@@ -73,7 +74,7 @@ def _gi_build_stub(parent):
val = constants[name] val = constants[name]
strval = str(val) strval = str(val)
if type(val) is str: if isinstance(val, str):
strval = '"%s"' % str(val).replace("\\", "\\\\") strval = '"%s"' % str(val).replace("\\", "\\\\")
ret += "%s = %s\n" % (name, strval) ret += "%s = %s\n" % (name, strval)
...@@ -82,7 +83,6 @@ def _gi_build_stub(parent): ...@@ -82,7 +83,6 @@ def _gi_build_stub(parent):
if functions: if functions:
ret += "# %s functions\n\n" % parent.__name__ ret += "# %s functions\n\n" % parent.__name__
for name in sorted(functions): for name in sorted(functions):
func = functions[name]
ret += "def %s(*args, **kwargs):\n" % name ret += "def %s(*args, **kwargs):\n" % name
ret += " pass\n" ret += " pass\n"
...@@ -91,7 +91,6 @@ def _gi_build_stub(parent): ...@@ -91,7 +91,6 @@ def _gi_build_stub(parent):
if methods: if methods:
ret += "# %s methods\n\n" % parent.__name__ ret += "# %s methods\n\n" % parent.__name__
for name in sorted(methods): for name in sorted(methods):
func = methods[name]
ret += "def %s(self, *args, **kwargs):\n" % name ret += "def %s(self, *args, **kwargs):\n" % name
ret += " pass\n" ret += " pass\n"
...@@ -135,7 +134,16 @@ def _import_gi_module(modname): ...@@ -135,7 +134,16 @@ def _import_gi_module(modname):
for m in itertools.chain(modnames, optional_modnames): for m in itertools.chain(modnames, optional_modnames):
try: try:
__import__(m) __import__(m)
modcode += _gi_build_stub(sys.modules[m]) with warnings.catch_warnings():
# Just inspecting the code can raise gi deprecation
# warnings, so ignore them.
try:
from gi import PyGIDeprecationWarning
warnings.simplefilter("ignore", PyGIDeprecationWarning)
except Exception:
pass
modcode += _gi_build_stub(sys.modules[m])
except ImportError: except ImportError:
if m not in optional_modnames: if m not in optional_modnames:
raise raise
...@@ -150,6 +158,38 @@ def _import_gi_module(modname): ...@@ -150,6 +158,38 @@ def _import_gi_module(modname):
raise AstroidBuildingException('Failed to import module %r' % modname) raise AstroidBuildingException('Failed to import module %r' % modname)
return astng return astng
def _looks_like_require_version(node):
# Return whether this looks like a call to gi.require_version(<name>, <version>)
# Only accept function calls with two constant arguments
if len(node.args) != 2:
return False
MANAGER.register_failed_import_hook(_import_gi_module) if not all(isinstance(arg, nodes.Const) for arg in node.args):
return False
func = node.func
if isinstance(func, nodes.Attribute):
if func.attrname != 'require_version':
return False
if isinstance(func.expr, nodes.Name) and func.expr.name == 'gi':
return True
return False
if isinstance(func, nodes.Name):
return func.name == 'require_version'
return False
def _register_require_version(node):
# Load the gi.require_version locally
try:
import gi
gi.require_version(node.args[0].value, node.args[1].value)
except Exception:
pass
return node
MANAGER.register_failed_import_hook(_import_gi_module)
MANAGER.register_transform(nodes.Call, _register_require_version, _looks_like_require_version)
...@@ -48,11 +48,14 @@ def _nose_tools_functions(): ...@@ -48,11 +48,14 @@ def _nose_tools_functions():
if method.name.startswith('assert') and '_' not in method.name: if method.name.startswith('assert') and '_' not in method.name:
pep8_name = _pep8(method.name) pep8_name = _pep8(method.name)
yield pep8_name, astroid.BoundMethod(method, case) yield pep8_name, astroid.BoundMethod(method, case)
if method.name == 'assertEqual':
# nose also exports assert_equals.
yield 'assert_equals', astroid.BoundMethod(method, case)
def _nose_tools_transform(node): def _nose_tools_transform(node):
for method_name, method in _nose_tools_functions(): for method_name, method in _nose_tools_functions():
node.locals[method_name] = [method] node._locals[method_name] = [method]
def _nose_tools_trivial_transform(): def _nose_tools_trivial_transform():
......
# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# 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 hooks for numpy."""
import astroid
# TODO(cpopa): drop when understanding augmented assignments
def numpy_core_transform():
return astroid.parse('''
from numpy.core import numeric
from numpy.core import fromnumeric
from numpy.core import defchararray
from numpy.core import records
from numpy.core import function_base
from numpy.core import machar
from numpy.core import getlimits
from numpy.core import shape_base
__all__ = (['char', 'rec', 'memmap', 'chararray'] + numeric.__all__ +
fromnumeric.__all__ +
records.__all__ +
function_base.__all__ +
machar.__all__ +
getlimits.__all__ +
shape_base.__all__)
''')
def numpy_transform():
return astroid.parse('''
from numpy import core
from numpy import matrixlib as _mat
from numpy import lib
__all__ = ['add_newdocs',
'ModuleDeprecationWarning',
'VisibleDeprecationWarning', 'linalg', 'fft', 'random',
'ctypeslib', 'ma',
'__version__', 'pkgload', 'PackageLoader',
'show_config'] + core.__all__ + _mat.__all__ + lib.__all__
''')
astroid.register_module_extender(astroid.MANAGER, 'numpy.core', numpy_core_transform)
astroid.register_module_extender(astroid.MANAGER, 'numpy', numpy_transform)
"""Astroid hooks for pytest.""" """Astroid hooks for pytest."""
from __future__ import absolute_import
from astroid import MANAGER, register_module_extender from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder from astroid.builder import AstroidBuilder
def pytest_transform(): def pytest_transform():
return AstroidBuilder(MANAGER).string_build(''' return AstroidBuilder(MANAGER).string_build('''
try: try:
import _pytest.mark import _pytest.mark
import _pytest.recwarn import _pytest.recwarn
import _pytest.runner import _pytest.runner
import _pytest.python import _pytest.python
except ImportError: import _pytest.skipping
pass import _pytest.assertion
else: except ImportError:
deprecated_call = _pytest.recwarn.deprecated_call pass
exit = _pytest.runner.exit else:
fail = _pytest.runner.fail deprecated_call = _pytest.recwarn.deprecated_call
fixture = _pytest.python.fixture warns = _pytest.recwarn.warns
importorskip = _pytest.runner.importorskip
mark = _pytest.mark.MarkGenerator() exit = _pytest.runner.exit
raises = _pytest.python.raises fail = _pytest.runner.fail
skip = _pytest.runner.skip skip = _pytest.runner.skip
yield_fixture = _pytest.python.yield_fixture importorskip = _pytest.runner.importorskip
''') xfail = _pytest.skipping.xfail
mark = _pytest.mark.MarkGenerator()
register_module_extender(MANAGER, 'pytest', pytest_transform) raises = _pytest.python.raises
register_module_extender(MANAGER, 'py.test', pytest_transform)
# New in pytest 3.0
try:
approx = _pytest.python.approx
register_assert_rewrite = _pytest.assertion.register_assert_rewrite
except AttributeError:
pass
# Moved in pytest 3.0
try:
import _pytest.freeze_support
freeze_includes = _pytest.freeze_support.freeze_includes
except ImportError:
try:
import _pytest.genscript
freeze_includes = _pytest.genscript.freeze_includes
except ImportError:
pass
try:
import _pytest.debugging
set_trace = _pytest.debugging.pytestPDB().set_trace
except ImportError:
try:
import _pytest.pdb
set_trace = _pytest.pdb.pytestPDB().set_trace
except ImportError:
pass
try:
import _pytest.fixtures
fixture = _pytest.fixtures.fixture
yield_fixture = _pytest.fixtures.yield_fixture
except ImportError:
try:
import _pytest.python
fixture = _pytest.python.fixture
yield_fixture = _pytest.python.yield_fixture
except ImportError:
pass
''')
register_module_extender(MANAGER, 'pytest', pytest_transform)
register_module_extender(MANAGER, 'py.test', pytest_transform)
"""Astroid hooks for the PyQT library."""
from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder
from astroid import nodes
from astroid import parse
def _looks_like_signal(node, signal_name='pyqtSignal'):
if '__class__' in node._instance_attrs:
cls = node._instance_attrs['__class__'][0]
return cls.name == signal_name
return False
def transform_pyqt_signal(node):
module = parse('''
class pyqtSignal(object):
def connect(self, slot, type=None, no_receiver_check=False):
pass
def disconnect(self, slot):
pass
def emit(self, *args):
pass
''')
signal_cls = module['pyqtSignal']
node._instance_attrs['emit'] = signal_cls['emit']
node._instance_attrs['disconnect'] = signal_cls['disconnect']
node._instance_attrs['connect'] = signal_cls['connect']
def pyqt4_qtcore_transform():
return AstroidBuilder(MANAGER).string_build('''
def SIGNAL(signal_name): pass
class QObject(object):
def emit(self, signal): pass
''')
register_module_extender(MANAGER, 'PyQt4.QtCore', pyqt4_qtcore_transform)
MANAGER.register_transform(nodes.FunctionDef, transform_pyqt_signal,
_looks_like_signal)
\ No newline at end of file
...@@ -23,7 +23,12 @@ from textwrap import dedent ...@@ -23,7 +23,12 @@ from textwrap import dedent
from astroid import MANAGER, register_module_extender from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder from astroid.builder import AstroidBuilder
from astroid.exceptions import AstroidBuildingException from astroid.exceptions import AstroidBuildingException, InferenceError
from astroid import nodes
SIX_ADD_METACLASS = 'six.add_metaclass'
def _indent(text, prefix, predicate=None): def _indent(text, prefix, predicate=None):
"""Adds 'prefix' to the beginning of selected lines in 'text'. """Adds 'prefix' to the beginning of selected lines in 'text'.
...@@ -254,8 +259,30 @@ def _six_fail_hook(modname): ...@@ -254,8 +259,30 @@ def _six_fail_hook(modname):
module.name = 'six.moves' module.name = 'six.moves'
return module return module
def transform_six_add_metaclass(node):
"""Check if the given class node is decorated with *six.add_metaclass*
If so, inject its argument as the metaclass of the underlying class.
"""
if not node.decorators:
return
for decorator in node.decorators.nodes:
if not isinstance(decorator, nodes.Call):
continue
try:
func = next(decorator.func.infer())
except InferenceError:
continue
if func.qname() == SIX_ADD_METACLASS and decorator.args:
metaclass = decorator.args[0]
node._metaclass = metaclass
return node
register_module_extender(MANAGER, 'six', six_moves_transform) register_module_extender(MANAGER, 'six', six_moves_transform)
register_module_extender(MANAGER, 'requests.packages.urllib3.packages.six', register_module_extender(MANAGER, 'requests.packages.urllib3.packages.six',
six_moves_transform) six_moves_transform)
MANAGER.register_failed_import_hook(_six_fail_hook) MANAGER.register_failed_import_hook(_six_fail_hook)
MANAGER.register_transform(nodes.ClassDef, transform_six_add_metaclass)
"""Astroid hooks for the ssl library."""
from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder
from astroid import nodes
from astroid import parse
def ssl_transform():
return parse('''
from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
from _ssl import _SSLContext, MemoryBIO
from _ssl import (
SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError,
SSLSyscallError, SSLEOFError,
)
from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj
from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes
try:
from _ssl import RAND_egd
except ImportError:
# LibreSSL does not provide RAND_egd
pass
from _ssl import (OP_ALL, OP_CIPHER_SERVER_PREFERENCE,
OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3,
OP_NO_TLSv1, OP_NO_TLSv1_1, OP_NO_TLSv1_2,
OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE)
from _ssl import (ALERT_DESCRIPTION_ACCESS_DENIED, ALERT_DESCRIPTION_BAD_CERTIFICATE,
ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE,
ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE,
ALERT_DESCRIPTION_BAD_RECORD_MAC,
ALERT_DESCRIPTION_CERTIFICATE_EXPIRED,
ALERT_DESCRIPTION_CERTIFICATE_REVOKED,
ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN,
ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE,
ALERT_DESCRIPTION_CLOSE_NOTIFY, ALERT_DESCRIPTION_DECODE_ERROR,
ALERT_DESCRIPTION_DECOMPRESSION_FAILURE,
ALERT_DESCRIPTION_DECRYPT_ERROR,
ALERT_DESCRIPTION_HANDSHAKE_FAILURE,
ALERT_DESCRIPTION_ILLEGAL_PARAMETER,
ALERT_DESCRIPTION_INSUFFICIENT_SECURITY,
ALERT_DESCRIPTION_INTERNAL_ERROR,
ALERT_DESCRIPTION_NO_RENEGOTIATION,
ALERT_DESCRIPTION_PROTOCOL_VERSION,
ALERT_DESCRIPTION_RECORD_OVERFLOW,
ALERT_DESCRIPTION_UNEXPECTED_MESSAGE,
ALERT_DESCRIPTION_UNKNOWN_CA,
ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY,
ALERT_DESCRIPTION_UNRECOGNIZED_NAME,
ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE,
ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION,
ALERT_DESCRIPTION_USER_CANCELLED)
from _ssl import (SSL_ERROR_EOF, SSL_ERROR_INVALID_ERROR_CODE, SSL_ERROR_SSL,
SSL_ERROR_SYSCALL, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_READ,
SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_X509_LOOKUP, SSL_ERROR_ZERO_RETURN)
from _ssl import VERIFY_CRL_CHECK_CHAIN, VERIFY_CRL_CHECK_LEAF, VERIFY_DEFAULT, VERIFY_X509_STRICT
from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN
from _ssl import _OPENSSL_API_VERSION
from _ssl import PROTOCOL_SSLv23, PROTOCOL_TLSv1, PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2
''')
register_module_extender(MANAGER, 'ssl', ssl_transform)
"""Astroid hooks for the Python 2 qt4 module.
Currently help understanding of :
* PyQT4.QtCore
"""
from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder
def pyqt4_qtcore_transform():
return AstroidBuilder(MANAGER).string_build('''
def SIGNAL(signal_name): pass
class QObject(object):
def emit(self, signal): pass
''')
register_module_extender(MANAGER, 'PyQt4.QtCore', pyqt4_qtcore_transform)
This diff is collapsed.
# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# 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/>.
"""Various context related utilities, including inference and call contexts."""
import contextlib
class InferenceContext(object):
__slots__ = ('path', 'lookupname', 'callcontext', 'boundnode', 'inferred')
def __init__(self, path=None, inferred=None):
self.path = path or set()
self.lookupname = None
self.callcontext = None
self.boundnode = None
self.inferred = inferred or {}
def push(self, node):
name = self.lookupname
if (node, name) in self.path:
raise StopIteration()
self.path.add((node, name))
def clone(self):
# XXX copy lookupname/callcontext ?
clone = InferenceContext(self.path, inferred=self.inferred)
clone.callcontext = self.callcontext
clone.boundnode = self.boundnode
return clone
def cache_generator(self, key, generator):
results = []
for result in generator:
results.append(result)
yield result
self.inferred[key] = tuple(results)
return
@contextlib.contextmanager
def restore_path(self):
path = set(self.path)
yield
self.path = path
class CallContext(object):
"""Holds information for a call site."""
__slots__ = ('args', 'keywords')
def __init__(self, args, keywords=None):
self.args = args
if keywords:
keywords = [(arg.arg, arg.value) for arg in keywords]
else:
keywords = []
self.keywords = keywords
def copy_context(context):
if context is not None:
return context.clone()
else:
return InferenceContext()
# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# 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/>.
#
# The code in this file was originally part of logilab-common, licensed under
# the same license.
""" A few useful function/method decorators."""
import wrapt
@wrapt.decorator
def cached(func, instance, args, kwargs):
"""Simple decorator to cache result of method calls without args."""
cache = getattr(instance, '__cache', None)
if cache is None:
instance.__cache = cache = {}
try:
return cache[func]
except KeyError:
cache[func] = result = func(*args, **kwargs)
return result
class cachedproperty(object):
""" Provides a cached property equivalent to the stacking of
@cached and @property, but more efficient.
After first usage, the <property_name> becomes part of the object's
__dict__. Doing:
del obj.<property_name> empties the cache.
Idea taken from the pyramid_ framework and the mercurial_ project.
.. _pyramid: http://pypi.python.org/pypi/pyramid
.. _mercurial: http://pypi.python.org/pypi/Mercurial
"""
__slots__ = ('wrapped',)
def __init__(self, wrapped):
try:
wrapped.__name__
except AttributeError:
raise TypeError('%s must have a __name__ attribute' %
wrapped)
self.wrapped = wrapped
@property
def __doc__(self):
doc = getattr(self.wrapped, '__doc__', None)
return ('<wrapped by the cachedproperty decorator>%s'
% ('\n%s' % doc if doc else ''))
def __get__(self, inst, objtype=None):
if inst is None:
return self
val = self.wrapped(inst)
setattr(inst, self.wrapped.__name__, val)
return val
...@@ -30,6 +30,26 @@ class AstroidBuildingException(AstroidError): ...@@ -30,6 +30,26 @@ class AstroidBuildingException(AstroidError):
class ResolveError(AstroidError): class ResolveError(AstroidError):
"""base class of astroid resolution/inference error""" """base class of astroid resolution/inference error"""
class MroError(ResolveError):
"""Error raised when there is a problem with method resolution of a class."""
class DuplicateBasesError(MroError):
"""Error raised when there are duplicate bases in the same class bases."""
class InconsistentMroError(MroError):
"""Error raised when a class's MRO is inconsistent."""
class SuperError(ResolveError):
"""Error raised when there is a problem with a super call."""
class SuperArgumentTypeError(SuperError):
"""Error raised when the super arguments are invalid."""
class NotFoundError(ResolveError): class NotFoundError(ResolveError):
"""raised when we are unable to resolve a name""" """raised when we are unable to resolve a name"""
......
This diff is collapsed.
This diff is collapsed.
...@@ -18,16 +18,16 @@ ...@@ -18,16 +18,16 @@
"""This module contains some mixins for the different nodes. """This module contains some mixins for the different nodes.
""" """
from logilab.common.decorators import cachedproperty import warnings
from astroid.exceptions import (AstroidBuildingException, InferenceError, from astroid import decorators
NotFoundError) from astroid import exceptions
class BlockRangeMixIn(object): class BlockRangeMixIn(object):
"""override block range """ """override block range """
@cachedproperty @decorators.cachedproperty
def blockstart_tolineno(self): def blockstart_tolineno(self):
return self.lineno return self.lineno
...@@ -55,15 +55,29 @@ class FilterStmtsMixin(object): ...@@ -55,15 +55,29 @@ class FilterStmtsMixin(object):
return [node], True return [node], True
return _stmts, False return _stmts, False
def ass_type(self): def assign_type(self):
return self return self
def ass_type(self):
warnings.warn('%s.ass_type() is deprecated and slated for removal '
'in astroid 2.0, use %s.assign_type() instead.'
% (type(self).__name__, type(self).__name__),
PendingDeprecationWarning, stacklevel=2)
return self.assign_type()
class AssignTypeMixin(object): class AssignTypeMixin(object):
def ass_type(self): def assign_type(self):
return self return self
def ass_type(self):
warnings.warn('%s.ass_type() is deprecated and slated for removal '
'in astroid 2.0, use %s.assign_type() instead.'
% (type(self).__name__, type(self).__name__),
PendingDeprecationWarning, stacklevel=2)
return self.assign_type()
def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt): def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt):
"""method used in filter_stmts""" """method used in filter_stmts"""
if self is mystmt: if self is mystmt:
...@@ -77,11 +91,18 @@ class AssignTypeMixin(object): ...@@ -77,11 +91,18 @@ class AssignTypeMixin(object):
class ParentAssignTypeMixin(AssignTypeMixin): class ParentAssignTypeMixin(AssignTypeMixin):
def assign_type(self):
return self.parent.assign_type()
def ass_type(self): def ass_type(self):
return self.parent.ass_type() warnings.warn('%s.ass_type() is deprecated and slated for removal '
'in astroid 2.0, use %s.assign_type() instead.'
% (type(self).__name__, type(self).__name__),
PendingDeprecationWarning, stacklevel=2)
return self.assign_type()
class FromImportMixIn(FilterStmtsMixin): class ImportFromMixin(FilterStmtsMixin):
"""MixIn for From and Import Nodes""" """MixIn for From and Import Nodes"""
def _infer_name(self, frame, name): def _infer_name(self, frame, name):
...@@ -104,11 +125,14 @@ class FromImportMixIn(FilterStmtsMixin): ...@@ -104,11 +125,14 @@ class FromImportMixIn(FilterStmtsMixin):
# FIXME: we used to raise InferenceError here, but why ? # FIXME: we used to raise InferenceError here, but why ?
return mymodule return mymodule
try: try:
return mymodule.import_module(modname, level=level) return mymodule.import_module(modname, level=level,
except AstroidBuildingException: relative_only=level and level >= 1)
raise InferenceError(modname) except exceptions.AstroidBuildingException as ex:
if isinstance(ex.args[0], SyntaxError):
raise exceptions.InferenceError(str(ex))
raise exceptions.InferenceError(modname)
except SyntaxError as ex: except SyntaxError as ex:
raise InferenceError(str(ex)) raise exceptions.InferenceError(str(ex))
def real_name(self, asname): def real_name(self, asname):
"""get name from 'as' name""" """get name from 'as' name"""
...@@ -120,5 +144,4 @@ class FromImportMixIn(FilterStmtsMixin): ...@@ -120,5 +144,4 @@ class FromImportMixIn(FilterStmtsMixin):
_asname = name _asname = name
if asname == _asname: if asname == _asname:
return name return name
raise NotFoundError(asname) raise exceptions.NotFoundError(asname)
This diff is collapsed.
This diff is collapsed.
...@@ -24,40 +24,54 @@ on all nodes : ...@@ -24,40 +24,54 @@ on all nodes :
.next_sibling(), returning next sibling statement node .next_sibling(), returning next sibling statement node
.statement(), returning the first parent node marked as statement node .statement(), returning the first parent node marked as statement node
.frame(), returning the first node defining a new local scope (i.e. .frame(), returning the first node defining a new local scope (i.e.
Module, Function or Class) Module, FunctionDef or ClassDef)
.set_local(name, node), define an identifier <name> on the first parent frame, .set_local(name, node), define an identifier <name> on the first parent frame,
with the node defining it. This is used by the astroid builder and should not with the node defining it. This is used by the astroid builder and should not
be used from out there. be used from out there.
on From and Import : on ImportFrom and Import :
.real_name(name), .real_name(name),
""" """
# pylint: disable=unused-import # pylint: disable=unused-import,redefined-builtin
from astroid.node_classes import (
Arguments, AssignAttr, Assert, Assign,
AssignName, AugAssign, Repr, BinOp, BoolOp, Break, Call, Compare,
Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete,
Dict, Expr, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For,
ImportFrom, Attribute, Global, If, IfExp, Import, Index, Keyword,
List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript,
TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom,
const_factory,
AsyncFor, Await, AsyncWith,
# Backwards-compatibility aliases
Backquote, Discard, AssName, AssAttr, Getattr, CallFunc, From,
# Node not present in the builtin ast module.
DictUnpack,
)
from astroid.scoped_nodes import (
Module, GeneratorExp, Lambda, DictComp,
ListComp, SetComp, FunctionDef, ClassDef,
AsyncFunctionDef,
# Backwards-compatibility aliases
Class, Function, GenExpr,
)
__docformat__ = "restructuredtext en"
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, YieldFrom, \
const_factory
from astroid.scoped_nodes import Module, GenExpr, Lambda, DictComp, \
ListComp, SetComp, Function, Class
ALL_NODE_CLASSES = ( ALL_NODE_CLASSES = (
Arguments, AssAttr, Assert, Assign, AssName, AugAssign, AsyncFunctionDef, AsyncFor, AsyncWith, Await,
Backquote, BinOp, BoolOp, Break,
CallFunc, Class, Compare, Comprehension, Const, Continue, Arguments, AssignAttr, Assert, Assign, AssignName, AugAssign,
Repr, BinOp, BoolOp, Break,
Call, ClassDef, Compare, Comprehension, Const, Continue,
Decorators, DelAttr, DelName, Delete, Decorators, DelAttr, DelName, Delete,
Dict, DictComp, Discard, Dict, DictComp, DictUnpack, Expr,
Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice,
For, From, Function, For, ImportFrom, FunctionDef,
Getattr, GenExpr, Global, Attribute, GeneratorExp, Global,
If, IfExp, Import, Index, If, IfExp, Import, Index,
Keyword, Keyword,
Lambda, List, ListComp, Lambda, List, ListComp,
...@@ -69,6 +83,5 @@ ALL_NODE_CLASSES = ( ...@@ -69,6 +83,5 @@ ALL_NODE_CLASSES = (
TryExcept, TryFinally, Tuple, TryExcept, TryFinally, Tuple,
UnaryOp, UnaryOp,
While, With, While, With,
Yield, YieldFrom Yield, YieldFrom,
) )
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
"""Utility functions for test code that uses astroid ASTs as input.""" """Utility functions for test code that uses astroid ASTs as input."""
import functools import functools
import sys import sys
import textwrap
from astroid import nodes from astroid import nodes
from astroid import builder from astroid import builder
...@@ -14,7 +13,6 @@ _TRANSIENT_FUNCTION = '__' ...@@ -14,7 +13,6 @@ _TRANSIENT_FUNCTION = '__'
# when calling extract_node. # when calling extract_node.
_STATEMENT_SELECTOR = '#@' _STATEMENT_SELECTOR = '#@'
def _extract_expressions(node): def _extract_expressions(node):
"""Find expressions in a call to _TRANSIENT_FUNCTION and extract them. """Find expressions in a call to _TRANSIENT_FUNCTION and extract them.
...@@ -28,7 +26,7 @@ def _extract_expressions(node): ...@@ -28,7 +26,7 @@ def _extract_expressions(node):
:yields: The sequence of wrapped expressions on the modified tree :yields: The sequence of wrapped expressions on the modified tree
expression can be found. expression can be found.
""" """
if (isinstance(node, nodes.CallFunc) if (isinstance(node, nodes.Call)
and isinstance(node.func, nodes.Name) and isinstance(node.func, nodes.Name)
and node.func.name == _TRANSIENT_FUNCTION): and node.func.name == _TRANSIENT_FUNCTION):
real_expr = node.args[0] real_expr = node.args[0]
...@@ -68,7 +66,7 @@ def _find_statement_by_line(node, line): ...@@ -68,7 +66,7 @@ def _find_statement_by_line(node, line):
can be found. can be found.
:rtype: astroid.bases.NodeNG or None :rtype: astroid.bases.NodeNG or None
""" """
if isinstance(node, (nodes.Class, nodes.Function)): if isinstance(node, (nodes.ClassDef, nodes.FunctionDef)):
# This is an inaccuracy in the AST: the nodes that can be # This is an inaccuracy in the AST: the nodes that can be
# decorated do not carry explicit information on which line # decorated do not carry explicit information on which line
# the actual definition (class/def), but .fromline seems to # the actual definition (class/def), but .fromline seems to
...@@ -142,7 +140,7 @@ def extract_node(code, module_name=''): ...@@ -142,7 +140,7 @@ def extract_node(code, module_name=''):
:rtype: astroid.bases.NodeNG, or a list of nodes. :rtype: astroid.bases.NodeNG, or a list of nodes.
""" """
def _extract(node): def _extract(node):
if isinstance(node, nodes.Discard): if isinstance(node, nodes.Expr):
return node.value return node.value
else: else:
return node return node
...@@ -152,7 +150,7 @@ def extract_node(code, module_name=''): ...@@ -152,7 +150,7 @@ def extract_node(code, module_name=''):
if line.strip().endswith(_STATEMENT_SELECTOR): if line.strip().endswith(_STATEMENT_SELECTOR):
requested_lines.append(idx + 1) requested_lines.append(idx + 1)
tree = build_module(code, module_name=module_name) tree = builder.parse(code, module_name=module_name)
extracted = [] extracted = []
if requested_lines: if requested_lines:
for line in requested_lines: for line in requested_lines:
...@@ -171,21 +169,6 @@ def extract_node(code, module_name=''): ...@@ -171,21 +169,6 @@ def extract_node(code, module_name=''):
return extracted return extracted
def build_module(code, module_name='', path=None):
"""Parses a string module with a builder.
:param code: The code for the module.
:type code: str
:param module_name: The name for the module
:type module_name: str
:param path: The path for the module
:type module_name: str
:returns: The module AST.
:rtype: astroid.bases.NodeNG
"""
code = textwrap.dedent(code)
return builder.AstroidBuilder(None).string_build(code, modname=module_name, path=path)
def require_version(minver=None, maxver=None): def require_version(minver=None, maxver=None):
""" Compare version of python interpreter to the given one. Skip the test """ Compare version of python interpreter to the given one. Skip the test
if older. if older.
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
from warnings import warn
warn('logilab.common.contexts module is deprecated, use logilab.common.shellutils instead',
DeprecationWarning, stacklevel=1)
from logilab.common.shellutils import tempfile, pushd
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment