python-parser 0.2.0

A complete Python parser based on nom.
Documentation
#!/usr/bin/env python3
import os
import sys
import ast
import time
import difflib
import tempfile
import unittest
import astpretty
import subprocess
import threading
import multiprocessing

def test_file(path):
    with open(path) as f:
        expected_ast = astpretty.pformat(ast.parse(f.read()), show_offsets=False)
    before = time.time()
    printer_output = subprocess.check_output(['./target/release/prettyprint', path])
    total_time = time.time() - before
    try:
        received_ast = astpretty.pformat(ast.parse(printer_output), show_offsets=False)
    except:
        print('Error while parsing the output from {}:'.format(path))
        raise
    if expected_ast == received_ast:
        print('({:03}ms) {}: ok'.format(int(total_time*1000), path))
        return
    print('========================')
    print(path)
    print('------------------------')
    #for line in difflib.unified_diff(received_ast, expected_ast): # OMG so slow
    #    print(line)
    with tempfile.NamedTemporaryFile('w+', prefix='expected-') as exp_file, \
            tempfile.NamedTemporaryFile('w+', prefix='received-') as received_file:
        exp_file.write(expected_ast)
        exp_file.seek(0)
        received_file.write(received_ast)
        received_file.seek(0)
        try:
            subprocess.check_output(
                    ['diff', '-u', received_file.name, exp_file.name],
                    universal_newlines=True)
        except subprocess.CalledProcessError as e:
            print(e.output)
        else:
            assert False, 'diff did not detect a different, but should have.'
    print('========================')
    exit(1)

def test_dir(path, with_threads=False):
    sem = threading.BoundedSemaphore(value=multiprocessing.cpu_count())
    def thread(path):
        sem.acquire()
        try:
            test_file(path)
        finally:
            sem.release()

    for (dirpath, dirname, filenames) in os.walk(path):
        if any(x.startswith('.') for x in dirpath.split(os.path.sep)):
            # dotfile
            continue
        for filename in filenames:
            if os.path.splitext(filename)[1] == '.py':
                if with_threads:
                    threading.Thread(target=thread, args=(os.path.join(dirpath, filename),)).start()
                else:
                    test_file(os.path.join(dirpath, filename))



def main():
    if '--with-threads' in sys.argv:
        sys.argv.remove('--with-threads')
        with_threads = True
    else:
        with_threads = False
    subprocess.check_output(['cargo', 'build', '--release'])
    for path in sys.argv[1:]:
        if os.path.isfile(path):
            test_file(path)
        elif os.path.isdir(path):
            test_dir(path, with_threads=with_threads)
        else:
            print('Error: no such file or directory: {}'.format(path))
            exit(1)

if __name__ == '__main__':
    main()