pylama-7.4.3/0000755000076500000240000000000013156240551013066 5ustar klenstaff00000000000000pylama-7.4.3/AUTHORS0000644000076500000240000000143513156236577014156 0ustar klenstaff00000000000000Maintainer: * Kirill Klenov Contributors: * Ankur Dedania (https://github.com/AbsoluteMSTR) * Daniel Hahler (https://github.com/blueyed) * Daniel O'onnell' (https://github.com/mruwnik) * Diego Rabatone Oliveira (https://github.com/diraol) * Fábio C. Barrionuevo da Luz (https://github.com/luzfcb) * GrandVizierOlaf (https://github.com/grandvizierolaf) * Grzegorz Śliwiński (https://github.com/fizyk) * Jarek Śmiejczak (https://github.com/jotes) * Jens Persson (https://github.com/MrShark) * Johan Bloemberg (https://github.com/aequitas) * Michael (https://github.com/michael-k) * Roman Osipenko (https://github.com/romanosipenko) * Serg Baburin (https://github.com/gmist) * Tomasz Karbownicki (https://github.com/trojkat) * lukaszpiotr (https://github.com/lukaszpiotr) pylama-7.4.3/Changelog0000644000076500000240000000714213156236577014721 0ustar klenstaff000000000000002017-09-04 horneds * Version 7.4.1 * Fix Windows encoding problem #108 2016-10-25 horneds * Version 7.2.0 * Replace PEP8 with pycodestyle (c) Serg Baburin 2015-08-17 k.klenov * --abspath 2015-06-30 horneds * Pyflakes 0.9.2 2015-06-03 horneds * Pyflakes 0.9.1-pre 2015-03-25 horneds * Version 6.2.0 * Pep257 0.5.0 * PEP8 1.6.3a0 * Pyflakes 0.8.2a0 2014-10-26 horneds * Version 6.1.0 2014-07-23 horneds * Fix mercurial hook installation (c) MrShark * Version 6.0.1 2014-07-01 horneds * Add sorting (--sort) * Version 6.0.0 2014-06-15 horneds * Better handling pylint properties 2014-06-11 horneds * Pytest support (as plugin) 2014-06-08 horneds * WARNING: Change format INI-options. See README for details. * INI configurations could be read from `pylama.ini`, `setup.cfg`, `pytest.ini`, `tox.ini` files. 2014-06-07 horneds * Reduce duplicate messages #3 * Update pep8 to version 1.6.0a0 2014-05-07 horneds * Update pep8 to version 1.5.7a0 * Update pyflakes to version 0.8.2a0 2014-05-04 horneds * Version 3.1.2 * Parse numbers from ini correctly (c) Grzegorz Śliwiński 2014-03-26 horneds * Version 3.1.1 * Update PEP8 to version 1.5.0 2014-03-24 horneds * File options (and modeline) 'lint_ignore' -> 'ignore', 'lint_select' -> 'select', 'lint' -> 'skip' * Update pep257 * Update pyflakes * Added frosted * Version 3.0.2 2013-11-12 horneds * Version 2.0.4 * Bugfix release 2013-10-27 horneds * Version 2.0.2 2013-10-13 horneds * Version 2.0.1 * Append JavaScript code checker (c) lukaszpiotr * Create plugin structure (move pylint, gjslint to plugins) 2013-09-16 horneds * Version 1.5.4 * fix default liners value for parsing options (c) Grzegorz Śliwiński 2013-09-05 horneds * Version 1.5.3 * Hotfix release 2013-08-30 horneds * Version 1.5.1 * Remove ordereddict requirement for python 2.6 * pep257 0.2.4 * pep8 1.4.7a0 2013-08-07 horneds * Version 1.4.0 * Pylint 1.0.0 * Pep257 0.2.3 * mccabe 0.2.1 2013-07-25 horneds * Version 1.3.3 2013-07-08 horneds * Merge settings from command lines, ini files and modelines * Version 1.3.1 2013-07-03 horneds * PEP8 1.4.6 * Pyflakes 0.7.3 2013-06-25 horneds * Fix file paths 2013-06-20 horneds * Version 1.1.0 * File's sections in `pylama.ini` now supports a filemasks 2013-06-17 horneds * WARNING: Change skipline pattern 'nolint' -> 'noqa' for better compatibility 2013-06-07 horneds * Version 1.0.4 * Added PEP257 checker * Experemental async support 2013-05-31 horneds * Version 1.0.2 * Fix release 1.0.0 2013-05-30 horneds * Beta release 1.0.0 2013-05-29 horneds * Version 0.3.8 * Added docs 2013-05-22 horneds * Version 0.3.6 * Fix release 0.3.5 2013-05-21 horneds * Version 0.3.5 * Now pylama can parse global and file-related options from file. 2013-05-15 horneds * Version 0.3.2 * Fix PEP8 UTF bug 2013-05-03 horneds * Version 0.3.1 * pylint 0.28.0 * pyflakes 0.7.3a0 2013-03-31 klen * Version 0.3.0; * Python3 support; 2013-03-29 klen * Added git and mercurial hooks; * Version 0.2.8 2013-03-22 klen * Version 0.2.7; * Added 'skipline' flag. See `# nolint`; * Added pylint parseable format; 2013-03-15 klen * Version 0.2.3 * Update pyflakes to 0.6.1 2013-03-14 klen * Version 0.2.2 * PEP8 to version 1.4.5; * Added Pylint 0.27.0 (disabled by default) 2013-02-15 klen * Version 0.1.4 * Update pep8 and pyflakes * `skip` option allowed to use unix file masks * `skip` option allowed to use many patterns (split by comma) * Added `report` option for file reports 2012-08-17 klen * Initial release pylama-7.4.3/LICENSE0000644000076500000240000001674312660640733014113 0ustar klenstaff00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. pylama-7.4.3/MANIFEST.in0000644000076500000240000000034712660640733014635 0ustar klenstaff00000000000000include AUTHORS include Changelog include LICENSE include MANIFEST.in include README.rst include requirements.txt recursive-include pylama * recursive-exclude * __pycache__ recursive-exclude * *.py[co] recursive-exclude * *.orig pylama-7.4.3/PKG-INFO0000644000076500000240000003275413156240551014176 0ustar klenstaff00000000000000Metadata-Version: 1.1 Name: pylama Version: 7.4.3 Summary: pylama -- Code audit tool for python Home-page: https://github.com/klen/pylama Author: Kirill Klenov Author-email: horneds@gmail.com License: GNU LGPL Description: |logo| Pylama ############# .. _description: Code audit tool for Python and JavaScript. Pylama wraps these tools: * pycodestyle_ (formerly pep8) © 2012-2013, Florent Xicluna; * pydocstyle_ (formerly pep257 by Vladimir Keleshev) © 2014, Amir Rachum; * PyFlakes_ © 2005-2013, Kevin Watters; * Mccabe_ © Ned Batchelder; * Pylint_ © 2013, Logilab (should be installed 'pylama_pylint' module); * Radon_ © Michele Lacchia * gjslint_ © The Closure Linter Authors (should be installed 'pylama_gjslint' module); .. _badges: .. image:: http://img.shields.io/travis/klen/pylama.svg?style=flat-square :target: http://travis-ci.org/klen/pylama :alt: Build Status .. image:: http://img.shields.io/coveralls/klen/pylama.svg?style=flat-square :target: https://coveralls.io/r/klen/pylama :alt: Coverals .. image:: http://img.shields.io/pypi/v/pylama.svg?style=flat-square :target: https://crate.io/packages/pylama :alt: Version .. image:: http://img.shields.io/gratipay/klen.svg?style=flat-square :target: https://www.gratipay.com/klen/ :alt: Donate .. _documentation: Docs are available at https://pylama.readthedocs.org/. Pull requests with documentation enhancements and/or fixes are awesome and most welcome. .. _contents: .. contents:: .. _requirements: Requirements: ============= - Python (2.7, 3.2, 3.3) - To use JavaScript checker (``gjslint``) you need to install ``python-gflags`` with ``pip install python-gflags``. - If your tests are failing on Win platform you are missing: ``curses`` - http://www.lfd.uci.edu/~gohlke/pythonlibs/ (The curses library supplies a terminal-independent screen-painting and keyboard-handling facility for text-based terminals) .. _installation: Installation: ============= **Pylama** could be installed using pip: :: :: $ pip install pylama .. _quickstart: Quickstart ========== **Pylama** is easy to use and really fun for checking code quality. Just run `pylama` and get common output from all pylama plugins (pycodestyle_, PyFlakes_ and etc) Recursive check the current directory. :: $ pylama Recursive check a path. :: $ pylama Ignore errors :: $ pylama -i W,E501 .. note:: You could choose a group erros `D`,`E1` and etc or special errors `C0312` Choose code checkers :: $ pylama -l "pycodestyle,mccabe" Choose code checkers for JavaScript:: $ pylama --linters=gjslint --ignore=E:0010 .. _options: Set Pylama (checkers) options ============================= Command line options -------------------- :: $ pylama --help usage: pylama [-h] [--verbose] [--version] [--format {pycodestyle,pylint}] [--select SELECT] [--sort SORT] [--linters LINTERS] [--ignore IGNORE] [--skip SKIP] [--report REPORT] [--hook] [--async] [--options OPTIONS] [--force] [--abspath] [paths [paths ...]] Code audit tool for python. positional arguments: paths Paths to files or directories for code check. optional arguments: -h, --help show this help message and exit --verbose, -v Verbose mode. --version show program's version number and exit --format {pycodestyle,pylint}, -f {pycodestyle,pylint} Choose errors format (pycodestyle, pylint). --select SELECT, -s SELECT Select errors and warnings. (comma-separated list) --sort SORT Sort result by error types. Ex. E,W,D --linters LINTERS, -l LINTERS Select linters. (comma-separated). Choices are mccabe,pycodestyle,pyflakes,pydocstyle. --ignore IGNORE, -i IGNORE Ignore errors and warnings. (comma-separated) --skip SKIP Skip files by masks (comma-separated, Ex. */messages.py) --report REPORT, -r REPORT Send report to file [REPORT] --hook Install Git (Mercurial) hook. --async Enable async mode. Useful for checking a lot of files. Not supported by pylint. --options FILE, -o FILE Specify configuration file. Looks for pylama.ini, setup.cfg, tox.ini, or pytest.ini in the current directory. --force, -F Force code checking (if linter doesnt allow) --abspath, -a Use absolute paths in output. .. _modeline: File modelines -------------- You can set options for **Pylama** inside a source file. Use pylama *modeline* for this. Format: :: # pylama:{name1}={value1}:{name2}={value2}:... :: .. Somethere in code # pylama:ignore=W:select=W301 Disable code checking for current file: :: .. Somethere in code # pylama:skip=1 Those options have a higher priority. .. _skiplines: Skip lines (noqa) ----------------- Just add `# noqa` in end of line to ignore. :: def urgent_fuction(): unused_var = 'No errors here' # noqa .. _config: Configuration file ------------------ **Pylama** looks for a configuration file in the current directory. The program searches for the first matching ini-style configuration file in the directories of command line argument. Pylama looks for the configuration in this order: :: pylama.ini setup.cfg tox.ini pytest.ini The "--option" / "-o" argument can be used to specify a configuration file. Pylama searches for sections whose names start with `pylama`. The "pylama" section configures global options like `linters` and `skip`. :: [pylama] format = pylint skip = */.tox/*,*/.env/* linters = pylint,mccabe ignore = F0401,C0111,E731 Set Code-checkers' options -------------------------- You could set options for special code checker with pylama configurations. :: [pylama:pyflakes] builtins = _ [pylama:pycodestyle] max_line_length = 100 [pylama:pylint] max_line_length = 100 disable = R See code-checkers' documentation for more info. Set options for file (group of files) ------------------------------------- You could set options for special file (group of files) with sections: The options have a higher priority than in the `pylama` section. :: [pylama:*/pylama/main.py] ignore = C901,R0914,W0212 select = R [pylama:*/tests.py] ignore = C0110 [pylama:*/setup.py] skip = 1 Pytest integration ================== Pylama has Pytest_ support. The package automatically registers itself as a pytest plugin during installation. Pylama also supports `pytest_cache` plugin. Check files with pylama :: pytest --pylama ... Recommended way to set pylama options when using pytest — configuration files (see below). Writing a linter ================ You can write a custom extension for Pylama. Custom linter should be a python module. Name should be like 'pylama_'. In 'setup.py', 'pylama.linter' entry point should be defined. :: setup( # ... entry_points={ 'pylama.linter': ['lintername = pylama_lintername.main:Linter'], } # ... ) 'Linter' should be instance of 'pylama.lint.Linter' class. Must implement two methods: 'allow' takes a path and returns true if linter can check this file for errors. 'run' takes a path and meta keywords params and returns a list of errors. Example: -------- Just a virtual 'WOW' checker. setup.py: :: setup( name='pylama_wow', install_requires=[ 'setuptools' ], entry_points={ 'pylama.linter': ['wow = pylama_wow.main:Linter'], } # ... ) pylama_wow.py: :: from pylama.lint import Linter as BaseLinter class Linter(BaseLinter): def allow(self, path): return 'wow' in path def run(self, path, **meta): with open(path) as f: if 'wow' in f.read(): return [{ lnum: 0, col: 0, text: 'Wow has been finded.', type: 'WOW' }] Run pylama from python code --------------------------- :: from pylama.main import check_path, parse_options my_redefined_options = {...} my_path = '...' options = parse_options([my_path], **my_redefined_options) errors = check_path(options) .. _bagtracker: Bug tracker ----------- If you have any suggestions, bug reports or annoyances please report them to the issue tracker at https://github.com/klen/pylama/issues .. _contributing: Contributing ------------ Development of `pylama` happens at GitHub: https://github.com/klen/pylama .. _contributors: Contributors ^^^^^^^^^^^^ See AUTHORS_. .. _license: License ------- Licensed under a `BSD license`_. .. _links: .. _AUTHORS: https://github.com/klen/pylama/blob/develop/AUTHORS .. _BSD license: http://www.linfo.org/bsdlicense.html .. _Mccabe: http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html .. _pydocstyle: https://github.com/PyCQA/pydocstyle/ .. _pycodestyle: https://github.com/PyCQA/pycodestyle .. _PyFlakes: https://github.com/pyflakes/pyflakes .. _Pylint: http://pylint.org .. _Pytest: http://pytest.org .. _gjslint: https://developers.google.com/closure/utilities .. _klen: http://klen.github.io/ .. _Radon: https://github.com/rubik/radon .. |logo| image:: https://raw.github.com/klen/pylama/develop/docs/_static/logo.png :width: 100 Keywords: pylint,pep8,pycodestyle,pyflakes,mccabe,linter,qa,pep257,pydocstyle Platform: Any Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Quality Assurance Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Natural Language :: English Classifier: Natural Language :: Russian Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Code Generators pylama-7.4.3/pylama/0000755000076500000240000000000013156240551014351 5ustar klenstaff00000000000000pylama-7.4.3/pylama/__init__.py0000644000076500000240000000035413156240532016463 0ustar klenstaff00000000000000"""Code audit tool for python. :copyright: 2013 by Kirill Klenov. :license: BSD, see LICENSE for more details. """ __version__ = "7.4.3" __project__ = "pylama" __author__ = "Kirill Klenov " __license__ = "GNU LGPL" pylama-7.4.3/pylama/__main__.py0000644000076500000240000000014513003673673016451 0ustar klenstaff00000000000000"""Support the module execution.""" from .main import shell if __name__ == '__main__': shell() pylama-7.4.3/pylama/async.py0000644000076500000240000000312613124226547016046 0ustar klenstaff00000000000000"""Support for checking code asynchronously.""" import logging import threading try: import Queue except ImportError: import queue as Queue try: import multiprocessing CPU_COUNT = multiprocessing.cpu_count() except (ImportError, NotImplementedError): CPU_COUNT = 1 from .core import run LOGGER = logging.getLogger('pylama') class Worker(threading.Thread): """Get tasks from queue and run.""" def __init__(self, path_queue, result_queue): """ Init worker. """ threading.Thread.__init__(self) self.path_queue = path_queue self.result_queue = result_queue def run(self): """ Run tasks from queue. """ while True: path, params = self.path_queue.get() errors = run(path, **params) self.result_queue.put(errors) self.path_queue.task_done() def check_async(paths, options, rootdir=None): """Check given paths asynchronously. :return list: list of errors """ LOGGER.info('Async code checking is enabled.') path_queue = Queue.Queue() result_queue = Queue.Queue() for num in range(CPU_COUNT): worker = Worker(path_queue, result_queue) worker.setDaemon(True) LOGGER.info('Start worker #%s', (num + 1)) worker.start() for path in paths: path_queue.put((path, dict(options=options, rootdir=rootdir))) path_queue.join() errors = [] while True: try: errors += result_queue.get(False) except Queue.Empty: break return errors # pylama:ignore=W0212,D210,F0001 pylama-7.4.3/pylama/config.py0000644000076500000240000001712513156236577016213 0ustar klenstaff00000000000000"""Parse arguments from command line and configuration files.""" import fnmatch import os import sys import re import logging from argparse import ArgumentParser from . import __version__ from .libs.inirama import Namespace from .lint.extensions import LINTERS #: A default checkers DEFAULT_LINTERS = 'pycodestyle', 'pyflakes', 'mccabe' CURDIR = os.getcwd() CONFIG_FILES = 'pylama.ini', 'setup.cfg', 'tox.ini', 'pytest.ini' #: The skip pattern SKIP_PATTERN = re.compile(r'# *noqa\b', re.I).search # Parse a modelines MODELINE_RE = re.compile(r'^\s*#\s+(?:pylama:)\s*((?:[\w_]*=[^:\n\s]+:?)+)', re.I | re.M) # Setup a logger LOGGER = logging.getLogger('pylama') LOGGER.propagate = False STREAM = logging.StreamHandler(sys.stdout) LOGGER.addHandler(STREAM) class _Default(object): # pylint: disable=too-few-public-methods def __init__(self, value=None): self.value = value def __str__(self): return str(self.value) def __repr__(self): return "<_Default [%s]>" % self.value def split_csp_str(val): """ Split comma separated string into unique values, keeping their order. :returns: list of splitted values """ seen = set() values = val if isinstance(val, (list, tuple)) else val.strip().split(',') return [x for x in values if x and not (x in seen or seen.add(x))] def parse_linters(linters): """ Initialize choosen linters. :returns: list of inited linters """ result = list() for name in split_csp_str(linters): linter = LINTERS.get(name) if linter: result.append((name, linter)) else: logging.warning("Linter `%s` not found.", name) return result def get_default_config_file(rootdir=None): """Search for configuration file.""" if rootdir is None: return DEFAULT_CONFIG_FILE for path in CONFIG_FILES: path = os.path.join(rootdir, path) if os.path.isfile(path) and os.access(path, os.R_OK): return path DEFAULT_CONFIG_FILE = get_default_config_file(CURDIR) PARSER = ArgumentParser(description="Code audit tool for python.") PARSER.add_argument( "paths", nargs='*', default=_Default([CURDIR]), help="Paths to files or directories for code check.") PARSER.add_argument( "--verbose", "-v", action='store_true', help="Verbose mode.") PARSER.add_argument('--version', action='version', version='%(prog)s ' + __version__) PARSER.add_argument( "--format", "-f", default=_Default('pycodestyle'), choices=['pep8', 'pycodestyle', 'pylint', 'parsable'], help="Choose errors format (pycodestyle, pylint, parsable).") PARSER.add_argument( "--select", "-s", default=_Default(''), type=split_csp_str, help="Select errors and warnings. (comma-separated list)") PARSER.add_argument( "--sort", default=_Default(''), type=split_csp_str, help="Sort result by error types. Ex. E,W,D") PARSER.add_argument( "--linters", "-l", default=_Default(','.join(DEFAULT_LINTERS)), type=parse_linters, help=( "Select linters. (comma-separated). Choices are %s." % ','.join(s for s in LINTERS.keys()) )) PARSER.add_argument( "--ignore", "-i", default=_Default(''), type=split_csp_str, help="Ignore errors and warnings. (comma-separated)") PARSER.add_argument( "--skip", default=_Default(''), type=lambda s: [re.compile(fnmatch.translate(p)) for p in s.split(',') if p], help="Skip files by masks (comma-separated, Ex. */messages.py)") PARSER.add_argument("--report", "-r", help="Send report to file [REPORT]") PARSER.add_argument( "--hook", action="store_true", help="Install Git (Mercurial) hook.") PARSER.add_argument( "--async", action="store_true", help="Enable async mode. Useful for checking a lot of files. " "Unsupported with pylint.") PARSER.add_argument( "--options", "-o", default=DEFAULT_CONFIG_FILE, metavar='FILE', help="Specify configuration file. " "Looks for {}, or {} in the current directory (default: {}).".format( ", ".join(CONFIG_FILES[:-1]), CONFIG_FILES[-1], DEFAULT_CONFIG_FILE)) PARSER.add_argument( "--force", "-F", action='store_true', default=_Default(False), help="Force code checking (if linter doesn't allow)") PARSER.add_argument( "--abspath", "-a", action='store_true', default=_Default(False), help="Use absolute paths in output.") ACTIONS = dict((a.dest, a) for a in PARSER._actions) # pylint: disable=protected-access def parse_options(args=None, config=True, rootdir=CURDIR, **overrides): # noqa """ Parse options from command line and configuration files. :return argparse.Namespace: """ args = args or [] # Parse args from command string options = PARSER.parse_args(args) options.file_params = dict() options.linters_params = dict() # Compile options from ini if config: cfg = get_config(str(options.options), rootdir=rootdir) for opt, val in cfg.default.items(): LOGGER.info('Find option %s (%s)', opt, val) passed_value = getattr(options, opt, _Default()) if isinstance(passed_value, _Default): if opt == 'paths': val = val.split() setattr(options, opt, _Default(val)) # Parse file related options for name, opts in cfg.sections.items(): if not name.startswith('pylama') or name == cfg.default_section: continue name = name[7:] if name in LINTERS: options.linters_params[name] = dict(opts) continue mask = re.compile(fnmatch.translate(name)) options.file_params[mask] = dict(opts) # Override options _override_options(options, **overrides) # Postprocess options for name in options.__dict__: value = getattr(options, name) if isinstance(value, _Default): setattr(options, name, process_value(name, value.value)) if options.async and 'pylint' in options.linters: LOGGER.warning('Can\'t parse code asynchronously with pylint enabled.') options.async = False return options def _override_options(options, **overrides): """Override options.""" for opt, val in overrides.items(): passed_value = getattr(options, opt, _Default()) if opt in ('ignore', 'select') and passed_value: value = process_value(opt, passed_value.value) value += process_value(opt, val) setattr(options, opt, value) elif isinstance(passed_value, _Default): setattr(options, opt, process_value(opt, val)) def process_value(name, value): """ Compile option value. """ action = ACTIONS.get(name) if not action: return value if callable(action.type): return action.type(value) if action.const: return bool(int(value)) return value def get_config(ini_path=None, rootdir=None): """ Load configuration from INI. :return Namespace: """ config = Namespace() config.default_section = 'pylama' if not ini_path or ini_path == 'None': path = get_default_config_file(rootdir) if path: config.read(path) else: config.read(ini_path) return config def setup_logger(options): """Do the logger setup with options.""" LOGGER.setLevel(logging.INFO if options.verbose else logging.WARN) if options.report: LOGGER.removeHandler(STREAM) LOGGER.addHandler(logging.FileHandler(options.report, mode='w')) if options.options: LOGGER.info('Try to read configuration from: ' + options.options) # pylama:ignore=W0212,D210,F0001 pylama-7.4.3/pylama/core.py0000644000076500000240000001313313156236577015671 0ustar klenstaff00000000000000"""Pylama's core functionality. Prepare params, check a modeline and run the checkers. """ import logging import sys import os.path as op from .config import process_value, LOGGER, MODELINE_RE, SKIP_PATTERN, CURDIR from .errors import Error, remove_duplicates from .lint.extensions import LINTERS def run(path='', code=None, rootdir=CURDIR, options=None): """Run code checkers with given params. :param path: (str) A file's path. :param code: (str) A code source :return errors: list of dictionaries with error's information """ errors = [] fileconfig = dict() linters = LINTERS linters_params = dict() lname = 'undefined' params = dict() path = op.relpath(path, rootdir) if options: linters = options.linters linters_params = options.linters_params for mask in options.file_params: if mask.match(path): fileconfig.update(options.file_params[mask]) if options.skip and any(p.match(path) for p in options.skip): LOGGER.info('Skip checking for path: %s', path) return [] try: with CodeContext(code, path) as ctx: code = ctx.code params = prepare_params(parse_modeline(code), fileconfig, options) LOGGER.debug('Checking params: %s', params) if params.get('skip'): return errors for item in params.get('linters') or linters: if not isinstance(item, tuple): item = (item, LINTERS.get(item)) lname, linter = item if not linter: continue lparams = linters_params.get(lname, dict()) LOGGER.info("Run %s %s", lname, lparams) linter_errors = linter.run( path, code=code, ignore=params.get("ignore", set()), select=params.get("select", set()), params=lparams) if linter_errors: for er in linter_errors: errors.append(Error(filename=path, linter=lname, **er)) except IOError as e: LOGGER.debug("IOError %s", e) errors.append(Error(text=str(e), filename=path, linter=lname)) except SyntaxError as e: LOGGER.debug("SyntaxError %s", e) errors.append( Error(linter='pylama', lnum=e.lineno, col=e.offset, text='E0100 SyntaxError: {}'.format(e.args[0]), filename=path)) except Exception as e: # noqa import traceback LOGGER.info(traceback.format_exc()) errors = filter_errors(errors, **params) # noqa errors = list(remove_duplicates(errors)) if code and errors: errors = filter_skiplines(code, errors) key = lambda e: e.lnum if options and options.sort: sort = dict((v, n) for n, v in enumerate(options.sort, 1)) key = lambda e: (sort.get(e.type, 999), e.lnum) return sorted(errors, key=key) def parse_modeline(code): """Parse params from file's modeline. :return dict: Linter params. """ seek = MODELINE_RE.search(code) if seek: return dict(v.split('=') for v in seek.group(1).split(':')) return dict() def prepare_params(modeline, fileconfig, options): """Prepare and merge a params from modelines and configs. :return dict: """ params = dict(skip=False, ignore=[], select=[], linters=[]) if options: params['ignore'] = list(options.ignore) params['select'] = list(options.select) for config in filter(None, [modeline, fileconfig]): for key in ('ignore', 'select', 'linters'): params[key] += process_value(key, config.get(key, [])) params['skip'] = bool(int(config.get('skip', False))) params['ignore'] = set(params['ignore']) params['select'] = set(params['select']) return params def filter_errors(errors, select=None, ignore=None, **params): """Filter errors by select and ignore options. :return bool: """ select = select or [] ignore = ignore or [] for e in errors: for s in select: if e.number.startswith(s): yield e break else: for s in ignore: if e.number.startswith(s): break else: yield e def filter_skiplines(code, errors): """Filter lines by `noqa`. :return list: A filtered errors """ if not errors: return errors enums = set(er.lnum for er in errors) removed = set([ num for num, l in enumerate(code.split('\n'), 1) if num in enums and SKIP_PATTERN(l) ]) if removed: errors = [er for er in errors if er.lnum not in removed] return errors class CodeContext(object): """Read file if code is None. """ def __init__(self, code, path): """ Init context. """ self.code = code self.path = path self._file = None def __enter__(self): """ Open a file and read it. """ if self.code is None: LOGGER.info("File is reading: %s", self.path) if sys.version_info >= (3, ): self._file = open(self.path, encoding='utf-8') else: self._file = open(self.path, 'rU') self.code = self._file.read() return self def __exit__(self, t, value, traceback): """ Close the file which was opened. """ if self._file is not None: self._file.close() if t and LOGGER.level == logging.DEBUG: LOGGER.debug(traceback) # pylama:ignore=R0912,D210,F0001 pylama-7.4.3/pylama/errors.py0000644000076500000240000000633413003674762016253 0ustar klenstaff00000000000000""" Don't duplicate same errors from different linters. """ import re from collections import defaultdict PATTERN_NUMBER = re.compile(r'^[A-Z]\d+$') DUPLICATES = ( # multiple statements on one line [('pycodestyle', 'E701'), ('pylint', 'C0321')], [('pep8', 'E701'), ('pylint', 'C0321')], # unused variable [('pylint', 'W0612'), ('pyflakes', 'W0612')], # undefined variable [('pylint', 'E0602'), ('pyflakes', 'E0602')], # unused import [('pylint', 'W0611'), ('pyflakes', 'W0611')], # whitespace before ')' [('pylint', 'C0326'), ('pycodestyle', 'E202')], [('pylint', 'C0326'), ('pep8', 'E202')], # whitespace before '(' [('pylint', 'C0326'), ('pycodestyle', 'E211')], [('pylint', 'C0326'), ('pep8', 'E211')], # multiple spaces after operator [('pylint', 'C0326'), ('pycodestyle', 'E222')], [('pylint', 'C0326'), ('pep8', 'E222')], # missing whitespace around operator [('pylint', 'C0326'), ('pycodestyle', 'E225')], [('pylint', 'C0326'), ('pep8', 'E225')], # unexpected spaces [('pylint', 'C0326'), ('pycodestyle', 'E251')], [('pylint', 'C0326'), ('pep8', 'E251')], # long lines [('pylint', 'C0301'), ('pycodestyle', 'E501')], [('pylint', 'C0301'), ('pep8', 'E501')], # statement ends with a semicolon [('pylint', 'W0301'), ('pycodestyle', 'E703')], [('pylint', 'W0301'), ('pep8', 'E703')], # multiple statements on one line [('pylint', 'C0321'), ('pycodestyle', 'E702')], # bad indentation [('pylint', 'W0311'), ('pycodestyle', 'E111')], [('pylint', 'W0311'), ('pep8', 'E111')], # wildcart import [('pylint', 'W00401'), ('pyflakes', 'W0401')], # module docstring [('pydocstyle', 'D100'), ('pylint', 'C0111')], [('pep257', 'D100'), ('pylint', 'C0111')], ) DUPLICATES = dict((key, values) for values in DUPLICATES for key in values) def remove_duplicates(errors): """ Filter duplicates from given error's list. """ passed = defaultdict(list) for error in errors: key = error.linter, error.number if key in DUPLICATES: if key in passed[error.lnum]: continue passed[error.lnum] = DUPLICATES[key] yield error class Error(object): """ Store an error's information. """ def __init__(self, linter="", col=1, lnum=1, type="E", text="unknown error", filename="", number="", **kwargs): """ Init error information with default values. """ text = ' '.join(str(text).strip().split('\n')) if linter: text = "%s [%s]" % (text, linter) number = number or text.split(' ', 1)[0] if not PATTERN_NUMBER.match(number): number = "" self._info = dict(linter=linter, col=col, lnum=lnum, type=type[:1], text=text, filename=filename, number=number) def __getattr__(self, name): return self._info[name] def __getitem__(self, name): return self._info[name] def get(self, name, default=None): """ Implement dictionary `get` method. """ return self._info.get(name, default) def __repr__(self): return "" % (self.number, self.linter) # pylama:ignore=W0622,D,R0924 pylama-7.4.3/pylama/hook.py0000644000076500000240000000575013156240532015671 0ustar klenstaff00000000000000"""SCM hooks. Integration with git and mercurial.""" from __future__ import absolute_import import sys from os import path as op, chmod from subprocess import Popen, PIPE from .main import LOGGER, process_paths from .config import parse_options, setup_logger try: from configparser import ConfigParser # noqa except ImportError: # Python 2 from ConfigParser import ConfigParser def run(command): """Run a shell command. :return str: Stdout """ p = Popen(command.split(), stdout=PIPE, stderr=PIPE) (stdout, stderr) = p.communicate() return (p.returncode, [line.strip() for line in stdout.splitlines()], [line.strip() for line in stderr.splitlines()]) def git_hook(error=True): """Run pylama after git commit.""" _, files_modified, _ = run("git diff-index --cached --name-only HEAD") options = parse_options() setup_logger(options) if sys.version_info >= (3,): candidates = [f.decode('utf-8') for f in files_modified] else: candidates = [str(f) for f in files_modified] if candidates: process_paths(options, candidates=candidates, error=error) def hg_hook(ui, repo, node=None, **kwargs): """Run pylama after mercurial commit.""" seen = set() paths = [] if len(repo): for rev in range(repo[node], len(repo)): for file_ in repo[rev].files(): file_ = op.join(repo.root, file_) if file_ in seen or not op.exists(file_): continue seen.add(file_) paths.append(file_) options = parse_options() setup_logger(options) if paths: process_paths(options, candidates=paths) def install_git(path): """Install hook in Git repository.""" hook = op.join(path, 'pre-commit') with open(hook, 'w') as fd: fd.write("""#!/usr/bin/env python import sys from pylama.hook import git_hook if __name__ == '__main__': sys.exit(git_hook()) """) chmod(hook, 484) def install_hg(path): """ Install hook in Mercurial repository. """ hook = op.join(path, 'hgrc') if not op.isfile(hook): open(hook, 'w+').close() c = ConfigParser() c.readfp(open(hook, 'r')) if not c.has_section('hooks'): c.add_section('hooks') if not c.has_option('hooks', 'commit'): c.set('hooks', 'commit', 'python:pylama.hooks.hg_hook') if not c.has_option('hooks', 'qrefresh'): c.set('hooks', 'qrefresh', 'python:pylama.hooks.hg_hook') c.write(open(hook, 'w+')) def install_hook(path): """ Auto definition of SCM and hook installation. """ git = op.join(path, '.git', 'hooks') hg = op.join(path, '.hg') if op.exists(git): install_git(git) LOGGER.warn('Git hook has been installed.') elif op.exists(hg): install_hg(hg) LOGGER.warn('Mercurial hook has been installed.') else: LOGGER.error('VCS has not found. Check your path.') sys.exit(1) # pylama:ignore=F0401,E1103,D210,F0001 pylama-7.4.3/pylama/libs/0000755000076500000240000000000013156240551015302 5ustar klenstaff00000000000000pylama-7.4.3/pylama/libs/__init__.py0000644000076500000240000000002612660640733017416 0ustar klenstaff00000000000000""" Support libs. """ pylama-7.4.3/pylama/libs/importlib.py0000644000076500000240000000245712660640733017672 0ustar klenstaff00000000000000"""Backport of importlib.import_module from 3.x.""" # While not critical (and in no way guaranteed!), it would be nice to keep this # code compatible with Python 2.3. import sys def _resolve_name(name, package, level): """Return the absolute name of the module to be imported.""" if not hasattr(package, 'rindex'): raise ValueError("'package' not set to a string") dot = len(package) for x in xrange(level, 1, -1): try: dot = package.rindex('.', 0, dot) except ValueError: raise ValueError("attempted relative import beyond top-level " "package") return "%s.%s" % (package[:dot], name) def import_module(name, package=None): """Import a module. The 'package' argument is required when performing a relative import. It specifies the package to use as the anchor point from which to resolve the relative import to an absolute import. """ if name.startswith('.'): if not package: raise TypeError("relative imports require the 'package' argument") level = 0 for character in name: if character != '.': break level += 1 name = _resolve_name(name[level:], package, level) __import__(name) return sys.modules[name] pylama-7.4.3/pylama/libs/inirama.py0000644000076500000240000002416113124226547017304 0ustar klenstaff00000000000000""" Inirama is a python module that parses INI files. .. _badges: .. include:: ../README.rst :start-after: .. _badges: :end-before: .. _contents: .. _description: .. include:: ../README.rst :start-after: .. _description: :end-before: .. _badges: :copyright: 2013 by Kirill Klenov. :license: BSD, see LICENSE for more details. """ from __future__ import unicode_literals, print_function __version__ = "0.8.0" __project__ = "Inirama" __author__ = "Kirill Klenov " __license__ = "BSD" import io import re import logging try: from collections import OrderedDict except ImportError: from UserDict import DictMixin class OrderedDict(dict, DictMixin): null = object() def __init__(self, *args, **kwargs): self.clear() self.update(*args, **kwargs) def clear(self): self.__map = dict() self.__order = list() dict.clear(self) def __setitem__(self, key, value): if key not in self: self.__map[key] = len(self.__order) self.__order.append(key) dict.__setitem__(self, key, value) def __delitem__(self, key): dict.__delitem__(self, key) self.__map.pop(key) self.__order = self.null def __iter__(self): for key in self.__order: if key is not self.null: yield key def keys(self): return list(self) setdefault = DictMixin.setdefault update = DictMixin.update pop = DictMixin.pop values = DictMixin.values items = DictMixin.items iterkeys = DictMixin.iterkeys itervalues = DictMixin.itervalues iteritems = DictMixin.iteritems NS_LOGGER = logging.getLogger('inirama') class Scanner(object): """ Split a code string on tokens. """ def __init__(self, source, ignore=None, patterns=None): """ Init Scanner instance. :param patterns: List of token patterns [(token, regexp)] :param ignore: List of ignored tokens """ self.reset(source) if patterns: self.patterns = [] for k, r in patterns: self.patterns.append((k, re.compile(r))) if ignore: self.ignore = ignore def reset(self, source): """ Reset scanner's state. :param source: Source for parsing """ self.tokens = [] self.source = source self.pos = 0 def scan(self): """ Scan source and grab tokens. """ self.pre_scan() token = None end = len(self.source) while self.pos < end: best_pat = None best_pat_len = 0 # Check patterns for p, regexp in self.patterns: m = regexp.match(self.source, self.pos) if m: best_pat = p best_pat_len = len(m.group(0)) break if best_pat is None: raise SyntaxError( "SyntaxError[@char {0}: {1}]".format( self.pos, "Bad token.")) # Ignore patterns if best_pat in self.ignore: self.pos += best_pat_len continue # Create token token = ( best_pat, self.source[self.pos:self.pos + best_pat_len], self.pos, self.pos + best_pat_len, ) self.pos = token[-1] self.tokens.append(token) def pre_scan(self): """ Prepare source. """ pass def __repr__(self): """ Print the last 5 tokens that have been scanned in. :return str: """ return '" class INIScanner(Scanner): """ Get tokens for INI. """ patterns = [ ('SECTION', re.compile(r'\[[^]]+\]')), ('IGNORE', re.compile(r'[ \r\t\n]+')), ('COMMENT', re.compile(r'[;#].*')), ('KEY_VALUE', re.compile(r'[^=\s]+\s*[:=].*')), ('CONTINUATION', re.compile(r'.*')) ] ignore = ['IGNORE'] def pre_scan(self): """ Prepare string for scanning. """ escape_re = re.compile(r'\\\n[\t ]+') self.source = escape_re.sub('', self.source) undefined = object() class Section(OrderedDict): """ Representation of INI section. """ def __init__(self, namespace, *args, **kwargs): super(Section, self).__init__(*args, **kwargs) self.namespace = namespace def __setitem__(self, name, value): value = str(value) if value.isdigit(): value = int(value) super(Section, self).__setitem__(name, value) class InterpolationSection(Section): """ INI section with interpolation support. """ var_re = re.compile('{([^}]+)}') def get(self, name, default=None): """ Get item by name. :return object: value or None if name not exists """ if name in self: return self[name] return default def __interpolate__(self, math): try: key = math.group(1).strip() return self.namespace.default.get(key) or self[key] except KeyError: return '' def __getitem__(self, name, raw=False): value = super(InterpolationSection, self).__getitem__(name) if not raw: sample = undefined while sample != value: try: sample, value = value, self.var_re.sub( self.__interpolate__, value) except RuntimeError: message = "Interpolation failed: {0}".format(name) NS_LOGGER.error(message) raise ValueError(message) return value def iteritems(self, raw=False): """ Iterate self items. """ for key in self: yield key, self.__getitem__(key, raw=raw) items = iteritems class Namespace(object): """ Default class for parsing INI. :param **default_items: Default items for default section. Usage ----- :: from inirama import Namespace ns = Namespace() ns.read('config.ini') print ns['section']['key'] ns['other']['new'] = 'value' ns.write('new_config.ini') """ #: Name of default section (:attr:`~inirama.Namespace.default`) default_section = 'DEFAULT' #: Dont raise any exception on file reading errors silent_read = True #: Class for generating sections section_type = Section def __init__(self, **default_items): self.sections = OrderedDict() for k, v in default_items.items(): self[self.default_section][k] = v @property def default(self): """ Return default section or empty dict. :return :class:`inirama.Section`: section """ return self.sections.get(self.default_section, dict()) def read(self, *files, **params): """ Read and parse INI files. :param *files: Files for reading :param **params: Params for parsing Set `update=False` for prevent values redefinition. """ for f in files: try: with io.open(f, encoding='utf-8') as ff: NS_LOGGER.info('Read from `{0}`'.format(ff.name)) self.parse(ff.read(), **params) except (IOError, TypeError, SyntaxError, io.UnsupportedOperation): if not self.silent_read: NS_LOGGER.error('Reading error `{0}`'.format(ff.name)) raise def write(self, f): """ Write namespace as INI file. :param f: File object or path to file. """ if isinstance(f, str): f = io.open(f, 'w', encoding='utf-8') if not hasattr(f, 'read'): raise AttributeError("Wrong type of file: {0}".format(type(f))) NS_LOGGER.info('Write to `{0}`'.format(f.name)) for section in self.sections.keys(): f.write('[{0}]\n'.format(section)) for k, v in self[section].items(): f.write('{0:15}= {1}\n'.format(k, v)) f.write('\n') f.close() def parse(self, source, update=True, **params): """ Parse INI source as string. :param source: Source of INI :param update: Replace already defined items """ scanner = INIScanner(source) scanner.scan() section = self.default_section name = None for token in scanner.tokens: if token[0] == 'KEY_VALUE': name, value = re.split('[=:]', token[1], 1) name, value = name.strip(), value.strip() if not update and name in self[section]: continue self[section][name] = value elif token[0] == 'SECTION': section = token[1].strip('[]') elif token[0] == 'CONTINUATION': if not name: raise SyntaxError( "SyntaxError[@char {0}: {1}]".format( token[2], "Bad continuation.")) self[section][name] += '\n' + token[1].strip() def __getitem__(self, name): """ Look name in self sections. :return :class:`inirama.Section`: section """ if name not in self.sections: self.sections[name] = self.section_type(self) return self.sections[name] def __contains__(self, name): return name in self.sections def __repr__(self): return "".format(self.sections) class InterpolationNamespace(Namespace): """ That implements the interpolation feature. :: from inirama import InterpolationNamespace ns = InterpolationNamespace() ns.parse(''' [main] test = value foo = bar {test} more_deep = wow {foo} ''') print ns['main']['more_deep'] # wow bar value """ section_type = InterpolationSection # pylama:ignore=D,W02,E731,W0621 pylama-7.4.3/pylama/lint/0000755000076500000240000000000013156240551015317 5ustar klenstaff00000000000000pylama-7.4.3/pylama/lint/__init__.py0000644000076500000240000000060512660640733017436 0ustar klenstaff00000000000000"""Custom module loader.""" class Linter(object): """Abstract class for linter plugin.""" @staticmethod def allow(path): """Check path is relevant for linter. :return bool: """ return path.endswith('.py') @staticmethod def run(path, **meta): """Method 'run' should be defined.""" raise NotImplementedError(__doc__) pylama-7.4.3/pylama/lint/extensions.py0000644000076500000240000000175713017243012020071 0ustar klenstaff00000000000000"""Load extensions.""" LINTERS = {} try: from pylama.lint.pylama_mccabe import Linter LINTERS['mccabe'] = Linter() except ImportError: pass try: from pylama.lint.pylama_pydocstyle import Linter LINTERS['pep257'] = Linter() # for compatibility LINTERS['pydocstyle'] = Linter() except ImportError: pass try: from pylama.lint.pylama_pycodestyle import Linter LINTERS['pep8'] = Linter() # for compability LINTERS['pycodestyle'] = Linter() except ImportError: pass try: from pylama.lint.pylama_pyflakes import Linter LINTERS['pyflakes'] = Linter() except ImportError: pass try: from pylama.lint.pylama_radon import Linter LINTERS['radon'] = Linter() except ImportError: pass from pkg_resources import iter_entry_points for entry in iter_entry_points('pylama.linter'): if entry.name not in LINTERS: try: LINTERS[entry.name] = entry.load()() except ImportError: pass # pylama:ignore=E0611 pylama-7.4.3/pylama/lint/pylama_mccabe.py0000644000076500000240000000126012763745576020471 0ustar klenstaff00000000000000"""Code complexity checking.""" from mccabe import McCabeChecker from pylama.lint import Linter as Abstract import ast class Linter(Abstract): """Run complexity checking.""" @staticmethod def run(path, code=None, params=None, **meta): """MCCabe code checking. :return list: List of errors. """ tree = compile(code, path, "exec", ast.PyCF_ONLY_AST) McCabeChecker.max_complexity = int(params.get('complexity', 10)) return [ {'lnum': lineno, 'offset': offset, 'text': text, 'type': McCabeChecker._code} for lineno, offset, text, _ in McCabeChecker(tree, path).run() ] # pylama:ignore=W0212 pylama-7.4.3/pylama/lint/pylama_pycodestyle.py0000644000076500000240000000351213124225704021577 0ustar klenstaff00000000000000"""pycodestyle support.""" from pycodestyle import BaseReport, StyleGuide, get_parser from pylama.lint import Linter as Abstract try: from StringIO import StringIO except ImportError: from io import StringIO class Linter(Abstract): """pycodestyle runner.""" @staticmethod def run(path, code=None, params=None, **meta): """Check code with pycodestyle. :return list: List of errors. """ parser = get_parser() for option in parser.option_list: if option.dest and option.dest in params: value = params[option.dest] if not isinstance(value, str): continue params[option.dest] = option.convert_value(option, params[option.dest]) P8Style = StyleGuide(reporter=_PycodestyleReport, **params) buf = StringIO(code) return P8Style.input_file(path, lines=buf.readlines()) class _PycodestyleReport(BaseReport): def __init__(self, *args, **kwargs): super(_PycodestyleReport, self).__init__(*args, **kwargs) self.errors = [] def init_file(self, filename, lines, expected, line_offset): """Prepare storage for errors.""" super(_PycodestyleReport, self).init_file( filename, lines, expected, line_offset) self.errors = [] def error(self, line_number, offset, text, check): """Save errors.""" code = super(_PycodestyleReport, self).error( line_number, offset, text, check) if code: self.errors.append(dict( text=text, type=code.replace('E', 'C'), col=offset + 1, lnum=line_number, )) def get_file_results(self): """Get errors. :return list: List of errors. """ return self.errors pylama-7.4.3/pylama/lint/pylama_pydocstyle.py0000644000076500000240000000223013156236577021445 0ustar klenstaff00000000000000"""pydocstyle support.""" THIRD_ARG = True try: #: Import for pydocstyle 2.0.0 and newer from pydocstyle import ConventionChecker as PyDocChecker except ImportError: #: Backward compatibility for pydocstyle prior to 2.0.0 from pydocstyle import PEP257Checker as PyDocChecker THIRD_ARG = False from pylama.lint import Linter as Abstract class Linter(Abstract): """Check pydocstyle errors.""" @staticmethod def run(path, code=None, params=None, **meta): """pydocstyle code checking. :return list: List of errors. """ if 'ignore_decorators' in params: ignore_decorators = params['ignore_decorators'] else: ignore_decorators = None check_source_args = (code, path, ignore_decorators) if THIRD_ARG else (code, path) return [{ 'lnum': e.line, # Remove colon after error code ("D403: ..." => "D403 ..."). 'text': (e.message[0:4] + e.message[5:] if e.message[4] == ':' else e.message), 'type': 'D', 'number': e.code } for e in PyDocChecker().check_source(*check_source_args)] pylama-7.4.3/pylama/lint/pylama_pyflakes.py0000644000076500000240000000431213017243051021044 0ustar klenstaff00000000000000"""Pyflakes support.""" from pyflakes import checker from pylama.lint import Linter as Abstract checker.messages.UnusedImport.message = "W0611 %r imported but unused" checker.messages.RedefinedWhileUnused.message = "W0404 redefinition of unused %r from line %r" checker.messages.RedefinedInListComp.message = "W0621 list comprehension redefines %r from line %r" checker.messages.ImportShadowedByLoopVar.message = "W0621 import %r from line %r shadowed by loop variable" checker.messages.ImportStarUsed.message = "W0401 'from %s import *' used; unable to detect undefined names" checker.messages.ImportStarUsage.message = "W0401 '%s may be undefined, or defined from star imports: %s'" checker.messages.UndefinedName.message = "E0602 undefined name %r" checker.messages.DoctestSyntaxError.message = "W0511 syntax error in doctest" checker.messages.UndefinedExport.message = "E0603 undefined name %r in __all__" checker.messages.UndefinedLocal.message = "E0602 local variable %r (defined in enclosing scope on line %r) referenced before assignment" checker.messages.DuplicateArgument.message = "E1122 duplicate argument %r in function definition" checker.messages.LateFutureImport.message = "W0410 future import(s) %r after other statements" checker.messages.UnusedVariable.message = "W0612 local variable %r is assigned to but never used" checker.messages.ReturnWithArgsInsideGenerator.message = "E0106 'return' with argument inside generator" checker.messages.ReturnOutsideFunction.message = "E0104 'return' outside function" class Linter(Abstract): """Pyflakes runner.""" @staticmethod def run(path, code=None, params=None, **meta): """Check code with pyflakes. :return list: List of errors. """ import _ast builtins = params.get("builtins", "") if builtins: builtins = builtins.split(",") tree = compile(code, path, "exec", _ast.PyCF_ONLY_AST) w = checker.Checker(tree, path, builtins=builtins) w.messages = sorted(w.messages, key=lambda m: m.lineno) return [{ 'lnum': m.lineno, 'text': m.message % m.message_args, 'type': m.message[0] } for m in w.messages] # pylama:ignore=E501,C0301 pylama-7.4.3/pylama/lint/pylama_radon.py0000644000076500000240000000172613003711765020346 0ustar klenstaff00000000000000from radon.visitors import ComplexityVisitor from radon.complexity import add_inner_blocks from pylama.lint import Linter as Abstract class Linter(Abstract): """Radon runner.""" @staticmethod def run(path, code=None, params=None, ignore=None, select=None, **meta): """Check code with Radon. :return list: List of errors. """ complexity = params.get('complexity', 10) no_assert = params.get('no_assert', False) show_closures = params.get('show_closures', False) visitor = ComplexityVisitor.from_code(code, no_assert=no_assert) blocks = visitor.blocks if show_closures: blocks = add_inner_blocks(blocks) return [ {'lnum': block.lineno, 'col': block.col_offset, 'type': 'R', 'number': 'R709', 'text': 'R701: %s is too complex %d' % (block.name, block.complexity)} for block in visitor.blocks if block.complexity > complexity ] pylama-7.4.3/pylama/main.py0000644000076500000240000000522613003712360015645 0ustar klenstaff00000000000000"""Pylama's shell support.""" from __future__ import absolute_import, with_statement import sys from os import walk, path as op from .config import parse_options, CURDIR, setup_logger from .core import LOGGER, run from .async import check_async def check_path(options, rootdir=None, candidates=None, code=None): """Check path. :param rootdir: Root directory (for making relative file paths) :param options: Parsed pylama options (from pylama.config.parse_options) :returns: (list) Errors list """ if not candidates: candidates = [] for path_ in options.paths: path = op.abspath(path_) if op.isdir(path): for root, _, files in walk(path): candidates += [op.relpath(op.join(root, f), CURDIR) for f in files] else: candidates.append(path) if rootdir is None: rootdir = path if op.isdir(path) else op.dirname(path) paths = [] for path in candidates: if not options.force and not any(l.allow(path) for _, l in options.linters): continue if not op.exists(path): continue paths.append(path) if options.async: return check_async(paths, options, rootdir) errors = [] for path in paths: errors += run(path=path, code=code, rootdir=rootdir, options=options) return errors def shell(args=None, error=True): """Endpoint for console. Parse a command arguments, configuration files and run a checkers. :return list: list of errors :raise SystemExit: """ if args is None: args = sys.argv[1:] options = parse_options(args) setup_logger(options) LOGGER.info(options) # Install VSC hook if options.hook: from .hook import install_hook for path in options.paths: return install_hook(path) return process_paths(options, error=error) def process_paths(options, candidates=None, error=True): """Process files and log errors.""" errors = check_path(options, rootdir=CURDIR, candidates=candidates) if options.format in ['pycodestyle', 'pep8']: pattern = "%(filename)s:%(lnum)s:%(col)s: %(text)s" elif options.format == 'pylint': pattern = "%(filename)s:%(lnum)s: [%(type)s] %(text)s" else: # 'parsable' pattern = "%(filename)s:%(lnum)s:%(col)s: [%(type)s] %(text)s" for er in errors: if options.abspath: er._info['filename'] = op.abspath(er.filename) LOGGER.warning(pattern, er._info) if error: sys.exit(int(bool(errors))) return errors if __name__ == '__main__': shell() # pylama:ignore=F0001 pylama-7.4.3/pylama/pytest.py0000644000076500000240000000537013156236577016275 0ustar klenstaff00000000000000""" py.test plugin for checking files with pylama. """ from __future__ import absolute_import from os import path as op import py # noqa import pytest HISTKEY = "pylama/mtimes" def pytest_load_initial_conftests(early_config, parser, args): # Marks have to be registered before usage # to not fail with --strict command line argument early_config.addinivalue_line( 'markers', 'pycodestyle: Mark test as using pylama code audit tool.') def pytest_addoption(parser): group = parser.getgroup("general") group.addoption( '--pylama', action='store_true', help="perform some pylama code checks on .py files") def pytest_sessionstart(session): config = session.config if config.option.pylama and getattr(config, 'cache', None): config._pylamamtimes = config.cache.get(HISTKEY, {}) def pytest_sessionfinish(session): config = session.config if hasattr(config, "_pylamamtimes"): config.cache.set(HISTKEY, config._pylamamtimes) def pytest_collect_file(path, parent): config = parent.config if config.option.pylama and path.ext == '.py': return PylamaItem(path, parent) class PylamaError(Exception): """ indicates an error during pylama checks. """ class PylamaItem(pytest.Item, pytest.File): def __init__(self, path, parent): super(PylamaItem, self).__init__(path, parent) self.add_marker("pycodestyle") self.cache = None self._pylamamtimes = None def setup(self): if not getattr(self.config, 'cache', None): return False self.cache = True self._pylamamtimes = self.fspath.mtime() pylamamtimes = self.config._pylamamtimes old = pylamamtimes.get(str(self.fspath), 0) if old == self._pylamamtimes: pytest.skip("file(s) previously passed Pylama checks") def runtest(self): errors = check_file(self.fspath) if errors: pattern = "%(filename)s:%(lnum)s:%(col)s: %(text)s" out = "\n".join([pattern % e._info for e in errors]) raise PylamaError(out) # update mtime only if test passed # otherwise failures would not be re-run next time if self.cache: self.config._pylamamtimes[str(self.fspath)] = self._pylamamtimes def repr_failure(self, excinfo): if excinfo.errisinstance(PylamaError): return excinfo.value.args[0] return super(PylamaItem, self).repr_failure(excinfo) def check_file(path): from pylama.main import parse_options, process_paths from pylama.config import CURDIR options = parse_options() path = op.relpath(str(path), CURDIR) return process_paths(options, candidates=[path], error=False) # pylama:ignore=D,E1002,W0212,F0001 pylama-7.4.3/pylama.egg-info/0000755000076500000240000000000013156240551016043 5ustar klenstaff00000000000000pylama-7.4.3/pylama.egg-info/dependency_links.txt0000644000076500000240000000000113156240551022111 0ustar klenstaff00000000000000 pylama-7.4.3/pylama.egg-info/entry_points.txt0000644000076500000240000000012113156240551021333 0ustar klenstaff00000000000000[console_scripts] pylama = pylama.main:shell [pytest11] pylama = pylama.pytest pylama-7.4.3/pylama.egg-info/not-zip-safe0000644000076500000240000000000112660643077020302 0ustar klenstaff00000000000000 pylama-7.4.3/pylama.egg-info/PKG-INFO0000644000076500000240000003275413156240551017153 0ustar klenstaff00000000000000Metadata-Version: 1.1 Name: pylama Version: 7.4.3 Summary: pylama -- Code audit tool for python Home-page: https://github.com/klen/pylama Author: Kirill Klenov Author-email: horneds@gmail.com License: GNU LGPL Description: |logo| Pylama ############# .. _description: Code audit tool for Python and JavaScript. Pylama wraps these tools: * pycodestyle_ (formerly pep8) © 2012-2013, Florent Xicluna; * pydocstyle_ (formerly pep257 by Vladimir Keleshev) © 2014, Amir Rachum; * PyFlakes_ © 2005-2013, Kevin Watters; * Mccabe_ © Ned Batchelder; * Pylint_ © 2013, Logilab (should be installed 'pylama_pylint' module); * Radon_ © Michele Lacchia * gjslint_ © The Closure Linter Authors (should be installed 'pylama_gjslint' module); .. _badges: .. image:: http://img.shields.io/travis/klen/pylama.svg?style=flat-square :target: http://travis-ci.org/klen/pylama :alt: Build Status .. image:: http://img.shields.io/coveralls/klen/pylama.svg?style=flat-square :target: https://coveralls.io/r/klen/pylama :alt: Coverals .. image:: http://img.shields.io/pypi/v/pylama.svg?style=flat-square :target: https://crate.io/packages/pylama :alt: Version .. image:: http://img.shields.io/gratipay/klen.svg?style=flat-square :target: https://www.gratipay.com/klen/ :alt: Donate .. _documentation: Docs are available at https://pylama.readthedocs.org/. Pull requests with documentation enhancements and/or fixes are awesome and most welcome. .. _contents: .. contents:: .. _requirements: Requirements: ============= - Python (2.7, 3.2, 3.3) - To use JavaScript checker (``gjslint``) you need to install ``python-gflags`` with ``pip install python-gflags``. - If your tests are failing on Win platform you are missing: ``curses`` - http://www.lfd.uci.edu/~gohlke/pythonlibs/ (The curses library supplies a terminal-independent screen-painting and keyboard-handling facility for text-based terminals) .. _installation: Installation: ============= **Pylama** could be installed using pip: :: :: $ pip install pylama .. _quickstart: Quickstart ========== **Pylama** is easy to use and really fun for checking code quality. Just run `pylama` and get common output from all pylama plugins (pycodestyle_, PyFlakes_ and etc) Recursive check the current directory. :: $ pylama Recursive check a path. :: $ pylama Ignore errors :: $ pylama -i W,E501 .. note:: You could choose a group erros `D`,`E1` and etc or special errors `C0312` Choose code checkers :: $ pylama -l "pycodestyle,mccabe" Choose code checkers for JavaScript:: $ pylama --linters=gjslint --ignore=E:0010 .. _options: Set Pylama (checkers) options ============================= Command line options -------------------- :: $ pylama --help usage: pylama [-h] [--verbose] [--version] [--format {pycodestyle,pylint}] [--select SELECT] [--sort SORT] [--linters LINTERS] [--ignore IGNORE] [--skip SKIP] [--report REPORT] [--hook] [--async] [--options OPTIONS] [--force] [--abspath] [paths [paths ...]] Code audit tool for python. positional arguments: paths Paths to files or directories for code check. optional arguments: -h, --help show this help message and exit --verbose, -v Verbose mode. --version show program's version number and exit --format {pycodestyle,pylint}, -f {pycodestyle,pylint} Choose errors format (pycodestyle, pylint). --select SELECT, -s SELECT Select errors and warnings. (comma-separated list) --sort SORT Sort result by error types. Ex. E,W,D --linters LINTERS, -l LINTERS Select linters. (comma-separated). Choices are mccabe,pycodestyle,pyflakes,pydocstyle. --ignore IGNORE, -i IGNORE Ignore errors and warnings. (comma-separated) --skip SKIP Skip files by masks (comma-separated, Ex. */messages.py) --report REPORT, -r REPORT Send report to file [REPORT] --hook Install Git (Mercurial) hook. --async Enable async mode. Useful for checking a lot of files. Not supported by pylint. --options FILE, -o FILE Specify configuration file. Looks for pylama.ini, setup.cfg, tox.ini, or pytest.ini in the current directory. --force, -F Force code checking (if linter doesnt allow) --abspath, -a Use absolute paths in output. .. _modeline: File modelines -------------- You can set options for **Pylama** inside a source file. Use pylama *modeline* for this. Format: :: # pylama:{name1}={value1}:{name2}={value2}:... :: .. Somethere in code # pylama:ignore=W:select=W301 Disable code checking for current file: :: .. Somethere in code # pylama:skip=1 Those options have a higher priority. .. _skiplines: Skip lines (noqa) ----------------- Just add `# noqa` in end of line to ignore. :: def urgent_fuction(): unused_var = 'No errors here' # noqa .. _config: Configuration file ------------------ **Pylama** looks for a configuration file in the current directory. The program searches for the first matching ini-style configuration file in the directories of command line argument. Pylama looks for the configuration in this order: :: pylama.ini setup.cfg tox.ini pytest.ini The "--option" / "-o" argument can be used to specify a configuration file. Pylama searches for sections whose names start with `pylama`. The "pylama" section configures global options like `linters` and `skip`. :: [pylama] format = pylint skip = */.tox/*,*/.env/* linters = pylint,mccabe ignore = F0401,C0111,E731 Set Code-checkers' options -------------------------- You could set options for special code checker with pylama configurations. :: [pylama:pyflakes] builtins = _ [pylama:pycodestyle] max_line_length = 100 [pylama:pylint] max_line_length = 100 disable = R See code-checkers' documentation for more info. Set options for file (group of files) ------------------------------------- You could set options for special file (group of files) with sections: The options have a higher priority than in the `pylama` section. :: [pylama:*/pylama/main.py] ignore = C901,R0914,W0212 select = R [pylama:*/tests.py] ignore = C0110 [pylama:*/setup.py] skip = 1 Pytest integration ================== Pylama has Pytest_ support. The package automatically registers itself as a pytest plugin during installation. Pylama also supports `pytest_cache` plugin. Check files with pylama :: pytest --pylama ... Recommended way to set pylama options when using pytest — configuration files (see below). Writing a linter ================ You can write a custom extension for Pylama. Custom linter should be a python module. Name should be like 'pylama_'. In 'setup.py', 'pylama.linter' entry point should be defined. :: setup( # ... entry_points={ 'pylama.linter': ['lintername = pylama_lintername.main:Linter'], } # ... ) 'Linter' should be instance of 'pylama.lint.Linter' class. Must implement two methods: 'allow' takes a path and returns true if linter can check this file for errors. 'run' takes a path and meta keywords params and returns a list of errors. Example: -------- Just a virtual 'WOW' checker. setup.py: :: setup( name='pylama_wow', install_requires=[ 'setuptools' ], entry_points={ 'pylama.linter': ['wow = pylama_wow.main:Linter'], } # ... ) pylama_wow.py: :: from pylama.lint import Linter as BaseLinter class Linter(BaseLinter): def allow(self, path): return 'wow' in path def run(self, path, **meta): with open(path) as f: if 'wow' in f.read(): return [{ lnum: 0, col: 0, text: 'Wow has been finded.', type: 'WOW' }] Run pylama from python code --------------------------- :: from pylama.main import check_path, parse_options my_redefined_options = {...} my_path = '...' options = parse_options([my_path], **my_redefined_options) errors = check_path(options) .. _bagtracker: Bug tracker ----------- If you have any suggestions, bug reports or annoyances please report them to the issue tracker at https://github.com/klen/pylama/issues .. _contributing: Contributing ------------ Development of `pylama` happens at GitHub: https://github.com/klen/pylama .. _contributors: Contributors ^^^^^^^^^^^^ See AUTHORS_. .. _license: License ------- Licensed under a `BSD license`_. .. _links: .. _AUTHORS: https://github.com/klen/pylama/blob/develop/AUTHORS .. _BSD license: http://www.linfo.org/bsdlicense.html .. _Mccabe: http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html .. _pydocstyle: https://github.com/PyCQA/pydocstyle/ .. _pycodestyle: https://github.com/PyCQA/pycodestyle .. _PyFlakes: https://github.com/pyflakes/pyflakes .. _Pylint: http://pylint.org .. _Pytest: http://pytest.org .. _gjslint: https://developers.google.com/closure/utilities .. _klen: http://klen.github.io/ .. _Radon: https://github.com/rubik/radon .. |logo| image:: https://raw.github.com/klen/pylama/develop/docs/_static/logo.png :width: 100 Keywords: pylint,pep8,pycodestyle,pyflakes,mccabe,linter,qa,pep257,pydocstyle Platform: Any Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Quality Assurance Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Natural Language :: English Classifier: Natural Language :: Russian Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Code Generators pylama-7.4.3/pylama.egg-info/requires.txt0000644000076500000240000000012413156240551020440 0ustar klenstaff00000000000000mccabe >= 0.5.2 pycodestyle >= 2.3.1 pydocstyle >= 2.0.0 pyflakes >= 1.5.0 pylama-7.4.3/pylama.egg-info/SOURCES.txt0000644000076500000240000000132213156240551017725 0ustar klenstaff00000000000000AUTHORS Changelog LICENSE MANIFEST.in README.rst requirements.txt setup.cfg setup.py pylama/__init__.py pylama/__main__.py pylama/async.py pylama/config.py pylama/core.py pylama/errors.py pylama/hook.py pylama/main.py pylama/pytest.py pylama.egg-info/PKG-INFO pylama.egg-info/SOURCES.txt pylama.egg-info/dependency_links.txt pylama.egg-info/entry_points.txt pylama.egg-info/not-zip-safe pylama.egg-info/requires.txt pylama.egg-info/top_level.txt pylama/libs/__init__.py pylama/libs/importlib.py pylama/libs/inirama.py pylama/lint/__init__.py pylama/lint/extensions.py pylama/lint/pylama_mccabe.py pylama/lint/pylama_pycodestyle.py pylama/lint/pylama_pydocstyle.py pylama/lint/pylama_pyflakes.py pylama/lint/pylama_radon.pypylama-7.4.3/pylama.egg-info/top_level.txt0000644000076500000240000000000713156240551020572 0ustar klenstaff00000000000000pylama pylama-7.4.3/README.rst0000644000076500000240000002275213156236577014602 0ustar klenstaff00000000000000|logo| Pylama ############# .. _description: Code audit tool for Python and JavaScript. Pylama wraps these tools: * pycodestyle_ (formerly pep8) © 2012-2013, Florent Xicluna; * pydocstyle_ (formerly pep257 by Vladimir Keleshev) © 2014, Amir Rachum; * PyFlakes_ © 2005-2013, Kevin Watters; * Mccabe_ © Ned Batchelder; * Pylint_ © 2013, Logilab (should be installed 'pylama_pylint' module); * Radon_ © Michele Lacchia * gjslint_ © The Closure Linter Authors (should be installed 'pylama_gjslint' module); .. _badges: .. image:: http://img.shields.io/travis/klen/pylama.svg?style=flat-square :target: http://travis-ci.org/klen/pylama :alt: Build Status .. image:: http://img.shields.io/coveralls/klen/pylama.svg?style=flat-square :target: https://coveralls.io/r/klen/pylama :alt: Coverals .. image:: http://img.shields.io/pypi/v/pylama.svg?style=flat-square :target: https://crate.io/packages/pylama :alt: Version .. image:: http://img.shields.io/gratipay/klen.svg?style=flat-square :target: https://www.gratipay.com/klen/ :alt: Donate .. _documentation: Docs are available at https://pylama.readthedocs.org/. Pull requests with documentation enhancements and/or fixes are awesome and most welcome. .. _contents: .. contents:: .. _requirements: Requirements: ============= - Python (2.7, 3.2, 3.3) - To use JavaScript checker (``gjslint``) you need to install ``python-gflags`` with ``pip install python-gflags``. - If your tests are failing on Win platform you are missing: ``curses`` - http://www.lfd.uci.edu/~gohlke/pythonlibs/ (The curses library supplies a terminal-independent screen-painting and keyboard-handling facility for text-based terminals) .. _installation: Installation: ============= **Pylama** could be installed using pip: :: :: $ pip install pylama .. _quickstart: Quickstart ========== **Pylama** is easy to use and really fun for checking code quality. Just run `pylama` and get common output from all pylama plugins (pycodestyle_, PyFlakes_ and etc) Recursive check the current directory. :: $ pylama Recursive check a path. :: $ pylama Ignore errors :: $ pylama -i W,E501 .. note:: You could choose a group erros `D`,`E1` and etc or special errors `C0312` Choose code checkers :: $ pylama -l "pycodestyle,mccabe" Choose code checkers for JavaScript:: $ pylama --linters=gjslint --ignore=E:0010 .. _options: Set Pylama (checkers) options ============================= Command line options -------------------- :: $ pylama --help usage: pylama [-h] [--verbose] [--version] [--format {pycodestyle,pylint}] [--select SELECT] [--sort SORT] [--linters LINTERS] [--ignore IGNORE] [--skip SKIP] [--report REPORT] [--hook] [--async] [--options OPTIONS] [--force] [--abspath] [paths [paths ...]] Code audit tool for python. positional arguments: paths Paths to files or directories for code check. optional arguments: -h, --help show this help message and exit --verbose, -v Verbose mode. --version show program's version number and exit --format {pycodestyle,pylint}, -f {pycodestyle,pylint} Choose errors format (pycodestyle, pylint). --select SELECT, -s SELECT Select errors and warnings. (comma-separated list) --sort SORT Sort result by error types. Ex. E,W,D --linters LINTERS, -l LINTERS Select linters. (comma-separated). Choices are mccabe,pycodestyle,pyflakes,pydocstyle. --ignore IGNORE, -i IGNORE Ignore errors and warnings. (comma-separated) --skip SKIP Skip files by masks (comma-separated, Ex. */messages.py) --report REPORT, -r REPORT Send report to file [REPORT] --hook Install Git (Mercurial) hook. --async Enable async mode. Useful for checking a lot of files. Not supported by pylint. --options FILE, -o FILE Specify configuration file. Looks for pylama.ini, setup.cfg, tox.ini, or pytest.ini in the current directory. --force, -F Force code checking (if linter doesnt allow) --abspath, -a Use absolute paths in output. .. _modeline: File modelines -------------- You can set options for **Pylama** inside a source file. Use pylama *modeline* for this. Format: :: # pylama:{name1}={value1}:{name2}={value2}:... :: .. Somethere in code # pylama:ignore=W:select=W301 Disable code checking for current file: :: .. Somethere in code # pylama:skip=1 Those options have a higher priority. .. _skiplines: Skip lines (noqa) ----------------- Just add `# noqa` in end of line to ignore. :: def urgent_fuction(): unused_var = 'No errors here' # noqa .. _config: Configuration file ------------------ **Pylama** looks for a configuration file in the current directory. The program searches for the first matching ini-style configuration file in the directories of command line argument. Pylama looks for the configuration in this order: :: pylama.ini setup.cfg tox.ini pytest.ini The "--option" / "-o" argument can be used to specify a configuration file. Pylama searches for sections whose names start with `pylama`. The "pylama" section configures global options like `linters` and `skip`. :: [pylama] format = pylint skip = */.tox/*,*/.env/* linters = pylint,mccabe ignore = F0401,C0111,E731 Set Code-checkers' options -------------------------- You could set options for special code checker with pylama configurations. :: [pylama:pyflakes] builtins = _ [pylama:pycodestyle] max_line_length = 100 [pylama:pylint] max_line_length = 100 disable = R See code-checkers' documentation for more info. Set options for file (group of files) ------------------------------------- You could set options for special file (group of files) with sections: The options have a higher priority than in the `pylama` section. :: [pylama:*/pylama/main.py] ignore = C901,R0914,W0212 select = R [pylama:*/tests.py] ignore = C0110 [pylama:*/setup.py] skip = 1 Pytest integration ================== Pylama has Pytest_ support. The package automatically registers itself as a pytest plugin during installation. Pylama also supports `pytest_cache` plugin. Check files with pylama :: pytest --pylama ... Recommended way to set pylama options when using pytest — configuration files (see below). Writing a linter ================ You can write a custom extension for Pylama. Custom linter should be a python module. Name should be like 'pylama_'. In 'setup.py', 'pylama.linter' entry point should be defined. :: setup( # ... entry_points={ 'pylama.linter': ['lintername = pylama_lintername.main:Linter'], } # ... ) 'Linter' should be instance of 'pylama.lint.Linter' class. Must implement two methods: 'allow' takes a path and returns true if linter can check this file for errors. 'run' takes a path and meta keywords params and returns a list of errors. Example: -------- Just a virtual 'WOW' checker. setup.py: :: setup( name='pylama_wow', install_requires=[ 'setuptools' ], entry_points={ 'pylama.linter': ['wow = pylama_wow.main:Linter'], } # ... ) pylama_wow.py: :: from pylama.lint import Linter as BaseLinter class Linter(BaseLinter): def allow(self, path): return 'wow' in path def run(self, path, **meta): with open(path) as f: if 'wow' in f.read(): return [{ lnum: 0, col: 0, text: 'Wow has been finded.', type: 'WOW' }] Run pylama from python code --------------------------- :: from pylama.main import check_path, parse_options my_redefined_options = {...} my_path = '...' options = parse_options([my_path], **my_redefined_options) errors = check_path(options) .. _bagtracker: Bug tracker ----------- If you have any suggestions, bug reports or annoyances please report them to the issue tracker at https://github.com/klen/pylama/issues .. _contributing: Contributing ------------ Development of `pylama` happens at GitHub: https://github.com/klen/pylama .. _contributors: Contributors ^^^^^^^^^^^^ See AUTHORS_. .. _license: License ------- Licensed under a `BSD license`_. .. _links: .. _AUTHORS: https://github.com/klen/pylama/blob/develop/AUTHORS .. _BSD license: http://www.linfo.org/bsdlicense.html .. _Mccabe: http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html .. _pydocstyle: https://github.com/PyCQA/pydocstyle/ .. _pycodestyle: https://github.com/PyCQA/pycodestyle .. _PyFlakes: https://github.com/pyflakes/pyflakes .. _Pylint: http://pylint.org .. _Pytest: http://pytest.org .. _gjslint: https://developers.google.com/closure/utilities .. _klen: http://klen.github.io/ .. _Radon: https://github.com/rubik/radon .. |logo| image:: https://raw.github.com/klen/pylama/develop/docs/_static/logo.png :width: 100 pylama-7.4.3/requirements.txt0000644000076500000240000000020013156236127016346 0ustar klenstaff00000000000000# Test requirements mccabe >= 0.5.2 pycodestyle >= 2.3.1 pydocstyle >= 2.0.0 pyflakes >= 1.5.0 # radon >= 1.4.2 pylama-7.4.3/setup.cfg0000644000076500000240000000110013156240551014677 0ustar klenstaff00000000000000[bdist_wheel] universal = 1 [wheel] universal = 1 [pylama] async = 1 ignore = D203,D213,F0401,C0111,E731,I0011 linters = pycodestyle,pyflakes,mccabe,pydocstyle,pylint paths = pylama skip = pylama/inirama.py,pylama/libs/* verbose = 0 [pylama:pyflakes] builtins = _ [pylama:pylint] disable = R0921,E1002 max-line-length = 100 [pylama:pycodestyle] max_line_length = 100 [pylama:pylama/core.py] ignore = C901,R0914 [pylama:pylama/main.py] ignore = R0914,W0212,C901,E1103 [pylama:test_pylama.py] ignore = D,E1103 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 pylama-7.4.3/setup.py0000644000076500000240000000400413124226547014602 0ustar klenstaff00000000000000#!/usr/bin/env python """Setup pylama installation.""" import re import sys from os import path as op from setuptools import setup, find_packages _read = lambda f: open( op.join(op.dirname(__file__), f)).read() if op.exists(f) else '' _meta = _read('pylama/__init__.py') _license = re.search(r'^__license__\s*=\s*"(.*)"', _meta, re.M).group(1) _project = re.search(r'^__project__\s*=\s*"(.*)"', _meta, re.M).group(1) _version = re.search(r'^__version__\s*=\s*"(.*)"', _meta, re.M).group(1) install_requires = [ l.replace('==', '>=') for l in _read('requirements.txt').split('\n') if l and not l.startswith('#') and not l.startswith('-')] meta = dict( name=_project, version=_version, license=_license, description=_read('DESCRIPTION'), long_description=_read('README.rst'), platforms=('Any'), zip_safe=False, keywords='pylint pep8 pycodestyle pyflakes mccabe linter qa pep257 pydocstyle'.split(), author='Kirill Klenov', author_email='horneds@gmail.com', url=' https://github.com/klen/pylama', packages=find_packages(exclude=['plugins']), entry_points={ 'console_scripts': [ 'pylama = pylama.main:shell', ], 'pytest11': ['pylama = pylama.pytest'], }, classifiers=[ "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Quality Assurance", 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'Natural Language :: English', 'Natural Language :: Russian', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Programming Language :: Python', 'Topic :: Software Development :: Code Generators', ], install_requires=install_requires, ) if __name__ == "__main__": setup(**meta)