Commit 53297790 authored by Mike Frysinger's avatar Mike Frysinger Committed by Commit Bot

pylint: rework wrapper to use vpython for multiple versions

This allows people to pin a specific version of pylint when running
it.  The default behavior is unchanged: we run pylint-1.5.6 still
as we've created a vpython wheel in cipd for it.  But for projects
that want to move to a newer version, they can pick any of the new
1.x wrappers.  Having the full set of versions here allows projects
to incrementally move to newer versions as the lint delta between
1.5 & 1.6 tends to be much smaller than 1.5 & 1.9.

We don't include support for the newer 2.x series as those only
support Python 3 which the codebase isn't ready for.

Bug: 866772
Test: `./pylint ...` still works & shows pylint-1.5
Test: `./pylint-1.5 ...` runs the v1.5 version
Test: `./pylint-1.8 ...` runs the v1.8 version
Change-Id: I7c47187493564db81e3eb28c6dbd09e6309ead46
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/1752507Reviewed-by: 's avatarRobbie Iannucci <iannucci@chromium.org>
Commit-Queue: Mike Frysinger <vapier@chromium.org>
parent ca1eb7d1
......@@ -13,7 +13,6 @@
/git-bash
/git.bat
/gitk.bat
/pylint.bat
/.pylint.d/
/python
/python.bat
......
......@@ -35,5 +35,12 @@ cipd_bin_setup
# Sync "gsutil.py".
python "$base_dir/gsutil.py" -- version 1> /dev/null
# Sync all the pylint versions.
for script in "$base_dir"/pylint-[0-9].[0-9]; do
# We have to silence stderr too because newer pylint versions will emit
# a spurious log to tell us what pylintrc file it's using. Ugh.
"$script" --version >/dev/null 2>&1
done
# Cleanup.
find "$base_dir" -iname "*.pyc" -delete || true
......@@ -834,9 +834,10 @@ def GetPylint(input_api, output_api, white_list=None, black_list=None,
# Windows needs help running python files so we explicitly specify
# the interpreter to use. It also has limitations on the size of
# the command-line, so we pass arguments via a pipe.
cmd = [input_api.python_executable,
input_api.os_path.join(_HERE, 'third_party', 'pylint.py'),
'--args-on-stdin']
tool = input_api.os_path.join(_HERE, 'pylint')
if input_api.platform == 'win32':
tool += '.bat'
cmd = [tool, '--args-on-stdin']
if len(flist) == 1:
description = flist[0]
else:
......
......@@ -3,17 +3,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# pylint -- a shell wrapper for pylint.py.
# pylint -- a shell wrapper for pylint.
base_dir=$(dirname "$0")
# Test if this script is running under a MSys install. If it is, we will
# hardcode the path to Python where possible.
OUTPUT="$(uname | grep 'MINGW')"
MINGW=$?
if [ -e "$base_dir/python.bat" -a $MINGW = 0 ]; then
PYTHONDONTWRITEBYTECODE=1 cmd.exe //c "$base_dir\\python.bat" "$base_dir\\pylint.py" "$@"
else
PYTHONDONTWRITEBYTECODE=1 exec "$base_dir/pylint.py" "$@"
fi
PYTHONDONTWRITEBYTECODE=1 exec "$base_dir/pylint-1.5" "$@"
#!/usr/bin/env vpython
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Note: We use tags here instead of refs because refs will always hit the
# network to see if there are any updates.
# [VPYTHON:BEGIN]
# # Third party dependencies. These are only listed because pylint itself needs
# # them. Feel free to add/remove anything here.
#
# wheel: <
# name: "infra/python/wheels/configparser-py2_py3"
# version: "version:3.5.0"
# >
# wheel: <
# name: "infra/python/wheels/futures-py2_py3"
# version: "version:3.1.1"
# >
# wheel: <
# name: "infra/python/wheels/isort-py2_py3"
# version: "version:4.3.4"
# >
# wheel: <
# name: "infra/python/wheels/wrapt/${vpython_platform}"
# version: "version:1.10.11"
# >
# wheel: <
# name: "infra/python/wheels/backports_functools_lru_cache-py2_py3"
# version: "version:1.5"
# >
# wheel: <
# name: "infra/python/wheels/lazy-object-proxy/${vpython_platform}"
# version: "version:1.3.1"
# >
# wheel: <
# name: "infra/python/wheels/singledispatch-py2_py3"
# version: "version:3.4.0.3"
# >
# wheel: <
# name: "infra/python/wheels/enum34-py2"
# version: "version:1.1.6"
# >
# wheel: <
# name: "infra/python/wheels/mccabe-py2_py3"
# version: "version:0.6.1"
# >
# wheel: <
# name: "infra/python/wheels/six-py2_py3"
# version: "version:1.10.0"
# >
#
# # Pylint dependencies.
#
# wheel: <
# name: "infra/python/wheels/astroid-py2_py3"
# version: "version:1.4.9"
# >
#
# wheel: <
# name: "infra/python/wheels/pylint-py2_py3"
# version: "version:1.5.6-551505593fa0d9c5372b79c95e3dfe9a"
# >
# [VPYTHON:END]
import sys
import pylint_main
sys.exit(pylint_main.main(sys.argv[1:]))
#!/usr/bin/env vpython
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Note: We use tags here instead of refs because refs will always hit the
# network to see if there are any updates.
# [VPYTHON:BEGIN]
# # Third party dependencies. These are only listed because pylint itself needs
# # them. Feel free to add/remove anything here.
#
# wheel: <
# name: "infra/python/wheels/configparser-py2_py3"
# version: "version:3.5.0"
# >
# wheel: <
# name: "infra/python/wheels/futures-py2_py3"
# version: "version:3.1.1"
# >
# wheel: <
# name: "infra/python/wheels/isort-py2_py3"
# version: "version:4.3.4"
# >
# wheel: <
# name: "infra/python/wheels/wrapt/${vpython_platform}"
# version: "version:1.10.11"
# >
# wheel: <
# name: "infra/python/wheels/backports_functools_lru_cache-py2_py3"
# version: "version:1.5"
# >
# wheel: <
# name: "infra/python/wheels/lazy-object-proxy/${vpython_platform}"
# version: "version:1.3.1"
# >
# wheel: <
# name: "infra/python/wheels/singledispatch-py2_py3"
# version: "version:3.4.0.3"
# >
# wheel: <
# name: "infra/python/wheels/enum34-py2"
# version: "version:1.1.6"
# >
# wheel: <
# name: "infra/python/wheels/mccabe-py2_py3"
# version: "version:0.6.1"
# >
# wheel: <
# name: "infra/python/wheels/six-py2_py3"
# version: "version:1.10.0"
# >
#
# # Pylint dependencies.
#
# wheel: <
# name: "infra/python/wheels/astroid-py2_py3"
# version: "version:1.4.9"
# >
#
# wheel: <
# name: "infra/python/wheels/pylint-py2_py3"
# version: "version:1.6.5"
# >
# [VPYTHON:END]
import sys
import pylint_main
sys.exit(pylint_main.main(sys.argv[1:]))
#!/usr/bin/env vpython
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Note: We use tags here instead of refs because refs will always hit the
# network to see if there are any updates.
# [VPYTHON:BEGIN]
# # Third party dependencies. These are only listed because pylint itself needs
# # them. Feel free to add/remove anything here.
#
# wheel: <
# name: "infra/python/wheels/configparser-py2_py3"
# version: "version:3.5.0"
# >
# wheel: <
# name: "infra/python/wheels/futures-py2_py3"
# version: "version:3.1.1"
# >
# wheel: <
# name: "infra/python/wheels/isort-py2_py3"
# version: "version:4.3.4"
# >
# wheel: <
# name: "infra/python/wheels/wrapt/${vpython_platform}"
# version: "version:1.10.11"
# >
# wheel: <
# name: "infra/python/wheels/backports_functools_lru_cache-py2_py3"
# version: "version:1.5"
# >
# wheel: <
# name: "infra/python/wheels/lazy-object-proxy/${vpython_platform}"
# version: "version:1.3.1"
# >
# wheel: <
# name: "infra/python/wheels/singledispatch-py2_py3"
# version: "version:3.4.0.3"
# >
# wheel: <
# name: "infra/python/wheels/enum34-py2"
# version: "version:1.1.6"
# >
# wheel: <
# name: "infra/python/wheels/mccabe-py2_py3"
# version: "version:0.6.1"
# >
# wheel: <
# name: "infra/python/wheels/six-py2_py3"
# version: "version:1.10.0"
# >
#
# # Pylint dependencies.
#
# wheel: <
# name: "infra/python/wheels/astroid-py2_py3"
# version: "version:1.5.3"
# >
#
# wheel: <
# name: "infra/python/wheels/pylint-py2_py3"
# version: "version:1.7.6"
# >
# [VPYTHON:END]
import sys
import pylint_main
sys.exit(pylint_main.main(sys.argv[1:]))
#!/usr/bin/env vpython
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Note: We use tags here instead of refs because refs will always hit the
# network to see if there are any updates.
# [VPYTHON:BEGIN]
# # Third party dependencies. These are only listed because pylint itself needs
# # them. Feel free to add/remove anything here.
#
# wheel: <
# name: "infra/python/wheels/configparser-py2_py3"
# version: "version:3.5.0"
# >
# wheel: <
# name: "infra/python/wheels/futures-py2_py3"
# version: "version:3.1.1"
# >
# wheel: <
# name: "infra/python/wheels/isort-py2_py3"
# version: "version:4.3.4"
# >
# wheel: <
# name: "infra/python/wheels/wrapt/${vpython_platform}"
# version: "version:1.10.11"
# >
# wheel: <
# name: "infra/python/wheels/backports_functools_lru_cache-py2_py3"
# version: "version:1.5"
# >
# wheel: <
# name: "infra/python/wheels/lazy-object-proxy/${vpython_platform}"
# version: "version:1.3.1"
# >
# wheel: <
# name: "infra/python/wheels/singledispatch-py2_py3"
# version: "version:3.4.0.3"
# >
# wheel: <
# name: "infra/python/wheels/enum34-py2"
# version: "version:1.1.6"
# >
# wheel: <
# name: "infra/python/wheels/mccabe-py2_py3"
# version: "version:0.6.1"
# >
# wheel: <
# name: "infra/python/wheels/six-py2_py3"
# version: "version:1.10.0"
# >
#
# # Pylint dependencies.
#
# wheel: <
# name: "infra/python/wheels/astroid-py2_py3"
# version: "version:1.6.6"
# >
#
# wheel: <
# name: "infra/python/wheels/pylint-py2_py3"
# version: "version:1.8.4"
# >
# [VPYTHON:END]
import sys
import pylint_main
sys.exit(pylint_main.main(sys.argv[1:]))
#!/usr/bin/env vpython
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Note: We use tags here instead of refs because refs will always hit the
# network to see if there are any updates.
# [VPYTHON:BEGIN]
# # Third party dependencies. These are only listed because pylint itself needs
# # them. Feel free to add/remove anything here.
#
# wheel: <
# name: "infra/python/wheels/configparser-py2_py3"
# version: "version:3.5.0"
# >
# wheel: <
# name: "infra/python/wheels/futures-py2_py3"
# version: "version:3.1.1"
# >
# wheel: <
# name: "infra/python/wheels/isort-py2_py3"
# version: "version:4.3.4"
# >
# wheel: <
# name: "infra/python/wheels/wrapt/${vpython_platform}"
# version: "version:1.10.11"
# >
# wheel: <
# name: "infra/python/wheels/backports_functools_lru_cache-py2_py3"
# version: "version:1.5"
# >
# wheel: <
# name: "infra/python/wheels/lazy-object-proxy/${vpython_platform}"
# version: "version:1.3.1"
# >
# wheel: <
# name: "infra/python/wheels/singledispatch-py2_py3"
# version: "version:3.4.0.3"
# >
# wheel: <
# name: "infra/python/wheels/enum34-py2"
# version: "version:1.1.6"
# >
# wheel: <
# name: "infra/python/wheels/mccabe-py2_py3"
# version: "version:0.6.1"
# >
# wheel: <
# name: "infra/python/wheels/six-py2_py3"
# version: "version:1.10.0"
# >
#
# # Pylint dependencies.
#
# wheel: <
# name: "infra/python/wheels/astroid-py2_py3"
# version: "version:1.6.6"
# >
#
# wheel: <
# name: "infra/python/wheels/pylint-py2_py3"
# version: "version:1.9.5"
# >
# [VPYTHON:END]
import sys
import pylint_main
sys.exit(pylint_main.main(sys.argv[1:]))
@echo off
:: Copyright 2019 The Chromium Authors. All rights reserved.
:: Use of this source code is governed by a BSD-style license that can be
:: found in the LICENSE file.
setlocal
:: Ensure that "depot_tools" is somewhere in PATH so this tool can be used
:: standalone, but allow other PATH manipulations to take priority.
set PATH=%PATH%;%~dp0
:: Defer control.
vpython "%~dp0\pylint-1.5" %*
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A wrapper script for using pylint from the command line."""
import os
import subprocess
import sys
_HERE = os.path.dirname(os.path.abspath(__file__))
_PYLINT = os.path.join(_HERE, 'third_party', 'pylint.py')
_RC_FILE = os.path.join(_HERE, 'pylintrc')
# Run pylint. We prepend the command-line with the depot_tools rcfile. If
# another rcfile is to be used, passing --rcfile a second time on the command-
# line will work fine.
command = [sys.executable, _PYLINT]
if os.path.isfile(_RC_FILE):
# The file can be removed to test 'normal' pylint behavior.
command.append('--rcfile=%s' % _RC_FILE)
command.extend(sys.argv[1:])
try:
sys.exit(subprocess.call(command))
except KeyboardInterrupt:
sys.stderr.write('interrupted\n')
sys.exit(1)
#!/usr/bin/env python
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Chromium wrapper for pylint for passing args via stdin.
This will be executed by vpython with the right pylint versions.
"""
from __future__ import print_function
import os
import sys
from pylint import lint
HERE = os.path.dirname(os.path.abspath(__file__))
PYLINT = os.path.join(HERE, 'pylint_main.py')
RC_FILE = os.path.join(HERE, 'pylintrc')
ARGS_ON_STDIN = '--args-on-stdin'
def main(argv):
"""Our main wrapper."""
# Add support for a custom mode where arguments are fed line by line on
# stdin. This allows us to get around command line length limitations.
if ARGS_ON_STDIN in argv:
argv = [x for x in argv if x != ARGS_ON_STDIN]
argv.extend(x.strip() for x in sys.stdin)
# We prepend the command-line with the depot_tools rcfile. If another rcfile
# is to be used, passing --rcfile a second time on the command-line will work
# fine.
if os.path.isfile(RC_FILE):
# The file can be removed to test 'normal' pylint behavior.
argv.insert(0, '--rcfile=%s' % RC_FILE)
lint.Run(argv)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
......@@ -2069,7 +2069,7 @@ the current line as well!
subprocess.Popen.return_value = process
presubmit.sigint_handler.wait.return_value = ('', None)
pylint = os.path.join(_ROOT, 'third_party', 'pylint.py')
pylint = os.path.join(_ROOT, 'pylint')
pylintrc = os.path.join(_ROOT, 'pylintrc')
env = {'PYTHONPATH': ''}
......@@ -2079,11 +2079,11 @@ the current line as well!
self.assertEqual([], results)
self.assertEqual(subprocess.Popen.mock_calls, [
mock.call(
['pyyyyython', pylint, '--args-on-stdin'], env=env,
[pylint, '--args-on-stdin'], env=env,
cwd='/foo', stderr=subprocess.STDOUT, stdout=subprocess.PIPE,
stdin=subprocess.PIPE),
mock.call(
['pyyyyython', pylint, '--args-on-stdin'], env=env,
[pylint, '--args-on-stdin'], env=env,
cwd='/foo', stderr=subprocess.STDOUT, stdout=subprocess.PIPE,
stdin=subprocess.PIPE),
])
......
Description:
This directory contains modules that pylint needs.
See the individual README.chromium files for details.
Local Modifications:
None
This diff is collapsed.
URL: https://github.com/PyCQA/astroid
URL: https://pypi.org/project/astroid/#files
Version: 1.4.9
License: GPL
License File: LICENSE.txt
Description:
This directory contains the astroid module, required for pylint.
Local Modifications:
None
# 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/>.
"""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 Instance, BoundMethod, UnboundMethod
from astroid.node_classes import are_exclusive, unpack_infer
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
# accessible from astroid package
from astroid.manager import AstroidManager
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.
WARNING: This can be fairly slow, as it has to convert every AST node back
to Python code; you should consider examining the AST directly instead.
"""
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(Call, inference_tip(infer_named_tuple),
predicate)
"""
def transform(node, infer_function=infer_function):
node._explicit_inference = infer_function
return node
return transform
def register_module_extender(manager, module_name, get_extension_mod):
def transform(node):
extension_module = get_extension_mod()
for name, objs in extension_module._locals.items():
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)
# 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])
# 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/>.
"""astroid packaging information"""
distname = 'astroid'
modname = 'astroid'
numversion = (1, 4, 9)
version = '.'.join([str(num) for num in numversion])
install_requires = ['six', 'lazy_object_proxy', 'wrapt']
license = 'LGPL'
author = 'Python Code Quality Authority'
author_email = 'code-quality@python.org'
mailinglist = "mailto://%s" % author_email
web = 'https://github.com/PyCQA/astroid'
description = "A abstract syntax tree for Python with inference support."
classifiers = ["Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Quality Assurance",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
]
# 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)
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/>.
"""Small AST optimizations."""
import _ast
from astroid import nodes
__all__ = ('ASTPeepholeOptimizer', )
try:
_TYPES = (_ast.Str, _ast.Bytes)
except AttributeError:
_TYPES = (_ast.Str, )
class ASTPeepholeOptimizer(object):
"""Class for applying small optimizations to generate new AST."""
def optimize_binop(self, node):
"""Optimize BinOps with string Const nodes on the lhs.
This fixes an infinite recursion crash, where multiple
strings are joined using the addition operator. With a
sufficient number of such strings, astroid will fail
with a maximum recursion limit exceeded. The
function will return a Const node with all the strings
already joined.
Return ``None`` if no AST node can be obtained
through optimization.
"""
ast_nodes = []
current = node
while isinstance(current, _ast.BinOp):
# lhs must be a BinOp with the addition operand.
if not isinstance(current.left, _ast.BinOp):
return
if (not isinstance(current.left.op, _ast.Add)
or not isinstance(current.op, _ast.Add)):
return
# rhs must a str / bytes.
if not isinstance(current.right, _TYPES):
return
ast_nodes.append(current.right.s)
current = current.left
if (isinstance(current, _ast.BinOp)
and isinstance(current.left, _TYPES)
and isinstance(current.right, _TYPES)):
# Stop early if we are at the last BinOp in
# the operation
ast_nodes.append(current.right.s)
ast_nodes.append(current.left.s)
break
if not ast_nodes:
return
# If we have inconsistent types, bail out.
known = type(ast_nodes[0])
if any(type(element) is not known
for element in ast_nodes[1:]):
return
value = known().join(reversed(ast_nodes))
newnode = nodes.Const(value)
return newnode
This diff is collapsed.
"""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)
"""Astroid hooks for the Python 2 GObject introspection bindings.
Helps with understanding everything imported from 'gi.repository'
"""
import inspect
import itertools
import sys
import re
import warnings
from astroid import MANAGER, AstroidBuildingException, nodes
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 (str(obj).startswith("<flags") or
str(obj).startswith("<enum ") or
str(obj).startswith("<GType ") or
inspect.isdatadescriptor(obj)):
constants[name] = 0
elif isinstance(obj, (int, str)):
constants[name] = obj
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 isinstance(val, 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):
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):
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
def _import_gi_module(modname):
# we only consider gi.repository submodules
if not modname.startswith('gi.repository.'):
raise AstroidBuildingException()
# build astroid representation unless we already tried so
if modname not in _inspected_modules:
modnames = [modname]
optional_modnames = []
# GLib and GObject may have some special case handling
# in pygobject that we need to cope with. However at
# least as of pygobject3-3.13.91 the _glib module doesn't
# exist anymore, so if treat these modules as optional.
if modname == 'gi.repository.GLib':
optional_modnames.append('gi._glib')
elif modname == 'gi.repository.GObject':
optional_modnames.append('gi._gobject')
try:
modcode = ''
for m in itertools.chain(modnames, optional_modnames):
try:
__import__(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:
if m not in optional_modnames:
raise
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
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
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)
from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder
def mechanize_transform():
return 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
''')
register_module_extender(MANAGER, 'mechanize', mechanize_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/>.
"""Hooks for nose library."""
import re
import textwrap
import astroid
import astroid.builder
_BUILDER = astroid.builder.AstroidBuilder(astroid.MANAGER)
def _pep8(name, caps=re.compile('([A-Z])')):
return caps.sub(lambda m: '_' + m.groups()[0].lower(), name)
def _nose_tools_functions():
"""Get an iterator of names and bound methods."""
module = _BUILDER.string_build(textwrap.dedent('''
import unittest
class Test(unittest.TestCase):
pass
a = Test()
'''))
try:
case = next(module['a'].infer())
except astroid.InferenceError:
return
for method in case.methods():
if method.name.startswith('assert') and '_' not in method.name:
pep8_name = _pep8(method.name)
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):
for method_name, method in _nose_tools_functions():
node._locals[method_name] = [method]
def _nose_tools_trivial_transform():
"""Custom transform for the nose.tools module."""
stub = _BUILDER.string_build('''__all__ = []''')
all_entries = ['ok_', 'eq_']
for pep8_name, method in _nose_tools_functions():
all_entries.append(pep8_name)
stub[pep8_name] = method
# Update the __all__ variable, since nose.tools
# does this manually with .append.
all_assign = stub['__all__'].parent
all_object = astroid.List(all_entries)
all_object.parent = all_assign
all_assign.value = all_object
return stub
astroid.register_module_extender(astroid.MANAGER, 'nose.tools.trivial',
_nose_tools_trivial_transform)
astroid.MANAGER.register_transform(astroid.Module, _nose_tools_transform,
lambda n: n.name == 'nose.tools')
# 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."""
from __future__ import absolute_import
from astroid import MANAGER, register_module_extender
from astroid.builder import AstroidBuilder
def pytest_transform():
return AstroidBuilder(MANAGER).string_build('''
try:
import _pytest.mark
import _pytest.recwarn
import _pytest.runner
import _pytest.python
import _pytest.skipping
import _pytest.assertion
except ImportError:
pass
else:
deprecated_call = _pytest.recwarn.deprecated_call
warns = _pytest.recwarn.warns
exit = _pytest.runner.exit
fail = _pytest.runner.fail
skip = _pytest.runner.skip
importorskip = _pytest.runner.importorskip
xfail = _pytest.skipping.xfail
mark = _pytest.mark.MarkGenerator()
raises = _pytest.python.raises
# 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
This diff is collapsed.
"""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)
This diff is collapsed.
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
# 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/>.
"""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 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):
"""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
"""
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.
Copyright (c) 2014-2017, Ionel Cristian Mărieș
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
URL: https://github.com/ionelmc/python-lazy-object-proxy
URL: https://pypi.org/project/lazy-object-proxy/#files
Version: 1.3.1
License: BSD-2
License File: LICENSE
Description:
This directory contains the lazy-object-proxy module, required for pylint.
Local Modifications:
None
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.
#!/usr/bin/env python
import pylint
pylint.run_pylint()
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