Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / pylint / epylint.py @ 745

History | View | Annotate | Download (6.33 KB)

1
# -*- coding: utf-8; mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4
2
# -*- vim:fenc=utf-8:ft=python:et:sw=4:ts=4:sts=4
3
# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
4
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
5
#
6
# This program is free software; you can redistribute it and/or modify it under
7
# the terms of the GNU General Public License as published by the Free Software
8
# Foundation; either version 2 of the License, or (at your option) any later
9
# version.
10
#
11
# This program is distributed in the hope that it will be useful, but WITHOUT
12
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
14
#
15
# You should have received a copy of the GNU General Public License along with
16
# this program; if not, write to the Free Software Foundation, Inc.,
17
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
"""Emacs and Flymake compatible Pylint.
19

20
This script is for integration with emacs and is compatible with flymake mode.
21

22
epylint walks out of python packages before invoking pylint. This avoids
23
reporting import errors that occur when a module within a package uses the
24
absolute import path to get another module within this package.
25

26
For example:
27
    - Suppose a package is structured as
28

29
        a/__init__.py
30
        a/b/x.py
31
        a/c/y.py
32

33
   - Then if y.py imports x as "from a.b import x" the following produces pylint
34
     errors
35

36
       cd a/c; pylint y.py
37

38
   - The following obviously doesn't
39

40
       pylint a/c/y.py
41

42
   - As this script will be invoked by emacs within the directory of the file
43
     we are checking we need to go out of it to avoid these false positives.
44

45

46
You may also use py_run to run pylint with desired options and get back (or not)
47
its output.
48
"""
49
from __future__ import print_function
50

    
51
import os
52
import os.path as osp
53
import sys
54
from subprocess import Popen, PIPE
55

    
56
def _get_env():
57
    '''Extracts the environment PYTHONPATH and appends the current sys.path to
58
    those.'''
59
    env = dict(os.environ)
60
    env['PYTHONPATH'] = os.pathsep.join(sys.path)
61
    return env
62

    
63
def lint(filename, options=None):
64
    """Pylint the given file.
65

66
    When run from emacs we will be in the directory of a file, and passed its
67
    filename.  If this file is part of a package and is trying to import other
68
    modules from within its own package or another package rooted in a directory
69
    below it, pylint will classify it as a failed import.
70

71
    To get around this, we traverse down the directory tree to find the root of
72
    the package this module is in.  We then invoke pylint from this directory.
73

74
    Finally, we must correct the filenames in the output generated by pylint so
75
    Emacs doesn't become confused (it will expect just the original filename,
76
    while pylint may extend it with extra directories if we've traversed down
77
    the tree)
78
    """
79
    # traverse downwards until we are out of a python package
80
    full_path = osp.abspath(filename)
81
    parent_path = osp.dirname(full_path)
82
    child_path = osp.basename(full_path)
83

    
84
    while parent_path != "/" and osp.exists(osp.join(parent_path, '__init__.py')):
85
        child_path = osp.join(osp.basename(parent_path), child_path)
86
        parent_path = osp.dirname(parent_path)
87

    
88
    # Start pylint
89
    # Ensure we use the python and pylint associated with the running epylint
90
    from pylint import lint as lint_mod
91
    lint_path = lint_mod.__file__
92
    options = options or ['--disable=C,R,I']
93
    cmd = [sys.executable, lint_path] + options + [
94
        '--msg-template', '{path}:{line}: {category} ({msg_id}, {symbol}, {obj}) {msg}',
95
        '-r', 'n', child_path]
96
    process = Popen(cmd, stdout=PIPE, cwd=parent_path, env=_get_env(),
97
                    universal_newlines=True)
98

    
99
    for line in process.stdout:
100
        # remove pylintrc warning
101
        if line.startswith("No config file found"):
102
            continue
103

    
104
        # modify the file name thats output to reverse the path traversal we made
105
        parts = line.split(":")
106
        if parts and parts[0] == child_path:
107
            line = ":".join([filename] + parts[1:])
108
        print(line, end=' ')
109

    
110
    process.wait()
111
    return process.returncode
112

    
113

    
114
def py_run(command_options='', return_std=False, stdout=None, stderr=None,
115
           script='epylint'):
116
    """Run pylint from python
117

118
    ``command_options`` is a string containing ``pylint`` command line options;
119
    ``return_std`` (boolean) indicates return of created standard output
120
    and error (see below);
121
    ``stdout`` and ``stderr`` are 'file-like' objects in which standard output
122
    could be written.
123

124
    Calling agent is responsible for stdout/err management (creation, close).
125
    Default standard output and error are those from sys,
126
    or standalone ones (``subprocess.PIPE``) are used
127
    if they are not set and ``return_std``.
128

129
    If ``return_std`` is set to ``True``, this function returns a 2-uple
130
    containing standard output and error related to created process,
131
    as follows: ``(stdout, stderr)``.
132

133
    A trivial usage could be as follows:
134
        >>> py_run( '--version')
135
        No config file found, using default configuration
136
        pylint 0.18.1,
137
            ...
138

139
    To silently run Pylint on a module, and get its standard output and error:
140
        >>> (pylint_stdout, pylint_stderr) = py_run( 'module_name.py', True)
141
    """
142
    # Create command line to call pylint
143
    if os.name == 'nt':
144
        script += '.bat'
145
    command_line = script + ' ' + command_options
146
    # Providing standard output and/or error if not set
147
    if stdout is None:
148
        if return_std:
149
            stdout = PIPE
150
        else:
151
            stdout = sys.stdout
152
    if stderr is None:
153
        if return_std:
154
            stderr = PIPE
155
        else:
156
            stderr = sys.stderr
157
    # Call pylint in a subprocess
158
    process = Popen(command_line, shell=True, stdout=stdout, stderr=stderr,
159
                    env=_get_env(), universal_newlines=True)
160
    process.wait()
161
    # Return standard output and error
162
    if return_std:
163
        return (process.stdout, process.stderr)
164

    
165

    
166
def Run():
167
    if len(sys.argv) == 1:
168
        print("Usage: %s <filename> [options]" % sys.argv[0])
169
        sys.exit(1)
170
    elif not osp.exists(sys.argv[1]):
171
        print("%s does not exist" % sys.argv[1])
172
        sys.exit(1)
173
    else:
174
        sys.exit(lint(sys.argv[1], sys.argv[2:]))
175

    
176

    
177
if __name__ == '__main__':
178
    Run()