Commit 267d659c authored by chrisha@google.com's avatar chrisha@google.com

Add pylint to depot_tools.

This CL adds pylint (and its dependencies) to third_party. It plumbs them into presubmit_canned_checks, and exposes a command-line tool to access pylint.

BUG=
TEST=

Review URL: https://chromiumcodereview.appspot.com/10447014

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@143016 0039d316-1c4b-4281-b951-d872f2087c98
parent e84b7541
......@@ -4,6 +4,7 @@
# Ignore the batch files produced by the Windows bootstrapping.
/git.bat
/gitk.bat
/pylint.bat
/python
/python.bat
/ssh.bat
......@@ -21,7 +22,6 @@
# Ignore locations where third-party tools are placed during bootstrapping.
/python_bin
/git_bin
/site-packages-py*
/svn_bin
# Ignore unittest related files.
......
@echo off
:: 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.
setlocal
set PATH=%~dp0python_bin;%PATH%
call python "%~dp0pylint.py" %*
@echo off
:: Copyright (c) 2011 The Chromium Authors. All rights reserved.
:: 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.
......@@ -122,8 +122,9 @@ echo Installing python ...
if exist "%WIN_TOOLS_ROOT_DIR%\python_bin\." rd /q /s "%WIN_TOOLS_ROOT_DIR%\python_bin"
call svn co -q %WIN_TOOLS_ROOT_URL%/third_party/python_26 "%WIN_TOOLS_ROOT_DIR%\python_bin"
if errorlevel 1 goto :PYTHON_FAIL
:: Create the batch file.
:: Create the batch files.
call copy /y "%~dp0python.new.bat" "%WIN_TOOLS_ROOT_DIR%\python.bat" 1>nul
call copy /y "%~dp0pylint.new.bat" "%WIN_TOOLS_ROOT_DIR%\pylint.bat" 1>nul
set ERRORLEVEL=0
goto :END
......
This diff is collapsed.
......@@ -227,7 +227,7 @@ def CheckChangeHasNoCrAndHasOnlyOneEol(input_api, output_api,
return outputs
def _ReportErrorFileAndLine(filename, line_num, line):
def _ReportErrorFileAndLine(filename, line_num, dummy_line):
"""Default error formatter for _FindNewViolationsOfRule."""
return '%s, line %s' % (filename, line_num)
......@@ -622,7 +622,7 @@ def RunPylint(input_api, output_api, white_list=None, black_list=None,
disabled_warnings=None):
"""Run pylint on python files.
The default white_list enforces looking only a *.py files.
The default white_list enforces looking only at *.py files.
"""
white_list = tuple(white_list or ('.*\.py$',))
black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
......@@ -640,62 +640,45 @@ def RunPylint(input_api, output_api, white_list=None, black_list=None,
if disabled_warnings:
extra_args.extend(['-d', ','.join(disabled_warnings)])
# On certain pylint/python version combination, running pylint throws a lot of
# warning messages.
import warnings
warnings.filterwarnings('ignore', category=DeprecationWarning)
try:
files = _FetchAllFiles(input_api, white_list, black_list)
if not files:
return []
# Now that at least one python file was modified and all the python files
# were listed, try to run pylint.
try:
from pylint import lint
from pylint.utils import UnknownMessage
input_api.logging.debug(
'Using pylint v%s from %s' % (lint.version, lint.__file__))
except ImportError:
if input_api.platform == 'win32':
return [output_api.PresubmitNotifyResult(
'Warning: Can\'t run pylint because it is not installed. Please '
'install manually\n'
'Cannot do static analysis of python files.')]
return [output_api.PresubmitError(
'Please install pylint with "sudo apt-get install python-setuptools; '
'sudo easy_install pylint"\n'
'or visit http://pypi.python.org/pypi/setuptools.\n'
'Cannot do static analysis of python files.')]
def run_lint(files):
try:
lint.Run(files + extra_args)
assert False
except SystemExit, e:
# pylint has the bad habit of calling sys.exit(), trap it here.
return e.code
except UnknownMessage, e:
return 'Please upgrade pylint: %s' % e
result = None
if not input_api.verbose:
result = run_lint(sorted(files))
else:
for filename in sorted(files):
print('Running pylint on %s' % filename)
result = run_lint([filename]) or result
if isinstance(result, basestring):
return [error_type(result)]
elif result:
return [error_type('Fix pylint errors first.')]
files = _FetchAllFiles(input_api, white_list, black_list)
if not files:
return []
finally:
warnings.filterwarnings('default', category=DeprecationWarning)
# Copy the system path to the environment so pylint can find the right
# imports.
env = input_api.environ.copy()
import sys
env['PYTHONPATH'] = input_api.os_path.pathsep.join(sys.path)
def run_lint(files):
# We can't import pylint directly due to licensing issues, so we run
# it in another process. Windows needs help running python files so we
# explicitly specify the interpreter to use.
command = [input_api.python_executable,
input_api.os_path.join(_HERE, 'third_party', 'pylint.py')]
try:
return input_api.subprocess.call(command + files + extra_args)
except OSError:
return 'Pylint failed!'
result = None
if not input_api.verbose:
result = run_lint(sorted(files))
else:
for filename in sorted(files):
print('Running pylint on %s' % filename)
result = run_lint([filename]) or result
if isinstance(result, basestring):
return [error_type(result)]
elif result:
return [error_type('Fix pylint errors first.')]
return []
# TODO(dpranke): Get the host_url from the input_api instead
def CheckRietveldTryJobExecution(input_api, output_api, host_url, platforms,
owner):
def CheckRietveldTryJobExecution(dummy_input_api, dummy_output_api,
dummy_host_url, dummy_platforms,
dummy_owner):
# Temporarily 'fix' the check while the Rietveld API is being upgraded to
# something sensible.
return []
......
#!/bin/sh
# 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.
# pylint -- a shell wrapper for pylint.py.
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 [ -d "$base_dir/python_bin" -a $MINGW = 0 ]; then
PYTHONDONTWRITEBYTECODE=1 exec "$base_dir/python_bin/python.exe" "$base_dir"/pylint.py "$@"
else
PYTHONDONTWRITEBYTECODE=1 exec "$base_dir/pylint.py" "$@"
fi
#!/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, '--rcfile=%s' % _RC_FILE] + sys.argv[1:]
sys.exit(subprocess.call(command))
......@@ -13,10 +13,8 @@ import StringIO
import sys
import time
# TODO(maruel): Include inside depot_tools.
from pylint import lint
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, _ROOT)
from testing_support.super_mox import mox, SuperMoxTestBase
......@@ -2136,13 +2134,17 @@ class CannedChecksUnittest(PresubmitTestsBase):
self.assertEquals(len(results), 0)
def testCannedRunPylint(self):
# lint.Run() always calls sys.exit()...
lint.Run = lambda x: sys.exit(0)
input_api = self.MockInputApi(None, True)
input_api.environ = self.mox.CreateMock(os.environ)
input_api.environ.copy().AndReturn({})
input_api.AffectedSourceFiles(mox.IgnoreArg()).AndReturn(True)
input_api.PresubmitLocalPath().AndReturn('/foo')
input_api.PresubmitLocalPath().AndReturn('/foo')
input_api.os_walk('/foo').AndReturn([('/foo', [], ['file1.py'])])
pylint = os.path.join(_ROOT, 'third_party', 'pylint.py')
pylintrc = os.path.join(_ROOT, 'pylintrc')
input_api.subprocess.call(
['pyyyyython', pylint, 'file1.py', '--rcfile=%s' % pylintrc])
self.mox.ReplayAll()
results = presubmit_canned_checks.RunPylint(
......
URL: http://www.logilab.org/
Description:
This directory contains the logilab-astng and logilab-common modules. See
the individual README.chromium files for details.
Local Modifications:
None
"""generated file, don't modify or your data will be lost"""
try:
__import__('pkg_resources').declare_namespace(__name__)
except ImportError:
pass
This diff is collapsed.
URL: http://pypi.python.org/pypi/setuptools
Version: 0.6c11
License: Dual Licensed PSF/ZPL
URL: http://www.logilab.org/project/logilab-astng
Version: 0.23.1
License: GPL
License File: LICENSE.txt
Description:
This directory contains the egg distribution for setuptools.
This directory contains the logilab-astng module, required for pylint.
Local Modifications:
None
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
# copyright 2003-2010 Sylvain Thenault, all rights reserved.
# contact mailto:thenault@gmail.com
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
"""Python Abstract Syntax Tree New Generation
The aim of this module is to provide a common base representation of
python source code for projects such as pychecker, pyreverse,
pylint... Well, actually the development of this library is essentially
governed by pylint's needs.
It extends class defined in the python's _ast module with some
additional methods and attributes. Instance attributes are added by a
builder object, which can either generate extended ast (let's call
them astng ;) by visiting an existent ast tree or by inspecting living
object. Methods are added by monkey patching ast classes.
Main modules are:
* nodes and scoped_nodes for more information about methods and
attributes added to different node classes
* the manager contains a high level object to get astng trees from
source files and living objects. It maintains a cache of previously
constructed tree for quick access
* builder contains the class responsible to build astng trees
"""
__doctype__ = "restructuredtext en"
import sys
if sys.version_info >= (3, 0):
BUILTINS_MODULE = 'builtins'
else:
BUILTINS_MODULE = '__builtin__'
# WARNING: internal imports order matters !
# make all exception classes accessible from astng package
from logilab.astng.exceptions import *
# make all node classes accessible from astng package
from logilab.astng.nodes import *
# trigger extra monkey-patching
from logilab.astng import inference
# more stuff available
from logilab.astng import raw_building
from logilab.astng.bases import YES, Instance, BoundMethod, UnboundMethod
from logilab.astng.node_classes import are_exclusive, unpack_infer
from logilab.astng.scoped_nodes import builtin_lookup
# make a manager instance (borg) as well as Project and Package classes
# accessible from astng package
from logilab.astng.manager import ASTNGManager, Project
MANAGER = ASTNGManager()
del ASTNGManager
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
# copyright 2003-2010 Sylvain Thenault, all rights reserved.
# contact mailto:thenault@gmail.com
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
"""logilab.astng packaging information"""
distname = 'logilab-astng'
modname = 'astng'
subpackage_of = 'logilab'
numversion = (0, 23, 1)
version = '.'.join([str(num) for num in numversion])
install_requires = ['logilab-common >= 0.53.0']
license = 'LGPL'
author = 'Logilab'
author_email = 'python-projects@lists.logilab.org'
mailinglist = "mailto://%s" % author_email
web = "http://www.logilab.org/project/%s" % distname
ftp = "ftp://ftp.logilab.org/pub/%s" % modname
description = "rebuild a new abstract syntax tree from Python's ast"
from os.path import join
include_dirs = [join('test', 'regrtest_data'),
join('test', 'data'), join('test', 'data2')]
This diff is collapsed.
This diff is collapsed.
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
# copyright 2003-2010 Sylvain Thenault, all rights reserved.
# contact mailto:thenault@gmail.com
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
"""The ASTNGBuilder makes astng from living object and / or from _ast
The builder is not thread safe and can't be used to parse different sources
at the same time.
"""
__docformat__ = "restructuredtext en"
import sys, re
from os.path import splitext, basename, dirname, exists, abspath
from logilab.common.modutils import modpath_from_file
from logilab.astng.exceptions import ASTNGBuildingException, InferenceError
from logilab.astng.raw_building import InspectBuilder
from logilab.astng.rebuilder import TreeRebuilder
from logilab.astng.manager import ASTNGManager
from logilab.astng.bases import YES, Instance
from _ast import PyCF_ONLY_AST
def parse(string):
return compile(string, "<string>", 'exec', PyCF_ONLY_AST)
if sys.version_info >= (3, 0):
from tokenize import detect_encoding
def open_source_file(filename):
byte_stream = open(filename, 'bU')
encoding = detect_encoding(byte_stream.readline)[0]
stream = open(filename, 'U', encoding=encoding)
try:
data = stream.read()
except UnicodeError, uex: # wrong encodingg
# detect_encoding returns utf-8 if no encoding specified
msg = 'Wrong (%s) or no encoding specified' % encoding
raise ASTNGBuildingException(msg)
return stream, encoding, data
else:
import re
_ENCODING_RGX = re.compile("\s*#+.*coding[:=]\s*([-\w.]+)")
def _guess_encoding(string):
"""get encoding from a python file as string or return None if not found
"""
# check for UTF-8 byte-order mark
if string.startswith('\xef\xbb\xbf'):
return 'UTF-8'
for line in string.split('\n', 2)[:2]:
# check for encoding declaration
match = _ENCODING_RGX.match(line)
if match is not None:
return match.group(1)
def open_source_file(filename):
"""get data for parsing a file"""
stream = open(filename, 'U')
data = stream.read()
encoding = _guess_encoding(data)
return stream, encoding, data
# ast NG builder ##############################################################
MANAGER = ASTNGManager()
class ASTNGBuilder(InspectBuilder):
"""provide astng building methods"""
rebuilder = TreeRebuilder()
def __init__(self, manager=None):
self._manager = manager or MANAGER
def module_build(self, module, modname=None):
"""build an astng from a living module instance
"""
node = None
path = getattr(module, '__file__', None)
if path is not None:
path_, ext = splitext(module.__file__)
if ext in ('.py', '.pyc', '.pyo') and exists(path_ + '.py'):
node = self.file_build(path_ + '.py', modname)
if node is None:
# this is a built-in module
# get a partial representation by introspection
node = self.inspect_build(module, modname=modname, path=path)
return node
def file_build(self, path, modname=None):
"""build astng from a source code file (i.e. from an ast)
path is expected to be a python source file
"""
try:
stream, encoding, data = open_source_file(path)
except IOError, exc:
msg = 'Unable to load file %r (%s)' % (path, exc)
raise ASTNGBuildingException(msg)
except SyntaxError, exc: # py3k encoding specification error
raise ASTNGBuildingException(exc)
except LookupError, exc: # unknown encoding
raise ASTNGBuildingException(exc)
# get module name if necessary
if modname is None:
try:
modname = '.'.join(modpath_from_file(path))
except ImportError:
modname = splitext(basename(path))[0]
# build astng representation
node = self.string_build(data, modname, path)
node.file_encoding = encoding
return node
def string_build(self, data, modname='', path=None):
"""build astng from source code string and return rebuilded astng"""
module = self._data_build(data, modname, path)
self._manager.astng_cache[module.name] = module
# post tree building steps after we stored the module in the cache:
for from_node in module._from_nodes:
self.add_from_names_to_locals(from_node)
# handle delayed assattr nodes
for delayed in module._delayed_assattr:
self.delayed_assattr(delayed)
if modname:
for transformer in self._manager.transformers:
transformer(module)
return module
def _data_build(self, data, modname, path):
"""build tree node from data and add some informations"""
# this method could be wrapped with a pickle/cache function
node = parse(data + '\n')
if path is not None:
node_file = abspath(path)
else:
node_file = '<?>'
if modname.endswith('.__init__'):
modname = modname[:-9]
package = True
else:
package = path and path.find('__init__.py') > -1 or False
self.rebuilder.init()
module = self.rebuilder.visit_module(node, modname, package)
module.file = module.path = node_file
module._from_nodes = self.rebuilder._from_nodes
module._delayed_assattr = self.rebuilder._delayed_assattr
return module
def add_from_names_to_locals(self, node):
"""store imported names to the locals;
resort the locals if coming from a delayed node
"""
_key_func = lambda node: node.fromlineno
def sort_locals(my_list):
my_list.sort(key=_key_func)
for (name, asname) in node.names:
if name == '*':
try:
imported = node.root().import_module(node.modname)
except ASTNGBuildingException:
continue
for name in imported.wildcard_import_names():
node.parent.set_local(name, node)
sort_locals(node.parent.scope().locals[name])
else:
node.parent.set_local(asname or name, node)
sort_locals(node.parent.scope().locals[asname or name])
def delayed_assattr(self, node):
"""visit a AssAttr node -> add name to locals, handle members
definition
"""
try:
frame = node.frame()
for infered in node.expr.infer():
if infered is YES:
continue
try:
if infered.__class__ is Instance:
infered = infered._proxied
iattrs = infered.instance_attrs
elif isinstance(infered, Instance):
# Const, Tuple, ... we may be wrong, may be not, but
# anyway we don't want to pollute builtin's namespace
continue
elif infered.is_function:
iattrs = infered.instance_attrs
else:
iattrs = infered.locals
except AttributeError:
# XXX log error
#import traceback
#traceback.print_exc()
continue
values = iattrs.setdefault(node.attrname, [])
if node in values:
continue
# get assign in __init__ first XXX useful ?
if frame.name == '__init__' and values and not \
values[0].frame().name == '__init__':
values.insert(0, node)
else:
values.append(node)
except InferenceError:
pass
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
# copyright 2003-2010 Sylvain Thenault, all rights reserved.
# contact mailto:thenault@gmail.com
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
"""this module contains exceptions used in the astng library
"""
__doctype__ = "restructuredtext en"
class ASTNGError(Exception):
"""base exception class for all astng related exceptions"""
class ASTNGBuildingException(ASTNGError):
"""exception class when we are unable to build an astng representation"""
class ResolveError(ASTNGError):
"""base class of astng resolution/inference error"""
class NotFoundError(ResolveError):
"""raised when we are unable to resolve a name"""
class InferenceError(ResolveError):
"""raised when we are unable to infer a node"""
class UnresolvableName(InferenceError):
"""raised when we are unable to resolve a name"""
class NoDefault(ASTNGError):
"""raised by function's `default_value` method when an argument has
no default value
"""
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
# copyright 2003-2010 Sylvain Thenault, all rights reserved.
# contact mailto:thenault@gmail.com
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
"""This module contains some mixins for the different nodes.
"""
from logilab.astng.exceptions import (ASTNGBuildingException, InferenceError,
NotFoundError)
class BlockRangeMixIn(object):
"""override block range """
def set_line_info(self, lastchild):
self.fromlineno = self.lineno
self.tolineno = lastchild.tolineno
self.blockstart_tolineno = self._blockstart_toline()
def _elsed_block_range(self, lineno, orelse, last=None):
"""handle block line numbers range for try/finally, for, if and while
statements
"""
if lineno == self.fromlineno:
return lineno, lineno
if orelse:
if lineno >= orelse[0].fromlineno:
return lineno, orelse[-1].tolineno
return lineno, orelse[0].fromlineno - 1
return lineno, last or self.tolineno
class FilterStmtsMixin(object):
"""Mixin for statement filtering and assignment type"""
def _get_filtered_stmts(self, _, node, _stmts, mystmt):
"""method used in _filter_stmts to get statemtents and trigger break"""
if self.statement() is mystmt:
# original node's statement is the assignment, only keep
# current node (gen exp, list comp)
return [node], True
return _stmts, False
def ass_type(self):
return self
class AssignTypeMixin(object):
def ass_type(self):
return self
def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt):
"""method used in filter_stmts"""
if self is mystmt:
return _stmts, True
if self.statement() is mystmt:
# original node's statement is the assignment, only keep
# current node (gen exp, list comp)
return [node], True
return _stmts, False
class ParentAssignTypeMixin(AssignTypeMixin):
def ass_type(self):
return self.parent.ass_type()
class FromImportMixIn(FilterStmtsMixin):
"""MixIn for From and Import Nodes"""
def _infer_name(self, frame, name):
return name
def do_import_module(self, modname):
"""return the ast for a module whose name is <modname> imported by <self>
"""
# handle special case where we are on a package node importing a module
# using the same name as the package, which may end in an infinite loop
# on relative imports
# XXX: no more needed ?
mymodule = self.root()
level = getattr(self, 'level', None) # Import as no level
# XXX we should investigate deeper if we really want to check
# importing itself: modname and mymodule.name be relative or absolute
if mymodule.relative_to_absolute_name(modname, level) == mymodule.name:
# FIXME: we used to raise InferenceError here, but why ?
return mymodule
try:
return mymodule.import_module(modname, level=level)
except ASTNGBuildingException:
raise InferenceError(modname)
except SyntaxError, ex:
raise InferenceError(str(ex))
def real_name(self, asname):
"""get name from 'as' name"""
for name, _asname in self.names:
if name == '*':
return asname
if not _asname:
name = name.split('.', 1)[0]
_asname = name
if asname == _asname:
return name
raise NotFoundError(asname)
This diff is collapsed.
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
# copyright 2003-2010 Sylvain Thenault, all rights reserved.
# contact mailto:thenault@gmail.com
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
"""
on all nodes :
.is_statement, returning true if the node should be considered as a
statement node
.root(), returning the root node of the tree (i.e. a Module)
.previous_sibling(), returning previous sibling statement node
.next_sibling(), returning next sibling statement node
.statement(), returning the first parent node marked as statement node
.frame(), returning the first node defining a new local scope (i.e.
Module, Function or Class)
.set_local(name, node), define an identifier <name> on the first parent frame,
with the node defining it. This is used by the astng builder and should not
be used from out there.
on From and Import :
.real_name(name),
"""
__docformat__ = "restructuredtext en"
from logilab.astng.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, \
const_factory
from logilab.astng.scoped_nodes import Module, GenExpr, Lambda, DictComp, \
ListComp, SetComp, Function, Class
ALL_NODE_CLASSES = (
Arguments, AssAttr, Assert, Assign, AssName, AugAssign,
Backquote, BinOp, BoolOp, Break,
CallFunc, Class, Compare, Comprehension, Const, Continue,
Decorators, DelAttr, DelName, Delete,
Dict, DictComp, Discard,
Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice,
For, From, Function,
Getattr, GenExpr, Global,
If, IfExp, Import, Index,
Keyword,
Lambda, List, ListComp,
Name, Nonlocal,
Module,
Pass, Print,
Raise, Return,
Set, SetComp, Slice, Starred, Subscript,
TryExcept, TryFinally, Tuple,
UnaryOp,
While, With,
Yield,
)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
# copyright 2003-2010 Sylvain Thenault, all rights reserved.
# contact mailto:thenault@gmail.com
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
"""this module contains some utilities to navigate in the tree or to
extract information from it
"""
__docformat__ = "restructuredtext en"
from logilab.astng.exceptions import ASTNGBuildingException
class ASTWalker:
"""a walker visiting a tree in preorder, calling on the handler:
* visit_<class name> on entering a node, where class name is the class of
the node in lower case
* leave_<class name> on leaving a node, where class name is the class of
the node in lower case
"""
def __init__(self, handler):
self.handler = handler
self._cache = {}
def walk(self, node, _done=None):
"""walk on the tree from <node>, getting callbacks from handler"""
if _done is None:
_done = set()
if node in _done:
raise AssertionError((id(node), node, node.parent))
_done.add(node)
self.visit(node)
for child_node in node.get_children():
self.handler.set_context(node, child_node)
assert child_node is not node
self.walk(child_node, _done)
self.leave(node)
assert node.parent is not node
def get_callbacks(self, node):
"""get callbacks from handler for the visited node"""
klass = node.__class__
methods = self._cache.get(klass)
if methods is None:
handler = self.handler
kid = klass.__name__.lower()
e_method = getattr(handler, 'visit_%s' % kid,
getattr(handler, 'visit_default', None))
l_method = getattr(handler, 'leave_%s' % kid,
getattr(handler, 'leave_default', None))
self._cache[klass] = (e_method, l_method)
else:
e_method, l_method = methods
return e_method, l_method
def visit(self, node):
"""walk on the tree from <node>, getting callbacks from handler"""
method = self.get_callbacks(node)[0]
if method is not None:
method(node)
def leave(self, node):
"""walk on the tree from <node>, getting callbacks from handler"""
method = self.get_callbacks(node)[1]
if method is not None:
method(node)
class LocalsVisitor(ASTWalker):
"""visit a project by traversing the locals dictionary"""
def __init__(self):
ASTWalker.__init__(self, self)
self._visited = {}
def visit(self, node):
"""launch the visit starting from the given node"""
if node in self._visited:
return
self._visited[node] = 1 # FIXME: use set ?
methods = self.get_callbacks(node)
if methods[0] is not None:
methods[0](node)
if 'locals' in node.__dict__: # skip Instance and other proxy
for name, local_node in node.items():
self.visit(local_node)
if methods[1] is not None:
return methods[1](node)
def _check_children(node):
"""a helper function to check children - parent relations"""
for child in node.get_children():
ok = False
if child is None:
print "Hm, child of %s is None" % node
continue
if not hasattr(child, 'parent'):
print " ERROR: %s has child %s %x with no parent" % (node, child, id(child))
elif not child.parent:
print " ERROR: %s has child %s %x with parent %r" % (node, child, id(child), child.parent)
elif child.parent is not node:
print " ERROR: %s %x has child %s %x with wrong parent %s" % (node,
id(node), child, id(child), child.parent)
else:
ok = True
if not ok:
print "lines;", node.lineno, child.lineno
print "of module", node.root(), node.root().name
raise ASTNGBuildingException
_check_children(child)
from _ast import PyCF_ONLY_AST
def parse(string):
return compile(string, "<string>", 'exec', PyCF_ONLY_AST)
class TreeTester(object):
'''A helper class to see _ast tree and compare with astng tree
indent: string for tree indent representation
lineno: bool to tell if we should print the line numbers
>>> tester = TreeTester('print')
>>> print tester.native_tree_repr()
<Module>
. body = [
. <Print>
. . nl = True
. ]
>>> print tester.astng_tree_repr()
Module()
body = [
Print()
dest =
values = [
]
]
'''
indent = '. '
lineno = False
def __init__(self, sourcecode):
self._string = ''
self.sourcecode = sourcecode
self._ast_node = None
self.build_ast()
def build_ast(self):
"""build the _ast tree from the source code"""
self._ast_node = parse(self.sourcecode)
def native_tree_repr(self, node=None, indent=''):
"""get a nice representation of the _ast tree"""
self._string = ''
if node is None:
node = self._ast_node
self._native_repr_tree(node, indent)
return self._string
def _native_repr_tree(self, node, indent, _done=None):
"""recursive method for the native tree representation"""
from _ast import Load as _Load, Store as _Store, Del as _Del
from _ast import AST as Node
if _done is None:
_done = set()
if node in _done:
self._string += '\nloop in tree: %r (%s)' % (node,
getattr(node, 'lineno', None))
return
_done.add(node)
self._string += '\n' + indent + '<%s>' % node.__class__.__name__
indent += self.indent
if not hasattr(node, '__dict__'):
self._string += '\n' + self.indent + " ** node has no __dict__ " + str(node)
return
node_dict = node.__dict__
if hasattr(node, '_attributes'):
for a in node._attributes:
attr = node_dict[a]
if attr is None:
continue
if a in ("lineno", "col_offset") and not self.lineno:
continue
self._string +='\n' + indent + a + " = " + repr(attr)
for field in node._fields or ():
attr = node_dict[field]
if attr is None:
continue
if isinstance(attr, list):
if not attr:
continue
self._string += '\n' + indent + field + ' = ['
for elt in attr:
self._native_repr_tree(elt, indent, _done)
self._string += '\n' + indent + ']'
continue
if isinstance(attr, (_Load, _Store, _Del)):
continue
if isinstance(attr, Node):
self._string += '\n' + indent + field + " = "
self._native_repr_tree(attr, indent, _done)
else:
self._string += '\n' + indent + field + " = " + repr(attr)
def build_astng_tree(self):
"""build astng tree from the _ast tree
"""
from logilab.astng.builder import ASTNGBuilder
tree = ASTNGBuilder().string_build(self.sourcecode)
return tree
def astng_tree_repr(self, ids=False):
"""build the astng tree and return a nice tree representation"""
mod = self.build_astng_tree()
return mod.repr_tree(ids)
__all__ = ('LocalsVisitor', 'ASTWalker',)
This diff is collapsed.
URL: http://www.logilab.org/project/logilab-common
Version: 0.57.1
License: GPL
License File: LICENSE.txt
Description:
This directory contains the logilab-common module, required for logilab-astng
and 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.
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.
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