Skip to content

Instantly share code, notes, and snippets.

@xZise
Last active August 29, 2015 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xZise/d265728409363ce5e556 to your computer and use it in GitHub Desktop.
Save xZise/d265728409363ce5e556 to your computer and use it in GitHub Desktop.
#!/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