Skip to content

Commit 2b790f3

Browse files
committed
Update pylama
1 parent 2882183 commit 2b790f3

7 files changed

Lines changed: 492 additions & 174 deletions

File tree

‎pylibs/pylama/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
" pylama -- Python code audit. "
22

3-
version_info = (0, 2, 4)
3+
version_info = (0, 3, 0)
44

55
__version__ = version = '.'.join(map(str, version_info))
66
__project__ = __name__

‎pylibs/pylama/hook.py‎

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import sys
2+
from os import path as op, chmod
3+
from subprocess import Popen, PIPE
4+
from .main import logger
5+
6+
7+
try:
8+
from configparser import ConfigParser # nolint
9+
except ImportError: # Python 2
10+
from ConfigParser import ConfigParser
11+
12+
13+
def run(command):
14+
p = Popen(command.split(), stdout=PIPE, stderr=PIPE)
15+
(stdout, stderr) = p.communicate()
16+
return (p.returncode, [line.strip() for line in stdout.splitlines()],
17+
[line.strip() for line in stderr.splitlines()])
18+
19+
20+
def git_hook():
21+
from .main import check_files
22+
_, files_modified, _ = run("git diff-index --cached --name-only HEAD")
23+
logger.setLevel('WARN')
24+
check_files([f for f in map(str, files_modified) if f.endswith('.py')])
25+
26+
27+
def hg_hook(ui, repo, **kwargs):
28+
from .main import check_files
29+
seen = set()
30+
paths = []
31+
for rev in range(repo[kwargs['node']], len(repo)):
32+
for file_ in repo[rev].files():
33+
file_ = op.join(repo.root, file_)
34+
if file_ in seen or not op.exists(file_):
35+
continue
36+
seen.add(file_)
37+
if file_.endswith('.py'):
38+
paths.append(file_)
39+
logger.setLevel('WARN')
40+
check_files(paths)
41+
42+
43+
def install_git(path):
44+
hook = op.join(path, 'pre-commit')
45+
with open(hook, 'w+') as fd:
46+
fd.write("""#!/usr/bin/env python
47+
import sys
48+
from pylama.hook import git_hook
49+
50+
if __name__ == '__main__':
51+
sys.exit(git_hook())
52+
""")
53+
chmod(hook, 484)
54+
return True
55+
56+
57+
def install_hg(path):
58+
hook = op.join(path, 'hgrc')
59+
if not op.isfile(hook):
60+
open(hook, 'w+').close()
61+
62+
c = ConfigParser()
63+
c.readfp(open(path, 'r'))
64+
if not c.has_section('hooks'):
65+
c.add_section('hooks')
66+
67+
if not c.has_option('hooks', 'commit'):
68+
c.set('hooks', 'commit', 'python:pylama.hooks.hg_hook')
69+
70+
if not c.has_option('hooks', 'qrefresh'):
71+
c.set('hooks', 'qrefresh', 'python:pylama.hooks.hg_hook')
72+
73+
c.write(open(path, 'w+'))
74+
return True
75+
76+
77+
def install_hook(path):
78+
git = op.join(path, '.git', 'hooks')
79+
hg = op.join(path, '.hg')
80+
if op.exists(git):
81+
install_git(git) and logger.warn('Git hook has been installed.') # nolint
82+
83+
elif op.exists(hg):
84+
install_hg(git) and logger.warn('Mercurial hook has been installed.') # nolint
85+
86+
else:
87+
logger.error('VCS has not found. Check your path.')
88+
sys.exit(1)

‎pylibs/pylama/main.py‎

Lines changed: 69 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
default_linters = 'pep8', 'pyflakes', 'mccabe'
1313
default_complexity = 10
1414
logger = logging.Logger('pylama')
15+
stream = logging.StreamHandler()
16+
logger.addHandler(stream)
1517

1618
SKIP_PATTERN = '# nolint'
1719

@@ -29,38 +31,40 @@ def run(path, ignore=None, select=None, linters=default_linters, **meta): # nol
2931
continue
3032

