Last active
August 29, 2015 14:05
-
-
Save xZise/d265728409363ce5e556 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import os | |
import re | |
import subprocess | |
import sys | |
import tempfile | |
import shutil | |
import traceback | |
if sys.version_info[0] >= 3: | |
basestring = (str, ) | |
def system(*args, **kwargs): | |
kwargs.setdefault('stdout', subprocess.PIPE) | |
proc = subprocess.Popen(args, **kwargs) | |
out, err = proc.communicate() | |
if out and sys.version_info[0] >= 3: | |
out = out.decode("utf8") | |
return out | |
def save_file(tempdir, name, rev): | |
filename = os.path.join(tempdir, name) | |
filepath = os.path.dirname(filename) | |
if not os.path.exists(filepath): | |
os.makedirs(filepath) | |
with open(filename, 'w') as f: | |
system('git', 'show', rev + ':' + name, stdout=f) | |
return filename | |
def ignore_prev(prog, filename): | |
output = system(prog, filename) | |
if output.strip(): | |
return output | |
else: | |
return False | |
def flake8(original, filename, previous): | |
return ignore_prev("flake8", filename) | |
all_warn = ("WARNING: __all__ is defined as a list, this means pep257 cannot " | |
"reliably detect contents of the __all__ variable, because it can " | |
"be mutated. Change __all__ to be an (immutable) tuple, to remove " | |
"this warning. Note, pep257 uses __all__ to detect which " | |
"definitions are public, to warn if public definitions are " | |
"missing docstrings. If __all__ is a (mutable) list, pep257 " | |
"cannot reliably assume its contents. pep257 will proceed " | |
"assuming __all__ is not mutated.") | |
#pywikibot/site.py:4629 in public method `newimages`: | |
# D102: Docstring missing | |
def read_pep257(output): | |
first = re.compile(r"^(.*):(\d+) (in (?:public|private) (?:nested )?(?:method|class|function) `.*`|at module level):$") | |
second = re.compile(r"^ (D\d{3}): (.*)") | |
output = output.splitlines() | |
# special case | |
if (len(output) == 1 and | |
output[0] == "Could not evaluate contents of __all__. That means " | |
"pep257 cannot decide which definitions are public. " | |
"Variable __all__ should be present at most once in each " | |
"file, in form `__all__ = ('a_public_function', " | |
"'APublicClass', ...)`. More info on __all__: " | |
"http://stackoverflow.com/q/44834/. "): | |
return None | |
messages = [] | |
i = 0 | |
while i < len(output): | |
if output[i].endswith(all_warn): | |
messages += [('__all__ warning')] | |
else: | |
first_match = first.match(output[i]) | |
second_match = second.match(output[i+1]) if len(output) > i + 1 else None | |
if first_match and second_match: | |
# file, line, err-code, message, location description | |
messages += [(first_match.group(1), first_match.group(2), | |
second_match.group(1), second_match.group(2), | |
first_match.group(3))] | |
else: | |
print("Error line {}".format(i)) | |
print(output[i]) | |
print(output[i + 1]) | |
raise Exception() | |
i += 1 | |
i += 1 | |
return messages | |
def pep257(original, new_messages, old_messages): | |
if isinstance(new_messages, basestring): | |
new_messages = read_pep257(system("pep257", new_messages, stderr=subprocess.STDOUT)) | |
if isinstance(old_messages, basestring): | |
old_messages = read_pep257(system("pep257", old_messages, stderr=subprocess.STDOUT)) | |
if new_messages is None or old_messages is None: | |
print("pep257 was unable to compare the changes in {}. Ignoring.".format(original)) | |
return False | |
differences = [] | |
pos_in_old = 0 | |
for new_message in new_messages: | |
for i in range(pos_in_old, len(old_messages)): | |
old_message = old_messages[i] | |
if old_message[2:] == new_message[2:]: | |
pos_in_old = i | |
break | |
else: | |
differences += [new_message] | |
if differences: | |
result = "" | |
for difference in differences: | |
if len(difference) == 1: | |
result += "{}\n".format(*difference) | |
else: | |
result += "{0}:{1} {4}:\n {2}: {3}\n".format(*difference) | |
return result | |
else: | |
return False | |
def main(verbose): | |
modified = re.compile('^([AM ])([AM ])\s+(.*\.py)', re.MULTILINE) | |
files = system('git', 'status', '--porcelain') | |
failed = False | |
### TEST can be removed later | |
for l in files.splitlines(): | |
if l and l[:2] not in {"??", " M", "M ", "MM"}: | |
print(l) | |
failed = True | |
if failed: | |
print("Found lines which don't match") | |
### | |
tempdir = tempfile.mkdtemp() | |
tempdir_prev = tempfile.mkdtemp() | |
try: | |
shutil.copyfile("tox.ini", os.path.join(tempdir, "tox.ini")) | |
shutil.copyfile("tox.ini", os.path.join(tempdir_prev, "tox.ini")) | |
file_parameters = [] | |
for match in modified.finditer(files): | |
name = match.group(3) | |
current = None | |
if verbose: | |
if match.group(2) == "M": | |
current = name | |
elif match.group(1) != " " and match.group(2) == " ": | |
current = name | |
elif match.group(2) != " ": | |
current = save_file(tempdir, name, '') | |
if current: | |
file_parameters += [(name, current, save_file(tempdir_prev, name, "HEAD"))] | |
tests = [("flake8", flake8), ("pep257", pep257)] | |
if verbose: | |
print("Test {} file(s)".format(len(file_parameters))) | |
for f in file_parameters: | |
for name, test in tests: | |
try: | |
output = test(*f) | |
except Exception as e: | |
failed = True | |
print("Unable to execute '{}' test for {}.".format(name, f[0])) | |
traceback.print_exc() | |
else: | |
if output: | |
failed = True | |
if isinstance(output, basestring): | |
print(output) | |
print("Failed '{}' for {}".format(name, f[0])) | |
elif verbose: | |
print("Finished '{}' for {}".format(name, f[0])) | |
finally: | |
shutil.rmtree(tempdir) | |
shutil.rmtree(tempdir_prev) | |
if failed: | |
sys.exit(1) | |
if __name__ == '__main__': | |
main(len(sys.argv) == 2 and sys.argv[1] == 'v') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment