summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimeon Simeonov2021-01-14 22:38:03 +0100
committerSimeon Simeonov2021-01-14 22:38:03 +0100
commitf2f3ee4a4d12741177ae691f56f502e2de61d63d (patch)
treee34119d17db6ea2e96c851443d32387964d6aaa6
parent5aa1a9a1051e4eba031b877a0aeea8a4afb93e42 (diff)
Replace pygit2 with GitPython and use subprocess for all checks
-rwxr-xr-xpygitchecker.py183
1 files changed, 105 insertions, 78 deletions
diff --git a/pygitchecker.py b/pygitchecker.py
index 8dea428..4f4a872 100755
--- a/pygitchecker.py
+++ b/pygitchecker.py
@@ -1,21 +1,21 @@
1#!/usr/bin/env python3 1#!/usr/bin/env python
2 2# -*- coding: utf-8 -*-
3"""Simple, on demand git checker"""
3import argparse 4import argparse
4import os 5import os
6import subprocess
5import sys 7import sys
6 8
7import pygit2 9import git
10
8 11
9from flake8.api import legacy as flake8_legacy 12class ValidationError(Exception):
13 """ValidationError"""
10 14
11 15
12MODIFIED_STATUSES = (pygit2.GIT_STATUS_WT_MODIFIED, 16def eprint(*arg, **kwargs):
13 pygit2.GIT_STATUS_INDEX_NEW, 17 """stdderr print wrapper"""
14 pygit2.GIT_STATUS_INDEX_MODIFIED) 18 print(*arg, file=sys.stderr, flush=True, **kwargs)
15MODIFIED_STATUSES_FULL = (pygit2.GIT_STATUS_WT_MODIFIED,
16 pygit2.GIT_STATUS_WT_NEW,
17 pygit2.GIT_STATUS_INDEX_NEW,
18 pygit2.GIT_STATUS_INDEX_MODIFIED)
19 19
20 20
21def is_git_repo(abspath): 21def is_git_repo(abspath):
@@ -25,35 +25,25 @@ def is_git_repo(abspath):
25 if not os.path.isdir(abspath): 25 if not os.path.isdir(abspath):
26 return False 26 return False
27 try: 27 try:
28 pygit2.Repository(abspath) 28 git.Repo(abspath)
29 return True 29 return True
30 except pygit2.GitError: 30 except git.InvalidGitRepositoryError:
31 return False 31 return False
32 32
33 33
34def modifications_exist(abspath, args): 34def modifications_exist(abspath):
35 """ 35 """
36 True if modifications exist for git repo representing abspath 36 True if modifications exist for git repo representing abspath
37 False if no modifications exist 37 False if no modifications exist
38 38
39 pygit2.GitError raised if abspath points to illegal repo 39 git.InvalidGitRepositoryError raised if abspath points to illegal repo
40 """ 40 """
41 repo = pygit2.Repository(abspath) 41 repo = git.Repo(abspath)
42 status = repo.status() 42 return bool(repo.is_dirty() or repo.index.diff(None))
43 if args.full_search:
44 statuses = MODIFIED_STATUSES_FULL
45 else:
46 statuses = MODIFIED_STATUSES
47 for gfile, state in status.items():
48 if state in statuses:
49 return True
50 return False
51 43
52 44
53def print_modification_status(abspath, status, args): 45def print_modification_status(abspath, status, args):
54 """ 46 """Prints the modification status for the given abspath"""
55 Prints the modification status for the give abspath
56 """
57 if status: 47 if status:
58 if args.verbose: 48 if args.verbose:
59 print(f'{abspath} -> modified') 49 print(f'{abspath} -> modified')
@@ -65,109 +55,141 @@ def print_modification_status(abspath, status, args):
65 return status 55 return status
66 56
67 57
68def fetch_git_repos(abspath): 58def fetch_git_repos(abspath: str):
69 """ 59 """Returns a list of git repo paths under abspath"""
70 Returns a list of git repo paths under abspath 60 repo_list = []
71 """ 61 for root, dirs, _ in os.walk(abspath):
72 repo_list = list()
73 for root, dirs, files in os.walk(abspath):
74 if '/.git' in root: 62 if '/.git' in root:
75 continue 63 continue
76 if '.git' in dirs: 64 if '.git' in dirs:
77 try: 65 try:
78 pygit2.Repository(root) 66 git.Repo(root)
79 repo_list.append(root) 67 repo_list.append(root)
80 except pygit2.GitError: 68 except git.InvalidGitRepositoryError:
81 continue 69 continue
82 return repo_list 70 return repo_list
83 71
84 72
85def fetch_git_files(args): 73def fetch_git_files(args):
86 """ 74 """Fetches new and modified .py-files using git"""
87 Fetches new and modified .py-files using pygit2
88 """
89 git_files_list = list()
90 try: 75 try:
91 repo = pygit2.Repository(args.repo_path) 76 repo = git.Repo(args.repo_path)
92 status = repo.status() 77 return [mfile.a_path for mfile in repo.index.diff(None)
93 except pygit2.GitError: 78 if mfile.a_path.lower().endswith('.py')]
94 print('{repo_path} is not a valid git repository'.format( 79 except git.InvalidGitRepositoryError:
95 repo_path=args.repo_path), 80 eprint(f'{args.repo_path} is not a valid git repository')
96 file=sys.stderr,
97 flush=True)
98 sys.exit(1) 81 sys.exit(1)
99 if not status:
100 return git_files_list
101 for gfile, state in status.items():
102 if state in MODIFIED_STATUSES and gfile.lower().endswith('.py'):
103 if args.verbose:
104 print(gfile)
105 git_files_list.append(gfile)
106 return git_files_list
107 82
108 83
109def fetch_all_files(abspath): 84def fetch_all_files(abspath):
110 """ 85 """Fetches all .py-files"""
111 Fetches all .py-files 86 all_files_list = []
112 """ 87 for root, _, files in os.walk(abspath):
113 all_files_list = list()
114 for root, dirs, files in os.walk(abspath):
115 for f in files: 88 for f in files:
116 if os.path.splitext(f)[1].lower() == '.py': 89 if os.path.splitext(f)[1].lower() == '.py':
117 all_files_list.append(os.path.join(abspath, root, f)) 90 all_files_list.append(os.path.join(abspath, root, f))
118 return all_files_list 91 return all_files_list
119 92
120 93
121if __name__ == '__main__': 94def validate_flake8(pfile):
95 """Validate using: flake8"""
96 try:
97 subprocess.run(['/usr/bin/flake8', pfile], check=True)
98 return True
99 except subprocess.CalledProcessError:
100 return False
101
102
103def validate_isort(pfile):
104 """Validate using: isort --check --diff"""
105 try:
106 subprocess.run(['/usr/bin/isort', '--check', '--diff', pfile],
107 check=True)
108 return True
109 except subprocess.CalledProcessError:
110 return False
111
112
113def validate_pylint(pfile):
114 """Validate using: pylint -r no --exit-zero"""
115 try:
116 subprocess.run(['/usr/bin/pylint', '-r', 'no', '--exit-zero', pfile],
117 check=True)
118 return True
119 except subprocess.CalledProcessError:
120 return False
121
122
123def validate_python_file(pfile, args):
124 """
125 Validates a single .py file
126
127 :return: False if validation fails, False otherwise
128 :rtype: bool
129 """
130 if not validate_flake8(pfile) and not args.continue_checks:
131 if args.verbose:
132 eprint(f'{pfile}: flake8 validation failed')
133 sys.exit(0)
134 if not validate_isort(pfile) and not args.continue_checks:
135 if args.verbose:
136 eprint(f'{pfile}: isort validation failed')
137 sys.exit(0)
138 if not validate_pylint(pfile) and not args.continue_checks:
139 if args.verbose:
140 eprint(f'{pfile}: pylint validation failed')
141 sys.exit(0)
142
143
144def main(inargs=None):
145 """Main entry point"""
122 parser = argparse.ArgumentParser( 146 parser = argparse.ArgumentParser(
123 description='The following options are available') 147 description='The following options are available')
124 parser.add_argument( 148 parser.add_argument(
125 'repo_path', 149 'repo_path',
126 metavar='REPO', 150 metavar='REPO',
151 nargs='?',
127 default=os.getcwd(), 152 default=os.getcwd(),
128 type=str,
129 help='Git repo path (default: cwd)') 153 help='Git repo path (default: cwd)')
130 parser.add_argument( 154 parser.add_argument(
131 '-a', '--all', 155 '-a', '--all-files',
132 action='store_true', 156 action='store_true',
133 dest='all_files', 157 dest='all_files',
134 default=False, 158 help='Check all files, not only the modified ones')
135 help='Check all .py files (not only the ones that are modified)') 159 parser.add_argument(
160 '-c', '--continue-checks',
161 action='store_true',
162 dest='continue_checks',
163 help='Continue with the checks if validation fails')
136 parser.add_argument( 164 parser.add_argument(
137 '-f', '--full', 165 '-f', '--full',
138 action='store_true', 166 action='store_true',
139 dest='full_search', 167 dest='full_search',
140 default=False,
141 help='Consider repos that contain untracked files (used with -m)') 168 help='Consider repos that contain untracked files (used with -m)')
142 parser.add_argument( 169 parser.add_argument(
143 '-m', '--modifications-only', 170 '-m', '--modifications-only',
144 action='store_true', 171 action='store_true',
145 dest='mod_only', 172 dest='mod_only',
146 default=False,
147 help='Only check for git modifications') 173 help='Only check for git modifications')
148 parser.add_argument( 174 parser.add_argument(
149 '-r', '--recursive', 175 '-r', '--recursive',
150 action='store_true', 176 action='store_true',
151 dest='recursive', 177 dest='recursive',
152 default=False,
153 help='Look for several repos recursively') 178 help='Look for several repos recursively')
154 parser.add_argument( 179 parser.add_argument(
155 '-v', '--verbose', 180 '-v', '--verbose',
156 action='store_true', 181 action='store_true',
157 dest='verbose', 182 dest='verbose',
158 default=False,
159 help='Verbosity') 183 help='Verbosity')
160 args = parser.parse_args() 184 args = parser.parse_args(inargs)
161 abs_repo_path = os.path.abspath(os.path.expanduser(args.repo_path)) 185 abs_repo_path = os.path.abspath(os.path.expanduser(args.repo_path))
162 if args.mod_only: 186 if args.mod_only:
163 if not args.recursive: 187 if not args.recursive:
164 if not is_git_repo(abs_repo_path): 188 if not is_git_repo(abs_repo_path):
165 print(f'{abs_repo_path} is not a valid git repository', 189 eprint(f'{abs_repo_path} is not a valid git repository')
166 file=sys.stderr,
167 flush=True)
168 sys.exit(1) 190 sys.exit(1)
169 print_modification_status(abs_repo_path, 191 print_modification_status(abs_repo_path,
170 modifications_exist(abs_repo_path, args), 192 modifications_exist(abs_repo_path),
171 args) 193 args)
172 sys.exit(0) 194 sys.exit(0)
173 # resursive 195 # resursive
@@ -178,7 +200,7 @@ if __name__ == '__main__':
178 repo_list.sort() 200 repo_list.sort()
179 for repo_path in repo_list: 201 for repo_path in repo_list:
180 print_modification_status(repo_path, 202 print_modification_status(repo_path,
181 modifications_exist(repo_path, args), 203 modifications_exist(repo_path),
182 args) 204 args)
183 sys.exit(0) 205 sys.exit(0)
184 if args.all_files: 206 if args.all_files:
@@ -188,6 +210,11 @@ if __name__ == '__main__':
188 if not selected_files: 210 if not selected_files:
189 print('No files selected') 211 print('No files selected')
190 sys.exit(0) 212 sys.exit(0)
191 report = flake8_legacy.get_style_guide().check_files(selected_files) 213 for target_file in selected_files:
192 if report.total_errors: 214 if args.verbose:
193 print('Total errors: {errors}'.format(errors=report.total_errors)) 215 print(f'Validating {target_file}:')
216 validate_python_file(target_file, args)
217
218
219if __name__ == '__main__':
220 main()