Skip to content

Commit 2b7c208

Browse files
committed
Update pylama
1 parent 77d60d5 commit 2b7c208

File tree

9 files changed

+1098
-169
lines changed

9 files changed

+1098
-169
lines changed

pylibs/pylama/__init__.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
"""
2-
Code audit tool for python. Pylama wraps these tools:
3-
4-
* PEP8_ (c) 2012-2013, Florent Xicluna;
5-
* PyFlakes_ (c) 2005-2013, Kevin Watters;
6-
* Pylint_ (c) 2013, Logilab;
7-
* Mccabe_ (c) Ned Batchelder;
8-
9-
| `Pylint doesnt supported in python3.`
2+
Code audit tool for python.
103
114
:copyright: 2013 by Kirill Klenov.
125
:license: BSD, see LICENSE for more details.
136
"""
147

15-
version_info = 1, 0, 2
8+
version_info = 1, 0, 4
169

1710
__version__ = version = '.'.join(map(str, version_info))
1811
__project__ = __name__

pylibs/pylama/core.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
""" Pylama core.
2+
"""
3+
import logging
4+
import re
5+
6+
from . import utils
7+
8+
9+
DEFAULT_LINTERS = 'pep8', 'pyflakes', 'mccabe'
10+
LOGGER = logging.getLogger('pylama')
11+
MODERE = re.compile(r'^\s*#\s+(?:pymode\:)?((?:lint[\w_]*=[^:\n\s]+:?)+)',
12+
re.I | re.M)
13+
SKIP_PATTERN = '# nolint'
14+
STREAM = logging.StreamHandler()
15+
16+
LOGGER.addHandler(STREAM)
17+
18+
19+
def run(path, ignore=None, select=None, linters=DEFAULT_LINTERS, config=None,
20+
**meta):
21+
""" Run code checking for path.
22+
23+
:return errors: list of dictionaries with error's information
24+
25+
"""
26+
errors = []
27+
ignore = ignore and list(ignore) or []
28+
select = select and list(select) or []
29+
30+
try:
31+
with open(path, 'rU') as f:
32+
code = f.read() + '\n\n'
33+
params = config or __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 lint in linters:
46+
try:
47+
linter = getattr(utils, lint)
48+
except AttributeError:
49+
LOGGER.warning("Linter `%s` not found.", lint)
50+
continue
51+
52+
result = linter(path, code=code, **meta)
53+
for e in result:
54+
e['col'] = e.get('col') or 0
55+
e['lnum'] = e.get('lnum') or 0
56+
e['type'] = e.get('type') or 'E'
57+
e['text'] = "{0} [{1}]".format((e.get(
58+
'text') or '').strip()
59+
.replace("'", "\"").split('\n')[0], lint)
60+
e['filename'] = path or ''
61+
try:
62+
if not params['skip'][e['lnum']]:
63+
errors.append(e)
64+
except IndexError:
65+
continue
66+
67+
except IOError as e:
68+
errors.append(dict(
69+
lnum=0,
70+
type='E',
71+
col=0,
72+
text=str(e)
73+
))
74+
75+
except SyntaxError as e:
76+
errors.append(dict(
77+
lnum=e.lineno or 0,
78+
type='E',
79+
col=e.offset or 0,
80+
text=e.args[0] + ' [%s]' % lint
81+
))
82+
83+
except Exception:
84+
import traceback
85+
logging.error(traceback.format_exc())
86+
87+
errors = [er for er in errors if __ignore_error(er, select, ignore)]
88+
return sorted(errors, key=lambda x: x['lnum'])
89+
90+
91+
def __parse_modeline(code):
92+
""" Parse modeline params from file.
93+
94+
:return dict: Linter params.
95+
96+
"""
97+
seek = MODERE.search(code)
98+
params = dict(lint=1)
99+
if seek:
100+
params = dict(v.split('=') for v in seek.group(1).split(':'))
101+
params['lint'] = int(params.get('lint', 1))
102+
return params
103+
104+
105+
def __ignore_error(e, select, ignore):
106+
for s in select:
107+
if e['text'].startswith(s):
108+
return True
109+
for i in ignore:
110+
if e['text'].startswith(i):
111+
return False
112+
return True

pylibs/pylama/hook.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
""" SCM hooks.
2+
"""
13
from __future__ import absolute_import
24

35
import sys
@@ -14,20 +16,29 @@
1416

1517

1618
def run(command):
19+
""" Run a shell command.
20+
21+
:return str: Stdout
22+
23+
"""
1724
p = Popen(command.split(), stdout=PIPE, stderr=PIPE)
1825
(stdout, stderr) = p.communicate()
1926
return (p.returncode, [line.strip() for line in stdout.splitlines()],
2027
[line.strip() for line in stderr.splitlines()])
2128

2229

2330
def git_hook():
31+
""" Run pylama after git commit. """
32+
2433
from .main import check_files
2534
_, files_modified, _ = run("git diff-index --cached --name-only HEAD")
2635
LOGGER.setLevel('WARN')
2736
check_files([f for f in map(str, files_modified) if f.endswith('.py')])
2837

