#!/usr/bin/env python3 import argparse import os import sys import pygit2 from flake8.api import legacy as flake8_legacy MODIFIED_STATUSES = (pygit2.GIT_STATUS_WT_MODIFIED, pygit2.GIT_STATUS_INDEX_NEW, pygit2.GIT_STATUS_INDEX_MODIFIED) MODIFIED_STATUSES_FULL = (pygit2.GIT_STATUS_WT_MODIFIED, pygit2.GIT_STATUS_WT_NEW, pygit2.GIT_STATUS_INDEX_NEW, pygit2.GIT_STATUS_INDEX_MODIFIED) def is_git_repo(abspath): """ True of abspath points to a git repo, False otherwise """ if not os.path.isdir(abspath): return False try: pygit2.Repository(abspath) return True except pygit2.GitError: return False def modifications_exist(abspath, args): """ True if modifications exist for git repo representing abspath False if no modifications exist pygit2.GitError raised if abspath points to illegal repo """ repo = pygit2.Repository(abspath) status = repo.status() if args.full_search: statuses = MODIFIED_STATUSES_FULL else: statuses = MODIFIED_STATUSES for gfile, state in status.items(): if state in statuses: return True return False def print_modification_status(abspath, status, args): """ Prints the modification status for the give abspath """ if status: if args.verbose: print(f'{abspath} -> modified') else: print(abspath) return status if args.verbose: print(f'{abspath} -> unchanged') return status def fetch_git_repos(abspath): """ Returns a list of git repo paths under abspath """ repo_list = list() for root, dirs, files in os.walk(abspath): if '.git' in dirs: try: pygit2.Repository(root) repo_list.append(root) except pygit2.GitError: continue return repo_list def fetch_git_files(args): """ Fetches new and modified .py-files using pygit2 """ git_files_list = list() try: repo = pygit2.Repository(args.repo_path) status = repo.status() except pygit2.GitError: print('{repo_path} is not a valid git repository'.format( repo_path=args.repo_path), file=sys.stderr, flush=True) sys.exit(1) if not status: return git_files_list for gfile, state in status.items(): if state in MODIFIED_STATUSES and gfile.lower().endswith('.py'): if args.verbose: print(gfile) git_files_list.append(gfile) return git_files_list def fetch_all_files(abspath): """ Fetches all .py-files """ all_files_list = list() for root, dirs, files in os.walk(abspath): for f in files: if os.path.splitext(f)[1].lower() == '.py': all_files_list.append(os.path.join(abspath, root, f)) return all_files_list if __name__ == '__main__': parser = argparse.ArgumentParser( description='The following options are available') parser.add_argument( 'repo_path', metavar='REPO', default=os.getcwd(), type=str, help='Git repo path (default: cwd)') parser.add_argument( '-a', '--all', action='store_true', dest='all_files', default=False, help='Check all .py files (not only the ones that are modified)') parser.add_argument( '-f', '--full', action='store_true', dest='full_search', default=False, help='Consider repos that contain untracked files (used with -m)') parser.add_argument( '-m', '--modifications-only', action='store_true', dest='mod_only', default=False, help='Only check for git modifications') parser.add_argument( '-r', '--recursive', action='store_true', dest='recursive', default=False, help='Look for several repos recursively') parser.add_argument( '-v', '--verbose', action='store_true', dest='verbose', default=False, help='Verbosity') args = parser.parse_args() abs_repo_path = os.path.abspath(os.path.expanduser(args.repo_path)) if args.mod_only: if not args.recursive: if not is_git_repo(abs_repo_path): print(f'{abs_repo_path} is not a valid git repository', file=sys.stderr, flush=True) sys.exit(1) print_modification_status(abs_repo_path, modifications_exist(abs_repo_path, args), args) sys.exit(0) # resursive repo_list = fetch_git_repos(abs_repo_path) if not repo_list: print('No git-repos found') sys.exit(0) repo_list.sort() for repo_path in repo_list: print_modification_status(repo_path, modifications_exist(repo_path, args), args) sys.exit(0) if args.all_files: selected_files = fetch_all_files(abs_repo_path) else: selected_files = fetch_git_files(args) if not selected_files: print('No files selected') sys.exit(0) report = flake8_legacy.get_style_guide().check_files(selected_files) if report.total_errors: print('Total errors: {errors}'.format(errors=report.total_errors))