4

I'm trying to execute Google's cpplint.py on a group of my files and collect the results to one log file. However, I have not managed to beat the subprocess module. My current code is here:

import os, subprocess

rootdir = "C:/users/me/Documents/dev/"
srcdir = "project/src/"

with open(rootdir+srcdir+"log.txt", mode='w', encoding='utf-8') as logfile:
    for subdir, dirs, files in os.walk(rootdir+srcdir):
        for file in files:
            if file.endswith(".h") or file.endswith(".cpp"):
                filewithpath=os.path.join(subdir, file)
                cmd=['c:/Python27/python.exe','C:/users/me/Documents/dev/cpplint.py','--filter=-whitespace,-legal,-build/include,-build/header_guard/', filewithpath]               
                output = subprocess.check_output(cmd)
                logfile.write(output.decode('ascii'))

Trying to run the above code throws an error:

  File "C:\Python32\lib\site.py", line 159
    file=sys.stderr)
        ^ SyntaxError: invalid syntax Traceback (most recent call last):   File "C:\Users\me\Documents\dev\project\src\verifier.py", line 19, in <module>
    output = subprocess.check_output(cmd)   File "C:\Python32\lib\subprocess.py", line 511, in check_output
    raise CalledProcessError(retcode, cmd, output=output) subprocess.CalledProcessError: Command '['c:/Python27/python.exe', 'C:/users/me/Documents/dev/cpplint.py', '--filter=-whitespace,-legal,-build/include,-build/header_guard/', 'C:/users/me/Documents/dev/project/src/aboutdialog.cpp']' returned non-zero exit status 1

If I substitute the cmd with something simpler like:

cmd=['C:/WinAVR-20100110/bin/avr-gcc.exe','--version']

Then the script works as expected.

I have also tried to use a single command string instead of a list of strings as cmd, but the result is the same. When debugging the code, I copied the list-of-strings-turned-into-the-command-line-command from the debugger and ran it in the Windows command line, and the command ran as expected.

The Python interpreter running my script is Python 3.2. Any tips are greatly appreciated.

