From 7b98ff3a9471c6b111f477ea06e8fc207568172a Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Sat, 6 May 2017 09:57:02 +0200 Subject: Avoid relying on inspect to list functions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allow to run python3 -m cProfile ./pycodestyle.py …. Otherwise, as cProfile looks to wrap every functions (in a way I did not studied in depth), the inspection of test function found no function, and no test were run while running under cProfile, meaning that: $ time python3 ./pycodestyle.py coefficients-148K.py [loads and loads of errors properly reported] real 0m4.712s user 0m4.684s sys 0m0.024s $ time python3 -m cProfile ./pycodestyle.py /home/mdk/Downloads/pystyle/stats/git-clones/PmagPy/PmagPy/coefficients-148K.py [no error reported as no check were found by inspection] real 0m0.447s user 0m0.436s sys 0m0.008s With this patch: $ time python3 -m cProfile ./pycodestyle.py /home/mdk/Downloads/pystyle/stats/git-clones/PmagPy/PmagPy/coefficients-148K.py [loads and loads of errors properly reported] real 0m4.889s user 0m4.852s sys 0m0.032s --- pycodestyle.py | 109 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 45 deletions(-) diff --git a/pycodestyle.py b/pycodestyle.py index 556467b..2e03301 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -141,11 +141,43 @@ DUNDER_REGEX = re.compile(r'^__([^\s]+)__ = ') COMMENT_WITH_NL = tokenize.generate_tokens(['#\n'].pop).send(None)[1] == '#\n' +_checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} + + +def _get_parameters(function): + if sys.version_info >= (3, 3): + return [parameter.name + for parameter + in inspect.signature(function).parameters.values() + if parameter.kind == parameter.POSITIONAL_OR_KEYWORD] + else: + return inspect.getargspec(function)[0] + + +def register_check(check, codes=None): + """Register a new check object.""" + def _add_check(check, kind, codes, args): + if check in _checks[kind]: + _checks[kind][check][0].extend(codes or []) + else: + _checks[kind][check] = (codes or [''], args) + if inspect.isfunction(check): + args = _get_parameters(check) + if args and args[0] in ('physical_line', 'logical_line'): + if codes is None: + codes = ERRORCODE_REGEX.findall(check.__doc__ or '') + _add_check(check, args[0], codes, args) + elif inspect.isclass(check): + if _get_parameters(check.__init__)[:2] == ['self', 'tree']: + _add_check(check, 'tree', codes, None) + return check + + ############################################################################## # Plugins (check functions) for physical lines ############################################################################## - +@register_check def tabs_or_spaces(physical_line, indent_char): r"""Never mix tabs and spaces. @@ -165,6 +197,7 @@ def tabs_or_spaces(physical_line, indent_char): return offset, "E101 indentation contains mixed spaces and tabs" +@register_check def tabs_obsolete(physical_line): r"""For new projects, spaces-only are strongly recommended over tabs. @@ -176,6 +209,7 @@ def tabs_obsolete(physical_line): return indent.index('\t'), "W191 indentation contains tabs" +@register_check def trailing_whitespace(physical_line): r"""Trailing whitespace is superfluous. @@ -197,6 +231,7 @@ def trailing_whitespace(physical_line): return 0, "W293 blank line contains whitespace" +@register_check def trailing_blank_lines(physical_line, lines, line_number, total_lines): r"""Trailing blank lines are superfluous. @@ -213,6 +248,7 @@ def trailing_blank_lines(physical_line, lines, line_number, total_lines): return len(physical_line), "W292 no newline at end of file" +@register_check def maximum_line_length(physical_line, max_line_length, multiline, noqa): r"""Limit all lines to a maximum of 79 characters. @@ -251,6 +287,7 @@ def maximum_line_length(physical_line, max_line_length, multiline, noqa): ############################################################################## +@register_check def blank_lines(logical_line, blank_lines, indent_level, line_number, blank_before, previous_logical, previous_unindented_logical_line, previous_indent_level, @@ -313,6 +350,7 @@ def blank_lines(logical_line, blank_lines, indent_level, line_number, "class or function definition, found %d" % blank_before +@register_check def extraneous_whitespace(logical_line): r"""Avoid extraneous whitespace. @@ -345,6 +383,7 @@ def extraneous_whitespace(logical_line): yield found, "%s whitespace before '%s'" % (code, char) +@register_check def whitespace_around_keywords(logical_line): r"""Avoid extraneous whitespace around keywords. @@ -368,6 +407,7 @@ def whitespace_around_keywords(logical_line): yield match.start(2), "E271 multiple spaces after keyword" +@register_check def missing_whitespace_after_import_keyword(logical_line): r"""Multiple imports in form from x import (a, b, c) should have space between import statement and parenthesised name list. @@ -385,6 +425,7 @@ def missing_whitespace_after_import_keyword(logical_line): yield pos, "E275 missing whitespace after keyword" +@register_check def missing_whitespace(logical_line): r"""Each comma, semicolon or colon should be followed by whitespace. @@ -411,6 +452,7 @@ def missing_whitespace(logical_line): yield index, "E231 missing whitespace after '%s'" % char +@register_check def indentation(logical_line, previous_logical, indent_char, indent_level, previous_indent_level): r"""Use 4 spaces per indentation level. @@ -442,6 +484,7 @@ def indentation(logical_line, previous_logical, indent_char, yield 0, tmpl % (3 + c, "unexpected indentation") +@register_check def continued_indentation(logical_line, tokens, indent_level, hang_closing, indent_char, noqa, verbose): r"""Continuation lines indentation. @@ -641,6 +684,7 @@ def continued_indentation(logical_line, tokens, indent_level, hang_closing, yield pos, "%s with same indent as next logical line" % code +@register_check def whitespace_before_parameters(logical_line, tokens): r"""Avoid extraneous whitespace. @@ -673,6 +717,7 @@ def whitespace_before_parameters(logical_line, tokens): prev_end = end +@register_check def whitespace_around_operator(logical_line): r"""Avoid extraneous whitespace around an operator. @@ -696,6 +741,7 @@ def whitespace_around_operator(logical_line): yield match.start(2), "E222 multiple spaces after operator" +@register_check def missing_whitespace_around_operator(logical_line, tokens): r"""Surround operators with a single space on either side. @@ -788,6 +834,7 @@ def missing_whitespace_around_operator(logical_line, tokens): prev_end = end +@register_check def whitespace_around_comma(logical_line): r"""Avoid extraneous whitespace after a comma or a colon. @@ -806,6 +853,7 @@ def whitespace_around_comma(logical_line): yield found, "E241 multiple spaces after '%s'" % m.group()[0] +@register_check def whitespace_around_named_parameter_equals(logical_line, tokens): r"""Don't use spaces around the '=' sign in function arguments. @@ -856,6 +904,7 @@ def whitespace_around_named_parameter_equals(logical_line, tokens): prev_end = end +@register_check def whitespace_before_comment(logical_line, tokens): r"""Separate inline comments by at least two spaces. @@ -897,6 +946,7 @@ def whitespace_before_comment(logical_line, tokens): prev_end = end +@register_check def imports_on_separate_lines(logical_line): r"""Place imports on separate lines. @@ -916,6 +966,7 @@ def imports_on_separate_lines(logical_line): yield found, "E401 multiple imports on one line" +@register_check def module_imports_on_top_of_file( logical_line, indent_level, checker_state, noqa): r"""Place imports at the top of the file. @@ -972,6 +1023,7 @@ def module_imports_on_top_of_file( checker_state['seen_non_imports'] = True +@register_check def compound_statements(logical_line): r"""Compound statements (on the same line) are generally discouraged. @@ -1032,6 +1084,7 @@ def compound_statements(logical_line): found = line.find(';', found + 1) +@register_check def explicit_line_join(logical_line, tokens): r"""Avoid explicit line join between brackets. @@ -1071,6 +1124,7 @@ def explicit_line_join(logical_line, tokens): parens -= 1 +@register_check def break_around_binary_operator(logical_line, tokens): r""" Avoid breaks before binary operators. @@ -1120,6 +1174,7 @@ def break_around_binary_operator(logical_line, tokens): previous_text = text +@register_check def comparison_to_singleton(logical_line, noqa): r"""Comparison to singletons should use "is" or "is not". @@ -1154,6 +1209,7 @@ def comparison_to_singleton(logical_line, noqa): (code, singleton, msg)) +@register_check def comparison_negative(logical_line): r"""Negative comparison should be done using "not in" and "is not". @@ -1175,6 +1231,7 @@ def comparison_negative(logical_line): yield pos, "E714 test for object identity should be 'is not'" +@register_check def comparison_type(logical_line, noqa): r"""Object type comparisons should always use isinstance(). @@ -1198,6 +1255,7 @@ def comparison_type(logical_line, noqa): yield match.start(), "E721 do not compare types, use 'isinstance()'" +@register_check def bare_except(logical_line, noqa): r"""When catching exceptions, mention specific exceptions when possible. @@ -1214,6 +1272,7 @@ def bare_except(logical_line, noqa): yield match.start(), "E722 do not use bare except'" +@register_check def ambiguous_identifier(logical_line, tokens): r"""Never use the characters 'l', 'O', or 'I' as variable names. @@ -1266,6 +1325,7 @@ def ambiguous_identifier(logical_line, tokens): prev_start = start +@register_check def python_3000_has_key(logical_line, noqa): r"""The {}.has_key() method is removed in Python 3: use the 'in' operator. @@ -1277,6 +1337,7 @@ def python_3000_has_key(logical_line, noqa): yield pos, "W601 .has_key() is deprecated, use 'in'" +@register_check def python_3000_raise_comma(logical_line): r"""When raising an exception, use "raise ValueError('message')". @@ -1290,6 +1351,7 @@ def python_3000_raise_comma(logical_line): yield match.end() - 1, "W602 deprecated form of raising exception" +@register_check def python_3000_not_equal(logical_line): r"""New code should always use != instead of <>. @@ -1303,6 +1365,7 @@ def python_3000_not_equal(logical_line): yield pos, "W603 '<>' is deprecated, use '!='" +@register_check def python_3000_backticks(logical_line): r"""Use repr() instead of backticks in Python 3. @@ -1471,50 +1534,6 @@ if COMMENT_WITH_NL: ############################################################################## -_checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} - - -def _get_parameters(function): - if sys.version_info >= (3, 3): - return [parameter.name - for parameter - in inspect.signature(function).parameters.values() - if parameter.kind == parameter.POSITIONAL_OR_KEYWORD] - else: - return inspect.getargspec(function)[0] - - -def register_check(check, codes=None): - """Register a new check object.""" - def _add_check(check, kind, codes, args): - if check in _checks[kind]: - _checks[kind][check][0].extend(codes or []) - else: - _checks[kind][check] = (codes or [''], args) - if inspect.isfunction(check): - args = _get_parameters(check) - if args and args[0] in ('physical_line', 'logical_line'): - if codes is None: - codes = ERRORCODE_REGEX.findall(check.__doc__ or '') - _add_check(check, args[0], codes, args) - elif inspect.isclass(check): - if _get_parameters(check.__init__)[:2] == ['self', 'tree']: - _add_check(check, 'tree', codes, None) - - -def init_checks_registry(): - """Register all globally visible functions. - - The first argument name is either 'physical_line' or 'logical_line'. - """ - mod = inspect.getmodule(register_check) - for (name, function) in inspect.getmembers(mod, inspect.isfunction): - register_check(function) - - -init_checks_registry() - - class Checker(object): """Load a Python source file, tokenize it, check coding style.""" -- cgit v1.2.1