2938

3039
def hg_hook(ui, repo, **kwargs):
40+
""" Run pylama after mercurial commit. """
41+
3142
from .main import check_files
3243
seen = set()
3344
paths = []
@@ -44,6 +55,7 @@ def hg_hook(ui, repo, **kwargs):
4455

4556

4657
def install_git(path):
58+
""" Install hook in Git repository. """
4759
hook = op.join(path, 'pre-commit')
4860
with open(hook, 'w+') as fd:
4961
fd.write("""#!/usr/bin/env python
@@ -54,10 +66,11 @@ def install_git(path):
5466
sys.exit(git_hook())
5567
""")
5668
chmod(hook, 484)
57-
return True
5869

5970

6071
def install_hg(path):
72+
""" Install hook in Mercurial repository. """
73+
6174
hook = op.join(path, 'hgrc')
6275
if not op.isfile(hook):
6376
open(hook, 'w+').close()
@@ -74,10 +87,11 @@ def install_hg(path):
7487
c.set('hooks', 'qrefresh', 'python:pylama.hooks.hg_hook')
7588

7689
c.write(open(path, 'w+'))
77-
return True
7890

7991

8092
def install_hook(path):
93+
""" Auto definition of SCM and hook installation. """
94+
8195
git = op.join(path, '.git', 'hooks')
8296
hg = op.join(path, '.hg')
8397
if op.exists(git):

pylibs/pylama/inirama.py

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
"""
2-
Parse INI files.
2+
Inirama is a python module that parses INI files.
33
4+
.. include:: ../README.rst
5+
:start-line: 5
6+
:end-line: 12
7+
8+
:copyright: 2013 by Kirill Klenov.
9+
:license: BSD, see LICENSE for more details.
410
"""
5-
from __future__ import absolute_import
11+
from __future__ import unicode_literals, print_function
612

713
import io
814
import re
915
import logging
1016
from collections import MutableMapping
1117
try:
1218
from collections import OrderedDict
13-
except ImportError as e:
19+
except ImportError:
1420
from ordereddict import OrderedDict
1521

1622

17-
__version__ = '0.2.9'
23+
__version__ = '0.4.0'
1824
__project__ = 'Inirama'
1925
__author__ = "Kirill Klenov <horneds@gmail.com>"
2026
__license__ = "BSD"
@@ -183,9 +189,33 @@ def __getitem__(self, name):
183189

184190

185191
class Namespace(object):
192+
""" Default class for parsing INI.
193+
194+
:param **default_items: Default items for default section.
195+
196+
Usage
197+
-----
198+
199+
::
200+
201+
from inirama import Namespace
186202
203+
ns = Namespace()
204+
ns.read('config.ini')
205+
206+
print ns['section']['key']
207+
208+
ns['other']['new'] = 'value'
209+
ns.write('new_config.ini')
210+
211+
"""
212+
#: Name of default section (:attr:`~inirama.Namespace.default`)
187213
default_section = 'DEFAULT'
214+
215+
#: Dont raise any exception on file reading erorrs
188216
silent_read = True
217+
218+
#: Class for generating sections
189219
section_type = Section
190220

191221
def __init__(self, **default_items):
@@ -201,6 +231,11 @@ def default(self):
201231

202232
def read(self, *files, **params):
203233
""" Read and parse INI files.
234+
235+
:param *files: Files for reading
236+
:param **params: Params for parsing
237+
238+
Set `update=False` for prevent values redefinition.
204239
"""
205240
for f in files:
206241
try:
@@ -214,7 +249,7 @@ def read(self, *files, **params):
214249

215250
def write(self, f):
216251
"""
217-
Write self as INI file.
252+
Write namespace as INI file.
218253
219254
:param f: File object or path to file.
220255
"""
@@ -233,7 +268,10 @@ def write(self, f):
233268
f.close()
234269

235270
def parse(self, source, update=True, **params):
236-
""" Parse INI source.
271+
""" Parse INI source as string.
272+
273+
:param source: Source of INI
274+
:param update: Replace alredy defined items
237275
"""
238276
scanner = INIScanner(source)
239277
scanner.scan()
@@ -266,7 +304,23 @@ def __repr__(self):
266304

267305

268306
class InterpolationNamespace(Namespace):
307+
""" That implements the interpolation feature.
308+
309+
::
310+
311+
from inirama import InterpolationNamespace
312+
313+
ns = InterpolationNamespace()
314+
ns.parse('''
315+
[main]
316+
test = value
317+
foo = bar {test}
318+
more_deep = wow {foo}
319+
''')
320+
print ns['main']['more_deep'] # wow bar value
321+
322+
"""
269323

270324
section_type = InterpolationSection
271325

272-
# lint_ignore=W0201,R0924,F0401
326+
# lint_ignore=W0201,R0924

0 commit comments

Comments
 (0)