Manjabes
  • 1,884
  • 3
  • 17
  • 34
  • are you using the flag shell=True when using the single command string? – fransua Oct 25 '11 at 11:17
  • The result is the same, whether I use shell=True, shell=False or don't define it at all. – Manjabes Oct 25 '11 at 11:34
  • I'm wondering whether this way to use a Python 2.6 interpreter is good or not. Using `subprocess` is sure a way to select the version, but would there be any other way to access the useful functions of this Python module? – Joël Oct 25 '11 at 11:50
  • The interesting thing is that the site.py module, that the original SyntaxError refers to, deals with importing packages, which is a strange place to encounter an error of this sort. – Manjabes Oct 25 '11 at 11:51
  • Is the use of mixed python versions (cmd=['c:/Python27/python.exe' ...) causing a problem? When I ran it with a dummy cpplint.py (that just prints "hello"), it didn't get a SyntaxError, it just stopped with "returned non-zero exit status 2". – Dave Oct 25 '11 at 11:59

3 Answers3

11

Looks like cpplint.py is simply exiting with a non-zero return code - which it might do, for instance, if it finds errors or "lint" in the source files it is checking.

See the documentation for subprocess.check_output. Note that if the command executed returns a non-zero exit code then a subprocess.CalledProcessError is raised.

You could work around it by watching for CalledProcessError, e.g.

try:
    output = subprocess.check_output(cmd)
except subprocess.CalledProcessError as e:
    # ack!  cpplint.py failed... report an error to the user?

EDIT:

The SyntaxError seems to be the key here, and is probably caused by C:\Python32\lib being in your PYTHONPATH (either explicitly, or, this could happen if it is your current working directory).

The Python interpreter (since about 1.5.2-ish) automatically runs import site when started. So, when this is the case, and your script goes to execute:

c:/Python27/python.exe C:/users/me/Documents/dev/cpplint.py ...

then the Python 2.7 interpreter will find C:\Python32\lib\site.py first, and try to load that, instead of the one (presumably) at C:\Python27\lib\site.py. The issue is that Python 3's site.py contains syntax incompatible with Python 2, so the process launched by subprocess.check_output is failing with a SyntaxError before it even gets a chance to run cpplint, which propagates the CalledProcessError.

Solution? Make sure Python2 gets a bonafide Python2 "PYTHONPATH", and likewise for Python3! In other words, make sure C:\Python32\lib is not in the PYTHONPATH search path when running the Python2 interpreter.

One way to do this in your case is to set an explicit environment when launching the process, e.g.:

python2_env = {"PYTHONPATH": "path/to/python2/stuff:..."}
output = subprocess.check_output(cmd, env=python2_env)
bjlaub
  • 2,707
  • 21
  • 12
  • I tried catching both the CalledProcessError and SyntaxError, but no luck :( – Manjabes Oct 25 '11 at 11:39
  • 1
    @Manjabes: the SyntaxError is curious. Something to try: run `python -c "import site"` (where 'python' is your Python 3.2 executable you're using to run *your* script, not cpplint), and see if you get a SyntaxError. If so your python3 install might be botched somehow? – bjlaub Oct 25 '11 at 12:53
  • 1
    @Manjabes: I've been able to repeat the SyntaxError by executing `site.py` from the Python 3.2.2 libs, *but using a Python 2.x interpreter*. So... I'm wondering if this is the root of your problems? What happens if you run *your* script using `c:/Python27/python.exe` instead of Python3? – bjlaub Oct 25 '11 at 13:01
  • Also: what is your current working directory when you run this? If you're in `C:\Python32\lib` (for some reason), python27 will fail when it tries to "import site" (which the interpreter does automatically) since it finds a `site.py` in the WD before the one at system level. Note that `C:\Python32\lib\site.py` uses syntax not compatible with Python2. – bjlaub Oct 25 '11 at 13:06
  • Yes, that was it. I usually use the 3.2 interpreter as my default Python and have it in my PATH (and its descendants in my PYTHONPATH). After setting a modified environment for the subprocesses, it worked! If you could repost your comment as an answer, I'll accept it. – Manjabes Oct 25 '11 at 13:18
  • On windows you must include the systemroot variable, to make it work. Eg: `python2_env = {"PYTHONPATH": "path/to/python2/stuff:..." , "SystemRoot":os.environ["SystemRoot"]}` See [subprocess documentation](http://docs.python.org/library/subprocess.html) – Tobber Jan 17 '12 at 02:32
1

I would request you to run this first

pipe = subprocess.Popen([cmd, options],stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = pipe.communicate()

You will get to know what exactly is the error behind it, since CalledProcessError is raised only if the exit code was non-zero.

avasal
  • 14,350
  • 4
  • 31
  • 47
  • 1
    stderr is empty, stdout is just like the first error in my original question: File "C:\Python32\lib\site.py", line 159 file=sys.stderr) ^ SyntaxError: invalid syntax – Manjabes Oct 25 '11 at 11:46
  • you sure you want '--filter=-whitespace,-legal,-build/include,-build/header_guard/' as a single parameter, or are they multiple parameters to the program – avasal Oct 25 '11 at 11:53
  • Even if I remove the --filter parameter altogether, the result is the same. – Manjabes Oct 25 '11 at 11:58
  • 1
    '--filter=-whitespace','-legal','-build/include','-build/header_guard/' ... in this way all the parameters will be treated as different rather than single – avasal Oct 25 '11 at 12:00
  • Yes, I understand but the script I'm trying to call expects the -whitespace,-legal etc. as part of the --filter parameter. Anyway, as I said, if I remove the '--filter...' from the list altogether, that doesn't get rid of the error. – Manjabes Oct 25 '11 at 12:05
1

I did it by replacing the def main() with the following (I editet the errorfunction too to get a proper csv-file):

errorlog = sys.stderr
sys.stderr = open("errorlog.csv","w")
sys.stderr.write("File;Line;Message;Category;Confidence\n")
for filename in filenames:
  ProcessFile(filename, _cpplint_state.verbose_level)
_cpplint_state.PrintErrorCounts()
sys.exit(_cpplint_state.error_count > 0)
sys.stdout = errorlog
sys.stderr.close()
Niy
  • 11
  • 1