3133
try:
32-
code = open(path, "rU").read() + '\n\n'
33-
params = parse_modeline(code)
34-
params['skip'] = [False]
35-
for line in code.split('\n'):
36-
params['skip'].append(line.endswith(SKIP_PATTERN))
37-
38-
if params.get('lint_ignore'):
39-
ignore += params.get('lint_ignore').split(',')
40-
41-
if params.get('lint_select'):
42-
select += params.get('lint_select').split(',')
43-
44-
if params.get('lint'):
45-
for e in linter(path, code=code, **meta):
46-
e['col'] = e.get('col') or 0
47-
e['lnum'] = e.get('lnum') or 0
48-
e['type'] = e.get('type') or 'W'
49-
e['text'] = "{0} [{1}]".format((e.get(
50-
'text') or '').strip()
51-
.replace("'", "\"").split('\n')[0], lint)
52-
e['filename'] = path or ''
53-
if not params['skip'][e['lnum']]:
54-
errors.append(e)
55-
56-
except IOError, e:
34+
with open(path, "rU") as f:
35+
code = f.read() + '\n\n'
36+
params = parse_modeline(code)
37+
params['skip'] = [False]
38+
for line in code.split('\n'):
39+
params['skip'].append(line.endswith(SKIP_PATTERN))
40+
41+
if params.get('lint_ignore'):
42+
ignore += params.get('lint_ignore').split(',')
43+
44+
if params.get('lint_select'):
45+
select += params.get('lint_select').split(',')
46+
47+
if params.get('lint'):
48+
result = linter(path, code=code, **meta)
49+
for e in result:
50+
e['col'] = e.get('col') or 0
51+
e['lnum'] = e.get('lnum') or 0
52+
e['type'] = e.get('type') or 'E'
53+
e['text'] = "{0} [{1}]".format((e.get(
54+
'text') or '').strip()
55+
.replace("'", "\"").split('\n')[0], lint)
56+
e['filename'] = path or ''
57+
if not params['skip'][e['lnum']]:
58+
errors.append(e)
59+
60+
except IOError as e:
5761
errors.append(dict(
5862
lnum=0,
5963
type='E',
6064
col=0,
6165
text=str(e)
6266
))
63-
except SyntaxError, e:
67+
except SyntaxError as e:
6468
errors.append(dict(
6569
lnum=e.lineno or 0,
6670
type='E',
@@ -69,7 +73,7 @@ def run(path, ignore=None, select=None, linters=default_linters, **meta): # nol
6973
))
7074
break
7175

72-
except Exception, e:
76+
except Exception as e:
7377
import traceback
7478
logging.error(traceback.format_exc())
7579

@@ -96,6 +100,9 @@ def shell():
96100

97101
split_csp_list = lambda s: list(set(i for i in s.split(',') if i))
98102

103+
parser.add_argument(
104+
"--format", "-f", default='pep8', choices=['pep8', 'pylint'],
105+
help="Error format.")
99106
parser.add_argument(
100107
"--select", "-s", default='',
101108
type=split_csp_list,
@@ -116,14 +123,19 @@ def shell():
116123
parser.add_argument("--complexity", "-c", default=default_complexity,
117124
type=int, help="Set mccabe complexity.")
118125
parser.add_argument("--report", "-r", help="Filename for report.")
126+
parser.add_argument("--hook", action="store_true",
127+
help="Install Git (Mercurial) hook.")
119128
args = parser.parse_args()
120129

121130
# Setup logger
122131
logger.setLevel(logging.INFO if args.verbose else logging.WARN)
123132
if args.report:
133+
logger.removeHandler(stream)
124134
logger.addHandler(logging.FileHandler(args.report, mode='w'))
125-
else:
126-
logger.addHandler(logging.StreamHandler())
135+
136+
if args.hook:
137+
from .hook import install_hook
138+
return install_hook(args.path)
127139

128140
paths = [args.path]
129141

@@ -132,16 +144,37 @@ def shell():
132144
for root, _, files in walk(args.path):
133145
paths += [op.join(root, f) for f in files if f.endswith('.py')]
134146

135-
for path in skip_paths(args, paths):
147+
check_files(
148+
paths,
149+
rootpath=args.path,
150+
skip=args.skip,
151+
frmt=args.format,
152+
ignore=args.ignore,
153+
select=args.select,
154+
linters=args.linters,
155+
complexity=args.complexity,
156+
)
157+
158+
159+
def check_files(paths, rootpath=None, skip=None, frmt="pep8",
160+
select=None, ignore=None, linters=default_linters,
161+
complexity=default_complexity):
162+
rootpath = rootpath or getcwd()
163+
pattern = "%(rel)s:%(lnum)s:%(col)s: %(text)s"
164+
if frmt == 'pylint':
165+
pattern = "%(rel)s:%(lnum)s: [%(type)s] %(text)s"
166+
167+
errors = []
168+
for path in skip_paths(skip, paths):
136169
logger.info("Parse file: %s" % path)
137-
errors = run(path, ignore=args.ignore, select=args.select,
138-
linters=args.linters, complexity=args.complexity)
170+
errors = run(path, ignore=ignore, select=select,
171+
linters=linters, complexity=complexity)
139172
for error in errors:
140173
try:
141174
error['rel'] = op.relpath(
142-
error['filename'], op.dirname(args.path))
175+
error['filename'], op.dirname(rootpath))
143176
error['col'] = error.get('col', 1)
144-
logger.warning("%(rel)s:%(lnum)s:%(col)s: %(text)s", error)
177+
logger.warning(pattern, error)
145178
except KeyError:
146179
continue
147180

@@ -152,9 +185,9 @@ def shell():
152185
r'^\s*#\s+(?:pymode\:)?((?:lint[\w_]*=[^:\n\s]+:?)+)', re.I | re.M)
153186

154187

155-
def skip_paths(args, paths):
188+
def skip_paths(skip, paths):
156189
for path in paths:
157-
if args.skip and any(pattern.match(path) for pattern in args.skip):
190+
if skip and any(pattern.match(path) for pattern in skip):
158191
continue
159192
yield path
160193

0 commit comments

Comments
 (0)