From 6d8412476a296b3a3691af1ffabcb672d9a4920f Mon Sep 17 00:00:00 2001 From: Ionel Cristian Maries Date: Sat, 14 Feb 2015 18:13:20 +0200 Subject: Move all package files to a pylint package. --- __init__.py | 46 - __main__.py | 3 - checkers/__init__.py | 124 -- checkers/base.py | 1253 ------------------ checkers/classes.py | 982 --------------- checkers/design_analysis.py | 370 ------ checkers/exceptions.py | 332 ----- checkers/format.py | 964 -------------- checkers/imports.py | 413 ------ checkers/logging.py | 256 ---- checkers/misc.py | 104 -- checkers/newstyle.py | 172 --- checkers/python3.py | 488 ------- checkers/raw_metrics.py | 129 -- checkers/similar.py | 372 ------ checkers/spelling.py | 250 ---- checkers/stdlib.py | 173 --- checkers/strings.py | 615 --------- checkers/typecheck.py | 627 --------- checkers/utils.py | 564 --------- checkers/variables.py | 1069 ---------------- config.py | 157 --- epylint.py | 176 --- gui.py | 531 -------- interfaces.py | 84 -- lint.py | 1332 -------------------- pylint/__init__.py | 46 + pylint/__main__.py | 3 + pylint/checkers/__init__.py | 124 ++ pylint/checkers/base.py | 1253 ++++++++++++++++++ pylint/checkers/classes.py | 982 +++++++++++++++ pylint/checkers/design_analysis.py | 370 ++++++ pylint/checkers/exceptions.py | 332 +++++ pylint/checkers/format.py | 964 ++++++++++++++ pylint/checkers/imports.py | 413 ++++++ pylint/checkers/logging.py | 256 ++++ pylint/checkers/misc.py | 104 ++ pylint/checkers/newstyle.py | 172 +++ pylint/checkers/python3.py | 488 +++++++ pylint/checkers/raw_metrics.py | 129 ++ pylint/checkers/similar.py | 372 ++++++ pylint/checkers/spelling.py | 250 ++++ pylint/checkers/stdlib.py | 173 +++ pylint/checkers/strings.py | 615 +++++++++ pylint/checkers/typecheck.py | 627 +++++++++ pylint/checkers/utils.py | 564 +++++++++ pylint/checkers/variables.py | 1069 ++++++++++++++++ pylint/config.py | 157 +++ pylint/epylint.py | 176 +++ pylint/gui.py | 531 ++++++++ pylint/interfaces.py | 84 ++ pylint/lint.py | 1332 ++++++++++++++++++++ pylint/pyreverse/__init__.py | 5 + pylint/pyreverse/diadefslib.py | 233 ++++ pylint/pyreverse/diagrams.py | 247 ++++ pylint/pyreverse/main.py | 124 ++ pylint/pyreverse/utils.py | 132 ++ pylint/pyreverse/writer.py | 199 +++ pylint/reporters/__init__.py | 133 ++ pylint/reporters/guireporter.py | 27 + pylint/reporters/html.py | 69 + pylint/reporters/json.py | 58 + pylint/reporters/text.py | 146 +++ pylint/test/data/__init__.py | 0 pylint/test/data/ascript | 2 + pylint/test/data/classes_No_Name.dot | 12 + pylint/test/data/clientmodule_test.py | 29 + pylint/test/data/packages_No_Name.dot | 8 + pylint/test/data/suppliermodule_test.py | 10 + pylint/test/functional/__init__.py | 0 pylint/test/functional/abstract_abc_methods.py | 17 + .../functional/abstract_class_instantiated_py2.py | 68 + .../functional/abstract_class_instantiated_py2.rc | 2 + .../functional/abstract_class_instantiated_py2.txt | 4 + .../functional/abstract_class_instantiated_py3.py | 98 ++ .../functional/abstract_class_instantiated_py3.rc | 2 + .../functional/abstract_class_instantiated_py3.txt | 4 + .../functional/abstract_class_instantiated_py34.py | 19 + .../functional/abstract_class_instantiated_py34.rc | 2 + .../abstract_class_instantiated_py34.txt | 1 + pylint/test/functional/abstract_method_py2.py | 91 ++ pylint/test/functional/abstract_method_py2.rc | 2 + pylint/test/functional/abstract_method_py2.txt | 16 + pylint/test/functional/abstract_method_py3.py | 89 ++ pylint/test/functional/abstract_method_py3.rc | 2 + pylint/test/functional/abstract_method_py3.txt | 16 + pylint/test/functional/access_to__name__.py | 21 + pylint/test/functional/access_to__name__.txt | 3 + .../test/functional/access_to_protected_members.py | 38 + .../functional/access_to_protected_members.txt | 5 + pylint/test/functional/anomalous_unicode_escape.py | 20 + .../test/functional/anomalous_unicode_escape.txt | 3 + pylint/test/functional/arguments.py | 98 ++ pylint/test/functional/arguments.txt | 20 + pylint/test/functional/assigning_non_slot.py | 100 ++ pylint/test/functional/assigning_non_slot.txt | 3 + pylint/test/functional/bad_context_manager.py | 60 + pylint/test/functional/bad_context_manager.txt | 3 + pylint/test/functional/bad_continuation.py | 189 +++ pylint/test/functional/bad_continuation.txt | 63 + pylint/test/functional/bad_inline_option.py | 5 + pylint/test/functional/bad_inline_option.rc | 2 + pylint/test/functional/bad_inline_option.txt | 1 + pylint/test/functional/bad_open_mode.py | 37 + pylint/test/functional/bad_open_mode.rc | 2 + pylint/test/functional/bad_open_mode.txt | 14 + pylint/test/functional/bad_open_mode_py3.py | 23 + pylint/test/functional/bad_open_mode_py3.rc | 2 + pylint/test/functional/bad_open_mode_py3.txt | 6 + pylint/test/functional/bad_reversed_sequence.py | 60 + pylint/test/functional/bad_reversed_sequence.txt | 9 + pylint/test/functional/boolean_datetime.py | 30 + pylint/test/functional/boolean_datetime.txt | 8 + pylint/test/functional/cellvar_escaping_loop.py | 129 ++ pylint/test/functional/cellvar_escaping_loop.txt | 8 + pylint/test/functional/class_members_py27.py | 51 + pylint/test/functional/class_members_py27.rc | 3 + pylint/test/functional/class_members_py27.txt | 6 + pylint/test/functional/class_members_py30.py | 49 + pylint/test/functional/class_members_py30.rc | 2 + pylint/test/functional/class_members_py30.txt | 6 + pylint/test/functional/class_scope.py | 22 + pylint/test/functional/class_scope.txt | 4 + pylint/test/functional/confidence_filter.py | 14 + pylint/test/functional/confidence_filter.rc | 3 + pylint/test/functional/confidence_filter.txt | 1 + .../test/functional/crash_missing_module_type.py | 18 + .../test/functional/crash_missing_module_type.txt | 0 pylint/test/functional/ctor_arguments.py | 78 ++ pylint/test/functional/ctor_arguments.txt | 17 + .../functional/defined_and_used_on_same_line.py | 30 + pylint/test/functional/docstrings.py | 83 ++ pylint/test/functional/docstrings.txt | 8 + .../test/functional/duplicate_dict_literal_key.py | 14 + .../test/functional/duplicate_dict_literal_key.txt | 1 + pylint/test/functional/exception_is_binary_op.py | 12 + pylint/test/functional/exception_is_binary_op.txt | 4 + pylint/test/functional/formatting.txt | 0 pylint/test/functional/future_import.py | 3 + pylint/test/functional/future_unicode_literals.py | 6 + pylint/test/functional/future_unicode_literals.txt | 1 + pylint/test/functional/generated_members.py | 7 + pylint/test/functional/generated_members.rc | 5 + pylint/test/functional/genexpr_variable_scope.py | 5 + pylint/test/functional/genexpr_variable_scope.txt | 1 + pylint/test/functional/globals.py | 25 + pylint/test/functional/globals.txt | 6 + pylint/test/functional/import_error.py | 18 + pylint/test/functional/import_error.txt | 2 + pylint/test/functional/indexing_exception.py | 15 + pylint/test/functional/indexing_exception.rc | 5 + pylint/test/functional/indexing_exception.txt | 3 + pylint/test/functional/inherit_non_class.py | 65 + pylint/test/functional/inherit_non_class.txt | 5 + pylint/test/functional/init_not_called.py | 59 + pylint/test/functional/init_not_called.txt | 6 + pylint/test/functional/invalid__all__object.py | 3 + pylint/test/functional/invalid__all__object.txt | 1 + pylint/test/functional/invalid_encoded_data.py | 5 + pylint/test/functional/invalid_encoded_data.txt | 1 + .../test/functional/invalid_exceptions_caught.py | 92 ++ .../test/functional/invalid_exceptions_caught.txt | 10 + .../test/functional/invalid_exceptions_raised.py | 76 ++ .../test/functional/invalid_exceptions_raised.txt | 12 + pylint/test/functional/invalid_name.py | 28 + pylint/test/functional/invalid_name.txt | 2 + pylint/test/functional/invalid_slice_index.py | 60 + pylint/test/functional/invalid_slice_index.txt | 5 + pylint/test/functional/line_endings.py | 4 + pylint/test/functional/line_endings.rc | 2 + pylint/test/functional/line_endings.txt | 2 + pylint/test/functional/long_lines_with_utf8.py | 7 + pylint/test/functional/long_lines_with_utf8.txt | 1 + pylint/test/functional/member_checks.py | 63 + pylint/test/functional/member_checks.txt | 9 + pylint/test/functional/method_hidden.py | 15 + pylint/test/functional/method_hidden.txt | 1 + pylint/test/functional/missing_final_newline.py | 4 + pylint/test/functional/missing_final_newline.txt | 1 + pylint/test/functional/missing_self_argument.py | 20 + pylint/test/functional/missing_self_argument.txt | 6 + pylint/test/functional/name_styles.py | 118 ++ pylint/test/functional/name_styles.rc | 5 + pylint/test/functional/name_styles.txt | 17 + .../test/functional/namedtuple_member_inference.py | 23 + .../functional/namedtuple_member_inference.txt | 2 + pylint/test/functional/names_in__all__.py | 46 + pylint/test/functional/names_in__all__.txt | 4 + pylint/test/functional/newstyle__slots__.py | 17 + pylint/test/functional/newstyle__slots__.txt | 2 + pylint/test/functional/newstyle_properties.py | 53 + pylint/test/functional/newstyle_properties.txt | 2 + pylint/test/functional/no_name_in_module.py | 26 + pylint/test/functional/no_name_in_module.txt | 11 + pylint/test/functional/old_style_class_py27.py | 18 + pylint/test/functional/old_style_class_py27.rc | 2 + pylint/test/functional/old_style_class_py27.txt | 2 + pylint/test/functional/pygtk_enum_crash.py | 11 + pylint/test/functional/pygtk_enum_crash.rc | 2 + pylint/test/functional/pygtk_import.py | 14 + pylint/test/functional/pygtk_import.rc | 2 + .../test/functional/raising_non_exception_py3.py | 13 + .../test/functional/raising_non_exception_py3.rc | 2 + .../test/functional/raising_non_exception_py3.txt | 1 + pylint/test/functional/redefined_builtin.py | 9 + pylint/test/functional/redefined_builtin.txt | 2 + .../test/functional/redundant_unittest_assert.py | 38 + .../test/functional/redundant_unittest_assert.txt | 6 + pylint/test/functional/slots_checks.py | 61 + pylint/test/functional/slots_checks.txt | 4 + pylint/test/functional/socketerror_import.py | 6 + pylint/test/functional/statement_without_effect.py | 65 + .../test/functional/statement_without_effect.txt | 60 + pylint/test/functional/string_formatting.py | 183 +++ pylint/test/functional/string_formatting.txt | 40 + pylint/test/functional/string_formatting_py27.py | 23 + pylint/test/functional/string_formatting_py27.rc | 3 + pylint/test/functional/string_formatting_py27.txt | 15 + pylint/test/functional/super_checks.py | 62 + pylint/test/functional/super_checks.txt | 9 + pylint/test/functional/superfluous_parens.py | 18 + pylint/test/functional/superfluous_parens.txt | 5 + .../test/functional/suspicious_str_strip_call.py | 9 + .../test/functional/suspicious_str_strip_call.rc | 2 + .../test/functional/suspicious_str_strip_call.txt | 3 + .../functional/suspicious_str_strip_call_py3.py | 9 + .../functional/suspicious_str_strip_call_py3.rc | 2 + .../functional/suspicious_str_strip_call_py3.txt | 3 + pylint/test/functional/too_many_branches.py | 69 + pylint/test/functional/too_many_branches.txt | 1 + pylint/test/functional/too_many_lines_disabled.py | 1018 +++++++++++++++ .../test/functional/unbalanced_tuple_unpacking.py | 88 ++ .../test/functional/unbalanced_tuple_unpacking.txt | 8 + .../functional/unbalanced_tuple_unpacking_py30.py | 11 + .../functional/unbalanced_tuple_unpacking_py30.rc | 2 + pylint/test/functional/undefined_variable.py | 128 ++ pylint/test/functional/undefined_variable.txt | 18 + pylint/test/functional/undefined_variable_py30.py | 58 + pylint/test/functional/undefined_variable_py30.rc | 2 + pylint/test/functional/undefined_variable_py30.txt | 6 + pylint/test/functional/uninferable_all_object.py | 9 + pylint/test/functional/unknown_encoding_py29.py | 7 + pylint/test/functional/unknown_encoding_py29.rc | 3 + pylint/test/functional/unknown_encoding_py29.txt | 1 + pylint/test/functional/unknown_encoding_py30.py | 7 + pylint/test/functional/unknown_encoding_py30.rc | 3 + pylint/test/functional/unknown_encoding_py30.txt | 1 + pylint/test/functional/unknown_encoding_pypy.py | 7 + pylint/test/functional/unknown_encoding_pypy.rc | 3 + pylint/test/functional/unknown_encoding_pypy.txt | 1 + pylint/test/functional/unnecessary_lambda.py | 50 + pylint/test/functional/unnecessary_lambda.txt | 7 + pylint/test/functional/unpacked_exceptions.py | 11 + pylint/test/functional/unpacked_exceptions.rc | 5 + pylint/test/functional/unpacked_exceptions.txt | 4 + pylint/test/functional/unpacking.py | 11 + pylint/test/functional/unpacking_non_sequence.py | 86 ++ pylint/test/functional/unpacking_non_sequence.txt | 12 + pylint/test/functional/unused_import.py | 12 + pylint/test/functional/unused_import.txt | 7 + pylint/test/functional/useless_else_on_loop.py | 55 + pylint/test/functional/useless_else_on_loop.txt | 5 + pylint/test/functional/with_used_before_assign.py | 11 + pylint/test/functional/with_used_before_assign.txt | 2 + pylint/test/functional/yield_outside_func.py | 4 + pylint/test/functional/yield_outside_func.txt | 1 + pylint/test/input/__init__.py | 1 + pylint/test/input/func_3k_removed_stuff_py_30.py | 13 + pylint/test/input/func_assert_2uple.py | 11 + pylint/test/input/func_attrs_definition_order.py | 33 + .../input/func_bad_assigment_to_exception_var.py | 30 + pylint/test/input/func_bad_cont_dictcomp_py27.py | 38 + .../test/input/func_bad_exception_context_py30.py | 24 + pylint/test/input/func_base_useless_pass.py | 9 + pylint/test/input/func_block_disable_msg.py | 1026 +++++++++++++++ .../input/func_break_or_return_in_try_finally.py | 44 + pylint/test/input/func_bug113231.py | 24 + pylint/test/input/func_continue_not_in_loop.py | 14 + pylint/test/input/func_dangerous_default.py | 76 ++ .../test/input/func_defining-attr-methods_order.py | 73 ++ pylint/test/input/func_deprecated_lambda_py_30.py | 24 + pylint/test/input/func_deprecated_module_py30.py | 12 + pylint/test/input/func_deprecated_module_py_30.py | 12 + pylint/test/input/func_disable_linebased.py | 14 + pylint/test/input/func_dotted_ancestor.py | 11 + pylint/test/input/func_e0001_py30.py | 12 + pylint/test/input/func_e0011.py | 5 + pylint/test/input/func_e0012.py | 5 + pylint/test/input/func_e0101.py | 35 + pylint/test/input/func_e0108.py | 14 + pylint/test/input/func_e0203.py | 19 + pylint/test/input/func_e0204.py | 19 + pylint/test/input/func_e0206.py | 20 + pylint/test/input/func_e0601.py | 9 + pylint/test/input/func_e0604.py | 13 + pylint/test/input/func_e12xx.py | 27 + pylint/test/input/func_e13xx.py | 21 + pylint/test/input/func_empty_module.py | 0 pylint/test/input/func_eval_used.py | 13 + pylint/test/input/func_excess_escapes.py | 30 + pylint/test/input/func_exec_used_py30.py | 13 + pylint/test/input/func_f0401.py | 9 + pylint/test/input/func_first_arg.py | 42 + pylint/test/input/func_fixme.py | 15 + pylint/test/input/func_i0011.py | 5 + pylint/test/input/func_i0012.py | 5 + pylint/test/input/func_i0013.py | 8 + pylint/test/input/func_i0014.py | 8 + pylint/test/input/func_i0020.py | 8 + pylint/test/input/func_i0022.py | 22 + pylint/test/input/func_import_syntax_error.py | 2 + pylint/test/input/func_indent.py | 24 + pylint/test/input/func_init_vars.py | 45 + pylint/test/input/func_interfaces.py | 112 ++ pylint/test/input/func_invalid_sequence_index.py | 210 +++ pylint/test/input/func_keyword_repeat.py | 11 + pylint/test/input/func_kwoa_py30.py | 12 + .../input/func_logging_not_lazy_with_logger.py | 14 + .../test/input/func_loopvar_in_dict_comp_py27.py | 8 + pylint/test/input/func_method_could_be_function.py | 60 + pylint/test/input/func_module___dict__.py | 9 + pylint/test/input/func_more_e0604.py | 9 + .../input/func_nameerror_on_string_substitution.py | 8 + pylint/test/input/func_no_dummy_redefined.py | 14 + ..._noerror___init___return_from_inner_function.py | 13 + ...oerror_access_attr_before_def_false_positive.py | 98 ++ pylint/test/input/func_noerror_base_init_vars.py | 35 + .../test/input/func_noerror_builtin_module_test.py | 9 + pylint/test/input/func_noerror_class_attributes.py | 17 + .../input/func_noerror_class_decorators_py26.py | 8 + ...unc_noerror_classes_meth_could_be_a_function.py | 34 + .../input/func_noerror_classes_meth_signature.py | 38 + ...func_noerror_classes_protected_member_access.py | 25 + pylint/test/input/func_noerror_crash_122793.py | 9 + pylint/test/input/func_noerror_crash_127416.py | 20 + pylint/test/input/func_noerror_decorator_scope.py | 19 + pylint/test/input/func_noerror_e1101_13784.py | 15 + ...func_noerror_e1101_9588_base_attr_aug_assign.py | 39 + .../test/input/func_noerror_e1101_but_getattr.py | 23 + pylint/test/input/func_noerror_encoding.py | 6 + pylint/test/input/func_noerror_except_pass.py | 12 + pylint/test/input/func_noerror_exception.py | 7 + .../func_noerror_external_classmethod_crash.py | 21 + pylint/test/input/func_noerror_factory_method.py | 23 + .../test/input/func_noerror_function_as_method.py | 18 + .../input/func_noerror_genexp_in_class_scope.py | 9 + .../test/input/func_noerror_indirect_interface.py | 16 + pylint/test/input/func_noerror_inner_classes.py | 33 + .../input/func_noerror_lambda_use_before_assign.py | 8 + pylint/test/input/func_noerror_long_utf8_line.py | 9 + pylint/test/input/func_noerror_mcs_attr_access.py | 20 + pylint/test/input/func_noerror_nested_classes.py | 18 + .../input/func_noerror_new_style_class_py_30.py | 45 + .../input/func_noerror_no_warning_docstring.py | 42 + pylint/test/input/func_noerror_nonregr.py | 13 + .../func_noerror_object_as_class_attribute.py | 19 + .../test/input/func_noerror_overloaded_operator.py | 21 + .../input/func_noerror_overriden_method_varargs.py | 19 + .../func_noerror_property_affectation_py26.py | 24 + .../test/input/func_noerror_raise_return_self.py | 15 + pylint/test/input/func_noerror_socket_member.py | 25 + pylint/test/input/func_noerror_static_method.py | 27 + .../func_noerror_staticmethod_as_decorator_py24.py | 33 + pylint/test/input/func_noerror_super_protected.py | 22 + .../input/func_noerror_unused_variable_py30.py | 14 + .../input/func_noerror_used_before_assignment.py | 5 + pylint/test/input/func_noerror_w0232.py | 10 + .../test/input/func_noerror_yield_assign_py25.py | 21 + pylint/test/input/func_noerror_yield_return_mix.py | 7 + .../test/input/func_non_iterator_returned_py30.py | 52 + .../test/input/func_non_iterator_returned_py_30.py | 52 + pylint/test/input/func_nonregr___file___global.py | 8 + pylint/test/input/func_operators.py | 19 + pylint/test/input/func_r0901.py | 27 + pylint/test/input/func_r0902.py | 28 + pylint/test/input/func_r0903.py | 30 + pylint/test/input/func_r0904.py | 80 ++ pylint/test/input/func_r0921.py | 15 + pylint/test/input/func_r0922.py | 21 + pylint/test/input/func_r0923.py | 31 + pylint/test/input/func_reqattrs.py | 1 + pylint/test/input/func_return_outside_func.py | 3 + pylint/test/input/func_return_yield_mix_py_33.py | 16 + .../test/input/func_set_literal_as_default_py27.py | 7 + pylint/test/input/func_syntax_error.py | 1 + pylint/test/input/func_tokenize_error.py | 6 + .../test/input/func_too_many_locals_arguments.py | 52 + pylint/test/input/func_too_many_returns_yields.py | 42 + pylint/test/input/func_toolonglines.py | 27 + pylint/test/input/func_trailing_whitespace.py | 8 + .../input/func_typecheck_callfunc_assigment.py | 63 + .../test/input/func_typecheck_non_callable_call.py | 118 ++ .../input/func_undefined_metaclass_var_py30.py | 27 + pylint/test/input/func_unreachable.py | 21 + pylint/test/input/func_unused_import_py30.py | 21 + .../test/input/func_unused_overridden_argument.py | 31 + pylint/test/input/func_use_for_or_listcomp_var.py | 27 + .../test/input/func_used_before_assignment_py30.py | 48 + ...nc_variables_unused_name_from_wilcard_import.py | 4 + pylint/test/input/func_w0101.py | 28 + pylint/test/input/func_w0102.py | 67 + pylint/test/input/func_w0103.py | 8 + pylint/test/input/func_w0104.py | 12 + pylint/test/input/func_w0105.py | 62 + pylint/test/input/func_w0110.py | 10 + pylint/test/input/func_w0111.py | 10 + pylint/test/input/func_w0112.py | 37 + pylint/test/input/func_w0122_py_30.py | 13 + pylint/test/input/func_w0151.py | 5 + pylint/test/input/func_w0152.py | 18 + pylint/test/input/func_w0202.py | 17 + pylint/test/input/func_w0205.py | 24 + pylint/test/input/func_w0233.py | 50 + pylint/test/input/func_w0302.py | 1016 +++++++++++++++ pylint/test/input/func_w0312.py | 11 + pylint/test/input/func_w0332_py_30.py | 4 + pylint/test/input/func_w0401.py | 9 + pylint/test/input/func_w0401_package/__init__.py | 2 + .../input/func_w0401_package/all_the_things.py | 9 + pylint/test/input/func_w0401_package/thing1.py | 3 + pylint/test/input/func_w0401_package/thing2.py | 7 + pylint/test/input/func_w0402.py | 12 + pylint/test/input/func_w0404.py | 27 + pylint/test/input/func_w0405.py | 31 + pylint/test/input/func_w0406.py | 9 + pylint/test/input/func_w0611.py | 25 + pylint/test/input/func_w0612.py | 37 + pylint/test/input/func_w0613.py | 42 + pylint/test/input/func_w0623_py30.py | 16 + pylint/test/input/func_w0623_py_30.py | 67 + pylint/test/input/func_w0631.py | 20 + pylint/test/input/func_w0702.py | 17 + pylint/test/input/func_w0703.py | 9 + pylint/test/input/func_w0704.py | 16 + pylint/test/input/func_w0705.py | 46 + pylint/test/input/func_w0801.py | 11 + pylint/test/input/func_w1201.py | 21 + pylint/test/input/func_w1202.py | 26 + pylint/test/input/func_with_without_as_py25.py | 12 + pylint/test/input/ignore_except_pass_by_default.py | 6 + pylint/test/input/indirect1.py | 4 + pylint/test/input/indirect2.py | 7 + pylint/test/input/indirect3.py | 5 + pylint/test/input/noext | 4 + pylint/test/input/similar1 | 22 + pylint/test/input/similar2 | 22 + pylint/test/input/syntax_error.py | 2 + pylint/test/input/w0401_cycle.py | 9 + pylint/test/input/w0801_same.py | 11 + pylint/test/messages/builtin_module.txt | 1 + .../test/messages/func_3k_removed_stuff_py_30.txt | 5 + pylint/test/messages/func_assert_2uple.txt | 2 + .../test/messages/func_attrs_definition_order.txt | 2 + .../func_bad_assigment_to_exception_var.txt | 5 + .../test/messages/func_bad_cont_dictcomp_py27.txt | 6 + .../messages/func_bad_exception_context_py30.txt | 3 + pylint/test/messages/func_base_useless_pass.txt | 1 + pylint/test/messages/func_block_disable_msg.txt | 13 + .../func_break_or_return_in_try_finally.txt | 3 + pylint/test/messages/func_bug113231.txt | 2 + pylint/test/messages/func_continue_not_in_loop.txt | 2 + pylint/test/messages/func_dangerous_default.txt | 13 + .../test/messages/func_dangerous_default_py30.txt | 13 + .../messages/func_defining-attr-methods_order.txt | 3 + .../test/messages/func_deprecated_lambda_py_30.txt | 2 + .../test/messages/func_deprecated_module_py30.txt | 2 + .../test/messages/func_deprecated_module_py_30.txt | 2 + pylint/test/messages/func_disable_linebased.txt | 2 + .../test/messages/func_disable_linebased_py30.txt | 2 + pylint/test/messages/func_dotted_ancestor.txt | 1 + pylint/test/messages/func_e0001_py30.txt | 2 + pylint/test/messages/func_e0011.txt | 1 + pylint/test/messages/func_e0012.txt | 1 + pylint/test/messages/func_e0101.txt | 2 + pylint/test/messages/func_e0108.txt | 3 + pylint/test/messages/func_e0203.txt | 1 + pylint/test/messages/func_e0204.txt | 3 + pylint/test/messages/func_e0206.txt | 3 + pylint/test/messages/func_e0601.txt | 1 + pylint/test/messages/func_e0604.txt | 1 + pylint/test/messages/func_e12xx.txt | 6 + pylint/test/messages/func_e13xx.txt | 13 + pylint/test/messages/func_e13xx_py30.txt | 11 + pylint/test/messages/func_empty_module.txt | 1 + pylint/test/messages/func_eval_used.txt | 4 + pylint/test/messages/func_excess_escapes.txt | 9 + pylint/test/messages/func_exec_used_py30.txt | 4 + pylint/test/messages/func_f0401.txt | 2 + pylint/test/messages/func_first_arg.txt | 5 + pylint/test/messages/func_fixme.txt | 5 + pylint/test/messages/func_i0011.txt | 2 + pylint/test/messages/func_i0012.txt | 1 + pylint/test/messages/func_i0013.txt | 1 + pylint/test/messages/func_i0014.txt | 2 + pylint/test/messages/func_i0020.txt | 2 + pylint/test/messages/func_i0022.txt | 21 + pylint/test/messages/func_import_syntax_error.txt | 4 + .../messages/func_import_syntax_error_py30.txt | 4 + pylint/test/messages/func_indent.txt | 5 + pylint/test/messages/func_init_vars.txt | 1 + pylint/test/messages/func_interfaces.txt | 6 + .../test/messages/func_invalid_sequence_index.txt | 19 + pylint/test/messages/func_keyword_repeat_py26.txt | 1 + pylint/test/messages/func_kwoa_py30.txt | 5 + .../messages/func_logging_not_lazy_with_logger.txt | 4 + .../messages/func_loopvar_in_dict_comp_py27.txt | 1 + .../messages/func_method_could_be_function.txt | 1 + pylint/test/messages/func_module___dict__.txt | 1 + pylint/test/messages/func_more_e0604.txt | 1 + .../func_nameerror_on_string_substitution.txt | 2 + pylint/test/messages/func_no_dummy_redefined.txt | 2 + .../messages/func_non_iterator_returned_py30.txt | 3 + .../messages/func_non_iterator_returned_py_30.txt | 3 + .../test/messages/func_nonregr___file___global.txt | 2 + pylint/test/messages/func_operators.txt | 6 + pylint/test/messages/func_r0901.txt | 2 + pylint/test/messages/func_r0902.txt | 1 + pylint/test/messages/func_r0903.txt | 1 + pylint/test/messages/func_r0904.txt | 1 + pylint/test/messages/func_r0921.txt | 1 + pylint/test/messages/func_r0922.txt | 1 + pylint/test/messages/func_r0923.txt | 1 + pylint/test/messages/func_raw_escapes.txt | 3 + pylint/test/messages/func_reqattrs.txt | 1 + pylint/test/messages/func_return_outside_func.txt | 1 + .../test/messages/func_return_yield_mix_py_33.txt | 2 + .../messages/func_set_literal_as_default_py27.txt | 1 + pylint/test/messages/func_syntax_error.txt | 3 + pylint/test/messages/func_tokenize_error.txt | 1 + .../messages/func_too_many_locals_arguments.txt | 2 + .../test/messages/func_too_many_returns_yields.txt | 1 + pylint/test/messages/func_toolonglines.txt | 6 + pylint/test/messages/func_toolonglines_py30.txt | 4 + pylint/test/messages/func_trailing_whitespace.txt | 3 + .../messages/func_typecheck_callfunc_assigment.txt | 3 + .../test/messages/func_typecheck_getattr_py30.txt | 9 + .../messages/func_typecheck_non_callable_call.txt | 8 + .../messages/func_undefined_metaclass_var_py30.txt | 2 + pylint/test/messages/func_unicode_literal_py26.txt | 0 .../test/messages/func_unicode_literal_py274.txt | 1 + pylint/test/messages/func_unreachable.txt | 3 + pylint/test/messages/func_unused_import_py30.txt | 1 + .../messages/func_unused_overridden_argument.txt | 1 + .../messages/func_use_for_or_listcomp_var_py29.txt | 3 + .../messages/func_use_for_or_listcomp_var_py30.txt | 3 + .../messages/func_used_before_assignment_py30.txt | 6 + ...c_variables_unused_name_from_wilcard_import.txt | 4 + pylint/test/messages/func_w0101.txt | 1 + pylint/test/messages/func_w0102.txt | 5 + pylint/test/messages/func_w0103.txt | 1 + pylint/test/messages/func_w0104.txt | 1 + pylint/test/messages/func_w0105.txt | 1 + pylint/test/messages/func_w0110.txt | 1 + pylint/test/messages/func_w0111.txt | 1 + pylint/test/messages/func_w0112.txt | 1 + pylint/test/messages/func_w0122_py_30.txt | 5 + pylint/test/messages/func_w0151.txt | 1 + pylint/test/messages/func_w0152_py29.txt | 2 + pylint/test/messages/func_w0152_py30.txt | 4 + pylint/test/messages/func_w0202.txt | 3 + pylint/test/messages/func_w0205.txt | 2 + pylint/test/messages/func_w0233.txt | 5 + pylint/test/messages/func_w0302.txt | 2 + pylint/test/messages/func_w0312.txt | 2 + pylint/test/messages/func_w0332_py_30.txt | 1 + pylint/test/messages/func_w0401.txt | 2 + pylint/test/messages/func_w0401_package.txt | 1 + pylint/test/messages/func_w0402.txt | 3 + pylint/test/messages/func_w0404.txt | 5 + pylint/test/messages/func_w0405.txt | 4 + pylint/test/messages/func_w0406.txt | 1 + pylint/test/messages/func_w0611.txt | 1 + pylint/test/messages/func_w0612.txt | 6 + pylint/test/messages/func_w0613.txt | 5 + pylint/test/messages/func_w0622.txt | 2 + pylint/test/messages/func_w0623.txt | 11 + pylint/test/messages/func_w0623_py30.txt | 3 + pylint/test/messages/func_w0623_py_30.txt | 12 + pylint/test/messages/func_w0631.txt | 1 + pylint/test/messages/func_w0702.txt | 2 + pylint/test/messages/func_w0703.txt | 1 + pylint/test/messages/func_w0704.txt | 1 + pylint/test/messages/func_w0705.txt | 15 + pylint/test/messages/func_w0801.txt | 11 + pylint/test/messages/func_w1201.txt | 3 + pylint/test/messages/func_w1202.txt | 4 + pylint/test/messages/func_with_without_as_py25.txt | 3 + pylint/test/regrtest_data/absimp/__init__.py | 5 + pylint/test/regrtest_data/absimp/string.py | 7 + pylint/test/regrtest_data/application_crash.py | 12 + pylint/test/regrtest_data/classdoc_usage.py | 17 + pylint/test/regrtest_data/decimal_inference.py | 10 + pylint/test/regrtest_data/descriptor_crash.py | 20 + pylint/test/regrtest_data/import_assign.py | 5 + .../import_package_subpackage_module.py | 49 + pylint/test/regrtest_data/module_global.py | 7 + pylint/test/regrtest_data/no_stdout_encoding.py | 5 + pylint/test/regrtest_data/numarray_import.py | 7 + pylint/test/regrtest_data/numarray_inf.py | 5 + pylint/test/regrtest_data/package/AudioTime.py | 3 + pylint/test/regrtest_data/package/__init__.py | 14 + .../regrtest_data/package/subpackage/__init__.py | 1 + .../regrtest_data/package/subpackage/module.py | 1 + pylint/test/regrtest_data/package_all/__init__.py | 3 + .../test/regrtest_data/package_all/notmissing.py | 2 + pylint/test/regrtest_data/precedence_test.py | 21 + .../special_attr_scope_lookup_crash.py | 3 + .../regrtest_data/try_finally_disable_msg_crash.py | 5 + pylint/test/test_func.py | 95 ++ pylint/test/test_functional.py | 369 ++++++ pylint/test/test_import_graph.py | 68 + pylint/test/test_regr.py | 142 +++ pylint/test/test_self.py | 152 +++ pylint/test/unittest_checker_base.py | 233 ++++ pylint/test/unittest_checker_classes.py | 82 ++ pylint/test/unittest_checker_exceptions.py | 61 + pylint/test/unittest_checker_format.py | 253 ++++ pylint/test/unittest_checker_logging.py | 47 + pylint/test/unittest_checker_misc.py | 49 + pylint/test/unittest_checker_python3.py | 301 +++++ pylint/test/unittest_checker_similar.py | 142 +++ pylint/test/unittest_checker_spelling.py | 93 ++ pylint/test/unittest_checker_typecheck.py | 42 + pylint/test/unittest_checker_variables.py | 99 ++ pylint/test/unittest_checkers_utils.py | 71 ++ pylint/test/unittest_lint.py | 697 ++++++++++ pylint/test/unittest_pyreverse_diadefs.py | 171 +++ pylint/test/unittest_pyreverse_writer.py | 136 ++ pylint/test/unittest_reporters_json.py | 61 + pylint/test/unittest_reporting.py | 125 ++ pylint/test/unittest_utils.py | 62 + pylint/testutils.py | 412 ++++++ pylint/utils.py | 919 ++++++++++++++ pyreverse/__init__.py | 5 - pyreverse/diadefslib.py | 233 ---- pyreverse/diagrams.py | 247 ---- pyreverse/main.py | 124 -- pyreverse/utils.py | 132 -- pyreverse/writer.py | 199 --- reporters/__init__.py | 133 -- reporters/guireporter.py | 27 - reporters/html.py | 69 - reporters/json.py | 58 - reporters/text.py | 146 --- test/data/__init__.py | 0 test/data/ascript | 2 - test/data/classes_No_Name.dot | 12 - test/data/clientmodule_test.py | 29 - test/data/packages_No_Name.dot | 8 - test/data/suppliermodule_test.py | 10 - test/functional/__init__.py | 0 test/functional/abstract_abc_methods.py | 17 - test/functional/abstract_class_instantiated_py2.py | 68 - test/functional/abstract_class_instantiated_py2.rc | 2 - .../functional/abstract_class_instantiated_py2.txt | 4 - test/functional/abstract_class_instantiated_py3.py | 98 -- test/functional/abstract_class_instantiated_py3.rc | 2 - .../functional/abstract_class_instantiated_py3.txt | 4 - .../functional/abstract_class_instantiated_py34.py | 19 - .../functional/abstract_class_instantiated_py34.rc | 2 - .../abstract_class_instantiated_py34.txt | 1 - test/functional/abstract_method_py2.py | 91 -- test/functional/abstract_method_py2.rc | 2 - test/functional/abstract_method_py2.txt | 16 - test/functional/abstract_method_py3.py | 89 -- test/functional/abstract_method_py3.rc | 2 - test/functional/abstract_method_py3.txt | 16 - test/functional/access_to__name__.py | 21 - test/functional/access_to__name__.txt | 3 - test/functional/access_to_protected_members.py | 38 - test/functional/access_to_protected_members.txt | 5 - test/functional/anomalous_unicode_escape.py | 20 - test/functional/anomalous_unicode_escape.txt | 3 - test/functional/arguments.py | 98 -- test/functional/arguments.txt | 20 - test/functional/assigning_non_slot.py | 100 -- test/functional/assigning_non_slot.txt | 3 - test/functional/bad_context_manager.py | 60 - test/functional/bad_context_manager.txt | 3 - test/functional/bad_continuation.py | 189 --- test/functional/bad_continuation.txt | 63 - test/functional/bad_inline_option.py | 5 - test/functional/bad_inline_option.rc | 2 - test/functional/bad_inline_option.txt | 1 - test/functional/bad_open_mode.py | 37 - test/functional/bad_open_mode.rc | 2 - test/functional/bad_open_mode.txt | 14 - test/functional/bad_open_mode_py3.py | 23 - test/functional/bad_open_mode_py3.rc | 2 - test/functional/bad_open_mode_py3.txt | 6 - test/functional/bad_reversed_sequence.py | 60 - test/functional/bad_reversed_sequence.txt | 9 - test/functional/boolean_datetime.py | 30 - test/functional/boolean_datetime.txt | 8 - test/functional/cellvar_escaping_loop.py | 129 -- test/functional/cellvar_escaping_loop.txt | 8 - test/functional/class_members_py27.py | 51 - test/functional/class_members_py27.rc | 3 - test/functional/class_members_py27.txt | 6 - test/functional/class_members_py30.py | 49 - test/functional/class_members_py30.rc | 2 - test/functional/class_members_py30.txt | 6 - test/functional/class_scope.py | 22 - test/functional/class_scope.txt | 4 - test/functional/confidence_filter.py | 14 - test/functional/confidence_filter.rc | 3 - test/functional/confidence_filter.txt | 1 - test/functional/crash_missing_module_type.py | 18 - test/functional/crash_missing_module_type.txt | 0 test/functional/ctor_arguments.py | 78 -- test/functional/ctor_arguments.txt | 17 - test/functional/defined_and_used_on_same_line.py | 30 - test/functional/docstrings.py | 83 -- test/functional/docstrings.txt | 8 - test/functional/duplicate_dict_literal_key.py | 14 - test/functional/duplicate_dict_literal_key.txt | 1 - test/functional/exception_is_binary_op.py | 12 - test/functional/exception_is_binary_op.txt | 4 - test/functional/formatting.txt | 0 test/functional/future_import.py | 3 - test/functional/future_unicode_literals.py | 6 - test/functional/future_unicode_literals.txt | 1 - test/functional/generated_members.py | 7 - test/functional/generated_members.rc | 5 - test/functional/genexpr_variable_scope.py | 5 - test/functional/genexpr_variable_scope.txt | 1 - test/functional/globals.py | 25 - test/functional/globals.txt | 6 - test/functional/import_error.py | 18 - test/functional/import_error.txt | 2 - test/functional/indexing_exception.py | 15 - test/functional/indexing_exception.rc | 5 - test/functional/indexing_exception.txt | 3 - test/functional/inherit_non_class.py | 65 - test/functional/inherit_non_class.txt | 5 - test/functional/init_not_called.py | 59 - test/functional/init_not_called.txt | 6 - test/functional/invalid__all__object.py | 3 - test/functional/invalid__all__object.txt | 1 - test/functional/invalid_encoded_data.py | 5 - test/functional/invalid_encoded_data.txt | 1 - test/functional/invalid_exceptions_caught.py | 92 -- test/functional/invalid_exceptions_caught.txt | 10 - test/functional/invalid_exceptions_raised.py | 76 -- test/functional/invalid_exceptions_raised.txt | 12 - test/functional/invalid_name.py | 28 - test/functional/invalid_name.txt | 2 - test/functional/invalid_slice_index.py | 60 - test/functional/invalid_slice_index.txt | 5 - test/functional/line_endings.py | 4 - test/functional/line_endings.rc | 2 - test/functional/line_endings.txt | 2 - test/functional/long_lines_with_utf8.py | 7 - test/functional/long_lines_with_utf8.txt | 1 - test/functional/member_checks.py | 63 - test/functional/member_checks.txt | 9 - test/functional/method_hidden.py | 15 - test/functional/method_hidden.txt | 1 - test/functional/missing_final_newline.py | 4 - test/functional/missing_final_newline.txt | 1 - test/functional/missing_self_argument.py | 20 - test/functional/missing_self_argument.txt | 6 - test/functional/name_styles.py | 118 -- test/functional/name_styles.rc | 5 - test/functional/name_styles.txt | 17 - test/functional/namedtuple_member_inference.py | 23 - test/functional/namedtuple_member_inference.txt | 2 - test/functional/names_in__all__.py | 46 - test/functional/names_in__all__.txt | 4 - test/functional/newstyle__slots__.py | 17 - test/functional/newstyle__slots__.txt | 2 - test/functional/newstyle_properties.py | 53 - test/functional/newstyle_properties.txt | 2 - test/functional/no_name_in_module.py | 26 - test/functional/no_name_in_module.txt | 11 - test/functional/old_style_class_py27.py | 18 - test/functional/old_style_class_py27.rc | 2 - test/functional/old_style_class_py27.txt | 2 - test/functional/pygtk_enum_crash.py | 11 - test/functional/pygtk_enum_crash.rc | 2 - test/functional/pygtk_import.py | 14 - test/functional/pygtk_import.rc | 2 - test/functional/raising_non_exception_py3.py | 13 - test/functional/raising_non_exception_py3.rc | 2 - test/functional/raising_non_exception_py3.txt | 1 - test/functional/redefined_builtin.py | 9 - test/functional/redefined_builtin.txt | 2 - test/functional/redundant_unittest_assert.py | 38 - test/functional/redundant_unittest_assert.txt | 6 - test/functional/slots_checks.py | 61 - test/functional/slots_checks.txt | 4 - test/functional/socketerror_import.py | 6 - test/functional/statement_without_effect.py | 65 - test/functional/statement_without_effect.txt | 60 - test/functional/string_formatting.py | 183 --- test/functional/string_formatting.txt | 40 - test/functional/string_formatting_py27.py | 23 - test/functional/string_formatting_py27.rc | 3 - test/functional/string_formatting_py27.txt | 15 - test/functional/super_checks.py | 62 - test/functional/super_checks.txt | 9 - test/functional/superfluous_parens.py | 18 - test/functional/superfluous_parens.txt | 5 - test/functional/suspicious_str_strip_call.py | 9 - test/functional/suspicious_str_strip_call.rc | 2 - test/functional/suspicious_str_strip_call.txt | 3 - test/functional/suspicious_str_strip_call_py3.py | 9 - test/functional/suspicious_str_strip_call_py3.rc | 2 - test/functional/suspicious_str_strip_call_py3.txt | 3 - test/functional/too_many_branches.py | 69 - test/functional/too_many_branches.txt | 1 - test/functional/too_many_lines_disabled.py | 1018 --------------- test/functional/unbalanced_tuple_unpacking.py | 88 -- test/functional/unbalanced_tuple_unpacking.txt | 8 - test/functional/unbalanced_tuple_unpacking_py30.py | 11 - test/functional/unbalanced_tuple_unpacking_py30.rc | 2 - test/functional/undefined_variable.py | 128 -- test/functional/undefined_variable.txt | 18 - test/functional/undefined_variable_py30.py | 58 - test/functional/undefined_variable_py30.rc | 2 - test/functional/undefined_variable_py30.txt | 6 - test/functional/uninferable_all_object.py | 9 - test/functional/unknown_encoding_py29.py | 7 - test/functional/unknown_encoding_py29.rc | 3 - test/functional/unknown_encoding_py29.txt | 1 - test/functional/unknown_encoding_py30.py | 7 - test/functional/unknown_encoding_py30.rc | 3 - test/functional/unknown_encoding_py30.txt | 1 - test/functional/unknown_encoding_pypy.py | 7 - test/functional/unknown_encoding_pypy.rc | 3 - test/functional/unknown_encoding_pypy.txt | 1 - test/functional/unnecessary_lambda.py | 50 - test/functional/unnecessary_lambda.txt | 7 - test/functional/unpacked_exceptions.py | 11 - test/functional/unpacked_exceptions.rc | 5 - test/functional/unpacked_exceptions.txt | 4 - test/functional/unpacking.py | 11 - test/functional/unpacking_non_sequence.py | 86 -- test/functional/unpacking_non_sequence.txt | 12 - test/functional/unused_import.py | 12 - test/functional/unused_import.txt | 7 - test/functional/useless_else_on_loop.py | 55 - test/functional/useless_else_on_loop.txt | 5 - test/functional/with_used_before_assign.py | 11 - test/functional/with_used_before_assign.txt | 2 - test/functional/yield_outside_func.py | 4 - test/functional/yield_outside_func.txt | 1 - test/input/__init__.py | 1 - test/input/func_3k_removed_stuff_py_30.py | 13 - test/input/func_assert_2uple.py | 11 - test/input/func_attrs_definition_order.py | 33 - test/input/func_bad_assigment_to_exception_var.py | 30 - test/input/func_bad_cont_dictcomp_py27.py | 38 - test/input/func_bad_exception_context_py30.py | 24 - test/input/func_base_useless_pass.py | 9 - test/input/func_block_disable_msg.py | 1026 --------------- test/input/func_break_or_return_in_try_finally.py | 44 - test/input/func_bug113231.py | 24 - test/input/func_continue_not_in_loop.py | 14 - test/input/func_dangerous_default.py | 76 -- test/input/func_defining-attr-methods_order.py | 73 -- test/input/func_deprecated_lambda_py_30.py | 24 - test/input/func_deprecated_module_py30.py | 12 - test/input/func_deprecated_module_py_30.py | 12 - test/input/func_disable_linebased.py | 14 - test/input/func_dotted_ancestor.py | 11 - test/input/func_e0001_py30.py | 12 - test/input/func_e0011.py | 5 - test/input/func_e0012.py | 5 - test/input/func_e0101.py | 35 - test/input/func_e0108.py | 14 - test/input/func_e0203.py | 19 - test/input/func_e0204.py | 19 - test/input/func_e0206.py | 20 - test/input/func_e0601.py | 9 - test/input/func_e0604.py | 13 - test/input/func_e12xx.py | 27 - test/input/func_e13xx.py | 21 - test/input/func_empty_module.py | 0 test/input/func_eval_used.py | 13 - test/input/func_excess_escapes.py | 30 - test/input/func_exec_used_py30.py | 13 - test/input/func_f0401.py | 9 - test/input/func_first_arg.py | 42 - test/input/func_fixme.py | 15 - test/input/func_i0011.py | 5 - test/input/func_i0012.py | 5 - test/input/func_i0013.py | 8 - test/input/func_i0014.py | 8 - test/input/func_i0020.py | 8 - test/input/func_i0022.py | 22 - test/input/func_import_syntax_error.py | 2 - test/input/func_indent.py | 24 - test/input/func_init_vars.py | 45 - test/input/func_interfaces.py | 112 -- test/input/func_invalid_sequence_index.py | 210 --- test/input/func_keyword_repeat.py | 11 - test/input/func_kwoa_py30.py | 12 - test/input/func_logging_not_lazy_with_logger.py | 14 - test/input/func_loopvar_in_dict_comp_py27.py | 8 - test/input/func_method_could_be_function.py | 60 - test/input/func_module___dict__.py | 9 - test/input/func_more_e0604.py | 9 - .../input/func_nameerror_on_string_substitution.py | 8 - test/input/func_no_dummy_redefined.py | 14 - ..._noerror___init___return_from_inner_function.py | 13 - ...oerror_access_attr_before_def_false_positive.py | 98 -- test/input/func_noerror_base_init_vars.py | 35 - test/input/func_noerror_builtin_module_test.py | 9 - test/input/func_noerror_class_attributes.py | 17 - test/input/func_noerror_class_decorators_py26.py | 8 - ...unc_noerror_classes_meth_could_be_a_function.py | 34 - test/input/func_noerror_classes_meth_signature.py | 38 - ...func_noerror_classes_protected_member_access.py | 25 - test/input/func_noerror_crash_122793.py | 9 - test/input/func_noerror_crash_127416.py | 20 - test/input/func_noerror_decorator_scope.py | 19 - test/input/func_noerror_e1101_13784.py | 15 - ...func_noerror_e1101_9588_base_attr_aug_assign.py | 39 - test/input/func_noerror_e1101_but_getattr.py | 23 - test/input/func_noerror_encoding.py | 6 - test/input/func_noerror_except_pass.py | 12 - test/input/func_noerror_exception.py | 7 - .../func_noerror_external_classmethod_crash.py | 21 - test/input/func_noerror_factory_method.py | 23 - test/input/func_noerror_function_as_method.py | 18 - test/input/func_noerror_genexp_in_class_scope.py | 9 - test/input/func_noerror_indirect_interface.py | 16 - test/input/func_noerror_inner_classes.py | 33 - .../input/func_noerror_lambda_use_before_assign.py | 8 - test/input/func_noerror_long_utf8_line.py | 9 - test/input/func_noerror_mcs_attr_access.py | 20 - test/input/func_noerror_nested_classes.py | 18 - test/input/func_noerror_new_style_class_py_30.py | 45 - test/input/func_noerror_no_warning_docstring.py | 42 - test/input/func_noerror_nonregr.py | 13 - .../func_noerror_object_as_class_attribute.py | 19 - test/input/func_noerror_overloaded_operator.py | 21 - .../input/func_noerror_overriden_method_varargs.py | 19 - .../func_noerror_property_affectation_py26.py | 24 - test/input/func_noerror_raise_return_self.py | 15 - test/input/func_noerror_socket_member.py | 25 - test/input/func_noerror_static_method.py | 27 - .../func_noerror_staticmethod_as_decorator_py24.py | 33 - test/input/func_noerror_super_protected.py | 22 - test/input/func_noerror_unused_variable_py30.py | 14 - test/input/func_noerror_used_before_assignment.py | 5 - test/input/func_noerror_w0232.py | 10 - test/input/func_noerror_yield_assign_py25.py | 21 - test/input/func_noerror_yield_return_mix.py | 7 - test/input/func_non_iterator_returned_py30.py | 52 - test/input/func_non_iterator_returned_py_30.py | 52 - test/input/func_nonregr___file___global.py | 8 - test/input/func_operators.py | 19 - test/input/func_r0901.py | 27 - test/input/func_r0902.py | 28 - test/input/func_r0903.py | 30 - test/input/func_r0904.py | 80 -- test/input/func_r0921.py | 15 - test/input/func_r0922.py | 21 - test/input/func_r0923.py | 31 - test/input/func_reqattrs.py | 1 - test/input/func_return_outside_func.py | 3 - test/input/func_return_yield_mix_py_33.py | 16 - test/input/func_set_literal_as_default_py27.py | 7 - test/input/func_syntax_error.py | 1 - test/input/func_tokenize_error.py | 6 - test/input/func_too_many_locals_arguments.py | 52 - test/input/func_too_many_returns_yields.py | 42 - test/input/func_toolonglines.py | 27 - test/input/func_trailing_whitespace.py | 8 - test/input/func_typecheck_callfunc_assigment.py | 63 - test/input/func_typecheck_non_callable_call.py | 118 -- test/input/func_undefined_metaclass_var_py30.py | 27 - test/input/func_unreachable.py | 21 - test/input/func_unused_import_py30.py | 21 - test/input/func_unused_overridden_argument.py | 31 - test/input/func_use_for_or_listcomp_var.py | 27 - test/input/func_used_before_assignment_py30.py | 48 - ...nc_variables_unused_name_from_wilcard_import.py | 4 - test/input/func_w0101.py | 28 - test/input/func_w0102.py | 67 - test/input/func_w0103.py | 8 - test/input/func_w0104.py | 12 - test/input/func_w0105.py | 62 - test/input/func_w0110.py | 10 - test/input/func_w0111.py | 10 - test/input/func_w0112.py | 37 - test/input/func_w0122_py_30.py | 13 - test/input/func_w0151.py | 5 - test/input/func_w0152.py | 18 - test/input/func_w0202.py | 17 - test/input/func_w0205.py | 24 - test/input/func_w0233.py | 50 - test/input/func_w0302.py | 1016 --------------- test/input/func_w0312.py | 11 - test/input/func_w0332_py_30.py | 4 - test/input/func_w0401.py | 9 - test/input/func_w0401_package/__init__.py | 2 - test/input/func_w0401_package/all_the_things.py | 9 - test/input/func_w0401_package/thing1.py | 3 - test/input/func_w0401_package/thing2.py | 7 - test/input/func_w0402.py | 12 - test/input/func_w0404.py | 27 - test/input/func_w0405.py | 31 - test/input/func_w0406.py | 9 - test/input/func_w0611.py | 25 - test/input/func_w0612.py | 37 - test/input/func_w0613.py | 42 - test/input/func_w0623_py30.py | 16 - test/input/func_w0623_py_30.py | 67 - test/input/func_w0631.py | 20 - test/input/func_w0702.py | 17 - test/input/func_w0703.py | 9 - test/input/func_w0704.py | 16 - test/input/func_w0705.py | 46 - test/input/func_w0801.py | 11 - test/input/func_w1201.py | 21 - test/input/func_w1202.py | 26 - test/input/func_with_without_as_py25.py | 12 - test/input/ignore_except_pass_by_default.py | 6 - test/input/indirect1.py | 4 - test/input/indirect2.py | 7 - test/input/indirect3.py | 5 - test/input/noext | 4 - test/input/similar1 | 22 - test/input/similar2 | 22 - test/input/syntax_error.py | 2 - test/input/w0401_cycle.py | 9 - test/input/w0801_same.py | 11 - test/messages/builtin_module.txt | 1 - test/messages/func_3k_removed_stuff_py_30.txt | 5 - test/messages/func_assert_2uple.txt | 2 - test/messages/func_attrs_definition_order.txt | 2 - .../func_bad_assigment_to_exception_var.txt | 5 - test/messages/func_bad_cont_dictcomp_py27.txt | 6 - test/messages/func_bad_exception_context_py30.txt | 3 - test/messages/func_base_useless_pass.txt | 1 - test/messages/func_block_disable_msg.txt | 13 - .../func_break_or_return_in_try_finally.txt | 3 - test/messages/func_bug113231.txt | 2 - test/messages/func_continue_not_in_loop.txt | 2 - test/messages/func_dangerous_default.txt | 13 - test/messages/func_dangerous_default_py30.txt | 13 - test/messages/func_defining-attr-methods_order.txt | 3 - test/messages/func_deprecated_lambda_py_30.txt | 2 - test/messages/func_deprecated_module_py30.txt | 2 - test/messages/func_deprecated_module_py_30.txt | 2 - test/messages/func_disable_linebased.txt | 2 - test/messages/func_disable_linebased_py30.txt | 2 - test/messages/func_dotted_ancestor.txt | 1 - test/messages/func_e0001_py30.txt | 2 - test/messages/func_e0011.txt | 1 - test/messages/func_e0012.txt | 1 - test/messages/func_e0101.txt | 2 - test/messages/func_e0108.txt | 3 - test/messages/func_e0203.txt | 1 - test/messages/func_e0204.txt | 3 - test/messages/func_e0206.txt | 3 - test/messages/func_e0601.txt | 1 - test/messages/func_e0604.txt | 1 - test/messages/func_e12xx.txt | 6 - test/messages/func_e13xx.txt | 13 - test/messages/func_e13xx_py30.txt | 11 - test/messages/func_empty_module.txt | 1 - test/messages/func_eval_used.txt | 4 - test/messages/func_excess_escapes.txt | 9 - test/messages/func_exec_used_py30.txt | 4 - test/messages/func_f0401.txt | 2 - test/messages/func_first_arg.txt | 5 - test/messages/func_fixme.txt | 5 - test/messages/func_i0011.txt | 2 - test/messages/func_i0012.txt | 1 - test/messages/func_i0013.txt | 1 - test/messages/func_i0014.txt | 2 - test/messages/func_i0020.txt | 2 - test/messages/func_i0022.txt | 21 - test/messages/func_import_syntax_error.txt | 4 - test/messages/func_import_syntax_error_py30.txt | 4 - test/messages/func_indent.txt | 5 - test/messages/func_init_vars.txt | 1 - test/messages/func_interfaces.txt | 6 - test/messages/func_invalid_sequence_index.txt | 19 - test/messages/func_keyword_repeat_py26.txt | 1 - test/messages/func_kwoa_py30.txt | 5 - .../messages/func_logging_not_lazy_with_logger.txt | 4 - test/messages/func_loopvar_in_dict_comp_py27.txt | 1 - test/messages/func_method_could_be_function.txt | 1 - test/messages/func_module___dict__.txt | 1 - test/messages/func_more_e0604.txt | 1 - .../func_nameerror_on_string_substitution.txt | 2 - test/messages/func_no_dummy_redefined.txt | 2 - test/messages/func_non_iterator_returned_py30.txt | 3 - test/messages/func_non_iterator_returned_py_30.txt | 3 - test/messages/func_nonregr___file___global.txt | 2 - test/messages/func_operators.txt | 6 - test/messages/func_r0901.txt | 2 - test/messages/func_r0902.txt | 1 - test/messages/func_r0903.txt | 1 - test/messages/func_r0904.txt | 1 - test/messages/func_r0921.txt | 1 - test/messages/func_r0922.txt | 1 - test/messages/func_r0923.txt | 1 - test/messages/func_raw_escapes.txt | 3 - test/messages/func_reqattrs.txt | 1 - test/messages/func_return_outside_func.txt | 1 - test/messages/func_return_yield_mix_py_33.txt | 2 - test/messages/func_set_literal_as_default_py27.txt | 1 - test/messages/func_syntax_error.txt | 3 - test/messages/func_tokenize_error.txt | 1 - test/messages/func_too_many_locals_arguments.txt | 2 - test/messages/func_too_many_returns_yields.txt | 1 - test/messages/func_toolonglines.txt | 6 - test/messages/func_toolonglines_py30.txt | 4 - test/messages/func_trailing_whitespace.txt | 3 - .../messages/func_typecheck_callfunc_assigment.txt | 3 - test/messages/func_typecheck_getattr_py30.txt | 9 - test/messages/func_typecheck_non_callable_call.txt | 8 - .../messages/func_undefined_metaclass_var_py30.txt | 2 - test/messages/func_unicode_literal_py26.txt | 0 test/messages/func_unicode_literal_py274.txt | 1 - test/messages/func_unreachable.txt | 3 - test/messages/func_unused_import_py30.txt | 1 - test/messages/func_unused_overridden_argument.txt | 1 - .../messages/func_use_for_or_listcomp_var_py29.txt | 3 - .../messages/func_use_for_or_listcomp_var_py30.txt | 3 - test/messages/func_used_before_assignment_py30.txt | 6 - ...c_variables_unused_name_from_wilcard_import.txt | 4 - test/messages/func_w0101.txt | 1 - test/messages/func_w0102.txt | 5 - test/messages/func_w0103.txt | 1 - test/messages/func_w0104.txt | 1 - test/messages/func_w0105.txt | 1 - test/messages/func_w0110.txt | 1 - test/messages/func_w0111.txt | 1 - test/messages/func_w0112.txt | 1 - test/messages/func_w0122_py_30.txt | 5 - test/messages/func_w0151.txt | 1 - test/messages/func_w0152_py29.txt | 2 - test/messages/func_w0152_py30.txt | 4 - test/messages/func_w0202.txt | 3 - test/messages/func_w0205.txt | 2 - test/messages/func_w0233.txt | 5 - test/messages/func_w0302.txt | 2 - test/messages/func_w0312.txt | 2 - test/messages/func_w0332_py_30.txt | 1 - test/messages/func_w0401.txt | 2 - test/messages/func_w0401_package.txt | 1 - test/messages/func_w0402.txt | 3 - test/messages/func_w0404.txt | 5 - test/messages/func_w0405.txt | 4 - test/messages/func_w0406.txt | 1 - test/messages/func_w0611.txt | 1 - test/messages/func_w0612.txt | 6 - test/messages/func_w0613.txt | 5 - test/messages/func_w0622.txt | 2 - test/messages/func_w0623.txt | 11 - test/messages/func_w0623_py30.txt | 3 - test/messages/func_w0623_py_30.txt | 12 - test/messages/func_w0631.txt | 1 - test/messages/func_w0702.txt | 2 - test/messages/func_w0703.txt | 1 - test/messages/func_w0704.txt | 1 - test/messages/func_w0705.txt | 15 - test/messages/func_w0801.txt | 11 - test/messages/func_w1201.txt | 3 - test/messages/func_w1202.txt | 4 - test/messages/func_with_without_as_py25.txt | 3 - test/regrtest_data/absimp/__init__.py | 5 - test/regrtest_data/absimp/string.py | 7 - test/regrtest_data/application_crash.py | 12 - test/regrtest_data/classdoc_usage.py | 17 - test/regrtest_data/decimal_inference.py | 10 - test/regrtest_data/descriptor_crash.py | 20 - test/regrtest_data/import_assign.py | 5 - .../import_package_subpackage_module.py | 49 - test/regrtest_data/module_global.py | 7 - test/regrtest_data/no_stdout_encoding.py | 5 - test/regrtest_data/numarray_import.py | 7 - test/regrtest_data/numarray_inf.py | 5 - test/regrtest_data/package/AudioTime.py | 3 - test/regrtest_data/package/__init__.py | 14 - test/regrtest_data/package/subpackage/__init__.py | 1 - test/regrtest_data/package/subpackage/module.py | 1 - test/regrtest_data/package_all/__init__.py | 3 - test/regrtest_data/package_all/notmissing.py | 2 - test/regrtest_data/precedence_test.py | 21 - .../special_attr_scope_lookup_crash.py | 3 - .../regrtest_data/try_finally_disable_msg_crash.py | 5 - test/test_func.py | 95 -- test/test_functional.py | 369 ------ test/test_import_graph.py | 68 - test/test_regr.py | 142 --- test/test_self.py | 152 --- test/unittest_checker_base.py | 233 ---- test/unittest_checker_classes.py | 82 -- test/unittest_checker_exceptions.py | 61 - test/unittest_checker_format.py | 253 ---- test/unittest_checker_logging.py | 47 - test/unittest_checker_misc.py | 49 - test/unittest_checker_python3.py | 301 ----- test/unittest_checker_similar.py | 142 --- test/unittest_checker_spelling.py | 93 -- test/unittest_checker_typecheck.py | 42 - test/unittest_checker_variables.py | 99 -- test/unittest_checkers_utils.py | 71 -- test/unittest_lint.py | 697 ---------- test/unittest_pyreverse_diadefs.py | 171 --- test/unittest_pyreverse_writer.py | 136 -- test/unittest_reporters_json.py | 61 - test/unittest_reporting.py | 125 -- test/unittest_utils.py | 62 - testutils.py | 412 ------ utils.py | 919 -------------- 1214 files changed, 30047 insertions(+), 30047 deletions(-) delete mode 100644 __init__.py delete mode 100644 __main__.py delete mode 100644 checkers/__init__.py delete mode 100644 checkers/base.py delete mode 100644 checkers/classes.py delete mode 100644 checkers/design_analysis.py delete mode 100644 checkers/exceptions.py delete mode 100644 checkers/format.py delete mode 100644 checkers/imports.py delete mode 100644 checkers/logging.py delete mode 100644 checkers/misc.py delete mode 100644 checkers/newstyle.py delete mode 100644 checkers/python3.py delete mode 100644 checkers/raw_metrics.py delete mode 100644 checkers/similar.py delete mode 100644 checkers/spelling.py delete mode 100644 checkers/stdlib.py delete mode 100644 checkers/strings.py delete mode 100644 checkers/typecheck.py delete mode 100644 checkers/utils.py delete mode 100644 checkers/variables.py delete mode 100644 config.py delete mode 100755 epylint.py delete mode 100644 gui.py delete mode 100644 interfaces.py delete mode 100644 lint.py create mode 100644 pylint/__init__.py create mode 100644 pylint/__main__.py create mode 100644 pylint/checkers/__init__.py create mode 100644 pylint/checkers/base.py create mode 100644 pylint/checkers/classes.py create mode 100644 pylint/checkers/design_analysis.py create mode 100644 pylint/checkers/exceptions.py create mode 100644 pylint/checkers/format.py create mode 100644 pylint/checkers/imports.py create mode 100644 pylint/checkers/logging.py create mode 100644 pylint/checkers/misc.py create mode 100644 pylint/checkers/newstyle.py create mode 100644 pylint/checkers/python3.py create mode 100644 pylint/checkers/raw_metrics.py create mode 100644 pylint/checkers/similar.py create mode 100644 pylint/checkers/spelling.py create mode 100644 pylint/checkers/stdlib.py create mode 100644 pylint/checkers/strings.py create mode 100644 pylint/checkers/typecheck.py create mode 100644 pylint/checkers/utils.py create mode 100644 pylint/checkers/variables.py create mode 100644 pylint/config.py create mode 100755 pylint/epylint.py create mode 100644 pylint/gui.py create mode 100644 pylint/interfaces.py create mode 100644 pylint/lint.py create mode 100644 pylint/pyreverse/__init__.py create mode 100644 pylint/pyreverse/diadefslib.py create mode 100644 pylint/pyreverse/diagrams.py create mode 100644 pylint/pyreverse/main.py create mode 100644 pylint/pyreverse/utils.py create mode 100644 pylint/pyreverse/writer.py create mode 100644 pylint/reporters/__init__.py create mode 100644 pylint/reporters/guireporter.py create mode 100644 pylint/reporters/html.py create mode 100644 pylint/reporters/json.py create mode 100644 pylint/reporters/text.py create mode 100644 pylint/test/data/__init__.py create mode 100755 pylint/test/data/ascript create mode 100644 pylint/test/data/classes_No_Name.dot create mode 100644 pylint/test/data/clientmodule_test.py create mode 100644 pylint/test/data/packages_No_Name.dot create mode 100644 pylint/test/data/suppliermodule_test.py create mode 100644 pylint/test/functional/__init__.py create mode 100644 pylint/test/functional/abstract_abc_methods.py create mode 100644 pylint/test/functional/abstract_class_instantiated_py2.py create mode 100644 pylint/test/functional/abstract_class_instantiated_py2.rc create mode 100644 pylint/test/functional/abstract_class_instantiated_py2.txt create mode 100644 pylint/test/functional/abstract_class_instantiated_py3.py create mode 100644 pylint/test/functional/abstract_class_instantiated_py3.rc create mode 100644 pylint/test/functional/abstract_class_instantiated_py3.txt create mode 100644 pylint/test/functional/abstract_class_instantiated_py34.py create mode 100644 pylint/test/functional/abstract_class_instantiated_py34.rc create mode 100644 pylint/test/functional/abstract_class_instantiated_py34.txt create mode 100644 pylint/test/functional/abstract_method_py2.py create mode 100644 pylint/test/functional/abstract_method_py2.rc create mode 100644 pylint/test/functional/abstract_method_py2.txt create mode 100644 pylint/test/functional/abstract_method_py3.py create mode 100644 pylint/test/functional/abstract_method_py3.rc create mode 100644 pylint/test/functional/abstract_method_py3.txt create mode 100644 pylint/test/functional/access_to__name__.py create mode 100644 pylint/test/functional/access_to__name__.txt create mode 100644 pylint/test/functional/access_to_protected_members.py create mode 100644 pylint/test/functional/access_to_protected_members.txt create mode 100644 pylint/test/functional/anomalous_unicode_escape.py create mode 100644 pylint/test/functional/anomalous_unicode_escape.txt create mode 100644 pylint/test/functional/arguments.py create mode 100644 pylint/test/functional/arguments.txt create mode 100644 pylint/test/functional/assigning_non_slot.py create mode 100644 pylint/test/functional/assigning_non_slot.txt create mode 100644 pylint/test/functional/bad_context_manager.py create mode 100644 pylint/test/functional/bad_context_manager.txt create mode 100644 pylint/test/functional/bad_continuation.py create mode 100644 pylint/test/functional/bad_continuation.txt create mode 100644 pylint/test/functional/bad_inline_option.py create mode 100644 pylint/test/functional/bad_inline_option.rc create mode 100644 pylint/test/functional/bad_inline_option.txt create mode 100644 pylint/test/functional/bad_open_mode.py create mode 100644 pylint/test/functional/bad_open_mode.rc create mode 100644 pylint/test/functional/bad_open_mode.txt create mode 100644 pylint/test/functional/bad_open_mode_py3.py create mode 100644 pylint/test/functional/bad_open_mode_py3.rc create mode 100644 pylint/test/functional/bad_open_mode_py3.txt create mode 100644 pylint/test/functional/bad_reversed_sequence.py create mode 100644 pylint/test/functional/bad_reversed_sequence.txt create mode 100644 pylint/test/functional/boolean_datetime.py create mode 100644 pylint/test/functional/boolean_datetime.txt create mode 100644 pylint/test/functional/cellvar_escaping_loop.py create mode 100644 pylint/test/functional/cellvar_escaping_loop.txt create mode 100644 pylint/test/functional/class_members_py27.py create mode 100644 pylint/test/functional/class_members_py27.rc create mode 100644 pylint/test/functional/class_members_py27.txt create mode 100644 pylint/test/functional/class_members_py30.py create mode 100644 pylint/test/functional/class_members_py30.rc create mode 100644 pylint/test/functional/class_members_py30.txt create mode 100644 pylint/test/functional/class_scope.py create mode 100644 pylint/test/functional/class_scope.txt create mode 100644 pylint/test/functional/confidence_filter.py create mode 100644 pylint/test/functional/confidence_filter.rc create mode 100644 pylint/test/functional/confidence_filter.txt create mode 100644 pylint/test/functional/crash_missing_module_type.py create mode 100644 pylint/test/functional/crash_missing_module_type.txt create mode 100644 pylint/test/functional/ctor_arguments.py create mode 100644 pylint/test/functional/ctor_arguments.txt create mode 100644 pylint/test/functional/defined_and_used_on_same_line.py create mode 100644 pylint/test/functional/docstrings.py create mode 100644 pylint/test/functional/docstrings.txt create mode 100644 pylint/test/functional/duplicate_dict_literal_key.py create mode 100644 pylint/test/functional/duplicate_dict_literal_key.txt create mode 100644 pylint/test/functional/exception_is_binary_op.py create mode 100644 pylint/test/functional/exception_is_binary_op.txt create mode 100644 pylint/test/functional/formatting.txt create mode 100644 pylint/test/functional/future_import.py create mode 100644 pylint/test/functional/future_unicode_literals.py create mode 100644 pylint/test/functional/future_unicode_literals.txt create mode 100644 pylint/test/functional/generated_members.py create mode 100644 pylint/test/functional/generated_members.rc create mode 100644 pylint/test/functional/genexpr_variable_scope.py create mode 100644 pylint/test/functional/genexpr_variable_scope.txt create mode 100644 pylint/test/functional/globals.py create mode 100644 pylint/test/functional/globals.txt create mode 100644 pylint/test/functional/import_error.py create mode 100644 pylint/test/functional/import_error.txt create mode 100644 pylint/test/functional/indexing_exception.py create mode 100644 pylint/test/functional/indexing_exception.rc create mode 100644 pylint/test/functional/indexing_exception.txt create mode 100644 pylint/test/functional/inherit_non_class.py create mode 100644 pylint/test/functional/inherit_non_class.txt create mode 100644 pylint/test/functional/init_not_called.py create mode 100644 pylint/test/functional/init_not_called.txt create mode 100644 pylint/test/functional/invalid__all__object.py create mode 100644 pylint/test/functional/invalid__all__object.txt create mode 100644 pylint/test/functional/invalid_encoded_data.py create mode 100644 pylint/test/functional/invalid_encoded_data.txt create mode 100644 pylint/test/functional/invalid_exceptions_caught.py create mode 100644 pylint/test/functional/invalid_exceptions_caught.txt create mode 100644 pylint/test/functional/invalid_exceptions_raised.py create mode 100644 pylint/test/functional/invalid_exceptions_raised.txt create mode 100644 pylint/test/functional/invalid_name.py create mode 100644 pylint/test/functional/invalid_name.txt create mode 100644 pylint/test/functional/invalid_slice_index.py create mode 100644 pylint/test/functional/invalid_slice_index.txt create mode 100644 pylint/test/functional/line_endings.py create mode 100644 pylint/test/functional/line_endings.rc create mode 100644 pylint/test/functional/line_endings.txt create mode 100644 pylint/test/functional/long_lines_with_utf8.py create mode 100644 pylint/test/functional/long_lines_with_utf8.txt create mode 100644 pylint/test/functional/member_checks.py create mode 100644 pylint/test/functional/member_checks.txt create mode 100644 pylint/test/functional/method_hidden.py create mode 100644 pylint/test/functional/method_hidden.txt create mode 100644 pylint/test/functional/missing_final_newline.py create mode 100644 pylint/test/functional/missing_final_newline.txt create mode 100644 pylint/test/functional/missing_self_argument.py create mode 100644 pylint/test/functional/missing_self_argument.txt create mode 100644 pylint/test/functional/name_styles.py create mode 100644 pylint/test/functional/name_styles.rc create mode 100644 pylint/test/functional/name_styles.txt create mode 100644 pylint/test/functional/namedtuple_member_inference.py create mode 100644 pylint/test/functional/namedtuple_member_inference.txt create mode 100644 pylint/test/functional/names_in__all__.py create mode 100644 pylint/test/functional/names_in__all__.txt create mode 100644 pylint/test/functional/newstyle__slots__.py create mode 100644 pylint/test/functional/newstyle__slots__.txt create mode 100644 pylint/test/functional/newstyle_properties.py create mode 100644 pylint/test/functional/newstyle_properties.txt create mode 100644 pylint/test/functional/no_name_in_module.py create mode 100644 pylint/test/functional/no_name_in_module.txt create mode 100644 pylint/test/functional/old_style_class_py27.py create mode 100644 pylint/test/functional/old_style_class_py27.rc create mode 100644 pylint/test/functional/old_style_class_py27.txt create mode 100644 pylint/test/functional/pygtk_enum_crash.py create mode 100644 pylint/test/functional/pygtk_enum_crash.rc create mode 100644 pylint/test/functional/pygtk_import.py create mode 100644 pylint/test/functional/pygtk_import.rc create mode 100644 pylint/test/functional/raising_non_exception_py3.py create mode 100644 pylint/test/functional/raising_non_exception_py3.rc create mode 100644 pylint/test/functional/raising_non_exception_py3.txt create mode 100644 pylint/test/functional/redefined_builtin.py create mode 100644 pylint/test/functional/redefined_builtin.txt create mode 100644 pylint/test/functional/redundant_unittest_assert.py create mode 100644 pylint/test/functional/redundant_unittest_assert.txt create mode 100644 pylint/test/functional/slots_checks.py create mode 100644 pylint/test/functional/slots_checks.txt create mode 100644 pylint/test/functional/socketerror_import.py create mode 100644 pylint/test/functional/statement_without_effect.py create mode 100644 pylint/test/functional/statement_without_effect.txt create mode 100644 pylint/test/functional/string_formatting.py create mode 100644 pylint/test/functional/string_formatting.txt create mode 100644 pylint/test/functional/string_formatting_py27.py create mode 100644 pylint/test/functional/string_formatting_py27.rc create mode 100644 pylint/test/functional/string_formatting_py27.txt create mode 100644 pylint/test/functional/super_checks.py create mode 100644 pylint/test/functional/super_checks.txt create mode 100644 pylint/test/functional/superfluous_parens.py create mode 100644 pylint/test/functional/superfluous_parens.txt create mode 100644 pylint/test/functional/suspicious_str_strip_call.py create mode 100644 pylint/test/functional/suspicious_str_strip_call.rc create mode 100644 pylint/test/functional/suspicious_str_strip_call.txt create mode 100644 pylint/test/functional/suspicious_str_strip_call_py3.py create mode 100644 pylint/test/functional/suspicious_str_strip_call_py3.rc create mode 100644 pylint/test/functional/suspicious_str_strip_call_py3.txt create mode 100644 pylint/test/functional/too_many_branches.py create mode 100644 pylint/test/functional/too_many_branches.txt create mode 100644 pylint/test/functional/too_many_lines_disabled.py create mode 100644 pylint/test/functional/unbalanced_tuple_unpacking.py create mode 100644 pylint/test/functional/unbalanced_tuple_unpacking.txt create mode 100644 pylint/test/functional/unbalanced_tuple_unpacking_py30.py create mode 100644 pylint/test/functional/unbalanced_tuple_unpacking_py30.rc create mode 100644 pylint/test/functional/undefined_variable.py create mode 100644 pylint/test/functional/undefined_variable.txt create mode 100644 pylint/test/functional/undefined_variable_py30.py create mode 100644 pylint/test/functional/undefined_variable_py30.rc create mode 100644 pylint/test/functional/undefined_variable_py30.txt create mode 100644 pylint/test/functional/uninferable_all_object.py create mode 100644 pylint/test/functional/unknown_encoding_py29.py create mode 100644 pylint/test/functional/unknown_encoding_py29.rc create mode 100644 pylint/test/functional/unknown_encoding_py29.txt create mode 100644 pylint/test/functional/unknown_encoding_py30.py create mode 100644 pylint/test/functional/unknown_encoding_py30.rc create mode 100644 pylint/test/functional/unknown_encoding_py30.txt create mode 100644 pylint/test/functional/unknown_encoding_pypy.py create mode 100644 pylint/test/functional/unknown_encoding_pypy.rc create mode 100644 pylint/test/functional/unknown_encoding_pypy.txt create mode 100644 pylint/test/functional/unnecessary_lambda.py create mode 100644 pylint/test/functional/unnecessary_lambda.txt create mode 100644 pylint/test/functional/unpacked_exceptions.py create mode 100644 pylint/test/functional/unpacked_exceptions.rc create mode 100644 pylint/test/functional/unpacked_exceptions.txt create mode 100644 pylint/test/functional/unpacking.py create mode 100644 pylint/test/functional/unpacking_non_sequence.py create mode 100644 pylint/test/functional/unpacking_non_sequence.txt create mode 100644 pylint/test/functional/unused_import.py create mode 100644 pylint/test/functional/unused_import.txt create mode 100644 pylint/test/functional/useless_else_on_loop.py create mode 100644 pylint/test/functional/useless_else_on_loop.txt create mode 100644 pylint/test/functional/with_used_before_assign.py create mode 100644 pylint/test/functional/with_used_before_assign.txt create mode 100644 pylint/test/functional/yield_outside_func.py create mode 100644 pylint/test/functional/yield_outside_func.txt create mode 100644 pylint/test/input/__init__.py create mode 100644 pylint/test/input/func_3k_removed_stuff_py_30.py create mode 100644 pylint/test/input/func_assert_2uple.py create mode 100644 pylint/test/input/func_attrs_definition_order.py create mode 100644 pylint/test/input/func_bad_assigment_to_exception_var.py create mode 100644 pylint/test/input/func_bad_cont_dictcomp_py27.py create mode 100644 pylint/test/input/func_bad_exception_context_py30.py create mode 100644 pylint/test/input/func_base_useless_pass.py create mode 100644 pylint/test/input/func_block_disable_msg.py create mode 100644 pylint/test/input/func_break_or_return_in_try_finally.py create mode 100644 pylint/test/input/func_bug113231.py create mode 100644 pylint/test/input/func_continue_not_in_loop.py create mode 100644 pylint/test/input/func_dangerous_default.py create mode 100644 pylint/test/input/func_defining-attr-methods_order.py create mode 100644 pylint/test/input/func_deprecated_lambda_py_30.py create mode 100644 pylint/test/input/func_deprecated_module_py30.py create mode 100644 pylint/test/input/func_deprecated_module_py_30.py create mode 100644 pylint/test/input/func_disable_linebased.py create mode 100644 pylint/test/input/func_dotted_ancestor.py create mode 100644 pylint/test/input/func_e0001_py30.py create mode 100644 pylint/test/input/func_e0011.py create mode 100644 pylint/test/input/func_e0012.py create mode 100644 pylint/test/input/func_e0101.py create mode 100644 pylint/test/input/func_e0108.py create mode 100644 pylint/test/input/func_e0203.py create mode 100644 pylint/test/input/func_e0204.py create mode 100644 pylint/test/input/func_e0206.py create mode 100644 pylint/test/input/func_e0601.py create mode 100644 pylint/test/input/func_e0604.py create mode 100644 pylint/test/input/func_e12xx.py create mode 100644 pylint/test/input/func_e13xx.py create mode 100644 pylint/test/input/func_empty_module.py create mode 100644 pylint/test/input/func_eval_used.py create mode 100644 pylint/test/input/func_excess_escapes.py create mode 100644 pylint/test/input/func_exec_used_py30.py create mode 100644 pylint/test/input/func_f0401.py create mode 100644 pylint/test/input/func_first_arg.py create mode 100644 pylint/test/input/func_fixme.py create mode 100644 pylint/test/input/func_i0011.py create mode 100644 pylint/test/input/func_i0012.py create mode 100644 pylint/test/input/func_i0013.py create mode 100644 pylint/test/input/func_i0014.py create mode 100644 pylint/test/input/func_i0020.py create mode 100644 pylint/test/input/func_i0022.py create mode 100644 pylint/test/input/func_import_syntax_error.py create mode 100644 pylint/test/input/func_indent.py create mode 100644 pylint/test/input/func_init_vars.py create mode 100644 pylint/test/input/func_interfaces.py create mode 100644 pylint/test/input/func_invalid_sequence_index.py create mode 100644 pylint/test/input/func_keyword_repeat.py create mode 100644 pylint/test/input/func_kwoa_py30.py create mode 100644 pylint/test/input/func_logging_not_lazy_with_logger.py create mode 100644 pylint/test/input/func_loopvar_in_dict_comp_py27.py create mode 100644 pylint/test/input/func_method_could_be_function.py create mode 100644 pylint/test/input/func_module___dict__.py create mode 100644 pylint/test/input/func_more_e0604.py create mode 100644 pylint/test/input/func_nameerror_on_string_substitution.py create mode 100644 pylint/test/input/func_no_dummy_redefined.py create mode 100644 pylint/test/input/func_noerror___init___return_from_inner_function.py create mode 100644 pylint/test/input/func_noerror_access_attr_before_def_false_positive.py create mode 100644 pylint/test/input/func_noerror_base_init_vars.py create mode 100644 pylint/test/input/func_noerror_builtin_module_test.py create mode 100644 pylint/test/input/func_noerror_class_attributes.py create mode 100644 pylint/test/input/func_noerror_class_decorators_py26.py create mode 100644 pylint/test/input/func_noerror_classes_meth_could_be_a_function.py create mode 100644 pylint/test/input/func_noerror_classes_meth_signature.py create mode 100644 pylint/test/input/func_noerror_classes_protected_member_access.py create mode 100644 pylint/test/input/func_noerror_crash_122793.py create mode 100644 pylint/test/input/func_noerror_crash_127416.py create mode 100644 pylint/test/input/func_noerror_decorator_scope.py create mode 100644 pylint/test/input/func_noerror_e1101_13784.py create mode 100644 pylint/test/input/func_noerror_e1101_9588_base_attr_aug_assign.py create mode 100644 pylint/test/input/func_noerror_e1101_but_getattr.py create mode 100644 pylint/test/input/func_noerror_encoding.py create mode 100644 pylint/test/input/func_noerror_except_pass.py create mode 100644 pylint/test/input/func_noerror_exception.py create mode 100644 pylint/test/input/func_noerror_external_classmethod_crash.py create mode 100644 pylint/test/input/func_noerror_factory_method.py create mode 100644 pylint/test/input/func_noerror_function_as_method.py create mode 100644 pylint/test/input/func_noerror_genexp_in_class_scope.py create mode 100644 pylint/test/input/func_noerror_indirect_interface.py create mode 100644 pylint/test/input/func_noerror_inner_classes.py create mode 100644 pylint/test/input/func_noerror_lambda_use_before_assign.py create mode 100644 pylint/test/input/func_noerror_long_utf8_line.py create mode 100644 pylint/test/input/func_noerror_mcs_attr_access.py create mode 100644 pylint/test/input/func_noerror_nested_classes.py create mode 100644 pylint/test/input/func_noerror_new_style_class_py_30.py create mode 100644 pylint/test/input/func_noerror_no_warning_docstring.py create mode 100644 pylint/test/input/func_noerror_nonregr.py create mode 100644 pylint/test/input/func_noerror_object_as_class_attribute.py create mode 100644 pylint/test/input/func_noerror_overloaded_operator.py create mode 100644 pylint/test/input/func_noerror_overriden_method_varargs.py create mode 100644 pylint/test/input/func_noerror_property_affectation_py26.py create mode 100644 pylint/test/input/func_noerror_raise_return_self.py create mode 100644 pylint/test/input/func_noerror_socket_member.py create mode 100644 pylint/test/input/func_noerror_static_method.py create mode 100644 pylint/test/input/func_noerror_staticmethod_as_decorator_py24.py create mode 100644 pylint/test/input/func_noerror_super_protected.py create mode 100644 pylint/test/input/func_noerror_unused_variable_py30.py create mode 100644 pylint/test/input/func_noerror_used_before_assignment.py create mode 100644 pylint/test/input/func_noerror_w0232.py create mode 100644 pylint/test/input/func_noerror_yield_assign_py25.py create mode 100644 pylint/test/input/func_noerror_yield_return_mix.py create mode 100644 pylint/test/input/func_non_iterator_returned_py30.py create mode 100644 pylint/test/input/func_non_iterator_returned_py_30.py create mode 100644 pylint/test/input/func_nonregr___file___global.py create mode 100644 pylint/test/input/func_operators.py create mode 100644 pylint/test/input/func_r0901.py create mode 100644 pylint/test/input/func_r0902.py create mode 100644 pylint/test/input/func_r0903.py create mode 100644 pylint/test/input/func_r0904.py create mode 100644 pylint/test/input/func_r0921.py create mode 100644 pylint/test/input/func_r0922.py create mode 100644 pylint/test/input/func_r0923.py create mode 100644 pylint/test/input/func_reqattrs.py create mode 100644 pylint/test/input/func_return_outside_func.py create mode 100644 pylint/test/input/func_return_yield_mix_py_33.py create mode 100644 pylint/test/input/func_set_literal_as_default_py27.py create mode 100644 pylint/test/input/func_syntax_error.py create mode 100644 pylint/test/input/func_tokenize_error.py create mode 100644 pylint/test/input/func_too_many_locals_arguments.py create mode 100644 pylint/test/input/func_too_many_returns_yields.py create mode 100644 pylint/test/input/func_toolonglines.py create mode 100644 pylint/test/input/func_trailing_whitespace.py create mode 100644 pylint/test/input/func_typecheck_callfunc_assigment.py create mode 100644 pylint/test/input/func_typecheck_non_callable_call.py create mode 100644 pylint/test/input/func_undefined_metaclass_var_py30.py create mode 100644 pylint/test/input/func_unreachable.py create mode 100644 pylint/test/input/func_unused_import_py30.py create mode 100644 pylint/test/input/func_unused_overridden_argument.py create mode 100644 pylint/test/input/func_use_for_or_listcomp_var.py create mode 100644 pylint/test/input/func_used_before_assignment_py30.py create mode 100644 pylint/test/input/func_variables_unused_name_from_wilcard_import.py create mode 100644 pylint/test/input/func_w0101.py create mode 100644 pylint/test/input/func_w0102.py create mode 100644 pylint/test/input/func_w0103.py create mode 100644 pylint/test/input/func_w0104.py create mode 100644 pylint/test/input/func_w0105.py create mode 100644 pylint/test/input/func_w0110.py create mode 100644 pylint/test/input/func_w0111.py create mode 100644 pylint/test/input/func_w0112.py create mode 100644 pylint/test/input/func_w0122_py_30.py create mode 100644 pylint/test/input/func_w0151.py create mode 100644 pylint/test/input/func_w0152.py create mode 100644 pylint/test/input/func_w0202.py create mode 100644 pylint/test/input/func_w0205.py create mode 100644 pylint/test/input/func_w0233.py create mode 100644 pylint/test/input/func_w0302.py create mode 100644 pylint/test/input/func_w0312.py create mode 100644 pylint/test/input/func_w0332_py_30.py create mode 100644 pylint/test/input/func_w0401.py create mode 100644 pylint/test/input/func_w0401_package/__init__.py create mode 100644 pylint/test/input/func_w0401_package/all_the_things.py create mode 100644 pylint/test/input/func_w0401_package/thing1.py create mode 100644 pylint/test/input/func_w0401_package/thing2.py create mode 100644 pylint/test/input/func_w0402.py create mode 100644 pylint/test/input/func_w0404.py create mode 100644 pylint/test/input/func_w0405.py create mode 100644 pylint/test/input/func_w0406.py create mode 100644 pylint/test/input/func_w0611.py create mode 100644 pylint/test/input/func_w0612.py create mode 100644 pylint/test/input/func_w0613.py create mode 100644 pylint/test/input/func_w0623_py30.py create mode 100644 pylint/test/input/func_w0623_py_30.py create mode 100644 pylint/test/input/func_w0631.py create mode 100644 pylint/test/input/func_w0702.py create mode 100644 pylint/test/input/func_w0703.py create mode 100644 pylint/test/input/func_w0704.py create mode 100644 pylint/test/input/func_w0705.py create mode 100644 pylint/test/input/func_w0801.py create mode 100644 pylint/test/input/func_w1201.py create mode 100644 pylint/test/input/func_w1202.py create mode 100644 pylint/test/input/func_with_without_as_py25.py create mode 100644 pylint/test/input/ignore_except_pass_by_default.py create mode 100644 pylint/test/input/indirect1.py create mode 100644 pylint/test/input/indirect2.py create mode 100644 pylint/test/input/indirect3.py create mode 100644 pylint/test/input/noext create mode 100644 pylint/test/input/similar1 create mode 100644 pylint/test/input/similar2 create mode 100644 pylint/test/input/syntax_error.py create mode 100644 pylint/test/input/w0401_cycle.py create mode 100644 pylint/test/input/w0801_same.py create mode 100644 pylint/test/messages/builtin_module.txt create mode 100644 pylint/test/messages/func_3k_removed_stuff_py_30.txt create mode 100644 pylint/test/messages/func_assert_2uple.txt create mode 100644 pylint/test/messages/func_attrs_definition_order.txt create mode 100644 pylint/test/messages/func_bad_assigment_to_exception_var.txt create mode 100644 pylint/test/messages/func_bad_cont_dictcomp_py27.txt create mode 100644 pylint/test/messages/func_bad_exception_context_py30.txt create mode 100644 pylint/test/messages/func_base_useless_pass.txt create mode 100644 pylint/test/messages/func_block_disable_msg.txt create mode 100644 pylint/test/messages/func_break_or_return_in_try_finally.txt create mode 100644 pylint/test/messages/func_bug113231.txt create mode 100644 pylint/test/messages/func_continue_not_in_loop.txt create mode 100644 pylint/test/messages/func_dangerous_default.txt create mode 100644 pylint/test/messages/func_dangerous_default_py30.txt create mode 100644 pylint/test/messages/func_defining-attr-methods_order.txt create mode 100644 pylint/test/messages/func_deprecated_lambda_py_30.txt create mode 100644 pylint/test/messages/func_deprecated_module_py30.txt create mode 100644 pylint/test/messages/func_deprecated_module_py_30.txt create mode 100644 pylint/test/messages/func_disable_linebased.txt create mode 100644 pylint/test/messages/func_disable_linebased_py30.txt create mode 100644 pylint/test/messages/func_dotted_ancestor.txt create mode 100644 pylint/test/messages/func_e0001_py30.txt create mode 100644 pylint/test/messages/func_e0011.txt create mode 100644 pylint/test/messages/func_e0012.txt create mode 100644 pylint/test/messages/func_e0101.txt create mode 100644 pylint/test/messages/func_e0108.txt create mode 100644 pylint/test/messages/func_e0203.txt create mode 100644 pylint/test/messages/func_e0204.txt create mode 100644 pylint/test/messages/func_e0206.txt create mode 100644 pylint/test/messages/func_e0601.txt create mode 100644 pylint/test/messages/func_e0604.txt create mode 100644 pylint/test/messages/func_e12xx.txt create mode 100644 pylint/test/messages/func_e13xx.txt create mode 100644 pylint/test/messages/func_e13xx_py30.txt create mode 100644 pylint/test/messages/func_empty_module.txt create mode 100644 pylint/test/messages/func_eval_used.txt create mode 100644 pylint/test/messages/func_excess_escapes.txt create mode 100644 pylint/test/messages/func_exec_used_py30.txt create mode 100644 pylint/test/messages/func_f0401.txt create mode 100644 pylint/test/messages/func_first_arg.txt create mode 100644 pylint/test/messages/func_fixme.txt create mode 100644 pylint/test/messages/func_i0011.txt create mode 100644 pylint/test/messages/func_i0012.txt create mode 100644 pylint/test/messages/func_i0013.txt create mode 100644 pylint/test/messages/func_i0014.txt create mode 100644 pylint/test/messages/func_i0020.txt create mode 100644 pylint/test/messages/func_i0022.txt create mode 100644 pylint/test/messages/func_import_syntax_error.txt create mode 100644 pylint/test/messages/func_import_syntax_error_py30.txt create mode 100644 pylint/test/messages/func_indent.txt create mode 100644 pylint/test/messages/func_init_vars.txt create mode 100644 pylint/test/messages/func_interfaces.txt create mode 100644 pylint/test/messages/func_invalid_sequence_index.txt create mode 100644 pylint/test/messages/func_keyword_repeat_py26.txt create mode 100644 pylint/test/messages/func_kwoa_py30.txt create mode 100644 pylint/test/messages/func_logging_not_lazy_with_logger.txt create mode 100644 pylint/test/messages/func_loopvar_in_dict_comp_py27.txt create mode 100644 pylint/test/messages/func_method_could_be_function.txt create mode 100644 pylint/test/messages/func_module___dict__.txt create mode 100644 pylint/test/messages/func_more_e0604.txt create mode 100644 pylint/test/messages/func_nameerror_on_string_substitution.txt create mode 100644 pylint/test/messages/func_no_dummy_redefined.txt create mode 100644 pylint/test/messages/func_non_iterator_returned_py30.txt create mode 100644 pylint/test/messages/func_non_iterator_returned_py_30.txt create mode 100644 pylint/test/messages/func_nonregr___file___global.txt create mode 100644 pylint/test/messages/func_operators.txt create mode 100644 pylint/test/messages/func_r0901.txt create mode 100644 pylint/test/messages/func_r0902.txt create mode 100644 pylint/test/messages/func_r0903.txt create mode 100644 pylint/test/messages/func_r0904.txt create mode 100644 pylint/test/messages/func_r0921.txt create mode 100644 pylint/test/messages/func_r0922.txt create mode 100644 pylint/test/messages/func_r0923.txt create mode 100644 pylint/test/messages/func_raw_escapes.txt create mode 100644 pylint/test/messages/func_reqattrs.txt create mode 100644 pylint/test/messages/func_return_outside_func.txt create mode 100644 pylint/test/messages/func_return_yield_mix_py_33.txt create mode 100644 pylint/test/messages/func_set_literal_as_default_py27.txt create mode 100644 pylint/test/messages/func_syntax_error.txt create mode 100644 pylint/test/messages/func_tokenize_error.txt create mode 100644 pylint/test/messages/func_too_many_locals_arguments.txt create mode 100644 pylint/test/messages/func_too_many_returns_yields.txt create mode 100644 pylint/test/messages/func_toolonglines.txt create mode 100644 pylint/test/messages/func_toolonglines_py30.txt create mode 100644 pylint/test/messages/func_trailing_whitespace.txt create mode 100644 pylint/test/messages/func_typecheck_callfunc_assigment.txt create mode 100644 pylint/test/messages/func_typecheck_getattr_py30.txt create mode 100644 pylint/test/messages/func_typecheck_non_callable_call.txt create mode 100644 pylint/test/messages/func_undefined_metaclass_var_py30.txt create mode 100644 pylint/test/messages/func_unicode_literal_py26.txt create mode 100644 pylint/test/messages/func_unicode_literal_py274.txt create mode 100644 pylint/test/messages/func_unreachable.txt create mode 100644 pylint/test/messages/func_unused_import_py30.txt create mode 100644 pylint/test/messages/func_unused_overridden_argument.txt create mode 100644 pylint/test/messages/func_use_for_or_listcomp_var_py29.txt create mode 100644 pylint/test/messages/func_use_for_or_listcomp_var_py30.txt create mode 100644 pylint/test/messages/func_used_before_assignment_py30.txt create mode 100644 pylint/test/messages/func_variables_unused_name_from_wilcard_import.txt create mode 100644 pylint/test/messages/func_w0101.txt create mode 100644 pylint/test/messages/func_w0102.txt create mode 100644 pylint/test/messages/func_w0103.txt create mode 100644 pylint/test/messages/func_w0104.txt create mode 100644 pylint/test/messages/func_w0105.txt create mode 100644 pylint/test/messages/func_w0110.txt create mode 100644 pylint/test/messages/func_w0111.txt create mode 100644 pylint/test/messages/func_w0112.txt create mode 100644 pylint/test/messages/func_w0122_py_30.txt create mode 100644 pylint/test/messages/func_w0151.txt create mode 100644 pylint/test/messages/func_w0152_py29.txt create mode 100644 pylint/test/messages/func_w0152_py30.txt create mode 100644 pylint/test/messages/func_w0202.txt create mode 100644 pylint/test/messages/func_w0205.txt create mode 100644 pylint/test/messages/func_w0233.txt create mode 100644 pylint/test/messages/func_w0302.txt create mode 100644 pylint/test/messages/func_w0312.txt create mode 100644 pylint/test/messages/func_w0332_py_30.txt create mode 100644 pylint/test/messages/func_w0401.txt create mode 100644 pylint/test/messages/func_w0401_package.txt create mode 100644 pylint/test/messages/func_w0402.txt create mode 100644 pylint/test/messages/func_w0404.txt create mode 100644 pylint/test/messages/func_w0405.txt create mode 100644 pylint/test/messages/func_w0406.txt create mode 100644 pylint/test/messages/func_w0611.txt create mode 100644 pylint/test/messages/func_w0612.txt create mode 100644 pylint/test/messages/func_w0613.txt create mode 100644 pylint/test/messages/func_w0622.txt create mode 100644 pylint/test/messages/func_w0623.txt create mode 100644 pylint/test/messages/func_w0623_py30.txt create mode 100644 pylint/test/messages/func_w0623_py_30.txt create mode 100644 pylint/test/messages/func_w0631.txt create mode 100644 pylint/test/messages/func_w0702.txt create mode 100644 pylint/test/messages/func_w0703.txt create mode 100644 pylint/test/messages/func_w0704.txt create mode 100644 pylint/test/messages/func_w0705.txt create mode 100644 pylint/test/messages/func_w0801.txt create mode 100644 pylint/test/messages/func_w1201.txt create mode 100644 pylint/test/messages/func_w1202.txt create mode 100644 pylint/test/messages/func_with_without_as_py25.txt create mode 100644 pylint/test/regrtest_data/absimp/__init__.py create mode 100644 pylint/test/regrtest_data/absimp/string.py create mode 100644 pylint/test/regrtest_data/application_crash.py create mode 100644 pylint/test/regrtest_data/classdoc_usage.py create mode 100644 pylint/test/regrtest_data/decimal_inference.py create mode 100644 pylint/test/regrtest_data/descriptor_crash.py create mode 100644 pylint/test/regrtest_data/import_assign.py create mode 100644 pylint/test/regrtest_data/import_package_subpackage_module.py create mode 100644 pylint/test/regrtest_data/module_global.py create mode 100644 pylint/test/regrtest_data/no_stdout_encoding.py create mode 100644 pylint/test/regrtest_data/numarray_import.py create mode 100644 pylint/test/regrtest_data/numarray_inf.py create mode 100644 pylint/test/regrtest_data/package/AudioTime.py create mode 100644 pylint/test/regrtest_data/package/__init__.py create mode 100644 pylint/test/regrtest_data/package/subpackage/__init__.py create mode 100644 pylint/test/regrtest_data/package/subpackage/module.py create mode 100644 pylint/test/regrtest_data/package_all/__init__.py create mode 100644 pylint/test/regrtest_data/package_all/notmissing.py create mode 100644 pylint/test/regrtest_data/precedence_test.py create mode 100644 pylint/test/regrtest_data/special_attr_scope_lookup_crash.py create mode 100644 pylint/test/regrtest_data/try_finally_disable_msg_crash.py create mode 100644 pylint/test/test_func.py create mode 100644 pylint/test/test_functional.py create mode 100644 pylint/test/test_import_graph.py create mode 100644 pylint/test/test_regr.py create mode 100644 pylint/test/test_self.py create mode 100644 pylint/test/unittest_checker_base.py create mode 100644 pylint/test/unittest_checker_classes.py create mode 100644 pylint/test/unittest_checker_exceptions.py create mode 100644 pylint/test/unittest_checker_format.py create mode 100644 pylint/test/unittest_checker_logging.py create mode 100644 pylint/test/unittest_checker_misc.py create mode 100644 pylint/test/unittest_checker_python3.py create mode 100644 pylint/test/unittest_checker_similar.py create mode 100644 pylint/test/unittest_checker_spelling.py create mode 100644 pylint/test/unittest_checker_typecheck.py create mode 100644 pylint/test/unittest_checker_variables.py create mode 100644 pylint/test/unittest_checkers_utils.py create mode 100644 pylint/test/unittest_lint.py create mode 100644 pylint/test/unittest_pyreverse_diadefs.py create mode 100644 pylint/test/unittest_pyreverse_writer.py create mode 100644 pylint/test/unittest_reporters_json.py create mode 100644 pylint/test/unittest_reporting.py create mode 100644 pylint/test/unittest_utils.py create mode 100644 pylint/testutils.py create mode 100644 pylint/utils.py delete mode 100644 pyreverse/__init__.py delete mode 100644 pyreverse/diadefslib.py delete mode 100644 pyreverse/diagrams.py delete mode 100644 pyreverse/main.py delete mode 100644 pyreverse/utils.py delete mode 100644 pyreverse/writer.py delete mode 100644 reporters/__init__.py delete mode 100644 reporters/guireporter.py delete mode 100644 reporters/html.py delete mode 100644 reporters/json.py delete mode 100644 reporters/text.py delete mode 100644 test/data/__init__.py delete mode 100755 test/data/ascript delete mode 100644 test/data/classes_No_Name.dot delete mode 100644 test/data/clientmodule_test.py delete mode 100644 test/data/packages_No_Name.dot delete mode 100644 test/data/suppliermodule_test.py delete mode 100644 test/functional/__init__.py delete mode 100644 test/functional/abstract_abc_methods.py delete mode 100644 test/functional/abstract_class_instantiated_py2.py delete mode 100644 test/functional/abstract_class_instantiated_py2.rc delete mode 100644 test/functional/abstract_class_instantiated_py2.txt delete mode 100644 test/functional/abstract_class_instantiated_py3.py delete mode 100644 test/functional/abstract_class_instantiated_py3.rc delete mode 100644 test/functional/abstract_class_instantiated_py3.txt delete mode 100644 test/functional/abstract_class_instantiated_py34.py delete mode 100644 test/functional/abstract_class_instantiated_py34.rc delete mode 100644 test/functional/abstract_class_instantiated_py34.txt delete mode 100644 test/functional/abstract_method_py2.py delete mode 100644 test/functional/abstract_method_py2.rc delete mode 100644 test/functional/abstract_method_py2.txt delete mode 100644 test/functional/abstract_method_py3.py delete mode 100644 test/functional/abstract_method_py3.rc delete mode 100644 test/functional/abstract_method_py3.txt delete mode 100644 test/functional/access_to__name__.py delete mode 100644 test/functional/access_to__name__.txt delete mode 100644 test/functional/access_to_protected_members.py delete mode 100644 test/functional/access_to_protected_members.txt delete mode 100644 test/functional/anomalous_unicode_escape.py delete mode 100644 test/functional/anomalous_unicode_escape.txt delete mode 100644 test/functional/arguments.py delete mode 100644 test/functional/arguments.txt delete mode 100644 test/functional/assigning_non_slot.py delete mode 100644 test/functional/assigning_non_slot.txt delete mode 100644 test/functional/bad_context_manager.py delete mode 100644 test/functional/bad_context_manager.txt delete mode 100644 test/functional/bad_continuation.py delete mode 100644 test/functional/bad_continuation.txt delete mode 100644 test/functional/bad_inline_option.py delete mode 100644 test/functional/bad_inline_option.rc delete mode 100644 test/functional/bad_inline_option.txt delete mode 100644 test/functional/bad_open_mode.py delete mode 100644 test/functional/bad_open_mode.rc delete mode 100644 test/functional/bad_open_mode.txt delete mode 100644 test/functional/bad_open_mode_py3.py delete mode 100644 test/functional/bad_open_mode_py3.rc delete mode 100644 test/functional/bad_open_mode_py3.txt delete mode 100644 test/functional/bad_reversed_sequence.py delete mode 100644 test/functional/bad_reversed_sequence.txt delete mode 100644 test/functional/boolean_datetime.py delete mode 100644 test/functional/boolean_datetime.txt delete mode 100644 test/functional/cellvar_escaping_loop.py delete mode 100644 test/functional/cellvar_escaping_loop.txt delete mode 100644 test/functional/class_members_py27.py delete mode 100644 test/functional/class_members_py27.rc delete mode 100644 test/functional/class_members_py27.txt delete mode 100644 test/functional/class_members_py30.py delete mode 100644 test/functional/class_members_py30.rc delete mode 100644 test/functional/class_members_py30.txt delete mode 100644 test/functional/class_scope.py delete mode 100644 test/functional/class_scope.txt delete mode 100644 test/functional/confidence_filter.py delete mode 100644 test/functional/confidence_filter.rc delete mode 100644 test/functional/confidence_filter.txt delete mode 100644 test/functional/crash_missing_module_type.py delete mode 100644 test/functional/crash_missing_module_type.txt delete mode 100644 test/functional/ctor_arguments.py delete mode 100644 test/functional/ctor_arguments.txt delete mode 100644 test/functional/defined_and_used_on_same_line.py delete mode 100644 test/functional/docstrings.py delete mode 100644 test/functional/docstrings.txt delete mode 100644 test/functional/duplicate_dict_literal_key.py delete mode 100644 test/functional/duplicate_dict_literal_key.txt delete mode 100644 test/functional/exception_is_binary_op.py delete mode 100644 test/functional/exception_is_binary_op.txt delete mode 100644 test/functional/formatting.txt delete mode 100644 test/functional/future_import.py delete mode 100644 test/functional/future_unicode_literals.py delete mode 100644 test/functional/future_unicode_literals.txt delete mode 100644 test/functional/generated_members.py delete mode 100644 test/functional/generated_members.rc delete mode 100644 test/functional/genexpr_variable_scope.py delete mode 100644 test/functional/genexpr_variable_scope.txt delete mode 100644 test/functional/globals.py delete mode 100644 test/functional/globals.txt delete mode 100644 test/functional/import_error.py delete mode 100644 test/functional/import_error.txt delete mode 100644 test/functional/indexing_exception.py delete mode 100644 test/functional/indexing_exception.rc delete mode 100644 test/functional/indexing_exception.txt delete mode 100644 test/functional/inherit_non_class.py delete mode 100644 test/functional/inherit_non_class.txt delete mode 100644 test/functional/init_not_called.py delete mode 100644 test/functional/init_not_called.txt delete mode 100644 test/functional/invalid__all__object.py delete mode 100644 test/functional/invalid__all__object.txt delete mode 100644 test/functional/invalid_encoded_data.py delete mode 100644 test/functional/invalid_encoded_data.txt delete mode 100644 test/functional/invalid_exceptions_caught.py delete mode 100644 test/functional/invalid_exceptions_caught.txt delete mode 100644 test/functional/invalid_exceptions_raised.py delete mode 100644 test/functional/invalid_exceptions_raised.txt delete mode 100644 test/functional/invalid_name.py delete mode 100644 test/functional/invalid_name.txt delete mode 100644 test/functional/invalid_slice_index.py delete mode 100644 test/functional/invalid_slice_index.txt delete mode 100644 test/functional/line_endings.py delete mode 100644 test/functional/line_endings.rc delete mode 100644 test/functional/line_endings.txt delete mode 100644 test/functional/long_lines_with_utf8.py delete mode 100644 test/functional/long_lines_with_utf8.txt delete mode 100644 test/functional/member_checks.py delete mode 100644 test/functional/member_checks.txt delete mode 100644 test/functional/method_hidden.py delete mode 100644 test/functional/method_hidden.txt delete mode 100644 test/functional/missing_final_newline.py delete mode 100644 test/functional/missing_final_newline.txt delete mode 100644 test/functional/missing_self_argument.py delete mode 100644 test/functional/missing_self_argument.txt delete mode 100644 test/functional/name_styles.py delete mode 100644 test/functional/name_styles.rc delete mode 100644 test/functional/name_styles.txt delete mode 100644 test/functional/namedtuple_member_inference.py delete mode 100644 test/functional/namedtuple_member_inference.txt delete mode 100644 test/functional/names_in__all__.py delete mode 100644 test/functional/names_in__all__.txt delete mode 100644 test/functional/newstyle__slots__.py delete mode 100644 test/functional/newstyle__slots__.txt delete mode 100644 test/functional/newstyle_properties.py delete mode 100644 test/functional/newstyle_properties.txt delete mode 100644 test/functional/no_name_in_module.py delete mode 100644 test/functional/no_name_in_module.txt delete mode 100644 test/functional/old_style_class_py27.py delete mode 100644 test/functional/old_style_class_py27.rc delete mode 100644 test/functional/old_style_class_py27.txt delete mode 100644 test/functional/pygtk_enum_crash.py delete mode 100644 test/functional/pygtk_enum_crash.rc delete mode 100644 test/functional/pygtk_import.py delete mode 100644 test/functional/pygtk_import.rc delete mode 100644 test/functional/raising_non_exception_py3.py delete mode 100644 test/functional/raising_non_exception_py3.rc delete mode 100644 test/functional/raising_non_exception_py3.txt delete mode 100644 test/functional/redefined_builtin.py delete mode 100644 test/functional/redefined_builtin.txt delete mode 100644 test/functional/redundant_unittest_assert.py delete mode 100644 test/functional/redundant_unittest_assert.txt delete mode 100644 test/functional/slots_checks.py delete mode 100644 test/functional/slots_checks.txt delete mode 100644 test/functional/socketerror_import.py delete mode 100644 test/functional/statement_without_effect.py delete mode 100644 test/functional/statement_without_effect.txt delete mode 100644 test/functional/string_formatting.py delete mode 100644 test/functional/string_formatting.txt delete mode 100644 test/functional/string_formatting_py27.py delete mode 100644 test/functional/string_formatting_py27.rc delete mode 100644 test/functional/string_formatting_py27.txt delete mode 100644 test/functional/super_checks.py delete mode 100644 test/functional/super_checks.txt delete mode 100644 test/functional/superfluous_parens.py delete mode 100644 test/functional/superfluous_parens.txt delete mode 100644 test/functional/suspicious_str_strip_call.py delete mode 100644 test/functional/suspicious_str_strip_call.rc delete mode 100644 test/functional/suspicious_str_strip_call.txt delete mode 100644 test/functional/suspicious_str_strip_call_py3.py delete mode 100644 test/functional/suspicious_str_strip_call_py3.rc delete mode 100644 test/functional/suspicious_str_strip_call_py3.txt delete mode 100644 test/functional/too_many_branches.py delete mode 100644 test/functional/too_many_branches.txt delete mode 100644 test/functional/too_many_lines_disabled.py delete mode 100644 test/functional/unbalanced_tuple_unpacking.py delete mode 100644 test/functional/unbalanced_tuple_unpacking.txt delete mode 100644 test/functional/unbalanced_tuple_unpacking_py30.py delete mode 100644 test/functional/unbalanced_tuple_unpacking_py30.rc delete mode 100644 test/functional/undefined_variable.py delete mode 100644 test/functional/undefined_variable.txt delete mode 100644 test/functional/undefined_variable_py30.py delete mode 100644 test/functional/undefined_variable_py30.rc delete mode 100644 test/functional/undefined_variable_py30.txt delete mode 100644 test/functional/uninferable_all_object.py delete mode 100644 test/functional/unknown_encoding_py29.py delete mode 100644 test/functional/unknown_encoding_py29.rc delete mode 100644 test/functional/unknown_encoding_py29.txt delete mode 100644 test/functional/unknown_encoding_py30.py delete mode 100644 test/functional/unknown_encoding_py30.rc delete mode 100644 test/functional/unknown_encoding_py30.txt delete mode 100644 test/functional/unknown_encoding_pypy.py delete mode 100644 test/functional/unknown_encoding_pypy.rc delete mode 100644 test/functional/unknown_encoding_pypy.txt delete mode 100644 test/functional/unnecessary_lambda.py delete mode 100644 test/functional/unnecessary_lambda.txt delete mode 100644 test/functional/unpacked_exceptions.py delete mode 100644 test/functional/unpacked_exceptions.rc delete mode 100644 test/functional/unpacked_exceptions.txt delete mode 100644 test/functional/unpacking.py delete mode 100644 test/functional/unpacking_non_sequence.py delete mode 100644 test/functional/unpacking_non_sequence.txt delete mode 100644 test/functional/unused_import.py delete mode 100644 test/functional/unused_import.txt delete mode 100644 test/functional/useless_else_on_loop.py delete mode 100644 test/functional/useless_else_on_loop.txt delete mode 100644 test/functional/with_used_before_assign.py delete mode 100644 test/functional/with_used_before_assign.txt delete mode 100644 test/functional/yield_outside_func.py delete mode 100644 test/functional/yield_outside_func.txt delete mode 100644 test/input/__init__.py delete mode 100644 test/input/func_3k_removed_stuff_py_30.py delete mode 100644 test/input/func_assert_2uple.py delete mode 100644 test/input/func_attrs_definition_order.py delete mode 100644 test/input/func_bad_assigment_to_exception_var.py delete mode 100644 test/input/func_bad_cont_dictcomp_py27.py delete mode 100644 test/input/func_bad_exception_context_py30.py delete mode 100644 test/input/func_base_useless_pass.py delete mode 100644 test/input/func_block_disable_msg.py delete mode 100644 test/input/func_break_or_return_in_try_finally.py delete mode 100644 test/input/func_bug113231.py delete mode 100644 test/input/func_continue_not_in_loop.py delete mode 100644 test/input/func_dangerous_default.py delete mode 100644 test/input/func_defining-attr-methods_order.py delete mode 100644 test/input/func_deprecated_lambda_py_30.py delete mode 100644 test/input/func_deprecated_module_py30.py delete mode 100644 test/input/func_deprecated_module_py_30.py delete mode 100644 test/input/func_disable_linebased.py delete mode 100644 test/input/func_dotted_ancestor.py delete mode 100644 test/input/func_e0001_py30.py delete mode 100644 test/input/func_e0011.py delete mode 100644 test/input/func_e0012.py delete mode 100644 test/input/func_e0101.py delete mode 100644 test/input/func_e0108.py delete mode 100644 test/input/func_e0203.py delete mode 100644 test/input/func_e0204.py delete mode 100644 test/input/func_e0206.py delete mode 100644 test/input/func_e0601.py delete mode 100644 test/input/func_e0604.py delete mode 100644 test/input/func_e12xx.py delete mode 100644 test/input/func_e13xx.py delete mode 100644 test/input/func_empty_module.py delete mode 100644 test/input/func_eval_used.py delete mode 100644 test/input/func_excess_escapes.py delete mode 100644 test/input/func_exec_used_py30.py delete mode 100644 test/input/func_f0401.py delete mode 100644 test/input/func_first_arg.py delete mode 100644 test/input/func_fixme.py delete mode 100644 test/input/func_i0011.py delete mode 100644 test/input/func_i0012.py delete mode 100644 test/input/func_i0013.py delete mode 100644 test/input/func_i0014.py delete mode 100644 test/input/func_i0020.py delete mode 100644 test/input/func_i0022.py delete mode 100644 test/input/func_import_syntax_error.py delete mode 100644 test/input/func_indent.py delete mode 100644 test/input/func_init_vars.py delete mode 100644 test/input/func_interfaces.py delete mode 100644 test/input/func_invalid_sequence_index.py delete mode 100644 test/input/func_keyword_repeat.py delete mode 100644 test/input/func_kwoa_py30.py delete mode 100644 test/input/func_logging_not_lazy_with_logger.py delete mode 100644 test/input/func_loopvar_in_dict_comp_py27.py delete mode 100644 test/input/func_method_could_be_function.py delete mode 100644 test/input/func_module___dict__.py delete mode 100644 test/input/func_more_e0604.py delete mode 100644 test/input/func_nameerror_on_string_substitution.py delete mode 100644 test/input/func_no_dummy_redefined.py delete mode 100644 test/input/func_noerror___init___return_from_inner_function.py delete mode 100644 test/input/func_noerror_access_attr_before_def_false_positive.py delete mode 100644 test/input/func_noerror_base_init_vars.py delete mode 100644 test/input/func_noerror_builtin_module_test.py delete mode 100644 test/input/func_noerror_class_attributes.py delete mode 100644 test/input/func_noerror_class_decorators_py26.py delete mode 100644 test/input/func_noerror_classes_meth_could_be_a_function.py delete mode 100644 test/input/func_noerror_classes_meth_signature.py delete mode 100644 test/input/func_noerror_classes_protected_member_access.py delete mode 100644 test/input/func_noerror_crash_122793.py delete mode 100644 test/input/func_noerror_crash_127416.py delete mode 100644 test/input/func_noerror_decorator_scope.py delete mode 100644 test/input/func_noerror_e1101_13784.py delete mode 100644 test/input/func_noerror_e1101_9588_base_attr_aug_assign.py delete mode 100644 test/input/func_noerror_e1101_but_getattr.py delete mode 100644 test/input/func_noerror_encoding.py delete mode 100644 test/input/func_noerror_except_pass.py delete mode 100644 test/input/func_noerror_exception.py delete mode 100644 test/input/func_noerror_external_classmethod_crash.py delete mode 100644 test/input/func_noerror_factory_method.py delete mode 100644 test/input/func_noerror_function_as_method.py delete mode 100644 test/input/func_noerror_genexp_in_class_scope.py delete mode 100644 test/input/func_noerror_indirect_interface.py delete mode 100644 test/input/func_noerror_inner_classes.py delete mode 100644 test/input/func_noerror_lambda_use_before_assign.py delete mode 100644 test/input/func_noerror_long_utf8_line.py delete mode 100644 test/input/func_noerror_mcs_attr_access.py delete mode 100644 test/input/func_noerror_nested_classes.py delete mode 100644 test/input/func_noerror_new_style_class_py_30.py delete mode 100644 test/input/func_noerror_no_warning_docstring.py delete mode 100644 test/input/func_noerror_nonregr.py delete mode 100644 test/input/func_noerror_object_as_class_attribute.py delete mode 100644 test/input/func_noerror_overloaded_operator.py delete mode 100644 test/input/func_noerror_overriden_method_varargs.py delete mode 100644 test/input/func_noerror_property_affectation_py26.py delete mode 100644 test/input/func_noerror_raise_return_self.py delete mode 100644 test/input/func_noerror_socket_member.py delete mode 100644 test/input/func_noerror_static_method.py delete mode 100644 test/input/func_noerror_staticmethod_as_decorator_py24.py delete mode 100644 test/input/func_noerror_super_protected.py delete mode 100644 test/input/func_noerror_unused_variable_py30.py delete mode 100644 test/input/func_noerror_used_before_assignment.py delete mode 100644 test/input/func_noerror_w0232.py delete mode 100644 test/input/func_noerror_yield_assign_py25.py delete mode 100644 test/input/func_noerror_yield_return_mix.py delete mode 100644 test/input/func_non_iterator_returned_py30.py delete mode 100644 test/input/func_non_iterator_returned_py_30.py delete mode 100644 test/input/func_nonregr___file___global.py delete mode 100644 test/input/func_operators.py delete mode 100644 test/input/func_r0901.py delete mode 100644 test/input/func_r0902.py delete mode 100644 test/input/func_r0903.py delete mode 100644 test/input/func_r0904.py delete mode 100644 test/input/func_r0921.py delete mode 100644 test/input/func_r0922.py delete mode 100644 test/input/func_r0923.py delete mode 100644 test/input/func_reqattrs.py delete mode 100644 test/input/func_return_outside_func.py delete mode 100644 test/input/func_return_yield_mix_py_33.py delete mode 100644 test/input/func_set_literal_as_default_py27.py delete mode 100644 test/input/func_syntax_error.py delete mode 100644 test/input/func_tokenize_error.py delete mode 100644 test/input/func_too_many_locals_arguments.py delete mode 100644 test/input/func_too_many_returns_yields.py delete mode 100644 test/input/func_toolonglines.py delete mode 100644 test/input/func_trailing_whitespace.py delete mode 100644 test/input/func_typecheck_callfunc_assigment.py delete mode 100644 test/input/func_typecheck_non_callable_call.py delete mode 100644 test/input/func_undefined_metaclass_var_py30.py delete mode 100644 test/input/func_unreachable.py delete mode 100644 test/input/func_unused_import_py30.py delete mode 100644 test/input/func_unused_overridden_argument.py delete mode 100644 test/input/func_use_for_or_listcomp_var.py delete mode 100644 test/input/func_used_before_assignment_py30.py delete mode 100644 test/input/func_variables_unused_name_from_wilcard_import.py delete mode 100644 test/input/func_w0101.py delete mode 100644 test/input/func_w0102.py delete mode 100644 test/input/func_w0103.py delete mode 100644 test/input/func_w0104.py delete mode 100644 test/input/func_w0105.py delete mode 100644 test/input/func_w0110.py delete mode 100644 test/input/func_w0111.py delete mode 100644 test/input/func_w0112.py delete mode 100644 test/input/func_w0122_py_30.py delete mode 100644 test/input/func_w0151.py delete mode 100644 test/input/func_w0152.py delete mode 100644 test/input/func_w0202.py delete mode 100644 test/input/func_w0205.py delete mode 100644 test/input/func_w0233.py delete mode 100644 test/input/func_w0302.py delete mode 100644 test/input/func_w0312.py delete mode 100644 test/input/func_w0332_py_30.py delete mode 100644 test/input/func_w0401.py delete mode 100644 test/input/func_w0401_package/__init__.py delete mode 100644 test/input/func_w0401_package/all_the_things.py delete mode 100644 test/input/func_w0401_package/thing1.py delete mode 100644 test/input/func_w0401_package/thing2.py delete mode 100644 test/input/func_w0402.py delete mode 100644 test/input/func_w0404.py delete mode 100644 test/input/func_w0405.py delete mode 100644 test/input/func_w0406.py delete mode 100644 test/input/func_w0611.py delete mode 100644 test/input/func_w0612.py delete mode 100644 test/input/func_w0613.py delete mode 100644 test/input/func_w0623_py30.py delete mode 100644 test/input/func_w0623_py_30.py delete mode 100644 test/input/func_w0631.py delete mode 100644 test/input/func_w0702.py delete mode 100644 test/input/func_w0703.py delete mode 100644 test/input/func_w0704.py delete mode 100644 test/input/func_w0705.py delete mode 100644 test/input/func_w0801.py delete mode 100644 test/input/func_w1201.py delete mode 100644 test/input/func_w1202.py delete mode 100644 test/input/func_with_without_as_py25.py delete mode 100644 test/input/ignore_except_pass_by_default.py delete mode 100644 test/input/indirect1.py delete mode 100644 test/input/indirect2.py delete mode 100644 test/input/indirect3.py delete mode 100644 test/input/noext delete mode 100644 test/input/similar1 delete mode 100644 test/input/similar2 delete mode 100644 test/input/syntax_error.py delete mode 100644 test/input/w0401_cycle.py delete mode 100644 test/input/w0801_same.py delete mode 100644 test/messages/builtin_module.txt delete mode 100644 test/messages/func_3k_removed_stuff_py_30.txt delete mode 100644 test/messages/func_assert_2uple.txt delete mode 100644 test/messages/func_attrs_definition_order.txt delete mode 100644 test/messages/func_bad_assigment_to_exception_var.txt delete mode 100644 test/messages/func_bad_cont_dictcomp_py27.txt delete mode 100644 test/messages/func_bad_exception_context_py30.txt delete mode 100644 test/messages/func_base_useless_pass.txt delete mode 100644 test/messages/func_block_disable_msg.txt delete mode 100644 test/messages/func_break_or_return_in_try_finally.txt delete mode 100644 test/messages/func_bug113231.txt delete mode 100644 test/messages/func_continue_not_in_loop.txt delete mode 100644 test/messages/func_dangerous_default.txt delete mode 100644 test/messages/func_dangerous_default_py30.txt delete mode 100644 test/messages/func_defining-attr-methods_order.txt delete mode 100644 test/messages/func_deprecated_lambda_py_30.txt delete mode 100644 test/messages/func_deprecated_module_py30.txt delete mode 100644 test/messages/func_deprecated_module_py_30.txt delete mode 100644 test/messages/func_disable_linebased.txt delete mode 100644 test/messages/func_disable_linebased_py30.txt delete mode 100644 test/messages/func_dotted_ancestor.txt delete mode 100644 test/messages/func_e0001_py30.txt delete mode 100644 test/messages/func_e0011.txt delete mode 100644 test/messages/func_e0012.txt delete mode 100644 test/messages/func_e0101.txt delete mode 100644 test/messages/func_e0108.txt delete mode 100644 test/messages/func_e0203.txt delete mode 100644 test/messages/func_e0204.txt delete mode 100644 test/messages/func_e0206.txt delete mode 100644 test/messages/func_e0601.txt delete mode 100644 test/messages/func_e0604.txt delete mode 100644 test/messages/func_e12xx.txt delete mode 100644 test/messages/func_e13xx.txt delete mode 100644 test/messages/func_e13xx_py30.txt delete mode 100644 test/messages/func_empty_module.txt delete mode 100644 test/messages/func_eval_used.txt delete mode 100644 test/messages/func_excess_escapes.txt delete mode 100644 test/messages/func_exec_used_py30.txt delete mode 100644 test/messages/func_f0401.txt delete mode 100644 test/messages/func_first_arg.txt delete mode 100644 test/messages/func_fixme.txt delete mode 100644 test/messages/func_i0011.txt delete mode 100644 test/messages/func_i0012.txt delete mode 100644 test/messages/func_i0013.txt delete mode 100644 test/messages/func_i0014.txt delete mode 100644 test/messages/func_i0020.txt delete mode 100644 test/messages/func_i0022.txt delete mode 100644 test/messages/func_import_syntax_error.txt delete mode 100644 test/messages/func_import_syntax_error_py30.txt delete mode 100644 test/messages/func_indent.txt delete mode 100644 test/messages/func_init_vars.txt delete mode 100644 test/messages/func_interfaces.txt delete mode 100644 test/messages/func_invalid_sequence_index.txt delete mode 100644 test/messages/func_keyword_repeat_py26.txt delete mode 100644 test/messages/func_kwoa_py30.txt delete mode 100644 test/messages/func_logging_not_lazy_with_logger.txt delete mode 100644 test/messages/func_loopvar_in_dict_comp_py27.txt delete mode 100644 test/messages/func_method_could_be_function.txt delete mode 100644 test/messages/func_module___dict__.txt delete mode 100644 test/messages/func_more_e0604.txt delete mode 100644 test/messages/func_nameerror_on_string_substitution.txt delete mode 100644 test/messages/func_no_dummy_redefined.txt delete mode 100644 test/messages/func_non_iterator_returned_py30.txt delete mode 100644 test/messages/func_non_iterator_returned_py_30.txt delete mode 100644 test/messages/func_nonregr___file___global.txt delete mode 100644 test/messages/func_operators.txt delete mode 100644 test/messages/func_r0901.txt delete mode 100644 test/messages/func_r0902.txt delete mode 100644 test/messages/func_r0903.txt delete mode 100644 test/messages/func_r0904.txt delete mode 100644 test/messages/func_r0921.txt delete mode 100644 test/messages/func_r0922.txt delete mode 100644 test/messages/func_r0923.txt delete mode 100644 test/messages/func_raw_escapes.txt delete mode 100644 test/messages/func_reqattrs.txt delete mode 100644 test/messages/func_return_outside_func.txt delete mode 100644 test/messages/func_return_yield_mix_py_33.txt delete mode 100644 test/messages/func_set_literal_as_default_py27.txt delete mode 100644 test/messages/func_syntax_error.txt delete mode 100644 test/messages/func_tokenize_error.txt delete mode 100644 test/messages/func_too_many_locals_arguments.txt delete mode 100644 test/messages/func_too_many_returns_yields.txt delete mode 100644 test/messages/func_toolonglines.txt delete mode 100644 test/messages/func_toolonglines_py30.txt delete mode 100644 test/messages/func_trailing_whitespace.txt delete mode 100644 test/messages/func_typecheck_callfunc_assigment.txt delete mode 100644 test/messages/func_typecheck_getattr_py30.txt delete mode 100644 test/messages/func_typecheck_non_callable_call.txt delete mode 100644 test/messages/func_undefined_metaclass_var_py30.txt delete mode 100644 test/messages/func_unicode_literal_py26.txt delete mode 100644 test/messages/func_unicode_literal_py274.txt delete mode 100644 test/messages/func_unreachable.txt delete mode 100644 test/messages/func_unused_import_py30.txt delete mode 100644 test/messages/func_unused_overridden_argument.txt delete mode 100644 test/messages/func_use_for_or_listcomp_var_py29.txt delete mode 100644 test/messages/func_use_for_or_listcomp_var_py30.txt delete mode 100644 test/messages/func_used_before_assignment_py30.txt delete mode 100644 test/messages/func_variables_unused_name_from_wilcard_import.txt delete mode 100644 test/messages/func_w0101.txt delete mode 100644 test/messages/func_w0102.txt delete mode 100644 test/messages/func_w0103.txt delete mode 100644 test/messages/func_w0104.txt delete mode 100644 test/messages/func_w0105.txt delete mode 100644 test/messages/func_w0110.txt delete mode 100644 test/messages/func_w0111.txt delete mode 100644 test/messages/func_w0112.txt delete mode 100644 test/messages/func_w0122_py_30.txt delete mode 100644 test/messages/func_w0151.txt delete mode 100644 test/messages/func_w0152_py29.txt delete mode 100644 test/messages/func_w0152_py30.txt delete mode 100644 test/messages/func_w0202.txt delete mode 100644 test/messages/func_w0205.txt delete mode 100644 test/messages/func_w0233.txt delete mode 100644 test/messages/func_w0302.txt delete mode 100644 test/messages/func_w0312.txt delete mode 100644 test/messages/func_w0332_py_30.txt delete mode 100644 test/messages/func_w0401.txt delete mode 100644 test/messages/func_w0401_package.txt delete mode 100644 test/messages/func_w0402.txt delete mode 100644 test/messages/func_w0404.txt delete mode 100644 test/messages/func_w0405.txt delete mode 100644 test/messages/func_w0406.txt delete mode 100644 test/messages/func_w0611.txt delete mode 100644 test/messages/func_w0612.txt delete mode 100644 test/messages/func_w0613.txt delete mode 100644 test/messages/func_w0622.txt delete mode 100644 test/messages/func_w0623.txt delete mode 100644 test/messages/func_w0623_py30.txt delete mode 100644 test/messages/func_w0623_py_30.txt delete mode 100644 test/messages/func_w0631.txt delete mode 100644 test/messages/func_w0702.txt delete mode 100644 test/messages/func_w0703.txt delete mode 100644 test/messages/func_w0704.txt delete mode 100644 test/messages/func_w0705.txt delete mode 100644 test/messages/func_w0801.txt delete mode 100644 test/messages/func_w1201.txt delete mode 100644 test/messages/func_w1202.txt delete mode 100644 test/messages/func_with_without_as_py25.txt delete mode 100644 test/regrtest_data/absimp/__init__.py delete mode 100644 test/regrtest_data/absimp/string.py delete mode 100644 test/regrtest_data/application_crash.py delete mode 100644 test/regrtest_data/classdoc_usage.py delete mode 100644 test/regrtest_data/decimal_inference.py delete mode 100644 test/regrtest_data/descriptor_crash.py delete mode 100644 test/regrtest_data/import_assign.py delete mode 100644 test/regrtest_data/import_package_subpackage_module.py delete mode 100644 test/regrtest_data/module_global.py delete mode 100644 test/regrtest_data/no_stdout_encoding.py delete mode 100644 test/regrtest_data/numarray_import.py delete mode 100644 test/regrtest_data/numarray_inf.py delete mode 100644 test/regrtest_data/package/AudioTime.py delete mode 100644 test/regrtest_data/package/__init__.py delete mode 100644 test/regrtest_data/package/subpackage/__init__.py delete mode 100644 test/regrtest_data/package/subpackage/module.py delete mode 100644 test/regrtest_data/package_all/__init__.py delete mode 100644 test/regrtest_data/package_all/notmissing.py delete mode 100644 test/regrtest_data/precedence_test.py delete mode 100644 test/regrtest_data/special_attr_scope_lookup_crash.py delete mode 100644 test/regrtest_data/try_finally_disable_msg_crash.py delete mode 100644 test/test_func.py delete mode 100644 test/test_functional.py delete mode 100644 test/test_import_graph.py delete mode 100644 test/test_regr.py delete mode 100644 test/test_self.py delete mode 100644 test/unittest_checker_base.py delete mode 100644 test/unittest_checker_classes.py delete mode 100644 test/unittest_checker_exceptions.py delete mode 100644 test/unittest_checker_format.py delete mode 100644 test/unittest_checker_logging.py delete mode 100644 test/unittest_checker_misc.py delete mode 100644 test/unittest_checker_python3.py delete mode 100644 test/unittest_checker_similar.py delete mode 100644 test/unittest_checker_spelling.py delete mode 100644 test/unittest_checker_typecheck.py delete mode 100644 test/unittest_checker_variables.py delete mode 100644 test/unittest_checkers_utils.py delete mode 100644 test/unittest_lint.py delete mode 100644 test/unittest_pyreverse_diadefs.py delete mode 100644 test/unittest_pyreverse_writer.py delete mode 100644 test/unittest_reporters_json.py delete mode 100644 test/unittest_reporting.py delete mode 100644 test/unittest_utils.py delete mode 100644 testutils.py delete mode 100644 utils.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index 82e557d..0000000 --- a/__init__.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2003-2012 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import sys - -from .__pkginfo__ import version as __version__ - -def run_pylint(): - """run pylint""" - from pylint.lint import Run - Run(sys.argv[1:]) - -def run_pylint_gui(): - """run pylint-gui""" - try: - from pylint.gui import Run - Run(sys.argv[1:]) - except ImportError: - sys.exit('tkinter is not available') - -def run_epylint(): - """run pylint""" - from pylint.epylint import Run - Run() - -def run_pyreverse(): - """run pyreverse""" - from pylint.pyreverse.main import Run - Run(sys.argv[1:]) - -def run_symilar(): - """run symilar""" - from pylint.checkers.similar import Run - Run(sys.argv[1:]) diff --git a/__main__.py b/__main__.py deleted file mode 100644 index 7716361..0000000 --- a/__main__.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env python -import pylint -pylint.run_pylint() diff --git a/checkers/__init__.py b/checkers/__init__.py deleted file mode 100644 index 51adb4d..0000000 --- a/checkers/__init__.py +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""utilities methods and classes for checkers - -Base id of standard checkers (used in msg and report ids): -01: base -02: classes -03: format -04: import -05: misc -06: variables -07: exceptions -08: similar -09: design_analysis -10: newstyle -11: typecheck -12: logging -13: string_format -14: string_constant -15: stdlib -16: python3 -17-50: not yet used: reserved for future internal checkers. -51-99: perhaps used: reserved for external checkers - -The raw_metrics checker has no number associated since it doesn't emit any -messages nor reports. XXX not true, emit a 07 report ! - -""" - -import sys -import tokenize -import warnings - -from logilab.common.configuration import OptionsProviderMixIn - -from pylint.reporters import diff_string -from pylint.utils import register_plugins -from pylint.interfaces import UNDEFINED - - -def table_lines_from_stats(stats, old_stats, columns): - """get values listed in from and , - and return a formated list of values, designed to be given to a - ureport.Table object - """ - lines = [] - for m_type in columns: - new = stats[m_type] - format = str # pylint: disable=redefined-builtin - if isinstance(new, float): - format = lambda num: '%.3f' % num - old = old_stats.get(m_type) - if old is not None: - diff_str = diff_string(old, new) - old = format(old) - else: - old, diff_str = 'NC', 'NC' - lines += (m_type.replace('_', ' '), format(new), old, diff_str) - return lines - - -class BaseChecker(OptionsProviderMixIn): - """base class for checkers""" - # checker name (you may reuse an existing one) - name = None - # options level (0 will be displaying in --help, 1 in --long-help) - level = 1 - # ordered list of options to control the ckecker behaviour - options = () - # messages issued by this checker - msgs = {} - # reports issued by this checker - reports = () - # mark this checker as enabled or not. - enabled = True - - def __init__(self, linter=None): - """checker instances should have the linter as argument - - linter is an object implementing ILinter - """ - self.name = self.name.lower() - OptionsProviderMixIn.__init__(self) - self.linter = linter - - def add_message(self, msg_id, line=None, node=None, args=None, confidence=UNDEFINED): - """add a message of a given type""" - self.linter.add_message(msg_id, line, node, args, confidence) - - # dummy methods implementing the IChecker interface - - def open(self): - """called before visiting project (i.e set of modules)""" - - def close(self): - """called after visiting project (i.e set of modules)""" - - -class BaseTokenChecker(BaseChecker): - """Base class for checkers that want to have access to the token stream.""" - - def process_tokens(self, tokens): - """Should be overridden by subclasses.""" - raise NotImplementedError() - - -def initialize(linter): - """initialize linter with checkers in this package """ - register_plugins(linter, __path__[0]) - -__all__ = ('BaseChecker', 'initialize') diff --git a/checkers/base.py b/checkers/base.py deleted file mode 100644 index 404f9de..0000000 --- a/checkers/base.py +++ /dev/null @@ -1,1253 +0,0 @@ -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# Copyright (c) 2009-2010 Arista Networks, Inc. -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""basic checker for Python code""" - -import collections -import itertools -import sys -import re - -import six -from six.moves import zip # pylint: disable=redefined-builtin - -from logilab.common.ureports import Table - -import astroid -import astroid.bases -from astroid import are_exclusive, InferenceError - -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH -from pylint.utils import EmptyReport -from pylint.reporters import diff_string -from pylint.checkers import BaseChecker -from pylint.checkers.utils import ( - check_messages, - clobber_in_except, - is_builtin_object, - is_inside_except, - overrides_a_method, - safe_infer, - get_argument_from_call, - has_known_bases, - NoSuchArgumentError, - is_import_error, - unimplemented_abstract_methods, - ) - - -# regex for class/function/variable/constant name -CLASS_NAME_RGX = re.compile('[A-Z_][a-zA-Z0-9]+$') -MOD_NAME_RGX = re.compile('(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$') -CONST_NAME_RGX = re.compile('(([A-Z_][A-Z0-9_]*)|(__.*__))$') -COMP_VAR_RGX = re.compile('[A-Za-z_][A-Za-z0-9_]*$') -DEFAULT_NAME_RGX = re.compile('[a-z_][a-z0-9_]{2,30}$') -CLASS_ATTRIBUTE_RGX = re.compile(r'([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$') -# do not require a doc string on system methods -NO_REQUIRED_DOC_RGX = re.compile('__.*__') -REVERSED_METHODS = (('__getitem__', '__len__'), - ('__reversed__', )) - -PY33 = sys.version_info >= (3, 3) -PY3K = sys.version_info >= (3, 0) -BAD_FUNCTIONS = ['map', 'filter'] -if sys.version_info < (3, 0): - BAD_FUNCTIONS.append('input') - -# Name categories that are always consistent with all naming conventions. -EXEMPT_NAME_CATEGORIES = set(('exempt', 'ignore')) - -# A mapping from builtin-qname -> symbol, to be used when generating messages -# about dangerous default values as arguments -DEFAULT_ARGUMENT_SYMBOLS = dict( - zip(['.'.join([astroid.bases.BUILTINS, x]) for x in ('set', 'dict', 'list')], - ['set()', '{}', '[]']) -) - -del re - -def _redefines_import(node): - """ Detect that the given node (AssName) is inside an - exception handler and redefines an import from the tryexcept body. - Returns True if the node redefines an import, False otherwise. - """ - current = node - while current and not isinstance(current.parent, astroid.ExceptHandler): - current = current.parent - if not current or not is_import_error(current.parent): - return False - try_block = current.parent.parent - for import_node in try_block.nodes_of_class((astroid.From, astroid.Import)): - for name, alias in import_node.names: - if alias: - if alias == node.name: - return True - elif name == node.name: - return True - return False - -def in_loop(node): - """return True if the node is inside a kind of for loop""" - parent = node.parent - while parent is not None: - if isinstance(parent, (astroid.For, astroid.ListComp, astroid.SetComp, - astroid.DictComp, astroid.GenExpr)): - return True - parent = parent.parent - return False - -def in_nested_list(nested_list, obj): - """return true if the object is an element of or of a nested - list - """ - for elmt in nested_list: - if isinstance(elmt, (list, tuple)): - if in_nested_list(elmt, obj): - return True - elif elmt == obj: - return True - return False - -def _loop_exits_early(loop): - """Returns true if a loop has a break statement in its body.""" - loop_nodes = (astroid.For, astroid.While) - # Loop over body explicitly to avoid matching break statements - # in orelse. - for child in loop.body: - if isinstance(child, loop_nodes): - # break statement may be in orelse of child loop. - # pylint: disable=superfluous-parens - for orelse in (child.orelse or ()): - for _ in orelse.nodes_of_class(astroid.Break, skip_klass=loop_nodes): - return True - continue - for _ in child.nodes_of_class(astroid.Break, skip_klass=loop_nodes): - return True - return False - -def _is_multi_naming_match(match, node_type, confidence): - return (match is not None and - match.lastgroup is not None and - match.lastgroup not in EXEMPT_NAME_CATEGORIES - and (node_type != 'method' or confidence != INFERENCE_FAILURE)) - - -if sys.version_info < (3, 0): - PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty')) -else: - PROPERTY_CLASSES = set(('builtins.property', 'abc.abstractproperty')) - - -def _determine_function_name_type(node): - """Determine the name type whose regex the a function's name should match. - - :param node: A function node. - :returns: One of ('function', 'method', 'attr') - """ - if not node.is_method(): - return 'function' - if node.decorators: - decorators = node.decorators.nodes - else: - decorators = [] - for decorator in decorators: - # If the function is a property (decorated with @property - # or @abc.abstractproperty), the name type is 'attr'. - if (isinstance(decorator, astroid.Name) or - (isinstance(decorator, astroid.Getattr) and - decorator.attrname == 'abstractproperty')): - infered = safe_infer(decorator) - if infered and infered.qname() in PROPERTY_CLASSES: - return 'attr' - # If the function is decorated using the prop_method.{setter,getter} - # form, treat it like an attribute as well. - elif (isinstance(decorator, astroid.Getattr) and - decorator.attrname in ('setter', 'deleter')): - return 'attr' - return 'method' - - - -def _has_abstract_methods(node): - """ - Determine if the given `node` has abstract methods. - - The methods should be made abstract by decorating them - with `abc` decorators. - """ - return len(unimplemented_abstract_methods(node)) > 0 - - -def report_by_type_stats(sect, stats, old_stats): - """make a report of - - * percentage of different types documented - * percentage of different types with a bad name - """ - # percentage of different types documented and/or with a bad name - nice_stats = {} - for node_type in ('module', 'class', 'method', 'function'): - try: - total = stats[node_type] - except KeyError: - raise EmptyReport() - nice_stats[node_type] = {} - if total != 0: - try: - documented = total - stats['undocumented_'+node_type] - percent = (documented * 100.) / total - nice_stats[node_type]['percent_documented'] = '%.2f' % percent - except KeyError: - nice_stats[node_type]['percent_documented'] = 'NC' - try: - percent = (stats['badname_'+node_type] * 100.) / total - nice_stats[node_type]['percent_badname'] = '%.2f' % percent - except KeyError: - nice_stats[node_type]['percent_badname'] = 'NC' - lines = ('type', 'number', 'old number', 'difference', - '%documented', '%badname') - for node_type in ('module', 'class', 'method', 'function'): - new = stats[node_type] - old = old_stats.get(node_type, None) - if old is not None: - diff_str = diff_string(old, new) - else: - old, diff_str = 'NC', 'NC' - lines += (node_type, str(new), str(old), diff_str, - nice_stats[node_type].get('percent_documented', '0'), - nice_stats[node_type].get('percent_badname', '0')) - sect.append(Table(children=lines, cols=6, rheaders=1)) - -def redefined_by_decorator(node): - """return True if the object is a method redefined via decorator. - - For example: - @property - def x(self): return self._x - @x.setter - def x(self, value): self._x = value - """ - if node.decorators: - for decorator in node.decorators.nodes: - if (isinstance(decorator, astroid.Getattr) and - getattr(decorator.expr, 'name', None) == node.name): - return True - return False - -class _BasicChecker(BaseChecker): - __implements__ = IAstroidChecker - name = 'basic' - -class BasicErrorChecker(_BasicChecker): - msgs = { - 'E0100': ('__init__ method is a generator', - 'init-is-generator', - 'Used when the special class method __init__ is turned into a ' - 'generator by a yield in its body.'), - 'E0101': ('Explicit return in __init__', - 'return-in-init', - 'Used when the special class method __init__ has an explicit ' - 'return value.'), - 'E0102': ('%s already defined line %s', - 'function-redefined', - 'Used when a function / class / method is redefined.'), - 'E0103': ('%r not properly in loop', - 'not-in-loop', - 'Used when break or continue keywords are used outside a loop.'), - 'E0104': ('Return outside function', - 'return-outside-function', - 'Used when a "return" statement is found outside a function or ' - 'method.'), - 'E0105': ('Yield outside function', - 'yield-outside-function', - 'Used when a "yield" statement is found outside a function or ' - 'method.'), - 'E0106': ('Return with argument inside generator', - 'return-arg-in-generator', - 'Used when a "return" statement with an argument is found ' - 'outside in a generator function or method (e.g. with some ' - '"yield" statements).', - {'maxversion': (3, 3)}), - 'E0107': ("Use of the non-existent %s operator", - 'nonexistent-operator', - "Used when you attempt to use the C-style pre-increment or" - "pre-decrement operator -- and ++, which doesn't exist in Python."), - 'E0108': ('Duplicate argument name %s in function definition', - 'duplicate-argument-name', - 'Duplicate argument names in function definitions are syntax' - ' errors.'), - 'E0110': ('Abstract class %r with abstract methods instantiated', - 'abstract-class-instantiated', - 'Used when an abstract class with `abc.ABCMeta` as metaclass ' - 'has abstract methods and is instantiated.'), - 'W0120': ('Else clause on loop without a break statement', - 'useless-else-on-loop', - 'Loops should only have an else clause if they can exit early ' - 'with a break statement, otherwise the statements under else ' - 'should be on the same scope as the loop itself.'), - } - - @check_messages('function-redefined') - def visit_class(self, node): - self._check_redefinition('class', node) - - @check_messages('init-is-generator', 'return-in-init', - 'function-redefined', 'return-arg-in-generator', - 'duplicate-argument-name') - def visit_function(self, node): - if not redefined_by_decorator(node): - self._check_redefinition(node.is_method() and 'method' or 'function', node) - # checks for max returns, branch, return in __init__ - returns = node.nodes_of_class(astroid.Return, - skip_klass=(astroid.Function, astroid.Class)) - if node.is_method() and node.name == '__init__': - if node.is_generator(): - self.add_message('init-is-generator', node=node) - else: - values = [r.value for r in returns] - # Are we returning anything but None from constructors - if [v for v in values - if not (v is None or - (isinstance(v, astroid.Const) and v.value is None) or - (isinstance(v, astroid.Name) and v.name == 'None') - )]: - self.add_message('return-in-init', node=node) - elif node.is_generator(): - # make sure we don't mix non-None returns and yields - if not PY33: - for retnode in returns: - if isinstance(retnode.value, astroid.Const) and \ - retnode.value.value is not None: - self.add_message('return-arg-in-generator', node=node, - line=retnode.fromlineno) - # Check for duplicate names - args = set() - for name in node.argnames(): - if name in args: - self.add_message('duplicate-argument-name', node=node, args=(name,)) - else: - args.add(name) - - - @check_messages('return-outside-function') - def visit_return(self, node): - if not isinstance(node.frame(), astroid.Function): - self.add_message('return-outside-function', node=node) - - @check_messages('yield-outside-function') - def visit_yield(self, node): - if not isinstance(node.frame(), (astroid.Function, astroid.Lambda)): - self.add_message('yield-outside-function', node=node) - - @check_messages('not-in-loop') - def visit_continue(self, node): - self._check_in_loop(node, 'continue') - - @check_messages('not-in-loop') - def visit_break(self, node): - self._check_in_loop(node, 'break') - - @check_messages('useless-else-on-loop') - def visit_for(self, node): - self._check_else_on_loop(node) - - @check_messages('useless-else-on-loop') - def visit_while(self, node): - self._check_else_on_loop(node) - - @check_messages('nonexistent-operator') - def visit_unaryop(self, node): - """check use of the non-existent ++ and -- operator operator""" - if ((node.op in '+-') and - isinstance(node.operand, astroid.UnaryOp) and - (node.operand.op == node.op)): - self.add_message('nonexistent-operator', node=node, args=node.op*2) - - @check_messages('abstract-class-instantiated') - def visit_callfunc(self, node): - """ Check instantiating abstract class with - abc.ABCMeta as metaclass. - """ - try: - infered = next(node.func.infer()) - except astroid.InferenceError: - return - if not isinstance(infered, astroid.Class): - return - # __init__ was called - metaclass = infered.metaclass() - abstract_methods = _has_abstract_methods(infered) - if metaclass is None: - # Python 3.4 has `abc.ABC`, which won't be detected - # by ClassNode.metaclass() - for ancestor in infered.ancestors(): - if ancestor.qname() == 'abc.ABC' and abstract_methods: - self.add_message('abstract-class-instantiated', - args=(infered.name, ), - node=node) - break - return - if metaclass.qname() == 'abc.ABCMeta' and abstract_methods: - self.add_message('abstract-class-instantiated', - args=(infered.name, ), - node=node) - - def _check_else_on_loop(self, node): - """Check that any loop with an else clause has a break statement.""" - if node.orelse and not _loop_exits_early(node): - self.add_message('useless-else-on-loop', node=node, - # This is not optimal, but the line previous - # to the first statement in the else clause - # will usually be the one that contains the else:. - line=node.orelse[0].lineno - 1) - - def _check_in_loop(self, node, node_name): - """check that a node is inside a for or while loop""" - _node = node.parent - while _node: - if isinstance(_node, (astroid.For, astroid.While)): - break - _node = _node.parent - else: - self.add_message('not-in-loop', node=node, args=node_name) - - def _check_redefinition(self, redeftype, node): - """check for redefinition of a function / method / class name""" - defined_self = node.parent.frame()[node.name] - if defined_self is not node and not are_exclusive(node, defined_self): - self.add_message('function-redefined', node=node, - args=(redeftype, defined_self.fromlineno)) - - - -class BasicChecker(_BasicChecker): - """checks for : - * doc strings - * number of arguments, local variables, branches, returns and statements in -functions, methods - * required module attributes - * dangerous default values as arguments - * redefinition of function / method / class - * uses of the global statement - """ - - __implements__ = IAstroidChecker - - name = 'basic' - msgs = { - 'W0101': ('Unreachable code', - 'unreachable', - 'Used when there is some code behind a "return" or "raise" ' - 'statement, which will never be accessed.'), - 'W0102': ('Dangerous default value %s as argument', - 'dangerous-default-value', - 'Used when a mutable value as list or dictionary is detected in ' - 'a default value for an argument.'), - 'W0104': ('Statement seems to have no effect', - 'pointless-statement', - 'Used when a statement doesn\'t have (or at least seems to) ' - 'any effect.'), - 'W0105': ('String statement has no effect', - 'pointless-string-statement', - 'Used when a string is used as a statement (which of course ' - 'has no effect). This is a particular case of W0104 with its ' - 'own message so you can easily disable it if you\'re using ' - 'those strings as documentation, instead of comments.'), - 'W0106': ('Expression "%s" is assigned to nothing', - 'expression-not-assigned', - 'Used when an expression that is not a function call is assigned ' - 'to nothing. Probably something else was intended.'), - 'W0108': ('Lambda may not be necessary', - 'unnecessary-lambda', - 'Used when the body of a lambda expression is a function call ' - 'on the same argument list as the lambda itself; such lambda ' - 'expressions are in all but a few cases replaceable with the ' - 'function being called in the body of the lambda.'), - 'W0109': ("Duplicate key %r in dictionary", - 'duplicate-key', - 'Used when a dictionary expression binds the same key multiple ' - 'times.'), - 'W0122': ('Use of exec', - 'exec-used', - 'Used when you use the "exec" statement (function for Python ' - '3), to discourage its usage. That doesn\'t ' - 'mean you can not use it !'), - 'W0123': ('Use of eval', - 'eval-used', - 'Used when you use the "eval" function, to discourage its ' - 'usage. Consider using `ast.literal_eval` for safely evaluating ' - 'strings containing Python expressions ' - 'from untrusted sources. '), - 'W0141': ('Used builtin function %r', - 'bad-builtin', - 'Used when a black listed builtin function is used (see the ' - 'bad-function option). Usual black listed functions are the ones ' - 'like map, or filter , where Python offers now some cleaner ' - 'alternative like list comprehension.'), - 'W0142': ('Used * or ** magic', - 'star-args', - 'Used when a function or method is called using `*args` or ' - '`**kwargs` to dispatch arguments. This doesn\'t improve ' - 'readability and should be used with care.'), - 'W0150': ("%s statement in finally block may swallow exception", - 'lost-exception', - 'Used when a break or a return statement is found inside the ' - 'finally clause of a try...finally block: the exceptions raised ' - 'in the try clause will be silently swallowed instead of being ' - 're-raised.'), - 'W0199': ('Assert called on a 2-uple. Did you mean \'assert x,y\'?', - 'assert-on-tuple', - 'A call of assert on a tuple will always evaluate to true if ' - 'the tuple is not empty, and will always evaluate to false if ' - 'it is.'), - 'C0121': ('Missing required attribute "%s"', # W0103 - 'missing-module-attribute', - 'Used when an attribute required for modules is missing.'), - - 'E0109': ('Missing argument to reversed()', - 'missing-reversed-argument', - 'Used when reversed() builtin didn\'t receive an argument.'), - 'E0111': ('The first reversed() argument is not a sequence', - 'bad-reversed-sequence', - 'Used when the first argument to reversed() builtin ' - 'isn\'t a sequence (does not implement __reversed__, ' - 'nor __getitem__ and __len__'), - - } - - options = (('required-attributes', - {'default' : (), 'type' : 'csv', - 'metavar' : '', - 'help' : 'Required attributes for module, separated by a ' - 'comma'} - ), - ('bad-functions', - {'default' : BAD_FUNCTIONS, - 'type' :'csv', 'metavar' : '', - 'help' : 'List of builtins function names that should not be ' - 'used, separated by a comma'} - ), - ) - reports = (('RP0101', 'Statistics by type', report_by_type_stats),) - - def __init__(self, linter): - _BasicChecker.__init__(self, linter) - self.stats = None - self._tryfinallys = None - - def open(self): - """initialize visit variables and statistics - """ - self._tryfinallys = [] - self.stats = self.linter.add_stats(module=0, function=0, - method=0, class_=0) - - @check_messages('missing-module-attribute') - def visit_module(self, node): - """check module name, docstring and required arguments - """ - self.stats['module'] += 1 - for attr in self.config.required_attributes: - if attr not in node: - self.add_message('missing-module-attribute', node=node, args=attr) - - def visit_class(self, node): # pylint: disable=unused-argument - """check module name, docstring and redefinition - increment branch counter - """ - self.stats['class'] += 1 - - @check_messages('pointless-statement', 'pointless-string-statement', - 'expression-not-assigned') - def visit_discard(self, node): - """check for various kind of statements without effect""" - expr = node.value - if isinstance(expr, astroid.Const) and isinstance(expr.value, - six.string_types): - # treat string statement in a separated message - # Handle PEP-257 attribute docstrings. - # An attribute docstring is defined as being a string right after - # an assignment at the module level, class level or __init__ level. - scope = expr.scope() - if isinstance(scope, (astroid.Class, astroid.Module, astroid.Function)): - if isinstance(scope, astroid.Function) and scope.name != '__init__': - pass - else: - sibling = expr.previous_sibling() - if (sibling is not None and sibling.scope() is scope and - isinstance(sibling, astroid.Assign)): - return - self.add_message('pointless-string-statement', node=node) - return - # ignore if this is : - # * a direct function call - # * the unique child of a try/except body - # * a yield (which are wrapped by a discard node in _ast XXX) - # warn W0106 if we have any underlying function call (we can't predict - # side effects), else pointless-statement - if (isinstance(expr, (astroid.Yield, astroid.CallFunc)) or - (isinstance(node.parent, astroid.TryExcept) and - node.parent.body == [node])): - return - if any(expr.nodes_of_class(astroid.CallFunc)): - self.add_message('expression-not-assigned', node=node, - args=expr.as_string()) - else: - self.add_message('pointless-statement', node=node) - - @check_messages('unnecessary-lambda') - def visit_lambda(self, node): - """check whether or not the lambda is suspicious - """ - # if the body of the lambda is a call expression with the same - # argument list as the lambda itself, then the lambda is - # possibly unnecessary and at least suspicious. - if node.args.defaults: - # If the arguments of the lambda include defaults, then a - # judgment cannot be made because there is no way to check - # that the defaults defined by the lambda are the same as - # the defaults defined by the function called in the body - # of the lambda. - return - call = node.body - if not isinstance(call, astroid.CallFunc): - # The body of the lambda must be a function call expression - # for the lambda to be unnecessary. - return - # XXX are lambda still different with astroid >= 0.18 ? - # *args and **kwargs need to be treated specially, since they - # are structured differently between the lambda and the function - # call (in the lambda they appear in the args.args list and are - # indicated as * and ** by two bits in the lambda's flags, but - # in the function call they are omitted from the args list and - # are indicated by separate attributes on the function call node). - ordinary_args = list(node.args.args) - if node.args.kwarg: - if (not call.kwargs - or not isinstance(call.kwargs, astroid.Name) - or node.args.kwarg != call.kwargs.name): - return - elif call.kwargs: - return - if node.args.vararg: - if (not call.starargs - or not isinstance(call.starargs, astroid.Name) - or node.args.vararg != call.starargs.name): - return - elif call.starargs: - return - # The "ordinary" arguments must be in a correspondence such that: - # ordinary_args[i].name == call.args[i].name. - if len(ordinary_args) != len(call.args): - return - for i in range(len(ordinary_args)): - if not isinstance(call.args[i], astroid.Name): - return - if node.args.args[i].name != call.args[i].name: - return - if (isinstance(node.body.func, astroid.Getattr) and - isinstance(node.body.func.expr, astroid.CallFunc)): - # Chained call, the intermediate call might - # return something else (but we don't check that, yet). - return - self.add_message('unnecessary-lambda', line=node.fromlineno, node=node) - - @check_messages('dangerous-default-value') - def visit_function(self, node): - """check function name, docstring, arguments, redefinition, - variable names, max locals - """ - self.stats[node.is_method() and 'method' or 'function'] += 1 - self._check_dangerous_default(node) - - def _check_dangerous_default(self, node): - # check for dangerous default values as arguments - is_iterable = lambda n: isinstance(n, (astroid.List, - astroid.Set, - astroid.Dict)) - for default in node.args.defaults: - try: - value = next(default.infer()) - except astroid.InferenceError: - continue - - if (isinstance(value, astroid.Instance) and - value.qname() in DEFAULT_ARGUMENT_SYMBOLS): - - if value is default: - msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()] - elif type(value) is astroid.Instance or is_iterable(value): - # We are here in the following situation(s): - # * a dict/set/list/tuple call which wasn't inferred - # to a syntax node ({}, () etc.). This can happen - # when the arguments are invalid or unknown to - # the inference. - # * a variable from somewhere else, which turns out to be a list - # or a dict. - if is_iterable(default): - msg = value.pytype() - elif isinstance(default, astroid.CallFunc): - msg = '%s() (%s)' % (value.name, value.qname()) - else: - msg = '%s (%s)' % (default.as_string(), value.qname()) - else: - # this argument is a name - msg = '%s (%s)' % (default.as_string(), - DEFAULT_ARGUMENT_SYMBOLS[value.qname()]) - self.add_message('dangerous-default-value', - node=node, - args=(msg, )) - - @check_messages('unreachable', 'lost-exception') - def visit_return(self, node): - """1 - check is the node has a right sibling (if so, that's some - unreachable code) - 2 - check is the node is inside the finally clause of a try...finally - block - """ - self._check_unreachable(node) - # Is it inside final body of a try...finally bloc ? - self._check_not_in_finally(node, 'return', (astroid.Function,)) - - @check_messages('unreachable') - def visit_continue(self, node): - """check is the node has a right sibling (if so, that's some unreachable - code) - """ - self._check_unreachable(node) - - @check_messages('unreachable', 'lost-exception') - def visit_break(self, node): - """1 - check is the node has a right sibling (if so, that's some - unreachable code) - 2 - check is the node is inside the finally clause of a try...finally - block - """ - # 1 - Is it right sibling ? - self._check_unreachable(node) - # 2 - Is it inside final body of a try...finally bloc ? - self._check_not_in_finally(node, 'break', (astroid.For, astroid.While,)) - - @check_messages('unreachable') - def visit_raise(self, node): - """check if the node has a right sibling (if so, that's some unreachable - code) - """ - self._check_unreachable(node) - - @check_messages('exec-used') - def visit_exec(self, node): - """just print a warning on exec statements""" - self.add_message('exec-used', node=node) - - @check_messages('bad-builtin', 'star-args', 'eval-used', - 'exec-used', 'missing-reversed-argument', - 'bad-reversed-sequence') - def visit_callfunc(self, node): - """visit a CallFunc node -> check if this is not a blacklisted builtin - call and check for * or ** use - """ - if isinstance(node.func, astroid.Name): - name = node.func.name - # ignore the name if it's not a builtin (i.e. not defined in the - # locals nor globals scope) - if not (name in node.frame() or - name in node.root()): - if name == 'exec': - self.add_message('exec-used', node=node) - elif name == 'reversed': - self._check_reversed(node) - elif name == 'eval': - self.add_message('eval-used', node=node) - if name in self.config.bad_functions: - self.add_message('bad-builtin', node=node, args=name) - if node.starargs or node.kwargs: - scope = node.scope() - if isinstance(scope, astroid.Function): - toprocess = [(n, vn) for (n, vn) in ((node.starargs, scope.args.vararg), - (node.kwargs, scope.args.kwarg)) if n] - if toprocess: - for cfnode, fargname in toprocess[:]: - if getattr(cfnode, 'name', None) == fargname: - toprocess.remove((cfnode, fargname)) - if not toprocess: - return # star-args can be skipped - self.add_message('star-args', node=node.func) - - @check_messages('assert-on-tuple') - def visit_assert(self, node): - """check the use of an assert statement on a tuple.""" - if node.fail is None and isinstance(node.test, astroid.Tuple) and \ - len(node.test.elts) == 2: - self.add_message('assert-on-tuple', node=node) - - @check_messages('duplicate-key') - def visit_dict(self, node): - """check duplicate key in dictionary""" - keys = set() - for k, _ in node.items: - if isinstance(k, astroid.Const): - key = k.value - if key in keys: - self.add_message('duplicate-key', node=node, args=key) - keys.add(key) - - def visit_tryfinally(self, node): - """update try...finally flag""" - self._tryfinallys.append(node) - - def leave_tryfinally(self, node): # pylint: disable=unused-argument - """update try...finally flag""" - self._tryfinallys.pop() - - def _check_unreachable(self, node): - """check unreachable code""" - unreach_stmt = node.next_sibling() - if unreach_stmt is not None: - self.add_message('unreachable', node=unreach_stmt) - - def _check_not_in_finally(self, node, node_name, breaker_classes=()): - """check that a node is not inside a finally clause of a - try...finally statement. - If we found before a try...finally bloc a parent which its type is - in breaker_classes, we skip the whole check.""" - # if self._tryfinallys is empty, we're not a in try...finally bloc - if not self._tryfinallys: - return - # the node could be a grand-grand...-children of the try...finally - _parent = node.parent - _node = node - while _parent and not isinstance(_parent, breaker_classes): - if hasattr(_parent, 'finalbody') and _node in _parent.finalbody: - self.add_message('lost-exception', node=node, args=node_name) - return - _node = _parent - _parent = _node.parent - - def _check_reversed(self, node): - """ check that the argument to `reversed` is a sequence """ - try: - argument = safe_infer(get_argument_from_call(node, position=0)) - except NoSuchArgumentError: - self.add_message('missing-reversed-argument', node=node) - else: - if argument is astroid.YES: - return - if argument is None: - # Nothing was infered. - # Try to see if we have iter(). - if isinstance(node.args[0], astroid.CallFunc): - try: - func = next(node.args[0].func.infer()) - except InferenceError: - return - if (getattr(func, 'name', None) == 'iter' and - is_builtin_object(func)): - self.add_message('bad-reversed-sequence', node=node) - return - - if isinstance(argument, astroid.Instance): - if (argument._proxied.name == 'dict' and - is_builtin_object(argument._proxied)): - self.add_message('bad-reversed-sequence', node=node) - return - elif any(ancestor.name == 'dict' and is_builtin_object(ancestor) - for ancestor in argument._proxied.ancestors()): - # mappings aren't accepted by reversed() - self.add_message('bad-reversed-sequence', node=node) - return - - for methods in REVERSED_METHODS: - for meth in methods: - try: - argument.getattr(meth) - except astroid.NotFoundError: - break - else: - break - else: - # Check if it is a .deque. It doesn't seem that - # we can retrieve special methods - # from C implemented constructs. - if argument._proxied.qname().endswith(".deque"): - return - self.add_message('bad-reversed-sequence', node=node) - elif not isinstance(argument, (astroid.List, astroid.Tuple)): - # everything else is not a proper sequence for reversed() - self.add_message('bad-reversed-sequence', node=node) - -_NAME_TYPES = { - 'module': (MOD_NAME_RGX, 'module'), - 'const': (CONST_NAME_RGX, 'constant'), - 'class': (CLASS_NAME_RGX, 'class'), - 'function': (DEFAULT_NAME_RGX, 'function'), - 'method': (DEFAULT_NAME_RGX, 'method'), - 'attr': (DEFAULT_NAME_RGX, 'attribute'), - 'argument': (DEFAULT_NAME_RGX, 'argument'), - 'variable': (DEFAULT_NAME_RGX, 'variable'), - 'class_attribute': (CLASS_ATTRIBUTE_RGX, 'class attribute'), - 'inlinevar': (COMP_VAR_RGX, 'inline iteration'), -} - -def _create_naming_options(): - name_options = [] - for name_type, (rgx, human_readable_name) in six.iteritems(_NAME_TYPES): - name_type = name_type.replace('_', '-') - name_options.append(( - '%s-rgx' % (name_type,), - {'default': rgx, 'type': 'regexp', 'metavar': '', - 'help': 'Regular expression matching correct %s names' % (human_readable_name,)})) - name_options.append(( - '%s-name-hint' % (name_type,), - {'default': rgx.pattern, 'type': 'string', 'metavar': '', - 'help': 'Naming hint for %s names' % (human_readable_name,)})) - return tuple(name_options) - -class NameChecker(_BasicChecker): - msgs = { - 'C0102': ('Black listed name "%s"', - 'blacklisted-name', - 'Used when the name is listed in the black list (unauthorized ' - 'names).'), - 'C0103': ('Invalid %s name "%s"%s', - 'invalid-name', - 'Used when the name doesn\'t match the regular expression ' - 'associated to its type (constant, variable, class...).'), - } - - options = (('good-names', - {'default' : ('i', 'j', 'k', 'ex', 'Run', '_'), - 'type' :'csv', 'metavar' : '', - 'help' : 'Good variable names which should always be accepted,' - ' separated by a comma'} - ), - ('bad-names', - {'default' : ('foo', 'bar', 'baz', 'toto', 'tutu', 'tata'), - 'type' :'csv', 'metavar' : '', - 'help' : 'Bad variable names which should always be refused, ' - 'separated by a comma'} - ), - ('name-group', - {'default' : (), - 'type' :'csv', 'metavar' : '', - 'help' : ('Colon-delimited sets of names that determine each' - ' other\'s naming style when the name regexes' - ' allow several styles.')} - ), - ('include-naming-hint', - {'default': False, 'type' : 'yn', 'metavar' : '', - 'help': 'Include a hint for the correct naming format with invalid-name'} - ), - ) + _create_naming_options() - - - def __init__(self, linter): - _BasicChecker.__init__(self, linter) - self._name_category = {} - self._name_group = {} - self._bad_names = {} - - def open(self): - self.stats = self.linter.add_stats(badname_module=0, - badname_class=0, badname_function=0, - badname_method=0, badname_attr=0, - badname_const=0, - badname_variable=0, - badname_inlinevar=0, - badname_argument=0, - badname_class_attribute=0) - for group in self.config.name_group: - for name_type in group.split(':'): - self._name_group[name_type] = 'group_%s' % (group,) - - @check_messages('blacklisted-name', 'invalid-name') - def visit_module(self, node): - self._check_name('module', node.name.split('.')[-1], node) - self._bad_names = {} - - def leave_module(self, node): # pylint: disable=unused-argument - for all_groups in six.itervalues(self._bad_names): - if len(all_groups) < 2: - continue - groups = collections.defaultdict(list) - min_warnings = sys.maxsize - for group in six.itervalues(all_groups): - groups[len(group)].append(group) - min_warnings = min(len(group), min_warnings) - if len(groups[min_warnings]) > 1: - by_line = sorted(groups[min_warnings], - key=lambda group: min(warning[0].lineno for warning in group)) - warnings = itertools.chain(*by_line[1:]) - else: - warnings = groups[min_warnings][0] - for args in warnings: - self._raise_name_warning(*args) - - @check_messages('blacklisted-name', 'invalid-name') - def visit_class(self, node): - self._check_name('class', node.name, node) - for attr, anodes in six.iteritems(node.instance_attrs): - if not list(node.instance_attr_ancestors(attr)): - self._check_name('attr', attr, anodes[0]) - - @check_messages('blacklisted-name', 'invalid-name') - def visit_function(self, node): - # Do not emit any warnings if the method is just an implementation - # of a base class method. - confidence = HIGH - if node.is_method(): - if overrides_a_method(node.parent.frame(), node.name): - return - confidence = (INFERENCE if has_known_bases(node.parent.frame()) - else INFERENCE_FAILURE) - - self._check_name(_determine_function_name_type(node), - node.name, node, confidence) - # Check argument names - args = node.args.args - if args is not None: - self._recursive_check_names(args, node) - - @check_messages('blacklisted-name', 'invalid-name') - def visit_global(self, node): - for name in node.names: - self._check_name('const', name, node) - - @check_messages('blacklisted-name', 'invalid-name') - def visit_assname(self, node): - """check module level assigned names""" - frame = node.frame() - ass_type = node.ass_type() - if isinstance(ass_type, astroid.Comprehension): - self._check_name('inlinevar', node.name, node) - elif isinstance(frame, astroid.Module): - if isinstance(ass_type, astroid.Assign) and not in_loop(ass_type): - if isinstance(safe_infer(ass_type.value), astroid.Class): - self._check_name('class', node.name, node) - else: - if not _redefines_import(node): - # Don't emit if the name redefines an import - # in an ImportError except handler. - self._check_name('const', node.name, node) - elif isinstance(ass_type, astroid.ExceptHandler): - self._check_name('variable', node.name, node) - elif isinstance(frame, astroid.Function): - # global introduced variable aren't in the function locals - if node.name in frame and node.name not in frame.argnames(): - if not _redefines_import(node): - self._check_name('variable', node.name, node) - elif isinstance(frame, astroid.Class): - if not list(frame.local_attr_ancestors(node.name)): - self._check_name('class_attribute', node.name, node) - - def _recursive_check_names(self, args, node): - """check names in a possibly recursive list """ - for arg in args: - if isinstance(arg, astroid.AssName): - self._check_name('argument', arg.name, node) - else: - self._recursive_check_names(arg.elts, node) - - def _find_name_group(self, node_type): - return self._name_group.get(node_type, node_type) - - def _raise_name_warning(self, node, node_type, name, confidence): - type_label = _NAME_TYPES[node_type][1] - hint = '' - if self.config.include_naming_hint: - hint = ' (hint: %s)' % (getattr(self.config, node_type + '_name_hint')) - self.add_message('invalid-name', node=node, args=(type_label, name, hint), - confidence=confidence) - self.stats['badname_' + node_type] += 1 - - def _check_name(self, node_type, name, node, confidence=HIGH): - """check for a name using the type's regexp""" - if is_inside_except(node): - clobbering, _ = clobber_in_except(node) - if clobbering: - return - if name in self.config.good_names: - return - if name in self.config.bad_names: - self.stats['badname_' + node_type] += 1 - self.add_message('blacklisted-name', node=node, args=name) - return - regexp = getattr(self.config, node_type + '_rgx') - match = regexp.match(name) - - if _is_multi_naming_match(match, node_type, confidence): - name_group = self._find_name_group(node_type) - bad_name_group = self._bad_names.setdefault(name_group, {}) - warnings = bad_name_group.setdefault(match.lastgroup, []) - warnings.append((node, node_type, name, confidence)) - - if match is None: - self._raise_name_warning(node, node_type, name, confidence) - - -class DocStringChecker(_BasicChecker): - msgs = { - 'C0111': ('Missing %s docstring', # W0131 - 'missing-docstring', - 'Used when a module, function, class or method has no docstring.' - 'Some special methods like __init__ doesn\'t necessary require a ' - 'docstring.'), - 'C0112': ('Empty %s docstring', # W0132 - 'empty-docstring', - 'Used when a module, function, class or method has an empty ' - 'docstring (it would be too easy ;).'), - } - options = (('no-docstring-rgx', - {'default' : NO_REQUIRED_DOC_RGX, - 'type' : 'regexp', 'metavar' : '', - 'help' : 'Regular expression which should only match ' - 'function or class names that do not require a ' - 'docstring.'} - ), - ('docstring-min-length', - {'default' : -1, - 'type' : 'int', 'metavar' : '', - 'help': ('Minimum line length for functions/classes that' - ' require docstrings, shorter ones are exempt.')} - ), - ) - - - def open(self): - self.stats = self.linter.add_stats(undocumented_module=0, - undocumented_function=0, - undocumented_method=0, - undocumented_class=0) - @check_messages('missing-docstring', 'empty-docstring') - def visit_module(self, node): - self._check_docstring('module', node) - - @check_messages('missing-docstring', 'empty-docstring') - def visit_class(self, node): - if self.config.no_docstring_rgx.match(node.name) is None: - self._check_docstring('class', node) - - @check_messages('missing-docstring', 'empty-docstring') - def visit_function(self, node): - if self.config.no_docstring_rgx.match(node.name) is None: - ftype = node.is_method() and 'method' or 'function' - if isinstance(node.parent.frame(), astroid.Class): - overridden = False - confidence = (INFERENCE if has_known_bases(node.parent.frame()) - else INFERENCE_FAILURE) - # check if node is from a method overridden by its ancestor - for ancestor in node.parent.frame().ancestors(): - if node.name in ancestor and \ - isinstance(ancestor[node.name], astroid.Function): - overridden = True - break - self._check_docstring(ftype, node, - report_missing=not overridden, - confidence=confidence) - else: - self._check_docstring(ftype, node) - - def _check_docstring(self, node_type, node, report_missing=True, - confidence=HIGH): - """check the node has a non empty docstring""" - docstring = node.doc - if docstring is None: - if not report_missing: - return - if node.body: - lines = node.body[-1].lineno - node.body[0].lineno + 1 - else: - lines = 0 - - if node_type == 'module' and not lines: - # If the module has no body, there's no reason - # to require a docstring. - return - max_lines = self.config.docstring_min_length - - if node_type != 'module' and max_lines > -1 and lines < max_lines: - return - self.stats['undocumented_'+node_type] += 1 - if (node.body and isinstance(node.body[0], astroid.Discard) and - isinstance(node.body[0].value, astroid.CallFunc)): - # Most likely a string with a format call. Let's see. - func = safe_infer(node.body[0].value.func) - if (isinstance(func, astroid.BoundMethod) - and isinstance(func.bound, astroid.Instance)): - # Strings in Python 3, others in Python 2. - if PY3K and func.bound.name == 'str': - return - elif func.bound.name in ('str', 'unicode', 'bytes'): - return - self.add_message('missing-docstring', node=node, args=(node_type,), - confidence=confidence) - elif not docstring.strip(): - self.stats['undocumented_'+node_type] += 1 - self.add_message('empty-docstring', node=node, args=(node_type,), - confidence=confidence) - - -class PassChecker(_BasicChecker): - """check if the pass statement is really necessary""" - msgs = {'W0107': ('Unnecessary pass statement', - 'unnecessary-pass', - 'Used when a "pass" statement that can be avoided is ' - 'encountered.'), - } - @check_messages('unnecessary-pass') - def visit_pass(self, node): - if len(node.parent.child_sequence(node)) > 1: - self.add_message('unnecessary-pass', node=node) - - -class LambdaForComprehensionChecker(_BasicChecker): - """check for using a lambda where a comprehension would do. - - See - where GvR says comprehensions would be clearer. - """ - - msgs = {'W0110': ('map/filter on lambda could be replaced by comprehension', - 'deprecated-lambda', - 'Used when a lambda is the first argument to "map" or ' - '"filter". It could be clearer as a list ' - 'comprehension or generator expression.', - {'maxversion': (3, 0)}), - } - - @check_messages('deprecated-lambda') - def visit_callfunc(self, node): - """visit a CallFunc node, check if map or filter are called with a - lambda - """ - if not node.args: - return - if not isinstance(node.args[0], astroid.Lambda): - return - infered = safe_infer(node.func) - if (is_builtin_object(infered) - and infered.name in ['map', 'filter']): - self.add_message('deprecated-lambda', node=node) - - -def register(linter): - """required method to auto register this checker""" - linter.register_checker(BasicErrorChecker(linter)) - linter.register_checker(BasicChecker(linter)) - linter.register_checker(NameChecker(linter)) - linter.register_checker(DocStringChecker(linter)) - linter.register_checker(PassChecker(linter)) - linter.register_checker(LambdaForComprehensionChecker(linter)) diff --git a/checkers/classes.py b/checkers/classes.py deleted file mode 100644 index 8e51a47..0000000 --- a/checkers/classes.py +++ /dev/null @@ -1,982 +0,0 @@ -# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""classes checker for Python code -""" -from __future__ import generators - -import sys -from collections import defaultdict - -import astroid -from astroid import YES, Instance, are_exclusive, AssAttr, Class -from astroid.bases import Generator, BUILTINS -from astroid.inference import InferenceContext - -from pylint.interfaces import IAstroidChecker -from pylint.checkers import BaseChecker -from pylint.checkers.utils import ( - PYMETHODS, overrides_a_method, check_messages, is_attr_private, - is_attr_protected, node_frame_class, safe_infer, is_builtin_object, - decorated_with_property, unimplemented_abstract_methods) -import six - -if sys.version_info >= (3, 0): - NEXT_METHOD = '__next__' -else: - NEXT_METHOD = 'next' -ITER_METHODS = ('__iter__', '__getitem__') - -def _called_in_methods(func, klass, methods): - """ Check if the func was called in any of the given methods, - belonging to the *klass*. Returns True if so, False otherwise. - """ - if not isinstance(func, astroid.Function): - return False - for method in methods: - try: - infered = klass.getattr(method) - except astroid.NotFoundError: - continue - for infer_method in infered: - for callfunc in infer_method.nodes_of_class(astroid.CallFunc): - try: - bound = next(callfunc.func.infer()) - except (astroid.InferenceError, StopIteration): - continue - if not isinstance(bound, astroid.BoundMethod): - continue - func_obj = bound._proxied - if isinstance(func_obj, astroid.UnboundMethod): - func_obj = func_obj._proxied - if func_obj.name == func.name: - return True - return False - -def class_is_abstract(node): - """return true if the given class node should be considered as an abstract - class - """ - for method in node.methods(): - if method.parent.frame() is node: - if method.is_abstract(pass_is_abstract=False): - return True - return False - -def _is_attribute_property(name, klass): - """ Check if the given attribute *name* is a property - in the given *klass*. - - It will look for `property` calls or for functions - with the given name, decorated by `property` or `property` - subclasses. - Returns ``True`` if the name is a property in the given klass, - ``False`` otherwise. - """ - - try: - attributes = klass.getattr(name) - except astroid.NotFoundError: - return False - property_name = "{0}.property".format(BUILTINS) - for attr in attributes: - try: - infered = next(attr.infer()) - except astroid.InferenceError: - continue - if (isinstance(infered, astroid.Function) and - decorated_with_property(infered)): - return True - if infered.pytype() == property_name: - return True - return False - - -MSGS = { - 'F0202': ('Unable to check methods signature (%s / %s)', - 'method-check-failed', - 'Used when Pylint has been unable to check methods signature \ - compatibility for an unexpected reason. Please report this kind \ - if you don\'t make sense of it.'), - - 'E0202': ('An attribute defined in %s line %s hides this method', - 'method-hidden', - 'Used when a class defines a method which is hidden by an ' - 'instance attribute from an ancestor class or set by some ' - 'client code.'), - 'E0203': ('Access to member %r before its definition line %s', - 'access-member-before-definition', - 'Used when an instance member is accessed before it\'s actually\ - assigned.'), - 'W0201': ('Attribute %r defined outside __init__', - 'attribute-defined-outside-init', - 'Used when an instance attribute is defined outside the __init__\ - method.'), - - 'W0212': ('Access to a protected member %s of a client class', # E0214 - 'protected-access', - 'Used when a protected member (i.e. class member with a name \ - beginning with an underscore) is access outside the class or a \ - descendant of the class where it\'s defined.'), - - 'E0211': ('Method has no argument', - 'no-method-argument', - 'Used when a method which should have the bound instance as \ - first argument has no argument defined.'), - 'E0213': ('Method should have "self" as first argument', - 'no-self-argument', - 'Used when a method has an attribute different the "self" as\ - first argument. This is considered as an error since this is\ - a so common convention that you shouldn\'t break it!'), - 'C0202': ('Class method %s should have %s as first argument', # E0212 - 'bad-classmethod-argument', - 'Used when a class method has a first argument named differently ' - 'than the value specified in valid-classmethod-first-arg option ' - '(default to "cls"), recommended to easily differentiate them ' - 'from regular instance methods.'), - 'C0203': ('Metaclass method %s should have %s as first argument', # E0214 - 'bad-mcs-method-argument', - 'Used when a metaclass method has a first agument named ' - 'differently than the value specified in valid-classmethod-first' - '-arg option (default to "cls"), recommended to easily ' - 'differentiate them from regular instance methods.'), - 'C0204': ('Metaclass class method %s should have %s as first argument', - 'bad-mcs-classmethod-argument', - 'Used when a metaclass class method has a first argument named ' - 'differently than the value specified in valid-metaclass-' - 'classmethod-first-arg option (default to "mcs"), recommended to ' - 'easily differentiate them from regular instance methods.'), - - 'W0211': ('Static method with %r as first argument', - 'bad-staticmethod-argument', - 'Used when a static method has "self" or a value specified in ' - 'valid-classmethod-first-arg option or ' - 'valid-metaclass-classmethod-first-arg option as first argument.' - ), - 'R0201': ('Method could be a function', - 'no-self-use', - 'Used when a method doesn\'t use its bound instance, and so could\ - be written as a function.' - ), - - 'E0221': ('Interface resolved to %s is not a class', - 'interface-is-not-class', - 'Used when a class claims to implement an interface which is not \ - a class.'), - 'E0222': ('Missing method %r from %s interface', - 'missing-interface-method', - 'Used when a method declared in an interface is missing from a \ - class implementing this interface'), - 'W0221': ('Arguments number differs from %s %r method', - 'arguments-differ', - 'Used when a method has a different number of arguments than in \ - the implemented interface or in an overridden method.'), - 'W0222': ('Signature differs from %s %r method', - 'signature-differs', - 'Used when a method signature is different than in the \ - implemented interface or in an overridden method.'), - 'W0223': ('Method %r is abstract in class %r but is not overridden', - 'abstract-method', - 'Used when an abstract method (i.e. raise NotImplementedError) is \ - not overridden in concrete class.' - ), - 'F0220': ('failed to resolve interfaces implemented by %s (%s)', # W0224 - 'unresolved-interface', - 'Used when a Pylint as failed to find interfaces implemented by \ - a class'), - - - 'W0231': ('__init__ method from base class %r is not called', - 'super-init-not-called', - 'Used when an ancestor class method has an __init__ method \ - which is not called by a derived class.'), - 'W0232': ('Class has no __init__ method', - 'no-init', - 'Used when a class has no __init__ method, neither its parent \ - classes.'), - 'W0233': ('__init__ method from a non direct base class %r is called', - 'non-parent-init-called', - 'Used when an __init__ method is called on a class which is not \ - in the direct ancestors for the analysed class.'), - 'W0234': ('__iter__ returns non-iterator', - 'non-iterator-returned', - 'Used when an __iter__ method returns something which is not an \ - iterable (i.e. has no `%s` method)' % NEXT_METHOD), - 'E0235': ('__exit__ must accept 3 arguments: type, value, traceback', - 'bad-context-manager', - 'Used when the __exit__ special method, belonging to a \ - context manager, does not accept 3 arguments \ - (type, value, traceback).'), - 'E0236': ('Invalid object %r in __slots__, must contain ' - 'only non empty strings', - 'invalid-slots-object', - 'Used when an invalid (non-string) object occurs in __slots__.'), - 'E0237': ('Assigning to attribute %r not defined in class slots', - 'assigning-non-slot', - 'Used when assigning to an attribute not defined ' - 'in the class slots.'), - 'E0238': ('Invalid __slots__ object', - 'invalid-slots', - 'Used when an invalid __slots__ is found in class. ' - 'Only a string, an iterable or a sequence is permitted.'), - 'E0239': ('Inheriting %r, which is not a class.', - 'inherit-non-class', - 'Used when a class inherits from something which is not a ' - 'class.'), - - - } - - -class ClassChecker(BaseChecker): - """checks for : - * methods without self as first argument - * overridden methods signature - * access only to existent members via self - * attributes not defined in the __init__ method - * supported interfaces implementation - * unreachable code - """ - - __implements__ = (IAstroidChecker,) - - # configuration section name - name = 'classes' - # messages - msgs = MSGS - priority = -2 - # configuration options - options = (('ignore-iface-methods', - {'default' : (#zope interface - 'isImplementedBy', 'deferred', 'extends', 'names', - 'namesAndDescriptions', 'queryDescriptionFor', 'getBases', - 'getDescriptionFor', 'getDoc', 'getName', 'getTaggedValue', - 'getTaggedValueTags', 'isEqualOrExtendedBy', 'setTaggedValue', - 'isImplementedByInstancesOf', - # twisted - 'adaptWith', - # logilab.common interface - 'is_implemented_by'), - 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of interface methods to ignore, \ -separated by a comma. This is used for instance to not check methods defines \ -in Zope\'s Interface base class.'} - ), - ('defining-attr-methods', - {'default' : ('__init__', '__new__', 'setUp'), - 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of method names used to declare (i.e. assign) \ -instance attributes.'} - ), - ('valid-classmethod-first-arg', - {'default' : ('cls',), - 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of valid names for the first argument in \ -a class method.'} - ), - ('valid-metaclass-classmethod-first-arg', - {'default' : ('mcs',), - 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of valid names for the first argument in \ -a metaclass class method.'} - ), - ('exclude-protected', - { - 'default': ( - # namedtuple public API. - '_asdict', '_fields', '_replace', '_source', '_make'), - 'type': 'csv', - 'metavar': '', - 'help': ('List of member names, which should be excluded ' - 'from the protected access warning.')} - )) - - def __init__(self, linter=None): - BaseChecker.__init__(self, linter) - self._accessed = [] - self._first_attrs = [] - self._meth_could_be_func = None - - def visit_class(self, node): - """init visit variable _accessed and check interfaces - """ - self._accessed.append(defaultdict(list)) - self._check_bases_classes(node) - self._check_interfaces(node) - # if not an interface, exception, metaclass - if node.type == 'class': - try: - node.local_attr('__init__') - except astroid.NotFoundError: - self.add_message('no-init', args=node, node=node) - self._check_slots(node) - self._check_proper_bases(node) - - @check_messages('inherit-non-class') - def _check_proper_bases(self, node): - """ - Detect that a class inherits something which is not - a class or a type. - """ - for base in node.bases: - ancestor = safe_infer(base) - if ancestor in (YES, None): - continue - if (isinstance(ancestor, astroid.Instance) and - ancestor.is_subtype_of('%s.type' % (BUILTINS,))): - continue - if not isinstance(ancestor, astroid.Class): - self.add_message('inherit-non-class', - args=base.as_string(), node=node) - - @check_messages('access-member-before-definition', - 'attribute-defined-outside-init') - def leave_class(self, cnode): - """close a class node: - check that instance attributes are defined in __init__ and check - access to existent members - """ - # check access to existent members on non metaclass classes - accessed = self._accessed.pop() - if cnode.type != 'metaclass': - self._check_accessed_members(cnode, accessed) - # checks attributes are defined in an allowed method such as __init__ - if not self.linter.is_message_enabled('attribute-defined-outside-init'): - return - defining_methods = self.config.defining_attr_methods - current_module = cnode.root() - for attr, nodes in six.iteritems(cnode.instance_attrs): - # skip nodes which are not in the current module and it may screw up - # the output, while it's not worth it - nodes = [n for n in nodes if not - isinstance(n.statement(), (astroid.Delete, astroid.AugAssign)) - and n.root() is current_module] - if not nodes: - continue # error detected by typechecking - # check if any method attr is defined in is a defining method - if any(node.frame().name in defining_methods - for node in nodes): - continue - - # check attribute is defined in a parent's __init__ - for parent in cnode.instance_attr_ancestors(attr): - attr_defined = False - # check if any parent method attr is defined in is a defining method - for node in parent.instance_attrs[attr]: - if node.frame().name in defining_methods: - attr_defined = True - if attr_defined: - # we're done :) - break - else: - # check attribute is defined as a class attribute - try: - cnode.local_attr(attr) - except astroid.NotFoundError: - for node in nodes: - if node.frame().name not in defining_methods: - # If the attribute was set by a callfunc in any - # of the defining methods, then don't emit - # the warning. - if _called_in_methods(node.frame(), cnode, - defining_methods): - continue - self.add_message('attribute-defined-outside-init', - args=attr, node=node) - - def visit_function(self, node): - """check method arguments, overriding""" - # ignore actual functions - if not node.is_method(): - return - klass = node.parent.frame() - self._meth_could_be_func = True - # check first argument is self if this is actually a method - self._check_first_arg_for_type(node, klass.type == 'metaclass') - if node.name == '__init__': - self._check_init(node) - return - # check signature if the method overloads inherited method - for overridden in klass.local_attr_ancestors(node.name): - # get astroid for the searched method - try: - meth_node = overridden[node.name] - except KeyError: - # we have found the method but it's not in the local - # dictionary. - # This may happen with astroid build from living objects - continue - if not isinstance(meth_node, astroid.Function): - continue - self._check_signature(node, meth_node, 'overridden') - break - if node.decorators: - for decorator in node.decorators.nodes: - if isinstance(decorator, astroid.Getattr) and \ - decorator.attrname in ('getter', 'setter', 'deleter'): - # attribute affectation will call this method, not hiding it - return - if isinstance(decorator, astroid.Name) and decorator.name == 'property': - # attribute affectation will either call a setter or raise - # an attribute error, anyway not hiding the function - return - # check if the method is hidden by an attribute - try: - overridden = klass.instance_attr(node.name)[0] # XXX - overridden_frame = overridden.frame() - if (isinstance(overridden_frame, astroid.Function) - and overridden_frame.type == 'method'): - overridden_frame = overridden_frame.parent.frame() - if (isinstance(overridden_frame, Class) - and klass.is_subtype_of(overridden_frame.qname())): - args = (overridden.root().name, overridden.fromlineno) - self.add_message('method-hidden', args=args, node=node) - except astroid.NotFoundError: - pass - - # check non-iterators in __iter__ - if node.name == '__iter__': - self._check_iter(node) - elif node.name == '__exit__': - self._check_exit(node) - - def _check_slots(self, node): - if '__slots__' not in node.locals: - return - for slots in node.igetattr('__slots__'): - # check if __slots__ is a valid type - for meth in ITER_METHODS: - try: - slots.getattr(meth) - break - except astroid.NotFoundError: - continue - else: - self.add_message('invalid-slots', node=node) - continue - - if isinstance(slots, astroid.Const): - # a string, ignore the following checks - continue - if not hasattr(slots, 'itered'): - # we can't obtain the values, maybe a .deque? - continue - - if isinstance(slots, astroid.Dict): - values = [item[0] for item in slots.items] - else: - values = slots.itered() - if values is YES: - return - - for elt in values: - try: - self._check_slots_elt(elt) - except astroid.InferenceError: - continue - - def _check_slots_elt(self, elt): - for infered in elt.infer(): - if infered is YES: - continue - if (not isinstance(infered, astroid.Const) or - not isinstance(infered.value, six.string_types)): - self.add_message('invalid-slots-object', - args=infered.as_string(), - node=elt) - continue - if not infered.value: - self.add_message('invalid-slots-object', - args=infered.as_string(), - node=elt) - - def _check_iter(self, node): - try: - infered = node.infer_call_result(node) - except astroid.InferenceError: - return - - for infered_node in infered: - if (infered_node is YES - or isinstance(infered_node, Generator)): - continue - if isinstance(infered_node, astroid.Instance): - try: - infered_node.local_attr(NEXT_METHOD) - except astroid.NotFoundError: - self.add_message('non-iterator-returned', - node=node) - break - - def _check_exit(self, node): - positional = sum(1 for arg in node.args.args if arg.name != 'self') - if positional < 3 and not node.args.vararg: - self.add_message('bad-context-manager', - node=node) - elif positional > 3: - self.add_message('bad-context-manager', - node=node) - - def leave_function(self, node): - """on method node, check if this method couldn't be a function - - ignore class, static and abstract methods, initializer, - methods overridden from a parent class and any - kind of method defined in an interface for this warning - """ - if node.is_method(): - if node.args.args is not None: - self._first_attrs.pop() - if not self.linter.is_message_enabled('no-self-use'): - return - class_node = node.parent.frame() - if (self._meth_could_be_func and node.type == 'method' - and not node.name in PYMETHODS - and not (node.is_abstract() or - overrides_a_method(class_node, node.name)) - and class_node.type != 'interface'): - self.add_message('no-self-use', node=node) - - def visit_getattr(self, node): - """check if the getattr is an access to a class member - if so, register it. Also check for access to protected - class member from outside its class (but ignore __special__ - methods) - """ - attrname = node.attrname - # Check self - if self.is_first_attr(node): - self._accessed[-1][attrname].append(node) - return - if not self.linter.is_message_enabled('protected-access'): - return - - self._check_protected_attribute_access(node) - - def visit_assattr(self, node): - if isinstance(node.ass_type(), astroid.AugAssign) and self.is_first_attr(node): - self._accessed[-1][node.attrname].append(node) - self._check_in_slots(node) - - def _check_in_slots(self, node): - """ Check that the given assattr node - is defined in the class slots. - """ - infered = safe_infer(node.expr) - if infered and isinstance(infered, Instance): - klass = infered._proxied - if '__slots__' not in klass.locals or not klass.newstyle: - return - - slots = klass.slots() - if slots is None: - return - # If any ancestor doesn't use slots, the slots - # defined for this class are superfluous. - if any('__slots__' not in ancestor.locals and - ancestor.name != 'object' - for ancestor in klass.ancestors()): - return - - if not any(slot.value == node.attrname for slot in slots): - # If we have a '__dict__' in slots, then - # assigning any name is valid. - if not any(slot.value == '__dict__' for slot in slots): - if _is_attribute_property(node.attrname, klass): - # Properties circumvent the slots mechanism, - # so we should not emit a warning for them. - return - self.add_message('assigning-non-slot', - args=(node.attrname, ), node=node) - - @check_messages('protected-access') - def visit_assign(self, assign_node): - node = assign_node.targets[0] - if not isinstance(node, AssAttr): - return - - if self.is_first_attr(node): - return - - self._check_protected_attribute_access(node) - - def _check_protected_attribute_access(self, node): - '''Given an attribute access node (set or get), check if attribute - access is legitimate. Call _check_first_attr with node before calling - this method. Valid cases are: - * self._attr in a method or cls._attr in a classmethod. Checked by - _check_first_attr. - * Klass._attr inside "Klass" class. - * Klass2._attr inside "Klass" class when Klass2 is a base class of - Klass. - ''' - attrname = node.attrname - - if (is_attr_protected(attrname) and - attrname not in self.config.exclude_protected): - - klass = node_frame_class(node) - - # XXX infer to be more safe and less dirty ?? - # in classes, check we are not getting a parent method - # through the class object or through super - callee = node.expr.as_string() - - # We are not in a class, no remaining valid case - if klass is None: - self.add_message('protected-access', node=node, args=attrname) - return - - # If the expression begins with a call to super, that's ok. - if isinstance(node.expr, astroid.CallFunc) and \ - isinstance(node.expr.func, astroid.Name) and \ - node.expr.func.name == 'super': - return - - # We are in a class, one remaining valid cases, Klass._attr inside - # Klass - if not (callee == klass.name or callee in klass.basenames): - # Detect property assignments in the body of the class. - # This is acceptable: - # - # class A: - # b = property(lambda: self._b) - - stmt = node.parent.statement() - try: - if (isinstance(stmt, astroid.Assign) and - (stmt in klass.body or klass.parent_of(stmt)) and - isinstance(stmt.value, astroid.CallFunc) and - isinstance(stmt.value.func, astroid.Name) and - stmt.value.func.name == 'property' and - is_builtin_object(next(stmt.value.func.infer(), None))): - return - except astroid.InferenceError: - pass - self.add_message('protected-access', node=node, args=attrname) - - def visit_name(self, node): - """check if the name handle an access to a class member - if so, register it - """ - if self._first_attrs and (node.name == self._first_attrs[-1] or - not self._first_attrs[-1]): - self._meth_could_be_func = False - - def _check_accessed_members(self, node, accessed): - """check that accessed members are defined""" - # XXX refactor, probably much simpler now that E0201 is in type checker - for attr, nodes in six.iteritems(accessed): - # deactivate "except doesn't do anything", that's expected - # pylint: disable=W0704 - try: - # is it a class attribute ? - node.local_attr(attr) - # yes, stop here - continue - except astroid.NotFoundError: - pass - # is it an instance attribute of a parent class ? - try: - next(node.instance_attr_ancestors(attr)) - # yes, stop here - continue - except StopIteration: - pass - # is it an instance attribute ? - try: - defstmts = node.instance_attr(attr) - except astroid.NotFoundError: - pass - else: - # filter out augment assignment nodes - defstmts = [stmt for stmt in defstmts if stmt not in nodes] - if not defstmts: - # only augment assignment for this node, no-member should be - # triggered by the typecheck checker - continue - # filter defstmts to only pick the first one when there are - # several assignments in the same scope - scope = defstmts[0].scope() - defstmts = [stmt for i, stmt in enumerate(defstmts) - if i == 0 or stmt.scope() is not scope] - # if there are still more than one, don't attempt to be smarter - # than we can be - if len(defstmts) == 1: - defstmt = defstmts[0] - # check that if the node is accessed in the same method as - # it's defined, it's accessed after the initial assignment - frame = defstmt.frame() - lno = defstmt.fromlineno - for _node in nodes: - if _node.frame() is frame and _node.fromlineno < lno \ - and not are_exclusive(_node.statement(), defstmt, - ('AttributeError', 'Exception', 'BaseException')): - self.add_message('access-member-before-definition', - node=_node, args=(attr, lno)) - - def _check_first_arg_for_type(self, node, metaclass=0): - """check the name of first argument, expect: - - * 'self' for a regular method - * 'cls' for a class method or a metaclass regular method (actually - valid-classmethod-first-arg value) - * 'mcs' for a metaclass class method (actually - valid-metaclass-classmethod-first-arg) - * not one of the above for a static method - """ - # don't care about functions with unknown argument (builtins) - if node.args.args is None: - return - first_arg = node.args.args and node.argnames()[0] - self._first_attrs.append(first_arg) - first = self._first_attrs[-1] - # static method - if node.type == 'staticmethod': - if (first_arg == 'self' or - first_arg in self.config.valid_classmethod_first_arg or - first_arg in self.config.valid_metaclass_classmethod_first_arg): - self.add_message('bad-staticmethod-argument', args=first, node=node) - return - self._first_attrs[-1] = None - # class / regular method with no args - elif not node.args.args: - self.add_message('no-method-argument', node=node) - # metaclass - elif metaclass: - # metaclass __new__ or classmethod - if node.type == 'classmethod': - self._check_first_arg_config( - first, - self.config.valid_metaclass_classmethod_first_arg, node, - 'bad-mcs-classmethod-argument', node.name) - # metaclass regular method - else: - self._check_first_arg_config( - first, - self.config.valid_classmethod_first_arg, node, - 'bad-mcs-method-argument', - node.name) - # regular class - else: - # class method - if node.type == 'classmethod': - self._check_first_arg_config( - first, - self.config.valid_classmethod_first_arg, node, - 'bad-classmethod-argument', - node.name) - # regular method without self as argument - elif first != 'self': - self.add_message('no-self-argument', node=node) - - def _check_first_arg_config(self, first, config, node, message, - method_name): - if first not in config: - if len(config) == 1: - valid = repr(config[0]) - else: - valid = ', '.join(repr(v) for v in config[:-1]) - valid = '%s or %r' % (valid, config[-1]) - self.add_message(message, args=(method_name, valid), node=node) - - def _check_bases_classes(self, node): - """check that the given class node implements abstract methods from - base classes - """ - def is_abstract(method): - return method.is_abstract(pass_is_abstract=False) - - # check if this class abstract - if class_is_abstract(node): - return - - methods = sorted( - unimplemented_abstract_methods(node, is_abstract).items(), - key=lambda item: item[0], - ) - for name, method in methods: - owner = method.parent.frame() - if owner is node: - continue - # owner is not this class, it must be a parent class - # check that the ancestor's method is not abstract - if name in node.locals: - # it is redefined as an attribute or with a descriptor - continue - self.add_message('abstract-method', node=node, - args=(name, owner.name)) - - def _check_interfaces(self, node): - """check that the given class node really implements declared - interfaces - """ - e0221_hack = [False] - def iface_handler(obj): - """filter interface objects, it should be classes""" - if not isinstance(obj, astroid.Class): - e0221_hack[0] = True - self.add_message('interface-is-not-class', node=node, - args=(obj.as_string(),)) - return False - return True - ignore_iface_methods = self.config.ignore_iface_methods - try: - for iface in node.interfaces(handler_func=iface_handler): - for imethod in iface.methods(): - name = imethod.name - if name.startswith('_') or name in ignore_iface_methods: - # don't check method beginning with an underscore, - # usually belonging to the interface implementation - continue - # get class method astroid - try: - method = node_method(node, name) - except astroid.NotFoundError: - self.add_message('missing-interface-method', - args=(name, iface.name), - node=node) - continue - # ignore inherited methods - if method.parent.frame() is not node: - continue - # check signature - self._check_signature(method, imethod, - '%s interface' % iface.name) - except astroid.InferenceError: - if e0221_hack[0]: - return - implements = Instance(node).getattr('__implements__')[0] - assignment = implements.parent - assert isinstance(assignment, astroid.Assign) - # assignment.expr can be a Name or a Tuple or whatever. - # Use as_string() for the message - # FIXME: in case of multiple interfaces, find which one could not - # be resolved - self.add_message('unresolved-interface', node=implements, - args=(node.name, assignment.value.as_string())) - - def _check_init(self, node): - """check that the __init__ method call super or ancestors'__init__ - method - """ - if (not self.linter.is_message_enabled('super-init-not-called') and - not self.linter.is_message_enabled('non-parent-init-called')): - return - klass_node = node.parent.frame() - to_call = _ancestors_to_call(klass_node) - not_called_yet = dict(to_call) - for stmt in node.nodes_of_class(astroid.CallFunc): - expr = stmt.func - if not isinstance(expr, astroid.Getattr) \ - or expr.attrname != '__init__': - continue - # skip the test if using super - if isinstance(expr.expr, astroid.CallFunc) and \ - isinstance(expr.expr.func, astroid.Name) and \ - expr.expr.func.name == 'super': - return - try: - for klass in expr.expr.infer(): - if klass is YES: - continue - # The infered klass can be super(), which was - # assigned to a variable and the `__init__` - # was called later. - # - # base = super() - # base.__init__(...) - - if (isinstance(klass, astroid.Instance) and - isinstance(klass._proxied, astroid.Class) and - is_builtin_object(klass._proxied) and - klass._proxied.name == 'super'): - return - try: - del not_called_yet[klass] - except KeyError: - if klass not in to_call: - self.add_message('non-parent-init-called', - node=expr, args=klass.name) - except astroid.InferenceError: - continue - for klass, method in six.iteritems(not_called_yet): - if klass.name == 'object' or method.parent.name == 'object': - continue - self.add_message('super-init-not-called', args=klass.name, node=node) - - def _check_signature(self, method1, refmethod, class_type): - """check that the signature of the two given methods match - - class_type is in 'class', 'interface' - """ - if not (isinstance(method1, astroid.Function) - and isinstance(refmethod, astroid.Function)): - self.add_message('method-check-failed', - args=(method1, refmethod), node=method1) - return - # don't care about functions with unknown argument (builtins) - if method1.args.args is None or refmethod.args.args is None: - return - # if we use *args, **kwargs, skip the below checks - if method1.args.vararg or method1.args.kwarg: - return - if is_attr_private(method1.name): - return - if len(method1.args.args) != len(refmethod.args.args): - self.add_message('arguments-differ', - args=(class_type, method1.name), - node=method1) - elif len(method1.args.defaults) < len(refmethod.args.defaults): - self.add_message('signature-differs', - args=(class_type, method1.name), - node=method1) - - def is_first_attr(self, node): - """Check that attribute lookup name use first attribute variable name - (self for method, cls for classmethod and mcs for metaclass). - """ - return self._first_attrs and isinstance(node.expr, astroid.Name) and \ - node.expr.name == self._first_attrs[-1] - -def _ancestors_to_call(klass_node, method='__init__'): - """return a dictionary where keys are the list of base classes providing - the queried method, and so that should/may be called from the method node - """ - to_call = {} - for base_node in klass_node.ancestors(recurs=False): - try: - to_call[base_node] = next(base_node.igetattr(method)) - except astroid.InferenceError: - continue - return to_call - - -def node_method(node, method_name): - """get astroid for on the given class node, ensuring it - is a Function node - """ - for n in node.local_attr(method_name): - if isinstance(n, astroid.Function): - return n - raise astroid.NotFoundError(method_name) - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(ClassChecker(linter)) diff --git a/checkers/design_analysis.py b/checkers/design_analysis.py deleted file mode 100644 index e361cca..0000000 --- a/checkers/design_analysis.py +++ /dev/null @@ -1,370 +0,0 @@ -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""check for signs of poor design""" - -import re -from collections import defaultdict - -from astroid import Function, If, InferenceError - -from pylint.interfaces import IAstroidChecker -from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages - -# regexp for ignored argument name -IGNORED_ARGUMENT_NAMES = re.compile('_.*') - - -def class_is_abstract(klass): - """return true if the given class node should be considered as an abstract - class - """ - for attr in klass.values(): - if isinstance(attr, Function): - if attr.is_abstract(pass_is_abstract=False): - return True - return False - - -MSGS = { - 'R0901': ('Too many ancestors (%s/%s)', - 'too-many-ancestors', - 'Used when class has too many parent classes, try to reduce \ - this to get a simpler (and so easier to use) class.'), - 'R0902': ('Too many instance attributes (%s/%s)', - 'too-many-instance-attributes', - 'Used when class has too many instance attributes, try to reduce \ - this to get a simpler (and so easier to use) class.'), - 'R0903': ('Too few public methods (%s/%s)', - 'too-few-public-methods', - 'Used when class has too few public methods, so be sure it\'s \ - really worth it.'), - 'R0904': ('Too many public methods (%s/%s)', - 'too-many-public-methods', - 'Used when class has too many public methods, try to reduce \ - this to get a simpler (and so easier to use) class.'), - - 'R0911': ('Too many return statements (%s/%s)', - 'too-many-return-statements', - 'Used when a function or method has too many return statement, \ - making it hard to follow.'), - 'R0912': ('Too many branches (%s/%s)', - 'too-many-branches', - 'Used when a function or method has too many branches, \ - making it hard to follow.'), - 'R0913': ('Too many arguments (%s/%s)', - 'too-many-arguments', - 'Used when a function or method takes too many arguments.'), - 'R0914': ('Too many local variables (%s/%s)', - 'too-many-locals', - 'Used when a function or method has too many local variables.'), - 'R0915': ('Too many statements (%s/%s)', - 'too-many-statements', - 'Used when a function or method has too many statements. You \ - should then split it in smaller functions / methods.'), - - 'R0921': ('Abstract class not referenced', - 'abstract-class-not-used', - 'Used when an abstract class is not used as ancestor anywhere.'), - 'R0922': ('Abstract class is only referenced %s times', - 'abstract-class-little-used', - 'Used when an abstract class is used less than X times as \ - ancestor.'), - 'R0923': ('Interface not implemented', - 'interface-not-implemented', - 'Used when an interface class is not implemented anywhere.'), - } - - -class MisdesignChecker(BaseChecker): - """checks for sign of poor/misdesign: - * number of methods, attributes, local variables... - * size, complexity of functions, methods - """ - - __implements__ = (IAstroidChecker,) - - # configuration section name - name = 'design' - # messages - msgs = MSGS - priority = -2 - # configuration options - options = (('max-args', - {'default' : 5, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of arguments for function / method'} - ), - ('ignored-argument-names', - {'default' : IGNORED_ARGUMENT_NAMES, - 'type' :'regexp', 'metavar' : '', - 'help' : 'Argument names that match this expression will be ' - 'ignored. Default to name with leading underscore'} - ), - ('max-locals', - {'default' : 15, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of locals for function / method body'} - ), - ('max-returns', - {'default' : 6, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of return / yield for function / ' - 'method body'} - ), - ('max-branches', - {'default' : 12, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of branch for function / method body'} - ), - ('max-statements', - {'default' : 50, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of statements in function / method ' - 'body'} - ), - ('max-parents', - {'default' : 7, - 'type' : 'int', - 'metavar' : '', - 'help' : 'Maximum number of parents for a class (see R0901).'} - ), - ('max-attributes', - {'default' : 7, - 'type' : 'int', - 'metavar' : '', - 'help' : 'Maximum number of attributes for a class \ -(see R0902).'} - ), - ('min-public-methods', - {'default' : 2, - 'type' : 'int', - 'metavar' : '', - 'help' : 'Minimum number of public methods for a class \ -(see R0903).'} - ), - ('max-public-methods', - {'default' : 20, - 'type' : 'int', - 'metavar' : '', - 'help' : 'Maximum number of public methods for a class \ -(see R0904).'} - ), - ) - - def __init__(self, linter=None): - BaseChecker.__init__(self, linter) - self.stats = None - self._returns = None - self._branches = None - self._used_abstracts = None - self._used_ifaces = None - self._abstracts = None - self._ifaces = None - self._stmts = 0 - - def open(self): - """initialize visit variables""" - self.stats = self.linter.add_stats() - self._returns = [] - self._branches = defaultdict(int) - self._used_abstracts = {} - self._used_ifaces = {} - self._abstracts = [] - self._ifaces = [] - - # Check 'R0921', 'R0922', 'R0923' - def close(self): - """check that abstract/interface classes are used""" - for abstract in self._abstracts: - if not abstract in self._used_abstracts: - self.add_message('abstract-class-not-used', node=abstract) - elif self._used_abstracts[abstract] < 2: - self.add_message('abstract-class-little-used', node=abstract, - args=self._used_abstracts[abstract]) - for iface in self._ifaces: - if not iface in self._used_ifaces: - self.add_message('interface-not-implemented', node=iface) - - @check_messages('too-many-ancestors', 'too-many-instance-attributes', - 'too-few-public-methods', 'too-many-public-methods', - 'abstract-class-not-used', 'abstract-class-little-used', - 'interface-not-implemented') - def visit_class(self, node): - """check size of inheritance hierarchy and number of instance attributes - """ - # Is the total inheritance hierarchy is 7 or less? - nb_parents = len(list(node.ancestors())) - if nb_parents > self.config.max_parents: - self.add_message('too-many-ancestors', node=node, - args=(nb_parents, self.config.max_parents)) - # Does the class contain less than 20 attributes for - # non-GUI classes (40 for GUI)? - # FIXME detect gui classes - if len(node.instance_attrs) > self.config.max_attributes: - self.add_message('too-many-instance-attributes', node=node, - args=(len(node.instance_attrs), - self.config.max_attributes)) - # update abstract / interface classes structures - if class_is_abstract(node): - self._abstracts.append(node) - elif node.type == 'interface' and node.name != 'Interface': - self._ifaces.append(node) - for parent in node.ancestors(False): - if parent.name == 'Interface': - continue - self._used_ifaces[parent] = 1 - try: - for iface in node.interfaces(): - self._used_ifaces[iface] = 1 - except InferenceError: - # XXX log ? - pass - for parent in node.ancestors(): - try: - self._used_abstracts[parent] += 1 - except KeyError: - self._used_abstracts[parent] = 1 - - @check_messages('too-few-public-methods', 'too-many-public-methods') - def leave_class(self, node): - """check number of public methods""" - my_methods = sum(1 for method in node.mymethods() - if not method.name.startswith('_')) - all_methods = sum(1 for method in node.methods() - if not method.name.startswith('_')) - - # Does the class contain less than n public methods ? - # This checks only the methods defined in the current class, - # since the user might not have control over the classes - # from the ancestors. It avoids some false positives - # for classes such as unittest.TestCase, which provides - # a lot of assert methods. It doesn't make sense to warn - # when the user subclasses TestCase to add his own tests. - if my_methods > self.config.max_public_methods: - self.add_message('too-many-public-methods', node=node, - args=(my_methods, - self.config.max_public_methods)) - # stop here for exception, metaclass and interface classes - if node.type != 'class': - return - - # Does the class contain more than n public methods ? - # This checks all the methods defined by ancestors and - # by the current class. - if all_methods < self.config.min_public_methods: - self.add_message('too-few-public-methods', node=node, - args=(all_methods, - self.config.min_public_methods)) - - @check_messages('too-many-return-statements', 'too-many-branches', - 'too-many-arguments', 'too-many-locals', - 'too-many-statements') - def visit_function(self, node): - """check function name, docstring, arguments, redefinition, - variable names, max locals - """ - # init branch and returns counters - self._returns.append(0) - # check number of arguments - args = node.args.args - if args is not None: - ignored_args_num = len( - [arg for arg in args - if self.config.ignored_argument_names.match(arg.name)]) - argnum = len(args) - ignored_args_num - if argnum > self.config.max_args: - self.add_message('too-many-arguments', node=node, - args=(len(args), self.config.max_args)) - else: - ignored_args_num = 0 - # check number of local variables - locnum = len(node.locals) - ignored_args_num - if locnum > self.config.max_locals: - self.add_message('too-many-locals', node=node, - args=(locnum, self.config.max_locals)) - # init statements counter - self._stmts = 1 - - @check_messages('too-many-return-statements', 'too-many-branches', - 'too-many-arguments', 'too-many-locals', - 'too-many-statements') - def leave_function(self, node): - """most of the work is done here on close: - checks for max returns, branch, return in __init__ - """ - returns = self._returns.pop() - if returns > self.config.max_returns: - self.add_message('too-many-return-statements', node=node, - args=(returns, self.config.max_returns)) - branches = self._branches[node] - if branches > self.config.max_branches: - self.add_message('too-many-branches', node=node, - args=(branches, self.config.max_branches)) - # check number of statements - if self._stmts > self.config.max_statements: - self.add_message('too-many-statements', node=node, - args=(self._stmts, self.config.max_statements)) - - def visit_return(self, _): - """count number of returns""" - if not self._returns: - return # return outside function, reported by the base checker - self._returns[-1] += 1 - - def visit_default(self, node): - """default visit method -> increments the statements counter if - necessary - """ - if node.is_statement: - self._stmts += 1 - - def visit_tryexcept(self, node): - """increments the branches counter""" - branches = len(node.handlers) - if node.orelse: - branches += 1 - self._inc_branch(node, branches) - self._stmts += branches - - def visit_tryfinally(self, node): - """increments the branches counter""" - self._inc_branch(node, 2) - self._stmts += 2 - - def visit_if(self, node): - """increments the branches counter""" - branches = 1 - # don't double count If nodes coming from some 'elif' - if node.orelse and (len(node.orelse) > 1 or - not isinstance(node.orelse[0], If)): - branches += 1 - self._inc_branch(node, branches) - self._stmts += branches - - def visit_while(self, node): - """increments the branches counter""" - branches = 1 - if node.orelse: - branches += 1 - self._inc_branch(node, branches) - - visit_for = visit_while - - def _inc_branch(self, node, branchesnum=1): - """increments the branches counter""" - self._branches[node.scope()] += branchesnum - - # FIXME: make a nice report... - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(MisdesignChecker(linter)) diff --git a/checkers/exceptions.py b/checkers/exceptions.py deleted file mode 100644 index 88a8f22..0000000 --- a/checkers/exceptions.py +++ /dev/null @@ -1,332 +0,0 @@ -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""exceptions handling (raising, catching, exceptions classes) checker -""" -import sys - -import astroid -from astroid import YES, Instance, unpack_infer, List, Tuple -from logilab.common.compat import builtins - -from pylint.checkers import BaseChecker -from pylint.checkers.utils import ( - is_empty, - is_raising, - check_messages, - inherit_from_std_ex, - EXCEPTIONS_MODULE, - has_known_bases, - safe_infer) -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE - - -def _annotated_unpack_infer(stmt, context=None): - """ - Recursively generate nodes inferred by the given statement. - If the inferred value is a list or a tuple, recurse on the elements. - Returns an iterator which yields tuples in the format - ('original node', 'infered node'). - """ - if isinstance(stmt, (List, Tuple)): - for elt in stmt.elts: - inferred = safe_infer(elt) - if inferred and inferred is not YES: - yield elt, inferred - return - for infered in stmt.infer(context): - if infered is YES: - continue - yield stmt, infered - - -PY3K = sys.version_info >= (3, 0) -OVERGENERAL_EXCEPTIONS = ('Exception',) -BUILTINS_NAME = builtins.__name__ -MSGS = { - 'E0701': ('Bad except clauses order (%s)', - 'bad-except-order', - 'Used when except clauses are not in the correct order (from the ' - 'more specific to the more generic). If you don\'t fix the order, ' - 'some exceptions may not be catched by the most specific handler.'), - 'E0702': ('Raising %s while only classes or instances are allowed', - 'raising-bad-type', - 'Used when something which is neither a class, an instance or a \ - string is raised (i.e. a `TypeError` will be raised).'), - 'E0703': ('Exception context set to something which is not an ' - 'exception, nor None', - 'bad-exception-context', - 'Used when using the syntax "raise ... from ...", ' - 'where the exception context is not an exception, ' - 'nor None.', - {'minversion': (3, 0)}), - 'E0710': ('Raising a new style class which doesn\'t inherit from BaseException', - 'raising-non-exception', - 'Used when a new style class which doesn\'t inherit from \ - BaseException is raised.'), - 'E0711': ('NotImplemented raised - should raise NotImplementedError', - 'notimplemented-raised', - 'Used when NotImplemented is raised instead of \ - NotImplementedError'), - 'E0712': ('Catching an exception which doesn\'t inherit from BaseException: %s', - 'catching-non-exception', - 'Used when a class which doesn\'t inherit from \ - BaseException is used as an exception in an except clause.'), - 'W0702': ('No exception type(s) specified', - 'bare-except', - 'Used when an except clause doesn\'t specify exceptions type to \ - catch.'), - 'W0703': ('Catching too general exception %s', - 'broad-except', - 'Used when an except catches a too general exception, \ - possibly burying unrelated errors.'), - 'W0704': ('Except doesn\'t do anything', - 'pointless-except', - 'Used when an except clause does nothing but "pass" and there is\ - no "else" clause.'), - 'W0710': ('Exception doesn\'t inherit from standard "Exception" class', - 'nonstandard-exception', - 'Used when a custom exception class is raised but doesn\'t \ - inherit from the builtin "Exception" class.', - {'maxversion': (3, 0)}), - 'W0711': ('Exception to catch is the result of a binary "%s" operation', - 'binary-op-exception', - 'Used when the exception to catch is of the form \ - "except A or B:". If intending to catch multiple, \ - rewrite as "except (A, B):"'), - } - - -class ExceptionsChecker(BaseChecker): - """checks for - * excepts without exception filter - * type of raise argument : string, Exceptions, other values - """ - - __implements__ = IAstroidChecker - - name = 'exceptions' - msgs = MSGS - priority = -4 - options = (('overgeneral-exceptions', - {'default' : OVERGENERAL_EXCEPTIONS, - 'type' :'csv', 'metavar' : '', - 'help' : 'Exceptions that will emit a warning ' - 'when being caught. Defaults to "%s"' % ( - ', '.join(OVERGENERAL_EXCEPTIONS),)} - ), - ) - - @check_messages('nonstandard-exception', - 'raising-bad-type', 'raising-non-exception', - 'notimplemented-raised', 'bad-exception-context') - def visit_raise(self, node): - """visit raise possibly inferring value""" - # ignore empty raise - if node.exc is None: - return - if PY3K and node.cause: - self._check_bad_exception_context(node) - - expr = node.exc - if self._check_raise_value(node, expr): - return - else: - try: - value = next(unpack_infer(expr)) - except astroid.InferenceError: - return - self._check_raise_value(node, value) - - def _check_bad_exception_context(self, node): - """Verify that the exception context is properly set. - - An exception context can be only `None` or an exception. - """ - cause = safe_infer(node.cause) - if cause in (YES, None): - return - if isinstance(cause, astroid.Const): - if cause.value is not None: - self.add_message('bad-exception-context', - node=node) - elif (not isinstance(cause, astroid.Class) and - not inherit_from_std_ex(cause)): - self.add_message('bad-exception-context', - node=node) - - def _check_raise_value(self, node, expr): - """check for bad values, string exception and class inheritance - """ - value_found = True - if isinstance(expr, astroid.Const): - value = expr.value - if not isinstance(value, str): - # raising-string will be emitted from python3 porting checker. - self.add_message('raising-bad-type', node=node, - args=value.__class__.__name__) - elif ((isinstance(expr, astroid.Name) and - expr.name in ('None', 'True', 'False')) or - isinstance(expr, (astroid.List, astroid.Dict, astroid.Tuple, - astroid.Module, astroid.Function))): - emit = True - if not PY3K and isinstance(expr, astroid.Tuple): - # On Python 2, using the following is not an error: - # raise (ZeroDivisionError, None) - # raise (ZeroDivisionError, ) - # What's left to do is to check that the first - # argument is indeed an exception. - # Verifying the other arguments is not - # the scope of this check. - first = expr.elts[0] - inferred = safe_infer(first) - if isinstance(inferred, Instance): - # pylint: disable=protected-access - inferred = inferred._proxied - if (inferred is YES or - isinstance(inferred, astroid.Class) - and inherit_from_std_ex(inferred)): - emit = False - if emit: - self.add_message('raising-bad-type', - node=node, - args=expr.name) - elif ((isinstance(expr, astroid.Name) and expr.name == 'NotImplemented') - or (isinstance(expr, astroid.CallFunc) and - isinstance(expr.func, astroid.Name) and - expr.func.name == 'NotImplemented')): - self.add_message('notimplemented-raised', node=node) - elif isinstance(expr, (Instance, astroid.Class)): - if isinstance(expr, Instance): - # pylint: disable=protected-access - expr = expr._proxied - if (isinstance(expr, astroid.Class) and - not inherit_from_std_ex(expr)): - if expr.newstyle: - self.add_message('raising-non-exception', node=node) - else: - if has_known_bases(expr): - confidence = INFERENCE - else: - confidence = INFERENCE_FAILURE - self.add_message( - 'nonstandard-exception', node=node, - confidence=confidence) - else: - value_found = False - else: - value_found = False - return value_found - - def _check_catching_non_exception(self, handler, exc, part): - if isinstance(exc, astroid.Tuple): - # Check if it is a tuple of exceptions. - inferred = [safe_infer(elt) for elt in exc.elts] - if any(node is astroid.YES for node in inferred): - # Don't emit if we don't know every component. - return - if all(node and inherit_from_std_ex(node) - for node in inferred): - return - - if not isinstance(exc, astroid.Class): - # Don't emit the warning if the infered stmt - # is None, but the exception handler is something else, - # maybe it was redefined. - if (isinstance(exc, astroid.Const) and - exc.value is None): - if ((isinstance(handler.type, astroid.Const) and - handler.type.value is None) or - handler.type.parent_of(exc)): - # If the exception handler catches None or - # the exception component, which is None, is - # defined by the entire exception handler, then - # emit a warning. - self.add_message('catching-non-exception', - node=handler.type, - args=(part.as_string(), )) - else: - self.add_message('catching-non-exception', - node=handler.type, - args=(part.as_string(), )) - return - if (not inherit_from_std_ex(exc) and - exc.root().name != BUILTINS_NAME): - if has_known_bases(exc): - self.add_message('catching-non-exception', - node=handler.type, - args=(exc.name, )) - - @check_messages('bare-except', 'broad-except', 'pointless-except', - 'binary-op-exception', 'bad-except-order', - 'catching-non-exception') - def visit_tryexcept(self, node): - """check for empty except""" - exceptions_classes = [] - nb_handlers = len(node.handlers) - for index, handler in enumerate(node.handlers): - # single except doing nothing but "pass" without else clause - if is_empty(handler.body) and not node.orelse: - self.add_message('pointless-except', - node=handler.type or handler.body[0]) - if handler.type is None: - if not is_raising(handler.body): - self.add_message('bare-except', node=handler) - # check if a "except:" is followed by some other - # except - if index < (nb_handlers - 1): - msg = 'empty except clause should always appear last' - self.add_message('bad-except-order', node=node, args=msg) - - elif isinstance(handler.type, astroid.BoolOp): - self.add_message('binary-op-exception', - node=handler, args=handler.type.op) - else: - try: - excs = list(_annotated_unpack_infer(handler.type)) - except astroid.InferenceError: - continue - for part, exc in excs: - if exc is YES: - continue - if (isinstance(exc, astroid.Instance) - and inherit_from_std_ex(exc)): - # pylint: disable=protected-access - exc = exc._proxied - - self._check_catching_non_exception(handler, exc, part) - - if not isinstance(exc, astroid.Class): - continue - - exc_ancestors = [anc for anc in exc.ancestors() - if isinstance(anc, astroid.Class)] - for previous_exc in exceptions_classes: - if previous_exc in exc_ancestors: - msg = '%s is an ancestor class of %s' % ( - previous_exc.name, exc.name) - self.add_message('bad-except-order', - node=handler.type, args=msg) - if (exc.name in self.config.overgeneral_exceptions - and exc.root().name == EXCEPTIONS_MODULE - and not is_raising(handler.body)): - self.add_message('broad-except', - args=exc.name, node=handler.type) - - exceptions_classes += [exc for _, exc in excs] - - -def register(linter): - """required method to auto register this checker""" - linter.register_checker(ExceptionsChecker(linter)) diff --git a/checkers/format.py b/checkers/format.py deleted file mode 100644 index 94a9e8e..0000000 --- a/checkers/format.py +++ /dev/null @@ -1,964 +0,0 @@ -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""Python code format's checker. - -By default try to follow Guido's style guide : - -http://www.python.org/doc/essays/styleguide.html - -Some parts of the process_token method is based from The Tab Nanny std module. -""" - -import keyword -import sys -import tokenize -from functools import reduce # pylint: disable=redefined-builtin - -import six -from six.moves import zip, map, filter # pylint: disable=redefined-builtin - -from astroid import nodes - -from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker -from pylint.checkers import BaseTokenChecker -from pylint.checkers.utils import check_messages -from pylint.utils import WarningScope, OPTION_RGX - -_CONTINUATION_BLOCK_OPENERS = ['elif', 'except', 'for', 'if', 'while', 'def', 'class'] -_KEYWORD_TOKENS = ['assert', 'del', 'elif', 'except', 'for', 'if', 'in', 'not', - 'raise', 'return', 'while', 'yield'] -if sys.version_info < (3, 0): - _KEYWORD_TOKENS.append('print') - -_SPACED_OPERATORS = ['==', '<', '>', '!=', '<>', '<=', '>=', - '+=', '-=', '*=', '**=', '/=', '//=', '&=', '|=', '^=', - '%=', '>>=', '<<='] -_OPENING_BRACKETS = ['(', '[', '{'] -_CLOSING_BRACKETS = [')', ']', '}'] -_TAB_LENGTH = 8 - -_EOL = frozenset([tokenize.NEWLINE, tokenize.NL, tokenize.COMMENT]) -_JUNK_TOKENS = (tokenize.COMMENT, tokenize.NL) - -# Whitespace checking policy constants -_MUST = 0 -_MUST_NOT = 1 -_IGNORE = 2 - -# Whitespace checking config constants -_DICT_SEPARATOR = 'dict-separator' -_TRAILING_COMMA = 'trailing-comma' -_NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR] - -MSGS = { - 'C0301': ('Line too long (%s/%s)', - 'line-too-long', - 'Used when a line is longer than a given number of characters.'), - 'C0302': ('Too many lines in module (%s/%s)', # was W0302 - 'too-many-lines', - 'Used when a module has too much lines, reducing its readability.' - ), - 'C0303': ('Trailing whitespace', - 'trailing-whitespace', - 'Used when there is whitespace between the end of a line and the ' - 'newline.'), - 'C0304': ('Final newline missing', - 'missing-final-newline', - 'Used when the last line in a file is missing a newline.'), - 'W0311': ('Bad indentation. Found %s %s, expected %s', - 'bad-indentation', - 'Used when an unexpected number of indentation\'s tabulations or ' - 'spaces has been found.'), - 'C0330': ('Wrong %s indentation%s.\n%s%s', - 'bad-continuation', - 'TODO'), - 'W0312': ('Found indentation with %ss instead of %ss', - 'mixed-indentation', - 'Used when there are some mixed tabs and spaces in a module.'), - 'W0301': ('Unnecessary semicolon', # was W0106 - 'unnecessary-semicolon', - 'Used when a statement is ended by a semi-colon (";"), which \ - isn\'t necessary (that\'s python, not C ;).'), - 'C0321': ('More than one statement on a single line', - 'multiple-statements', - 'Used when more than on statement are found on the same line.', - {'scope': WarningScope.NODE}), - 'C0325' : ('Unnecessary parens after %r keyword', - 'superfluous-parens', - 'Used when a single item in parentheses follows an if, for, or ' - 'other keyword.'), - 'C0326': ('%s space %s %s %s\n%s', - 'bad-whitespace', - ('Used when a wrong number of spaces is used around an operator, ' - 'bracket or block opener.'), - {'old_names': [('C0323', 'no-space-after-operator'), - ('C0324', 'no-space-after-comma'), - ('C0322', 'no-space-before-operator')]}), - 'W0332': ('Use of "l" as long integer identifier', - 'lowercase-l-suffix', - 'Used when a lower case "l" is used to mark a long integer. You ' - 'should use a upper case "L" since the letter "l" looks too much ' - 'like the digit "1"', - {'maxversion': (3, 0)}), - 'C0327': ('Mixed line endings LF and CRLF', - 'mixed-line-endings', - 'Used when there are mixed (LF and CRLF) newline signs in a file.'), - 'C0328': ('Unexpected line ending format. There is \'%s\' while it should be \'%s\'.', - 'unexpected-line-ending-format', - 'Used when there is different newline than expected.'), - } - - -def _underline_token(token): - length = token[3][1] - token[2][1] - offset = token[2][1] - return token[4] + (' ' * offset) + ('^' * length) - - -def _column_distance(token1, token2): - if token1 == token2: - return 0 - if token2[3] < token1[3]: - token1, token2 = token2, token1 - if token1[3][0] != token2[2][0]: - return None - return token2[2][1] - token1[3][1] - - -def _last_token_on_line_is(tokens, line_end, token): - return (line_end > 0 and tokens.token(line_end-1) == token or - line_end > 1 and tokens.token(line_end-2) == token - and tokens.type(line_end-1) == tokenize.COMMENT) - - -def _token_followed_by_eol(tokens, position): - return (tokens.type(position+1) == tokenize.NL or - tokens.type(position+1) == tokenize.COMMENT and - tokens.type(position+2) == tokenize.NL) - - -def _get_indent_length(line): - """Return the length of the indentation on the given token's line.""" - result = 0 - for char in line: - if char == ' ': - result += 1 - elif char == '\t': - result += _TAB_LENGTH - else: - break - return result - - -def _get_indent_hint_line(bar_positions, bad_position): - """Return a line with |s for each of the positions in the given lists.""" - if not bar_positions: - return '' - markers = [(pos, '|') for pos in bar_positions] - markers.append((bad_position, '^')) - markers.sort() - line = [' '] * (markers[-1][0] + 1) - for position, marker in markers: - line[position] = marker - return ''.join(line) - - -class _ContinuedIndent(object): - __slots__ = ('valid_outdent_offsets', - 'valid_continuation_offsets', - 'context_type', - 'token', - 'position') - - def __init__(self, - context_type, - token, - position, - valid_outdent_offsets, - valid_continuation_offsets): - self.valid_outdent_offsets = valid_outdent_offsets - self.valid_continuation_offsets = valid_continuation_offsets - self.context_type = context_type - self.position = position - self.token = token - - -# The contexts for hanging indents. -# A hanging indented dictionary value after : -HANGING_DICT_VALUE = 'dict-value' -# Hanging indentation in an expression. -HANGING = 'hanging' -# Hanging indentation in a block header. -HANGING_BLOCK = 'hanging-block' -# Continued indentation inside an expression. -CONTINUED = 'continued' -# Continued indentation in a block header. -CONTINUED_BLOCK = 'continued-block' - -SINGLE_LINE = 'single' -WITH_BODY = 'multi' - -_CONTINUATION_MSG_PARTS = { - HANGING_DICT_VALUE: ('hanging', ' in dict value'), - HANGING: ('hanging', ''), - HANGING_BLOCK: ('hanging', ' before block'), - CONTINUED: ('continued', ''), - CONTINUED_BLOCK: ('continued', ' before block'), -} - - -def _Offsets(*args): - """Valid indentation offsets for a continued line.""" - return dict((a, None) for a in args) - - -def _BeforeBlockOffsets(single, with_body): - """Valid alternative indent offsets for continued lines before blocks. - - :param single: Valid offset for statements on a single logical line. - :param with_body: Valid offset for statements on several lines. - """ - return {single: SINGLE_LINE, with_body: WITH_BODY} - - -class TokenWrapper(object): - """A wrapper for readable access to token information.""" - - def __init__(self, tokens): - self._tokens = tokens - - def token(self, idx): - return self._tokens[idx][1] - - def type(self, idx): - return self._tokens[idx][0] - - def start_line(self, idx): - return self._tokens[idx][2][0] - - def start_col(self, idx): - return self._tokens[idx][2][1] - - def line(self, idx): - return self._tokens[idx][4] - - -class ContinuedLineState(object): - """Tracker for continued indentation inside a logical line.""" - - def __init__(self, tokens, config): - self._line_start = -1 - self._cont_stack = [] - self._is_block_opener = False - self.retained_warnings = [] - self._config = config - self._tokens = TokenWrapper(tokens) - - @property - def has_content(self): - return bool(self._cont_stack) - - @property - def _block_indent_size(self): - return len(self._config.indent_string.replace('\t', ' ' * _TAB_LENGTH)) - - @property - def _continuation_size(self): - return self._config.indent_after_paren - - def handle_line_start(self, pos): - """Record the first non-junk token at the start of a line.""" - if self._line_start > -1: - return - self._is_block_opener = self._tokens.token(pos) in _CONTINUATION_BLOCK_OPENERS - self._line_start = pos - - def next_physical_line(self): - """Prepares the tracker for a new physical line (NL).""" - self._line_start = -1 - self._is_block_opener = False - - def next_logical_line(self): - """Prepares the tracker for a new logical line (NEWLINE). - - A new logical line only starts with block indentation. - """ - self.next_physical_line() - self.retained_warnings = [] - self._cont_stack = [] - - def add_block_warning(self, token_position, state, valid_offsets): - self.retained_warnings.append((token_position, state, valid_offsets)) - - def get_valid_offsets(self, idx): - """"Returns the valid offsets for the token at the given position.""" - # The closing brace on a dict or the 'for' in a dict comprehension may - # reset two indent levels because the dict value is ended implicitly - stack_top = -1 - if self._tokens.token(idx) in ('}', 'for') and self._cont_stack[-1].token == ':': - stack_top = -2 - indent = self._cont_stack[stack_top] - if self._tokens.token(idx) in _CLOSING_BRACKETS: - valid_offsets = indent.valid_outdent_offsets - else: - valid_offsets = indent.valid_continuation_offsets - return indent, valid_offsets.copy() - - def _hanging_indent_after_bracket(self, bracket, position): - """Extracts indentation information for a hanging indent.""" - indentation = _get_indent_length(self._tokens.line(position)) - if self._is_block_opener and self._continuation_size == self._block_indent_size: - return _ContinuedIndent( - HANGING_BLOCK, - bracket, - position, - _Offsets(indentation + self._continuation_size, indentation), - _BeforeBlockOffsets(indentation + self._continuation_size, - indentation + self._continuation_size * 2)) - elif bracket == ':': - # If the dict key was on the same line as the open brace, the new - # correct indent should be relative to the key instead of the - # current indent level - paren_align = self._cont_stack[-1].valid_outdent_offsets - next_align = self._cont_stack[-1].valid_continuation_offsets.copy() - next_align_keys = list(next_align.keys()) - next_align[next_align_keys[0] + self._continuation_size] = True - # Note that the continuation of - # d = { - # 'a': 'b' - # 'c' - # } - # is handled by the special-casing for hanging continued string indents. - return _ContinuedIndent(HANGING_DICT_VALUE, bracket, position, paren_align, next_align) - else: - return _ContinuedIndent( - HANGING, - bracket, - position, - _Offsets(indentation, indentation + self._continuation_size), - _Offsets(indentation + self._continuation_size)) - - def _continuation_inside_bracket(self, bracket, pos): - """Extracts indentation information for a continued indent.""" - indentation = _get_indent_length(self._tokens.line(pos)) - if self._is_block_opener and self._tokens.start_col(pos+1) - indentation == self._block_indent_size: - return _ContinuedIndent( - CONTINUED_BLOCK, - bracket, - pos, - _Offsets(self._tokens.start_col(pos)), - _BeforeBlockOffsets(self._tokens.start_col(pos+1), - self._tokens.start_col(pos+1) + self._continuation_size)) - else: - return _ContinuedIndent( - CONTINUED, - bracket, - pos, - _Offsets(self._tokens.start_col(pos)), - _Offsets(self._tokens.start_col(pos+1))) - - def pop_token(self): - self._cont_stack.pop() - - def push_token(self, token, position): - """Pushes a new token for continued indentation on the stack. - - Tokens that can modify continued indentation offsets are: - * opening brackets - * 'lambda' - * : inside dictionaries - - push_token relies on the caller to filter out those - interesting tokens. - - :param token: The concrete token - :param position: The position of the token in the stream. - """ - if _token_followed_by_eol(self._tokens, position): - self._cont_stack.append( - self._hanging_indent_after_bracket(token, position)) - else: - self._cont_stack.append( - self._continuation_inside_bracket(token, position)) - - -class FormatChecker(BaseTokenChecker): - """checks for : - * unauthorized constructions - * strict indentation - * line length - """ - - __implements__ = (ITokenChecker, IAstroidChecker, IRawChecker) - - # configuration section name - name = 'format' - # messages - msgs = MSGS - # configuration options - # for available dict keys/values see the optik parser 'add_option' method - options = (('max-line-length', - {'default' : 100, 'type' : "int", 'metavar' : '', - 'help' : 'Maximum number of characters on a single line.'}), - ('ignore-long-lines', - {'type': 'regexp', 'metavar': '', - 'default': r'^\s*(# )??$', - 'help': ('Regexp for a line that is allowed to be longer than ' - 'the limit.')}), - ('single-line-if-stmt', - {'default': False, 'type' : 'yn', 'metavar' : '', - 'help' : ('Allow the body of an if to be on the same ' - 'line as the test if there is no else.')}), - ('no-space-check', - {'default': ','.join(_NO_SPACE_CHECK_CHOICES), - 'type': 'multiple_choice', - 'choices': _NO_SPACE_CHECK_CHOICES, - 'help': ('List of optional constructs for which whitespace ' - 'checking is disabled')}), - ('max-module-lines', - {'default' : 1000, 'type' : 'int', 'metavar' : '', - 'help': 'Maximum number of lines in a module'} - ), - ('indent-string', - {'default' : ' ', 'type' : "string", 'metavar' : '', - 'help' : 'String used as indentation unit. This is usually ' - '" " (4 spaces) or "\\t" (1 tab).'}), - ('indent-after-paren', - {'type': 'int', 'metavar': '', 'default': 4, - 'help': 'Number of spaces of indent required inside a hanging ' - ' or continued line.'}), - ('expected-line-ending-format', - {'type': 'choice', 'metavar': '', 'default': '', - 'choices': ['', 'LF', 'CRLF'], - 'help': 'Expected format of line ending, e.g. empty (any line ending), LF or CRLF.'}), - ) - - def __init__(self, linter=None): - BaseTokenChecker.__init__(self, linter) - self._lines = None - self._visited_lines = None - self._bracket_stack = [None] - - def _pop_token(self): - self._bracket_stack.pop() - self._current_line.pop_token() - - def _push_token(self, token, idx): - self._bracket_stack.append(token) - self._current_line.push_token(token, idx) - - def new_line(self, tokens, line_end, line_start): - """a new line has been encountered, process it if necessary""" - if _last_token_on_line_is(tokens, line_end, ';'): - self.add_message('unnecessary-semicolon', line=tokens.start_line(line_end)) - - line_num = tokens.start_line(line_start) - line = tokens.line(line_start) - if tokens.type(line_start) not in _JUNK_TOKENS: - self._lines[line_num] = line.split('\n')[0] - self.check_lines(line, line_num) - - def process_module(self, module): - self._keywords_with_parens = set() - if 'print_function' in module.future_imports: - self._keywords_with_parens.add('print') - - def _check_keyword_parentheses(self, tokens, start): - """Check that there are not unnecessary parens after a keyword. - - Parens are unnecessary if there is exactly one balanced outer pair on a - line, and it is followed by a colon, and contains no commas (i.e. is not a - tuple). - - Args: - tokens: list of Tokens; the entire list of Tokens. - start: int; the position of the keyword in the token list. - """ - # If the next token is not a paren, we're fine. - if self._inside_brackets(':') and tokens[start][1] == 'for': - self._pop_token() - if tokens[start+1][1] != '(': - return - - found_and_or = False - depth = 0 - keyword_token = tokens[start][1] - line_num = tokens[start][2][0] - - for i in range(start, len(tokens) - 1): - token = tokens[i] - - # If we hit a newline, then assume any parens were for continuation. - if token[0] == tokenize.NL: - return - - if token[1] == '(': - depth += 1 - elif token[1] == ')': - depth -= 1 - if not depth: - # ')' can't happen after if (foo), since it would be a syntax error. - if (tokens[i+1][1] in (':', ')', ']', '}', 'in') or - tokens[i+1][0] in (tokenize.NEWLINE, - tokenize.ENDMARKER, - tokenize.COMMENT)): - # The empty tuple () is always accepted. - if i == start + 2: - return - if keyword_token == 'not': - if not found_and_or: - self.add_message('superfluous-parens', line=line_num, - args=keyword_token) - elif keyword_token in ('return', 'yield'): - self.add_message('superfluous-parens', line=line_num, - args=keyword_token) - elif keyword_token not in self._keywords_with_parens: - if not (tokens[i+1][1] == 'in' and found_and_or): - self.add_message('superfluous-parens', line=line_num, - args=keyword_token) - return - elif depth == 1: - # This is a tuple, which is always acceptable. - if token[1] == ',': - return - # 'and' and 'or' are the only boolean operators with lower precedence - # than 'not', so parens are only required when they are found. - elif token[1] in ('and', 'or'): - found_and_or = True - # A yield inside an expression must always be in parentheses, - # quit early without error. - elif token[1] == 'yield': - return - # A generator expression always has a 'for' token in it, and - # the 'for' token is only legal inside parens when it is in a - # generator expression. The parens are necessary here, so bail - # without an error. - elif token[1] == 'for': - return - - def _opening_bracket(self, tokens, i): - self._push_token(tokens[i][1], i) - # Special case: ignore slices - if tokens[i][1] == '[' and tokens[i+1][1] == ':': - return - - if (i > 0 and (tokens[i-1][0] == tokenize.NAME and - not (keyword.iskeyword(tokens[i-1][1])) - or tokens[i-1][1] in _CLOSING_BRACKETS)): - self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT)) - else: - self._check_space(tokens, i, (_IGNORE, _MUST_NOT)) - - def _closing_bracket(self, tokens, i): - if self._inside_brackets(':'): - self._pop_token() - self._pop_token() - # Special case: ignore slices - if tokens[i-1][1] == ':' and tokens[i][1] == ']': - return - policy_before = _MUST_NOT - if tokens[i][1] in _CLOSING_BRACKETS and tokens[i-1][1] == ',': - if _TRAILING_COMMA in self.config.no_space_check: - policy_before = _IGNORE - - self._check_space(tokens, i, (policy_before, _IGNORE)) - - def _check_equals_spacing(self, tokens, i): - """Check the spacing of a single equals sign.""" - if self._inside_brackets('(') or self._inside_brackets('lambda'): - self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT)) - else: - self._check_space(tokens, i, (_MUST, _MUST)) - - def _open_lambda(self, tokens, i): # pylint:disable=unused-argument - self._push_token('lambda', i) - - def _handle_colon(self, tokens, i): - # Special case: ignore slices - if self._inside_brackets('['): - return - if (self._inside_brackets('{') and - _DICT_SEPARATOR in self.config.no_space_check): - policy = (_IGNORE, _IGNORE) - else: - policy = (_MUST_NOT, _MUST) - self._check_space(tokens, i, policy) - - if self._inside_brackets('lambda'): - self._pop_token() - elif self._inside_brackets('{'): - self._push_token(':', i) - - def _handle_comma(self, tokens, i): - # Only require a following whitespace if this is - # not a hanging comma before a closing bracket. - if tokens[i+1][1] in _CLOSING_BRACKETS: - self._check_space(tokens, i, (_MUST_NOT, _IGNORE)) - else: - self._check_space(tokens, i, (_MUST_NOT, _MUST)) - if self._inside_brackets(':'): - self._pop_token() - - def _check_surrounded_by_space(self, tokens, i): - """Check that a binary operator is surrounded by exactly one space.""" - self._check_space(tokens, i, (_MUST, _MUST)) - - def _check_space(self, tokens, i, policies): - def _policy_string(policy): - if policy == _MUST: - return 'Exactly one', 'required' - else: - return 'No', 'allowed' - - def _name_construct(token): - if token[1] == ',': - return 'comma' - elif token[1] == ':': - return ':' - elif token[1] in '()[]{}': - return 'bracket' - elif token[1] in ('<', '>', '<=', '>=', '!=', '=='): - return 'comparison' - else: - if self._inside_brackets('('): - return 'keyword argument assignment' - else: - return 'assignment' - - good_space = [True, True] - token = tokens[i] - pairs = [(tokens[i-1], token), (token, tokens[i+1])] - - for other_idx, (policy, token_pair) in enumerate(zip(policies, pairs)): - if token_pair[other_idx][0] in _EOL or policy == _IGNORE: - continue - - distance = _column_distance(*token_pair) - if distance is None: - continue - good_space[other_idx] = ( - (policy == _MUST and distance == 1) or - (policy == _MUST_NOT and distance == 0)) - - warnings = [] - if not any(good_space) and policies[0] == policies[1]: - warnings.append((policies[0], 'around')) - else: - for ok, policy, position in zip(good_space, policies, ('before', 'after')): - if not ok: - warnings.append((policy, position)) - for policy, position in warnings: - construct = _name_construct(token) - count, state = _policy_string(policy) - self.add_message('bad-whitespace', line=token[2][0], - args=(count, state, position, construct, - _underline_token(token))) - - def _inside_brackets(self, left): - return self._bracket_stack[-1] == left - - def _prepare_token_dispatcher(self): - raw = [ - (_KEYWORD_TOKENS, - self._check_keyword_parentheses), - - (_OPENING_BRACKETS, self._opening_bracket), - - (_CLOSING_BRACKETS, self._closing_bracket), - - (['='], self._check_equals_spacing), - - (_SPACED_OPERATORS, self._check_surrounded_by_space), - - ([','], self._handle_comma), - - ([':'], self._handle_colon), - - (['lambda'], self._open_lambda), - - ] - - dispatch = {} - for tokens, handler in raw: - for token in tokens: - dispatch[token] = handler - return dispatch - - def process_tokens(self, tokens): - """process tokens and search for : - - _ non strict indentation (i.e. not always using the parameter as - indent unit) - _ too long lines (i.e. longer than ) - _ optionally bad construct (if given, bad_construct must be a compiled - regular expression). - """ - self._bracket_stack = [None] - indents = [0] - check_equal = False - line_num = 0 - self._lines = {} - self._visited_lines = {} - token_handlers = self._prepare_token_dispatcher() - self._last_line_ending = None - - self._current_line = ContinuedLineState(tokens, self.config) - for idx, (tok_type, token, start, _, line) in enumerate(tokens): - if start[0] != line_num: - line_num = start[0] - # A tokenizer oddity: if an indented line contains a multi-line - # docstring, the line member of the INDENT token does not contain - # the full line; therefore we check the next token on the line. - if tok_type == tokenize.INDENT: - self.new_line(TokenWrapper(tokens), idx-1, idx+1) - else: - self.new_line(TokenWrapper(tokens), idx-1, idx) - - if tok_type == tokenize.NEWLINE: - # a program statement, or ENDMARKER, will eventually follow, - # after some (possibly empty) run of tokens of the form - # (NL | COMMENT)* (INDENT | DEDENT+)? - # If an INDENT appears, setting check_equal is wrong, and will - # be undone when we see the INDENT. - check_equal = True - self._process_retained_warnings(TokenWrapper(tokens), idx) - self._current_line.next_logical_line() - self._check_line_ending(token, line_num) - elif tok_type == tokenize.INDENT: - check_equal = False - self.check_indent_level(token, indents[-1]+1, line_num) - indents.append(indents[-1]+1) - elif tok_type == tokenize.DEDENT: - # there's nothing we need to check here! what's important is - # that when the run of DEDENTs ends, the indentation of the - # program statement (or ENDMARKER) that triggered the run is - # equal to what's left at the top of the indents stack - check_equal = True - if len(indents) > 1: - del indents[-1] - elif tok_type == tokenize.NL: - self._check_continued_indentation(TokenWrapper(tokens), idx+1) - self._current_line.next_physical_line() - elif tok_type != tokenize.COMMENT: - self._current_line.handle_line_start(idx) - # This is the first concrete token following a NEWLINE, so it - # must be the first token of the next program statement, or an - # ENDMARKER; the "line" argument exposes the leading whitespace - # for this statement; in the case of ENDMARKER, line is an empty - # string, so will properly match the empty string with which the - # "indents" stack was seeded - if check_equal: - check_equal = False - self.check_indent_level(line, indents[-1], line_num) - - if tok_type == tokenize.NUMBER and token.endswith('l'): - self.add_message('lowercase-l-suffix', line=line_num) - - try: - handler = token_handlers[token] - except KeyError: - pass - else: - handler(tokens, idx) - - line_num -= 1 # to be ok with "wc -l" - if line_num > self.config.max_module_lines: - # Get the line where the too-many-lines (or its message id) - # was disabled or default to 1. - symbol = self.linter.msgs_store.check_message_id('too-many-lines') - names = (symbol.msgid, 'too-many-lines') - line = next(filter(None, - map(self.linter._pragma_lineno.get, names)), 1) - self.add_message('too-many-lines', - args=(line_num, self.config.max_module_lines), - line=line) - - def _check_line_ending(self, line_ending, line_num): - # check if line endings are mixed - if self._last_line_ending is not None: - if line_ending != self._last_line_ending: - self.add_message('mixed-line-endings', line=line_num) - - self._last_line_ending = line_ending - - # check if line ending is as expected - expected = self.config.expected_line_ending_format - if expected: - line_ending = reduce(lambda x, y: x + y if x != y else x, line_ending, "") # reduce multiple \n\n\n\n to one \n - line_ending = 'LF' if line_ending == '\n' else 'CRLF' - if line_ending != expected: - self.add_message('unexpected-line-ending-format', args=(line_ending, expected), line=line_num) - - - def _process_retained_warnings(self, tokens, current_pos): - single_line_block_stmt = not _last_token_on_line_is(tokens, current_pos, ':') - - for indent_pos, state, offsets in self._current_line.retained_warnings: - block_type = offsets[tokens.start_col(indent_pos)] - hints = dict((k, v) for k, v in six.iteritems(offsets) - if v != block_type) - if single_line_block_stmt and block_type == WITH_BODY: - self._add_continuation_message(state, hints, tokens, indent_pos) - elif not single_line_block_stmt and block_type == SINGLE_LINE: - self._add_continuation_message(state, hints, tokens, indent_pos) - - def _check_continued_indentation(self, tokens, next_idx): - def same_token_around_nl(token_type): - return (tokens.type(next_idx) == token_type and - tokens.type(next_idx-2) == token_type) - - # Do not issue any warnings if the next line is empty. - if not self._current_line.has_content or tokens.type(next_idx) == tokenize.NL: - return - - state, valid_offsets = self._current_line.get_valid_offsets(next_idx) - # Special handling for hanging comments and strings. If the last line ended - # with a comment (string) and the new line contains only a comment, the line - # may also be indented to the start of the previous token. - if same_token_around_nl(tokenize.COMMENT) or same_token_around_nl(tokenize.STRING): - valid_offsets[tokens.start_col(next_idx-2)] = True - - # We can only decide if the indentation of a continued line before opening - # a new block is valid once we know of the body of the block is on the - # same line as the block opener. Since the token processing is single-pass, - # emitting those warnings is delayed until the block opener is processed. - if (state.context_type in (HANGING_BLOCK, CONTINUED_BLOCK) - and tokens.start_col(next_idx) in valid_offsets): - self._current_line.add_block_warning(next_idx, state, valid_offsets) - elif tokens.start_col(next_idx) not in valid_offsets: - self._add_continuation_message(state, valid_offsets, tokens, next_idx) - - def _add_continuation_message(self, state, offsets, tokens, position): - readable_type, readable_position = _CONTINUATION_MSG_PARTS[state.context_type] - hint_line = _get_indent_hint_line(offsets, tokens.start_col(position)) - self.add_message( - 'bad-continuation', - line=tokens.start_line(position), - args=(readable_type, readable_position, tokens.line(position), hint_line)) - - @check_messages('multiple-statements') - def visit_default(self, node): - """check the node line number and check it if not yet done""" - if not node.is_statement: - return - if not node.root().pure_python: - return # XXX block visit of child nodes - prev_sibl = node.previous_sibling() - if prev_sibl is not None: - prev_line = prev_sibl.fromlineno - else: - # The line on which a finally: occurs in a try/finally - # is not directly represented in the AST. We infer it - # by taking the last line of the body and adding 1, which - # should be the line of finally: - if (isinstance(node.parent, nodes.TryFinally) - and node in node.parent.finalbody): - prev_line = node.parent.body[0].tolineno + 1 - else: - prev_line = node.parent.statement().fromlineno - line = node.fromlineno - assert line, node - if prev_line == line and self._visited_lines.get(line) != 2: - self._check_multi_statement_line(node, line) - return - if line in self._visited_lines: - return - try: - tolineno = node.blockstart_tolineno - except AttributeError: - tolineno = node.tolineno - assert tolineno, node - lines = [] - for line in range(line, tolineno + 1): - self._visited_lines[line] = 1 - try: - lines.append(self._lines[line].rstrip()) - except KeyError: - lines.append('') - - def _check_multi_statement_line(self, node, line): - """Check for lines containing multiple statements.""" - # Do not warn about multiple nested context managers - # in with statements. - if isinstance(node, nodes.With): - return - # For try... except... finally..., the two nodes - # appear to be on the same line due to how the AST is built. - if (isinstance(node, nodes.TryExcept) and - isinstance(node.parent, nodes.TryFinally)): - return - if (isinstance(node.parent, nodes.If) and not node.parent.orelse - and self.config.single_line_if_stmt): - return - self.add_message('multiple-statements', node=node) - self._visited_lines[line] = 2 - - def check_lines(self, lines, i): - """check lines have less than a maximum number of characters - """ - max_chars = self.config.max_line_length - ignore_long_line = self.config.ignore_long_lines - - for line in lines.splitlines(True): - if not line.endswith('\n'): - self.add_message('missing-final-newline', line=i) - else: - stripped_line = line.rstrip() - if line[len(stripped_line):] not in ('\n', '\r\n'): - self.add_message('trailing-whitespace', line=i) - # Don't count excess whitespace in the line length. - line = stripped_line - mobj = OPTION_RGX.search(line) - if mobj and mobj.group(1).split('=', 1)[0].strip() == 'disable': - line = line.split('#')[0].rstrip() - - if len(line) > max_chars and not ignore_long_line.search(line): - self.add_message('line-too-long', line=i, args=(len(line), max_chars)) - i += 1 - - def check_indent_level(self, string, expected, line_num): - """return the indent level of the string - """ - indent = self.config.indent_string - if indent == '\\t': # \t is not interpreted in the configuration file - indent = '\t' - level = 0 - unit_size = len(indent) - while string[:unit_size] == indent: - string = string[unit_size:] - level += 1 - suppl = '' - while string and string[0] in ' \t': - if string[0] != indent[0]: - if string[0] == '\t': - args = ('tab', 'space') - else: - args = ('space', 'tab') - self.add_message('mixed-indentation', args=args, line=line_num) - return level - suppl += string[0] - string = string[1:] - if level != expected or suppl: - i_type = 'spaces' - if indent[0] == '\t': - i_type = 'tabs' - self.add_message('bad-indentation', line=line_num, - args=(level * unit_size + len(suppl), i_type, - expected * unit_size)) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(FormatChecker(linter)) diff --git a/checkers/imports.py b/checkers/imports.py deleted file mode 100644 index 1969eeb..0000000 --- a/checkers/imports.py +++ /dev/null @@ -1,413 +0,0 @@ -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""imports checkers for Python code""" - -import sys -from collections import defaultdict - -import six -from six.moves import map # pylint: disable=redefined-builtin - -from logilab.common.graph import get_cycles, DotBackend -from logilab.common.ureports import VerbatimText, Paragraph - -import astroid -from astroid import are_exclusive -from astroid.modutils import get_module_part, is_standard_module - -from pylint.interfaces import IAstroidChecker -from pylint.utils import EmptyReport -from pylint.checkers import BaseChecker -from pylint.checkers.utils import check_messages, is_import_error - -def _except_import_error(node): - """ - Check if the try-except node has an ImportError handler. - Return True if an ImportError handler was infered, False otherwise. - """ - if not isinstance(node, astroid.TryExcept): - return - return any(map(is_import_error, node.handlers)) - -def get_first_import(node, context, name, base, level): - """return the node where [base.] is imported or None if not found - """ - fullname = '%s.%s' % (base, name) if base else name - - first = None - found = False - for first in context.body: - if first is node: - continue - if first.scope() is node.scope() and first.fromlineno > node.fromlineno: - continue - if isinstance(first, astroid.Import): - if any(fullname == iname[0] for iname in first.names): - found = True - break - elif isinstance(first, astroid.From): - if level == first.level and any( - fullname == '%s.%s' % (first.modname, iname[0]) - for iname in first.names): - found = True - break - if found and not are_exclusive(first, node): - return first - -# utilities to represents import dependencies as tree and dot graph ########### - -def make_tree_defs(mod_files_list): - """get a list of 2-uple (module, list_of_files_which_import_this_module), - it will return a dictionary to represent this as a tree - """ - tree_defs = {} - for mod, files in mod_files_list: - node = (tree_defs, ()) - for prefix in mod.split('.'): - node = node[0].setdefault(prefix, [{}, []]) - node[1] += files - return tree_defs - -def repr_tree_defs(data, indent_str=None): - """return a string which represents imports as a tree""" - lines = [] - nodes = data.items() - for i, (mod, (sub, files)) in enumerate(sorted(nodes, key=lambda x: x[0])): - if not files: - files = '' - else: - files = '(%s)' % ','.join(files) - if indent_str is None: - lines.append('%s %s' % (mod, files)) - sub_indent_str = ' ' - else: - lines.append(r'%s\-%s %s' % (indent_str, mod, files)) - if i == len(nodes)-1: - sub_indent_str = '%s ' % indent_str - else: - sub_indent_str = '%s| ' % indent_str - if sub: - lines.append(repr_tree_defs(sub, sub_indent_str)) - return '\n'.join(lines) - - -def dependencies_graph(filename, dep_info): - """write dependencies as a dot (graphviz) file - """ - done = {} - printer = DotBackend(filename[:-4], rankdir='LR') - printer.emit('URL="." node[shape="box"]') - for modname, dependencies in sorted(six.iteritems(dep_info)): - done[modname] = 1 - printer.emit_node(modname) - for modname in dependencies: - if modname not in done: - done[modname] = 1 - printer.emit_node(modname) - for depmodname, dependencies in sorted(six.iteritems(dep_info)): - for modname in dependencies: - printer.emit_edge(modname, depmodname) - printer.generate(filename) - - -def make_graph(filename, dep_info, sect, gtype): - """generate a dependencies graph and add some information about it in the - report's section - """ - dependencies_graph(filename, dep_info) - sect.append(Paragraph('%simports graph has been written to %s' - % (gtype, filename))) - - -# the import checker itself ################################################### - -MSGS = { - 'F0401': ('Unable to import %s', - 'import-error', - 'Used when pylint has been unable to import a module.'), - 'R0401': ('Cyclic import (%s)', - 'cyclic-import', - 'Used when a cyclic import between two or more modules is \ - detected.'), - - 'W0401': ('Wildcard import %s', - 'wildcard-import', - 'Used when `from module import *` is detected.'), - 'W0402': ('Uses of a deprecated module %r', - 'deprecated-module', - 'Used a module marked as deprecated is imported.'), - 'W0403': ('Relative import %r, should be %r', - 'relative-import', - 'Used when an import relative to the package directory is ' - 'detected.', - {'maxversion': (3, 0)}), - 'W0404': ('Reimport %r (imported line %s)', - 'reimported', - 'Used when a module is reimported multiple times.'), - 'W0406': ('Module import itself', - 'import-self', - 'Used when a module is importing itself.'), - - 'W0410': ('__future__ import is not the first non docstring statement', - 'misplaced-future', - 'Python 2.5 and greater require __future__ import to be the \ - first non docstring statement in the module.', - {'maxversion': (3, 0)}), - } - -class ImportsChecker(BaseChecker): - """checks for - * external modules dependencies - * relative / wildcard imports - * cyclic imports - * uses of deprecated modules - """ - - __implements__ = IAstroidChecker - - name = 'imports' - msgs = MSGS - priority = -2 - - if sys.version_info < (3,): - deprecated_modules = ('regsub', 'TERMIOS', 'Bastion', 'rexec') - else: - deprecated_modules = ('stringprep', 'optparse') - options = (('deprecated-modules', - {'default' : deprecated_modules, - 'type' : 'csv', - 'metavar' : '', - 'help' : 'Deprecated modules which should not be used, \ -separated by a comma'} - ), - ('import-graph', - {'default' : '', - 'type' : 'string', - 'metavar' : '', - 'help' : 'Create a graph of every (i.e. internal and \ -external) dependencies in the given file (report RP0402 must not be disabled)'} - ), - ('ext-import-graph', - {'default' : '', - 'type' : 'string', - 'metavar' : '', - 'help' : 'Create a graph of external dependencies in the \ -given file (report RP0402 must not be disabled)'} - ), - ('int-import-graph', - {'default' : '', - 'type' : 'string', - 'metavar' : '', - 'help' : 'Create a graph of internal dependencies in the \ -given file (report RP0402 must not be disabled)'} - ), - ) - - def __init__(self, linter=None): - BaseChecker.__init__(self, linter) - self.stats = None - self.import_graph = None - self.__int_dep_info = self.__ext_dep_info = None - self.reports = (('RP0401', 'External dependencies', - self.report_external_dependencies), - ('RP0402', 'Modules dependencies graph', - self.report_dependencies_graph), - ) - - def open(self): - """called before visiting project (i.e set of modules)""" - self.linter.add_stats(dependencies={}) - self.linter.add_stats(cycles=[]) - self.stats = self.linter.stats - self.import_graph = defaultdict(set) - - def close(self): - """called before visiting project (i.e set of modules)""" - # don't try to compute cycles if the associated message is disabled - if self.linter.is_message_enabled('cyclic-import'): - vertices = list(self.import_graph) - for cycle in get_cycles(self.import_graph, vertices=vertices): - self.add_message('cyclic-import', args=' -> '.join(cycle)) - - def visit_import(self, node): - """triggered when an import statement is seen""" - modnode = node.root() - for name, _ in node.names: - importedmodnode = self.get_imported_module(node, name) - if importedmodnode is None: - continue - self._check_relative_import(modnode, node, importedmodnode, name) - self._add_imported_module(node, importedmodnode.name) - self._check_deprecated_module(node, name) - self._check_reimport(node, name) - - # TODO This appears to be the list of all messages of the checker... - # @check_messages('W0410', 'W0401', 'W0403', 'W0402', 'W0404', 'W0406', 'F0401') - @check_messages(*(MSGS.keys())) - def visit_from(self, node): - """triggered when a from statement is seen""" - basename = node.modname - if basename == '__future__': - # check if this is the first non-docstring statement in the module - prev = node.previous_sibling() - if prev: - # consecutive future statements are possible - if not (isinstance(prev, astroid.From) - and prev.modname == '__future__'): - self.add_message('misplaced-future', node=node) - return - for name, _ in node.names: - if name == '*': - self.add_message('wildcard-import', args=basename, node=node) - modnode = node.root() - importedmodnode = self.get_imported_module(node, basename) - if importedmodnode is None: - return - self._check_relative_import(modnode, node, importedmodnode, basename) - self._check_deprecated_module(node, basename) - for name, _ in node.names: - if name != '*': - self._add_imported_module(node, '%s.%s' % (importedmodnode.name, name)) - self._check_reimport(node, name, basename, node.level) - - def get_imported_module(self, importnode, modname): - try: - return importnode.do_import_module(modname) - except astroid.InferenceError as ex: - if str(ex) != modname: - args = '%r (%s)' % (modname, ex) - else: - args = repr(modname) - if not _except_import_error(importnode.parent): - self.add_message("import-error", args=args, node=importnode) - - def _check_relative_import(self, modnode, importnode, importedmodnode, - importedasname): - """check relative import. node is either an Import or From node, modname - the imported module name. - """ - if not self.linter.is_message_enabled('relative-import'): - return - if importedmodnode.file is None: - return False # built-in module - if modnode is importedmodnode: - return False # module importing itself - if modnode.absolute_import_activated() or getattr(importnode, 'level', None): - return False - if importedmodnode.name != importedasname: - # this must be a relative import... - self.add_message('relative-import', - args=(importedasname, importedmodnode.name), - node=importnode) - - def _add_imported_module(self, node, importedmodname): - """notify an imported module, used to analyze dependencies""" - try: - importedmodname = get_module_part(importedmodname) - except ImportError: - pass - context_name = node.root().name - if context_name == importedmodname: - # module importing itself ! - self.add_message('import-self', node=node) - elif not is_standard_module(importedmodname): - # handle dependencies - importedmodnames = self.stats['dependencies'].setdefault( - importedmodname, set()) - if not context_name in importedmodnames: - importedmodnames.add(context_name) - # update import graph - mgraph = self.import_graph[context_name] - if importedmodname not in mgraph: - mgraph.add(importedmodname) - - def _check_deprecated_module(self, node, mod_path): - """check if the module is deprecated""" - for mod_name in self.config.deprecated_modules: - if mod_path == mod_name or mod_path.startswith(mod_name + '.'): - self.add_message('deprecated-module', node=node, args=mod_path) - - def _check_reimport(self, node, name, basename=None, level=None): - """check if the import is necessary (i.e. not already done)""" - if not self.linter.is_message_enabled('reimported'): - return - frame = node.frame() - root = node.root() - contexts = [(frame, level)] - if root is not frame: - contexts.append((root, None)) - for context, level in contexts: - first = get_first_import(node, context, name, basename, level) - if first is not None: - self.add_message('reimported', node=node, - args=(name, first.fromlineno)) - - - def report_external_dependencies(self, sect, _, dummy): - """return a verbatim layout for displaying dependencies""" - dep_info = make_tree_defs(six.iteritems(self._external_dependencies_info())) - if not dep_info: - raise EmptyReport() - tree_str = repr_tree_defs(dep_info) - sect.append(VerbatimText(tree_str)) - - def report_dependencies_graph(self, sect, _, dummy): - """write dependencies as a dot (graphviz) file""" - dep_info = self.stats['dependencies'] - if not dep_info or not (self.config.import_graph - or self.config.ext_import_graph - or self.config.int_import_graph): - raise EmptyReport() - filename = self.config.import_graph - if filename: - make_graph(filename, dep_info, sect, '') - filename = self.config.ext_import_graph - if filename: - make_graph(filename, self._external_dependencies_info(), - sect, 'external ') - filename = self.config.int_import_graph - if filename: - make_graph(filename, self._internal_dependencies_info(), - sect, 'internal ') - - def _external_dependencies_info(self): - """return cached external dependencies information or build and - cache them - """ - if self.__ext_dep_info is None: - package = self.linter.current_name - self.__ext_dep_info = result = {} - for importee, importers in six.iteritems(self.stats['dependencies']): - if not importee.startswith(package): - result[importee] = importers - return self.__ext_dep_info - - def _internal_dependencies_info(self): - """return cached internal dependencies information or build and - cache them - """ - if self.__int_dep_info is None: - package = self.linter.current_name - self.__int_dep_info = result = {} - for importee, importers in six.iteritems(self.stats['dependencies']): - if importee.startswith(package): - result[importee] = importers - return self.__int_dep_info - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(ImportsChecker(linter)) diff --git a/checkers/logging.py b/checkers/logging.py deleted file mode 100644 index 897c1c7..0000000 --- a/checkers/logging.py +++ /dev/null @@ -1,256 +0,0 @@ -# Copyright (c) 2009-2010 Google, Inc. -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""checker for use of Python logging -""" - -import astroid -from pylint import checkers -from pylint import interfaces -from pylint.checkers import utils -from pylint.checkers.utils import check_messages - -import six - - -MSGS = { - 'W1201': ('Specify string format arguments as logging function parameters', - 'logging-not-lazy', - 'Used when a logging statement has a call form of ' - '"logging.(format_string % (format_args...))". ' - 'Such calls should leave string interpolation to the logging ' - 'method itself and be written ' - '"logging.(format_string, format_args...)" ' - 'so that the program may avoid incurring the cost of the ' - 'interpolation in those cases in which no message will be ' - 'logged. For more, see ' - 'http://www.python.org/dev/peps/pep-0282/.'), - 'W1202': ('Use % formatting in logging functions but pass the % ' - 'parameters as arguments', - 'logging-format-interpolation', - 'Used when a logging statement has a call form of ' - '"logging.(format_string.format(format_args...))"' - '. Such calls should use % formatting instead, but leave ' - 'interpolation to the logging function by passing the parameters ' - 'as arguments.'), - 'E1200': ('Unsupported logging format character %r (%#02x) at index %d', - 'logging-unsupported-format', - 'Used when an unsupported format character is used in a logging\ - statement format string.'), - 'E1201': ('Logging format string ends in middle of conversion specifier', - 'logging-format-truncated', - 'Used when a logging statement format string terminates before\ - the end of a conversion specifier.'), - 'E1205': ('Too many arguments for logging format string', - 'logging-too-many-args', - 'Used when a logging format string is given too few arguments.'), - 'E1206': ('Not enough arguments for logging format string', - 'logging-too-few-args', - 'Used when a logging format string is given too many arguments'), - } - - -CHECKED_CONVENIENCE_FUNCTIONS = set([ - 'critical', 'debug', 'error', 'exception', 'fatal', 'info', 'warn', - 'warning']) - -def is_method_call(callfunc_node, types=(), methods=()): - """Determines if a CallFunc node represents a method call. - - Args: - callfunc_node: The CallFunc AST node to check. - types: Optional sequence of caller type names to restrict check. - methods: Optional sequence of method names to restrict check. - - Returns: - True, if the node represents a method call for the given type and - method names, False otherwise. - """ - if not isinstance(callfunc_node, astroid.CallFunc): - return False - func = utils.safe_infer(callfunc_node.func) - return (isinstance(func, astroid.BoundMethod) - and isinstance(func.bound, astroid.Instance) - and (func.bound.name in types if types else True) - and (func.name in methods if methods else True)) - - - -class LoggingChecker(checkers.BaseChecker): - """Checks use of the logging module.""" - - __implements__ = interfaces.IAstroidChecker - name = 'logging' - msgs = MSGS - - options = (('logging-modules', - {'default': ('logging',), - 'type': 'csv', - 'metavar': '', - 'help': 'Logging modules to check that the string format ' - 'arguments are in logging function parameter format'} - ), - ) - - def visit_module(self, node): # pylint: disable=unused-argument - """Clears any state left in this checker from last module checked.""" - # The code being checked can just as easily "import logging as foo", - # so it is necessary to process the imports and store in this field - # what name the logging module is actually given. - self._logging_names = set() - logging_mods = self.config.logging_modules - - self._logging_modules = set(logging_mods) - self._from_imports = {} - for logging_mod in logging_mods: - parts = logging_mod.rsplit('.', 1) - if len(parts) > 1: - self._from_imports[parts[0]] = parts[1] - - def visit_from(self, node): - """Checks to see if a module uses a non-Python logging module.""" - try: - logging_name = self._from_imports[node.modname] - for module, as_name in node.names: - if module == logging_name: - self._logging_names.add(as_name or module) - except KeyError: - pass - - def visit_import(self, node): - """Checks to see if this module uses Python's built-in logging.""" - for module, as_name in node.names: - if module in self._logging_modules: - self._logging_names.add(as_name or module) - - @check_messages(*(MSGS.keys())) - def visit_callfunc(self, node): - """Checks calls to logging methods.""" - def is_logging_name(): - return (isinstance(node.func, astroid.Getattr) and - isinstance(node.func.expr, astroid.Name) and - node.func.expr.name in self._logging_names) - - def is_logger_class(): - try: - for inferred in node.func.infer(): - if isinstance(inferred, astroid.BoundMethod): - parent = inferred._proxied.parent - if (isinstance(parent, astroid.Class) and - (parent.qname() == 'logging.Logger' or - any(ancestor.qname() == 'logging.Logger' - for ancestor in parent.ancestors()))): - return True, inferred._proxied.name - except astroid.exceptions.InferenceError: - pass - return False, None - - if is_logging_name(): - name = node.func.attrname - else: - result, name = is_logger_class() - if not result: - return - self._check_log_method(node, name) - - def _check_log_method(self, node, name): - """Checks calls to logging.log(level, format, *format_args).""" - if name == 'log': - if node.starargs or node.kwargs or len(node.args) < 2: - # Either a malformed call, star args, or double-star args. Beyond - # the scope of this checker. - return - format_pos = 1 - elif name in CHECKED_CONVENIENCE_FUNCTIONS: - if node.starargs or node.kwargs or not node.args: - # Either no args, star args, or double-star args. Beyond the - # scope of this checker. - return - format_pos = 0 - else: - return - - if isinstance(node.args[format_pos], astroid.BinOp) and node.args[format_pos].op == '%': - self.add_message('logging-not-lazy', node=node) - elif isinstance(node.args[format_pos], astroid.CallFunc): - self._check_call_func(node.args[format_pos]) - elif isinstance(node.args[format_pos], astroid.Const): - self._check_format_string(node, format_pos) - - def _check_call_func(self, callfunc_node): - """Checks that function call is not format_string.format(). - - Args: - callfunc_node: CallFunc AST node to be checked. - """ - if is_method_call(callfunc_node, ('str', 'unicode'), ('format',)): - self.add_message('logging-format-interpolation', node=callfunc_node) - - def _check_format_string(self, node, format_arg): - """Checks that format string tokens match the supplied arguments. - - Args: - node: AST node to be checked. - format_arg: Index of the format string in the node arguments. - """ - num_args = _count_supplied_tokens(node.args[format_arg + 1:]) - if not num_args: - # If no args were supplied, then all format strings are valid - - # don't check any further. - return - format_string = node.args[format_arg].value - if not isinstance(format_string, six.string_types): - # If the log format is constant non-string (e.g. logging.debug(5)), - # ensure there are no arguments. - required_num_args = 0 - else: - try: - keyword_args, required_num_args = \ - utils.parse_format_string(format_string) - if keyword_args: - # Keyword checking on logging strings is complicated by - # special keywords - out of scope. - return - except utils.UnsupportedFormatCharacter as ex: - char = format_string[ex.index] - self.add_message('logging-unsupported-format', node=node, - args=(char, ord(char), ex.index)) - return - except utils.IncompleteFormatString: - self.add_message('logging-format-truncated', node=node) - return - if num_args > required_num_args: - self.add_message('logging-too-many-args', node=node) - elif num_args < required_num_args: - self.add_message('logging-too-few-args', node=node) - - -def _count_supplied_tokens(args): - """Counts the number of tokens in an args list. - - The Python log functions allow for special keyword arguments: func, - exc_info and extra. To handle these cases correctly, we only count - arguments that aren't keywords. - - Args: - args: List of AST nodes that are arguments for a log format string. - - Returns: - Number of AST nodes that aren't keywords. - """ - return sum(1 for arg in args if not isinstance(arg, astroid.Keyword)) - - -def register(linter): - """Required method to auto-register this checker.""" - linter.register_checker(LoggingChecker(linter)) diff --git a/checkers/misc.py b/checkers/misc.py deleted file mode 100644 index 7fbe70b..0000000 --- a/checkers/misc.py +++ /dev/null @@ -1,104 +0,0 @@ -# pylint: disable=W0511 -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -""" Copyright (c) 2000-2010 LOGILAB S.A. (Paris, FRANCE). - http://www.logilab.fr/ -- mailto:contact@logilab.fr - -Check source code is ascii only or has an encoding declaration (PEP 263) -""" - -import re - -from pylint.interfaces import IRawChecker -from pylint.checkers import BaseChecker -import six - - -MSGS = { - 'W0511': ('%s', - 'fixme', - 'Used when a warning note as FIXME or XXX is detected.'), - 'W0512': ('Cannot decode using encoding "%s", unexpected byte at position %d', - 'invalid-encoded-data', - 'Used when a source line cannot be decoded using the specified ' - 'source file encoding.', - {'maxversion': (3, 0)}), -} - - -class EncodingChecker(BaseChecker): - - """checks for: - * warning notes in the code like FIXME, XXX - * encoding issues. - """ - __implements__ = IRawChecker - - # configuration section name - name = 'miscellaneous' - msgs = MSGS - - options = (('notes', - {'type': 'csv', 'metavar': '', - 'default': ('FIXME', 'XXX', 'TODO'), - 'help': ('List of note tags to take in consideration, ' - 'separated by a comma.')}),) - - def _check_note(self, notes, lineno, line): - # First, simply check if the notes are in the line at all. This is an - # optimisation to prevent using the regular expression on every line, - # but rather only on lines which may actually contain one of the notes. - # This prevents a pathological problem with lines that are hundreds - # of thousands of characters long. - for note in self.config.notes: - if note in line: - break - else: - return - - match = notes.search(line) - if not match: - return - self.add_message('fixme', args=line[match.start(1):-1], line=lineno) - - def _check_encoding(self, lineno, line, file_encoding): - try: - return six.text_type(line, file_encoding) - except UnicodeDecodeError as ex: - self.add_message('invalid-encoded-data', line=lineno, - args=(file_encoding, ex.args[2])) - - def process_module(self, module): - """inspect the source file to find encoding problem or fixmes like - notes - """ - if self.config.notes: - notes = re.compile( - r'.*?#\s*(%s)(:*\s*.+)' % "|".join(self.config.notes)) - else: - notes = None - if module.file_encoding: - encoding = module.file_encoding - else: - encoding = 'ascii' - - with module.stream() as stream: - for lineno, line in enumerate(stream): - line = self._check_encoding(lineno + 1, line, encoding) - if line is not None and notes: - self._check_note(notes, lineno + 1, line) - - -def register(linter): - """required method to auto register this checker""" - linter.register_checker(EncodingChecker(linter)) diff --git a/checkers/newstyle.py b/checkers/newstyle.py deleted file mode 100644 index f74e7f1..0000000 --- a/checkers/newstyle.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright (c) 2005-2014 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""check for new / old style related problems -""" -import sys - -import astroid - -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH -from pylint.checkers import BaseChecker -from pylint.checkers.utils import ( - check_messages, - has_known_bases, - node_frame_class, -) - -MSGS = { - 'E1001': ('Use of __slots__ on an old style class', - 'slots-on-old-class', - 'Used when an old style class uses the __slots__ attribute.', - {'maxversion': (3, 0)}), - 'E1002': ('Use of super on an old style class', - 'super-on-old-class', - 'Used when an old style class uses the super builtin.', - {'maxversion': (3, 0)}), - 'E1003': ('Bad first argument %r given to super()', - 'bad-super-call', - 'Used when another argument than the current class is given as \ - first argument of the super builtin.'), - 'E1004': ('Missing argument to super()', - 'missing-super-argument', - 'Used when the super builtin didn\'t receive an \ - argument.', - {'maxversion': (3, 0)}), - 'W1001': ('Use of "property" on an old style class', - 'property-on-old-class', - 'Used when Pylint detect the use of the builtin "property" \ - on an old style class while this is relying on new style \ - classes features.', - {'maxversion': (3, 0)}), - 'C1001': ('Old-style class defined.', - 'old-style-class', - 'Used when a class is defined that does not inherit from another' - 'class and does not inherit explicitly from "object".', - {'maxversion': (3, 0)}) - } - - -class NewStyleConflictChecker(BaseChecker): - """checks for usage of new style capabilities on old style classes and - other new/old styles conflicts problems - * use of property, __slots__, super - * "super" usage - """ - - __implements__ = (IAstroidChecker,) - - # configuration section name - name = 'newstyle' - # messages - msgs = MSGS - priority = -2 - # configuration options - options = () - - @check_messages('slots-on-old-class', 'old-style-class') - def visit_class(self, node): - """ Check __slots__ in old style classes and old - style class definition. - """ - if '__slots__' in node and not node.newstyle: - confidence = (INFERENCE if has_known_bases(node) - else INFERENCE_FAILURE) - self.add_message('slots-on-old-class', node=node, - confidence=confidence) - # The node type could be class, exception, metaclass, or - # interface. Presumably, the non-class-type nodes would always - # have an explicit base class anyway. - if not node.bases and node.type == 'class' and not node.metaclass(): - # We use confidence HIGH here because this message should only ever - # be emitted for classes at the root of the inheritance hierarchyself. - self.add_message('old-style-class', node=node, confidence=HIGH) - - @check_messages('property-on-old-class') - def visit_callfunc(self, node): - """check property usage""" - parent = node.parent.frame() - if (isinstance(parent, astroid.Class) and - not parent.newstyle and - isinstance(node.func, astroid.Name)): - confidence = (INFERENCE if has_known_bases(parent) - else INFERENCE_FAILURE) - name = node.func.name - if name == 'property': - self.add_message('property-on-old-class', node=node, - confidence=confidence) - - @check_messages('super-on-old-class', 'bad-super-call', 'missing-super-argument') - def visit_function(self, node): - """check use of super""" - # ignore actual functions or method within a new style class - if not node.is_method(): - return - klass = node.parent.frame() - for stmt in node.nodes_of_class(astroid.CallFunc): - if node_frame_class(stmt) != node_frame_class(node): - # Don't look down in other scopes. - continue - expr = stmt.func - if not isinstance(expr, astroid.Getattr): - continue - call = expr.expr - # skip the test if using super - if isinstance(call, astroid.CallFunc) and \ - isinstance(call.func, astroid.Name) and \ - call.func.name == 'super': - confidence = (INFERENCE if has_known_bases(klass) - else INFERENCE_FAILURE) - if not klass.newstyle: - # super should not be used on an old style class - self.add_message('super-on-old-class', node=node, - confidence=confidence) - else: - # super first arg should be the class - if not call.args and sys.version_info[0] == 3: - # unless Python 3 - continue - - try: - supcls = (call.args and next(call.args[0].infer()) - or None) - except astroid.InferenceError: - continue - - if supcls is None: - self.add_message('missing-super-argument', node=call, - confidence=confidence) - continue - - if klass is not supcls: - name = None - # if supcls is not YES, then supcls was infered - # and use its name. Otherwise, try to look - # for call.args[0].name - if supcls is not astroid.YES: - name = supcls.name - else: - if hasattr(call.args[0], 'name'): - name = call.args[0].name - if name is not None: - self.add_message('bad-super-call', - node=call, - args=(name, ), - confidence=confidence) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(NewStyleConflictChecker(linter)) diff --git a/checkers/python3.py b/checkers/python3.py deleted file mode 100644 index 880d5c5..0000000 --- a/checkers/python3.py +++ /dev/null @@ -1,488 +0,0 @@ -# Copyright 2014 Google Inc. -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""Check Python 2 code for Python 2/3 source-compatible issues.""" -from __future__ import absolute_import - -import re -import tokenize - -import astroid -from pylint import checkers, interfaces -from pylint.utils import WarningScope -from pylint.checkers import utils - - -_ZERO = re.compile("^0+$") - -def _is_old_octal(literal): - if _ZERO.match(literal): - return False - if re.match('0\d+', literal): - try: - int(literal, 8) - except ValueError: - return False - return True - -def _check_dict_node(node): - inferred_types = set() - try: - inferred = node.infer() - for inferred_node in inferred: - inferred_types.add(inferred_node) - except (astroid.InferenceError, astroid.UnresolvableName): - pass - return (not inferred_types - or any(isinstance(x, astroid.Dict) for x in inferred_types)) - - -class Python3Checker(checkers.BaseChecker): - - __implements__ = interfaces.IAstroidChecker - enabled = False - name = 'python3' - - msgs = { - # Errors for what will syntactically break in Python 3, warnings for - # everything else. - 'E1601': ('print statement used', - 'print-statement', - 'Used when a print statement is used ' - '(`print` is a function in Python 3)', - {'maxversion': (3, 0)}), - 'E1602': ('Parameter unpacking specified', - 'parameter-unpacking', - 'Used when parameter unpacking is specified for a function' - "(Python 3 doesn't allow it)", - {'maxversion': (3, 0)}), - 'E1603': ('Implicit unpacking of exceptions is not supported ' - 'in Python 3', - 'unpacking-in-except', - 'Python3 will not allow implicit unpacking of ' - 'exceptions in except clauses. ' - 'See http://www.python.org/dev/peps/pep-3110/', - {'maxversion': (3, 0), - 'old_names': [('W0712', 'unpacking-in-except')]}), - 'E1604': ('Use raise ErrorClass(args) instead of ' - 'raise ErrorClass, args.', - 'old-raise-syntax', - "Used when the alternate raise syntax " - "'raise foo, bar' is used " - "instead of 'raise foo(bar)'.", - {'maxversion': (3, 0), - 'old_names': [('W0121', 'old-raise-syntax')]}), - 'E1605': ('Use of the `` operator', - 'backtick', - 'Used when the deprecated "``" (backtick) operator is used ' - 'instead of the str() function.', - {'scope': WarningScope.NODE, - 'maxversion': (3, 0), - 'old_names': [('W0333', 'backtick')]}), - 'W1601': ('apply built-in referenced', - 'apply-builtin', - 'Used when the apply built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1602': ('basestring built-in referenced', - 'basestring-builtin', - 'Used when the basestring built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1603': ('buffer built-in referenced', - 'buffer-builtin', - 'Used when the buffer built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1604': ('cmp built-in referenced', - 'cmp-builtin', - 'Used when the cmp built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1605': ('coerce built-in referenced', - 'coerce-builtin', - 'Used when the coerce built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1606': ('execfile built-in referenced', - 'execfile-builtin', - 'Used when the execfile built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1607': ('file built-in referenced', - 'file-builtin', - 'Used when the file built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1608': ('long built-in referenced', - 'long-builtin', - 'Used when the long built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1609': ('raw_input built-in referenced', - 'raw_input-builtin', - 'Used when the raw_input built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1610': ('reduce built-in referenced', - 'reduce-builtin', - 'Used when the reduce built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1611': ('StandardError built-in referenced', - 'standarderror-builtin', - 'Used when the StandardError built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1612': ('unicode built-in referenced', - 'unicode-builtin', - 'Used when the unicode built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1613': ('xrange built-in referenced', - 'xrange-builtin', - 'Used when the xrange built-in function is referenced ' - '(missing from Python 3)', - {'maxversion': (3, 0)}), - 'W1614': ('__coerce__ method defined', - 'coerce-method', - 'Used when a __coerce__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1615': ('__delslice__ method defined', - 'delslice-method', - 'Used when a __delslice__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1616': ('__getslice__ method defined', - 'getslice-method', - 'Used when a __getslice__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1617': ('__setslice__ method defined', - 'setslice-method', - 'Used when a __setslice__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1618': ('import missing `from __future__ import absolute_import`', - 'no-absolute-import', - 'Used when an import is not accompanied by ' - '`from __future__ import absolute_import`' - ' (default behaviour in Python 3)', - {'maxversion': (3, 0)}), - 'W1619': ('division w/o __future__ statement', - 'old-division', - 'Used for non-floor division w/o a float literal or ' - '``from __future__ import division``' - '(Python 3 returns a float for int division unconditionally)', - {'maxversion': (3, 0)}), - 'W1620': ('Calling a dict.iter*() method', - 'dict-iter-method', - 'Used for calls to dict.iterkeys(), itervalues() or iteritems() ' - '(Python 3 lacks these methods)', - {'maxversion': (3, 0)}), - 'W1621': ('Calling a dict.view*() method', - 'dict-view-method', - 'Used for calls to dict.viewkeys(), viewvalues() or viewitems() ' - '(Python 3 lacks these methods)', - {'maxversion': (3, 0)}), - 'W1622': ('Called a next() method on an object', - 'next-method-called', - "Used when an object's next() method is called " - '(Python 3 uses the next() built-in function)', - {'maxversion': (3, 0)}), - 'W1623': ("Assigning to a class' __metaclass__ attribute", - 'metaclass-assignment', - "Used when a metaclass is specified by assigning to __metaclass__ " - '(Python 3 specifies the metaclass as a class statement argument)', - {'maxversion': (3, 0)}), - 'W1624': ('Indexing exceptions will not work on Python 3', - 'indexing-exception', - 'Indexing exceptions will not work on Python 3. Use ' - '`exception.args[index]` instead.', - {'maxversion': (3, 0), - 'old_names': [('W0713', 'indexing-exception')]}), - 'W1625': ('Raising a string exception', - 'raising-string', - 'Used when a string exception is raised. This will not ' - 'work on Python 3.', - {'maxversion': (3, 0), - 'old_names': [('W0701', 'raising-string')]}), - 'W1626': ('reload built-in referenced', - 'reload-builtin', - 'Used when the reload built-in function is referenced ' - '(missing from Python 3). You can use instead imp.reload ' - 'or importlib.reload.', - {'maxversion': (3, 0)}), - 'W1627': ('__oct__ method defined', - 'oct-method', - 'Used when a __oct__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1628': ('__hex__ method defined', - 'hex-method', - 'Used when a __hex__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1629': ('__nonzero__ method defined', - 'nonzero-method', - 'Used when a __nonzero__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1630': ('__cmp__ method defined', - 'cmp-method', - 'Used when a __cmp__ method is defined ' - '(method is not used by Python 3)', - {'maxversion': (3, 0)}), - 'W1631': ('map is used as implicitly evaluated call', - 'implicit-map-evaluation', - 'Used when the map builtin is used as implicitly ' - 'evaluated call, as in "map(func, args)" on a single line. ' - 'This behaviour will not work in Python 3, where ' - 'map is a generator and must be evaluated. ' - 'Prefer a for-loop as alternative.', - {'maxversion': (3, 0)}), - 'W1632': ('input built-in referenced', - 'input-builtin', - 'Used when the input built-in is referenced ' - '(backwards-incompatible semantics in Python 3)', - {'maxversion': (3, 0)}), - 'W1633': ('round built-in referenced', - 'round-builtin', - 'Used when the round built-in is referenced ' - '(backwards-incompatible semantics in Python 3)', - {'maxversion': (3, 0)}), - 'W1634': ('intern built-in referenced', - 'intern-builtin', - 'Used when the intern built-in is referenced ' - '(Moved to sys.intern in Python 3)', - {'maxversion': (3, 0)}), - 'W1635': ('unichr built-in referenced', - 'unichr-builtin', - 'Used when the unichr built-in is referenced ' - '(Use chr in Python 3)', - {'maxversion': (3, 0)}), - } - - _bad_builtins = frozenset([ - 'apply', - 'basestring', - 'buffer', - 'cmp', - 'coerce', - 'execfile', - 'file', - 'input', # Not missing, but incompatible semantics - 'intern', - 'long', - 'raw_input', - 'reduce', - 'round', # Not missing, but incompatible semantics - 'StandardError', - 'unichr', - 'unicode', - 'xrange', - 'reload', - ]) - - _unused_magic_methods = frozenset([ - '__coerce__', - '__delslice__', - '__getslice__', - '__setslice__', - '__oct__', - '__hex__', - '__nonzero__', - '__cmp__', - ]) - - def __init__(self, *args, **kwargs): - self._future_division = False - self._future_absolute_import = False - super(Python3Checker, self).__init__(*args, **kwargs) - - def visit_function(self, node): - if node.is_method() and node.name in self._unused_magic_methods: - method_name = node.name - if node.name.startswith('__'): - method_name = node.name[2:-2] - self.add_message(method_name + '-method', node=node) - - @utils.check_messages('parameter-unpacking') - def visit_arguments(self, node): - for arg in node.args: - if isinstance(arg, astroid.Tuple): - self.add_message('parameter-unpacking', node=arg) - - @utils.check_messages('implicit-map-evaluation') - def visit_discard(self, node): - if (isinstance(node.value, astroid.CallFunc) and - isinstance(node.value.func, astroid.Name) and - node.value.func.name == 'map'): - module = node.value.func.lookup('map')[0] - if getattr(module, 'name', None) == '__builtin__': - self.add_message('implicit-map-evaluation', node=node) - - def visit_name(self, node): - """Detect when a "bad" built-in is referenced.""" - found_node = node.lookup(node.name)[0] - if getattr(found_node, 'name', None) == '__builtin__': - if node.name in self._bad_builtins: - message = node.name.lower() + '-builtin' - self.add_message(message, node=node) - - @utils.check_messages('print-statement') - def visit_print(self, node): - self.add_message('print-statement', node=node) - - @utils.check_messages('no-absolute-import') - def visit_from(self, node): - if node.modname == '__future__': - for name, _ in node.names: - if name == 'division': - self._future_division = True - elif name == 'absolute_import': - self._future_absolute_import = True - elif not self._future_absolute_import: - self.add_message('no-absolute-import', node=node) - - @utils.check_messages('no-absolute-import') - def visit_import(self, node): - if not self._future_absolute_import: - self.add_message('no-absolute-import', node=node) - - @utils.check_messages('metaclass-assignment') - def visit_class(self, node): - if '__metaclass__' in node.locals: - self.add_message('metaclass-assignment', node=node) - - @utils.check_messages('old-division') - def visit_binop(self, node): - if not self._future_division and node.op == '/': - for arg in (node.left, node.right): - if isinstance(arg, astroid.Const) and isinstance(arg.value, float): - break - else: - self.add_message('old-division', node=node) - - @utils.check_messages('next-method-called', - 'dict-iter-method', - 'dict-view-method') - def visit_callfunc(self, node): - if not isinstance(node.func, astroid.Getattr): - return - if any([node.args, node.starargs, node.kwargs]): - return - if node.func.attrname == 'next': - self.add_message('next-method-called', node=node) - else: - if _check_dict_node(node.func.expr): - if node.func.attrname in ('iterkeys', 'itervalues', 'iteritems'): - self.add_message('dict-iter-method', node=node) - elif node.func.attrname in ('viewkeys', 'viewvalues', 'viewitems'): - self.add_message('dict-view-method', node=node) - - @utils.check_messages('indexing-exception') - def visit_subscript(self, node): - """ Look for indexing exceptions. """ - try: - for infered in node.value.infer(): - if not isinstance(infered, astroid.Instance): - continue - if utils.inherit_from_std_ex(infered): - self.add_message('indexing-exception', node=node) - except astroid.InferenceError: - return - - @utils.check_messages('unpacking-in-except') - def visit_excepthandler(self, node): - """Visit an except handler block and check for exception unpacking.""" - if isinstance(node.name, (astroid.Tuple, astroid.List)): - self.add_message('unpacking-in-except', node=node) - - @utils.check_messages('backtick') - def visit_backquote(self, node): - self.add_message('backtick', node=node) - - @utils.check_messages('raising-string', 'old-raise-syntax') - def visit_raise(self, node): - """Visit a raise statement and check for raising - strings or old-raise-syntax. - """ - if (node.exc is not None and - node.inst is not None and - node.tback is None): - self.add_message('old-raise-syntax', node=node) - - # Ignore empty raise. - if node.exc is None: - return - expr = node.exc - if self._check_raise_value(node, expr): - return - else: - try: - value = next(astroid.unpack_infer(expr)) - except astroid.InferenceError: - return - self._check_raise_value(node, value) - - def _check_raise_value(self, node, expr): - if isinstance(expr, astroid.Const): - value = expr.value - if isinstance(value, str): - self.add_message('raising-string', node=node) - return True - - -class Python3TokenChecker(checkers.BaseTokenChecker): - __implements__ = interfaces.ITokenChecker - name = 'python3' - enabled = False - - msgs = { - 'E1606': ('Use of long suffix', - 'long-suffix', - 'Used when "l" or "L" is used to mark a long integer. ' - 'This will not work in Python 3, since `int` and `long` ' - 'types have merged.', - {'maxversion': (3, 0)}), - 'E1607': ('Use of the <> operator', - 'old-ne-operator', - 'Used when the deprecated "<>" operator is used instead ' - 'of "!=". This is removed in Python 3.', - {'maxversion': (3, 0), - 'old_names': [('W0331', 'old-ne-operator')]}), - 'E1608': ('Use of old octal literal', - 'old-octal-literal', - 'Usen when encountering the old octal syntax, ' - 'removed in Python 3. To use the new syntax, ' - 'prepend 0o on the number.', - {'maxversion': (3, 0)}), - } - - def process_tokens(self, tokens): - for idx, (tok_type, token, start, _, _) in enumerate(tokens): - if tok_type == tokenize.NUMBER: - if token.lower().endswith('l'): - # This has a different semantic than lowercase-l-suffix. - self.add_message('long-suffix', line=start[0]) - elif _is_old_octal(token): - self.add_message('old-octal-literal', line=start[0]) - if tokens[idx][1] == '<>': - self.add_message('old-ne-operator', line=tokens[idx][2][0]) - - -def register(linter): - linter.register_checker(Python3Checker(linter)) - linter.register_checker(Python3TokenChecker(linter)) diff --git a/checkers/raw_metrics.py b/checkers/raw_metrics.py deleted file mode 100644 index 71fecf6..0000000 --- a/checkers/raw_metrics.py +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -""" Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE). - http://www.logilab.fr/ -- mailto:contact@logilab.fr - -Raw metrics checker -""" - -import tokenize - -# pylint now requires pylint >= 2.2, so this is no longer necessary -#if not hasattr(tokenize, 'NL'): -# raise ValueError("tokenize.NL doesn't exist -- tokenize module too old") - -from logilab.common.ureports import Table - -from pylint.interfaces import ITokenChecker -from pylint.utils import EmptyReport -from pylint.checkers import BaseTokenChecker -from pylint.reporters import diff_string - -def report_raw_stats(sect, stats, old_stats): - """calculate percentage of code / doc / comment / empty - """ - total_lines = stats['total_lines'] - if not total_lines: - raise EmptyReport() - sect.description = '%s lines have been analyzed' % total_lines - lines = ('type', 'number', '%', 'previous', 'difference') - for node_type in ('code', 'docstring', 'comment', 'empty'): - key = node_type + '_lines' - total = stats[key] - percent = float(total * 100) / total_lines - old = old_stats.get(key, None) - if old is not None: - diff_str = diff_string(old, total) - else: - old, diff_str = 'NC', 'NC' - lines += (node_type, str(total), '%.2f' % percent, - str(old), diff_str) - sect.append(Table(children=lines, cols=5, rheaders=1)) - - -class RawMetricsChecker(BaseTokenChecker): - """does not check anything but gives some raw metrics : - * total number of lines - * total number of code lines - * total number of docstring lines - * total number of comments lines - * total number of empty lines - """ - - __implements__ = (ITokenChecker,) - - # configuration section name - name = 'metrics' - # configuration options - options = () - # messages - msgs = {} - # reports - reports = (('RP0701', 'Raw metrics', report_raw_stats),) - - def __init__(self, linter): - BaseTokenChecker.__init__(self, linter) - self.stats = None - - def open(self): - """init statistics""" - self.stats = self.linter.add_stats(total_lines=0, code_lines=0, - empty_lines=0, docstring_lines=0, - comment_lines=0) - - def process_tokens(self, tokens): - """update stats""" - i = 0 - tokens = list(tokens) - while i < len(tokens): - i, lines_number, line_type = get_type(tokens, i) - self.stats['total_lines'] += lines_number - self.stats[line_type] += lines_number - - -JUNK = (tokenize.NL, tokenize.INDENT, tokenize.NEWLINE, tokenize.ENDMARKER) - -def get_type(tokens, start_index): - """return the line type : docstring, comment, code, empty""" - i = start_index - tok_type = tokens[i][0] - start = tokens[i][2] - pos = start - line_type = None - while i < len(tokens) and tokens[i][2][0] == start[0]: - tok_type = tokens[i][0] - pos = tokens[i][3] - if line_type is None: - if tok_type == tokenize.STRING: - line_type = 'docstring_lines' - elif tok_type == tokenize.COMMENT: - line_type = 'comment_lines' - elif tok_type in JUNK: - pass - else: - line_type = 'code_lines' - i += 1 - if line_type is None: - line_type = 'empty_lines' - elif i < len(tokens) and tok_type == tokenize.NEWLINE: - i += 1 - return i, pos[0] - start[0] + 1, line_type - - -def register(linter): - """ required method to auto register this checker """ - linter.register_checker(RawMetricsChecker(linter)) - diff --git a/checkers/similar.py b/checkers/similar.py deleted file mode 100644 index 9542077..0000000 --- a/checkers/similar.py +++ /dev/null @@ -1,372 +0,0 @@ -# pylint: disable=W0622 -# Copyright (c) 2004-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""a similarities / code duplication command line tool and pylint checker -""" -from __future__ import print_function -import sys -from collections import defaultdict - -from logilab.common.ureports import Table - -from pylint.interfaces import IRawChecker -from pylint.checkers import BaseChecker, table_lines_from_stats - -import six -from six.moves import zip - - -class Similar(object): - """finds copy-pasted lines of code in a project""" - - def __init__(self, min_lines=4, ignore_comments=False, - ignore_docstrings=False, ignore_imports=False): - self.min_lines = min_lines - self.ignore_comments = ignore_comments - self.ignore_docstrings = ignore_docstrings - self.ignore_imports = ignore_imports - self.linesets = [] - - def append_stream(self, streamid, stream, encoding=None): - """append a file to search for similarities""" - if encoding is None: - readlines = stream.readlines - else: - readlines = lambda: [line.decode(encoding) for line in stream] - try: - self.linesets.append(LineSet(streamid, - readlines(), - self.ignore_comments, - self.ignore_docstrings, - self.ignore_imports)) - except UnicodeDecodeError: - pass - - def run(self): - """start looking for similarities and display results on stdout""" - self._display_sims(self._compute_sims()) - - def _compute_sims(self): - """compute similarities in appended files""" - no_duplicates = defaultdict(list) - for num, lineset1, idx1, lineset2, idx2 in self._iter_sims(): - duplicate = no_duplicates[num] - for couples in duplicate: - if (lineset1, idx1) in couples or (lineset2, idx2) in couples: - couples.add((lineset1, idx1)) - couples.add((lineset2, idx2)) - break - else: - duplicate.append(set([(lineset1, idx1), (lineset2, idx2)])) - sims = [] - for num, ensembles in six.iteritems(no_duplicates): - for couples in ensembles: - sims.append((num, couples)) - sims.sort() - sims.reverse() - return sims - - def _display_sims(self, sims): - """display computed similarities on stdout""" - nb_lignes_dupliquees = 0 - for num, couples in sims: - print() - print(num, "similar lines in", len(couples), "files") - couples = sorted(couples) - for lineset, idx in couples: - print("==%s:%s" % (lineset.name, idx)) - # pylint: disable=W0631 - for line in lineset._real_lines[idx:idx+num]: - print(" ", line.rstrip()) - nb_lignes_dupliquees += num * (len(couples)-1) - nb_total_lignes = sum([len(lineset) for lineset in self.linesets]) - print("TOTAL lines=%s duplicates=%s percent=%.2f" \ - % (nb_total_lignes, nb_lignes_dupliquees, - nb_lignes_dupliquees*100. / nb_total_lignes)) - - def _find_common(self, lineset1, lineset2): - """find similarities in the two given linesets""" - lines1 = lineset1.enumerate_stripped - lines2 = lineset2.enumerate_stripped - find = lineset2.find - index1 = 0 - min_lines = self.min_lines - while index1 < len(lineset1): - skip = 1 - num = 0 - for index2 in find(lineset1[index1]): - non_blank = 0 - for num, ((_, line1), (_, line2)) in enumerate( - zip(lines1(index1), lines2(index2))): - if line1 != line2: - if non_blank > min_lines: - yield num, lineset1, index1, lineset2, index2 - skip = max(skip, num) - break - if line1: - non_blank += 1 - else: - # we may have reach the end - num += 1 - if non_blank > min_lines: - yield num, lineset1, index1, lineset2, index2 - skip = max(skip, num) - index1 += skip - - def _iter_sims(self): - """iterate on similarities among all files, by making a cartesian - product - """ - for idx, lineset in enumerate(self.linesets[:-1]): - for lineset2 in self.linesets[idx+1:]: - for sim in self._find_common(lineset, lineset2): - yield sim - -def stripped_lines(lines, ignore_comments, ignore_docstrings, ignore_imports): - """return lines with leading/trailing whitespace and any ignored code - features removed - """ - - strippedlines = [] - docstring = None - for line in lines: - line = line.strip() - if ignore_docstrings: - if not docstring and \ - (line.startswith('"""') or line.startswith("'''")): - docstring = line[:3] - line = line[3:] - if docstring: - if line.endswith(docstring): - docstring = None - line = '' - if ignore_imports: - if line.startswith("import ") or line.startswith("from "): - line = '' - if ignore_comments: - # XXX should use regex in checkers/format to avoid cutting - # at a "#" in a string - line = line.split('#', 1)[0].strip() - strippedlines.append(line) - return strippedlines - - -class LineSet(object): - """Holds and indexes all the lines of a single source file""" - def __init__(self, name, lines, ignore_comments=False, - ignore_docstrings=False, ignore_imports=False): - self.name = name - self._real_lines = lines - self._stripped_lines = stripped_lines(lines, ignore_comments, - ignore_docstrings, - ignore_imports) - self._index = self._mk_index() - - def __str__(self): - return '' % self.name - - def __len__(self): - return len(self._real_lines) - - def __getitem__(self, index): - return self._stripped_lines[index] - - def __lt__(self, other): - return self.name < other.name - - def __hash__(self): - return id(self) - - def enumerate_stripped(self, start_at=0): - """return an iterator on stripped lines, starting from a given index - if specified, else 0 - """ - idx = start_at - if start_at: - lines = self._stripped_lines[start_at:] - else: - lines = self._stripped_lines - for line in lines: - #if line: - yield idx, line - idx += 1 - - def find(self, stripped_line): - """return positions of the given stripped line in this set""" - return self._index.get(stripped_line, ()) - - def _mk_index(self): - """create the index for this set""" - index = defaultdict(list) - for line_no, line in enumerate(self._stripped_lines): - if line: - index[line].append(line_no) - return index - - -MSGS = {'R0801': ('Similar lines in %s files\n%s', - 'duplicate-code', - 'Indicates that a set of similar lines has been detected \ - among multiple file. This usually means that the code should \ - be refactored to avoid this duplication.')} - -def report_similarities(sect, stats, old_stats): - """make a layout with some stats about duplication""" - lines = ['', 'now', 'previous', 'difference'] - lines += table_lines_from_stats(stats, old_stats, - ('nb_duplicated_lines', - 'percent_duplicated_lines')) - sect.append(Table(children=lines, cols=4, rheaders=1, cheaders=1)) - - -# wrapper to get a pylint checker from the similar class -class SimilarChecker(BaseChecker, Similar): - """checks for similarities and duplicated code. This computation may be - memory / CPU intensive, so you should disable it if you experiment some - problems. - """ - - __implements__ = (IRawChecker,) - # configuration section name - name = 'similarities' - # messages - msgs = MSGS - # configuration options - # for available dict keys/values see the optik parser 'add_option' method - options = (('min-similarity-lines', - {'default' : 4, 'type' : "int", 'metavar' : '', - 'help' : 'Minimum lines number of a similarity.'}), - ('ignore-comments', - {'default' : True, 'type' : 'yn', 'metavar' : '', - 'help': 'Ignore comments when computing similarities.'} - ), - ('ignore-docstrings', - {'default' : True, 'type' : 'yn', 'metavar' : '', - 'help': 'Ignore docstrings when computing similarities.'} - ), - ('ignore-imports', - {'default' : False, 'type' : 'yn', 'metavar' : '', - 'help': 'Ignore imports when computing similarities.'} - ), - ) - # reports - reports = (('RP0801', 'Duplication', report_similarities),) - - def __init__(self, linter=None): - BaseChecker.__init__(self, linter) - Similar.__init__(self, min_lines=4, - ignore_comments=True, ignore_docstrings=True) - self.stats = None - - def set_option(self, optname, value, action=None, optdict=None): - """method called to set an option (registered in the options list) - - overridden to report options setting to Similar - """ - BaseChecker.set_option(self, optname, value, action, optdict) - if optname == 'min-similarity-lines': - self.min_lines = self.config.min_similarity_lines - elif optname == 'ignore-comments': - self.ignore_comments = self.config.ignore_comments - elif optname == 'ignore-docstrings': - self.ignore_docstrings = self.config.ignore_docstrings - elif optname == 'ignore-imports': - self.ignore_imports = self.config.ignore_imports - - def open(self): - """init the checkers: reset linesets and statistics information""" - self.linesets = [] - self.stats = self.linter.add_stats(nb_duplicated_lines=0, - percent_duplicated_lines=0) - - def process_module(self, node): - """process a module - - the module's content is accessible via the stream object - - stream must implement the readlines method - """ - with node.stream() as stream: - self.append_stream(self.linter.current_name, - stream, - node.file_encoding) - - def close(self): - """compute and display similarities on closing (i.e. end of parsing)""" - total = sum([len(lineset) for lineset in self.linesets]) - duplicated = 0 - stats = self.stats - for num, couples in self._compute_sims(): - msg = [] - for lineset, idx in couples: - msg.append("==%s:%s" % (lineset.name, idx)) - msg.sort() - # pylint: disable=W0631 - for line in lineset._real_lines[idx:idx+num]: - msg.append(line.rstrip()) - self.add_message('R0801', args=(len(couples), '\n'.join(msg))) - duplicated += num * (len(couples) - 1) - stats['nb_duplicated_lines'] = duplicated - stats['percent_duplicated_lines'] = total and duplicated * 100. / total - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(SimilarChecker(linter)) - -def usage(status=0): - """display command line usage information""" - print("finds copy pasted blocks in a set of files") - print() - print('Usage: symilar [-d|--duplicates min_duplicated_lines] \ -[-i|--ignore-comments] [--ignore-docstrings] [--ignore-imports] file1...') - sys.exit(status) - -def Run(argv=None): - """standalone command line access point""" - if argv is None: - argv = sys.argv[1:] - from getopt import getopt - s_opts = 'hdi' - l_opts = ('help', 'duplicates=', 'ignore-comments', 'ignore-imports', - 'ignore-docstrings') - min_lines = 4 - ignore_comments = False - ignore_docstrings = False - ignore_imports = False - opts, args = getopt(argv, s_opts, l_opts) - for opt, val in opts: - if opt in ('-d', '--duplicates'): - min_lines = int(val) - elif opt in ('-h', '--help'): - usage() - elif opt in ('-i', '--ignore-comments'): - ignore_comments = True - elif opt in ('--ignore-docstrings',): - ignore_docstrings = True - elif opt in ('--ignore-imports',): - ignore_imports = True - if not args: - usage(1) - sim = Similar(min_lines, ignore_comments, ignore_docstrings, ignore_imports) - for filename in args: - with open(filename) as stream: - sim.append_stream(filename, stream) - sim.run() - sys.exit(0) - -if __name__ == '__main__': - Run() diff --git a/checkers/spelling.py b/checkers/spelling.py deleted file mode 100644 index f6edd5d..0000000 --- a/checkers/spelling.py +++ /dev/null @@ -1,250 +0,0 @@ -# Copyright 2014 Michal Nowikowski. -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""Checker for spelling errors in comments and docstrings. -""" - -import sys -import tokenize -import string -import re - -if sys.version_info[0] >= 3: - maketrans = str.maketrans -else: - maketrans = string.maketrans - -from pylint.interfaces import ITokenChecker, IAstroidChecker -from pylint.checkers import BaseTokenChecker -from pylint.checkers.utils import check_messages - -try: - import enchant -except ImportError: - enchant = None - -if enchant is not None: - br = enchant.Broker() - dicts = br.list_dicts() - dict_choices = [''] + [d[0] for d in dicts] - dicts = ["%s (%s)" % (d[0], d[1].name) for d in dicts] - dicts = ", ".join(dicts) - instr = "" -else: - dicts = "none" - dict_choices = [''] - instr = " To make it working install python-enchant package." - -table = maketrans("", "") - -class SpellingChecker(BaseTokenChecker): - """Check spelling in comments and docstrings""" - __implements__ = (ITokenChecker, IAstroidChecker) - name = 'spelling' - msgs = { - 'C0401': ('Wrong spelling of a word \'%s\' in a comment:\n%s\n' - '%s\nDid you mean: \'%s\'?', - 'wrong-spelling-in-comment', - 'Used when a word in comment is not spelled correctly.'), - 'C0402': ('Wrong spelling of a word \'%s\' in a docstring:\n%s\n' - '%s\nDid you mean: \'%s\'?', - 'wrong-spelling-in-docstring', - 'Used when a word in docstring is not spelled correctly.'), - 'C0403': ('Invalid characters %r in a docstring', - 'invalid-characters-in-docstring', - 'Used when a word in docstring cannot be checked by enchant.'), - } - options = (('spelling-dict', - {'default' : '', 'type' : 'choice', 'metavar' : '', - 'choices': dict_choices, - 'help' : 'Spelling dictionary name. ' - 'Available dictionaries: %s.%s' % (dicts, instr)}), - ('spelling-ignore-words', - {'default' : '', - 'type' : 'string', - 'metavar' : '', - 'help' : 'List of comma separated words that ' - 'should not be checked.'}), - ('spelling-private-dict-file', - {'default' : '', - 'type' : 'string', - 'metavar' : '', - 'help' : 'A path to a file that contains private ' - 'dictionary; one word per line.'}), - ('spelling-store-unknown-words', - {'default' : 'n', 'type' : 'yn', 'metavar' : '', - 'help' : 'Tells whether to store unknown words to ' - 'indicated private dictionary in ' - '--spelling-private-dict-file option instead of ' - 'raising a message.'}), - ) - - def open(self): - self.initialized = False - self.private_dict_file = None - - if enchant is None: - return - dict_name = self.config.spelling_dict - if not dict_name: - return - - self.ignore_list = [w.strip() for w in self.config.spelling_ignore_words.split(",")] - # "param" appears in docstring in param description and - # "pylint" appears in comments in pylint pragmas. - self.ignore_list.extend(["param", "pylint"]) - - if self.config.spelling_private_dict_file: - self.spelling_dict = enchant.DictWithPWL( - dict_name, self.config.spelling_private_dict_file) - self.private_dict_file = open( - self.config.spelling_private_dict_file, "a") - else: - self.spelling_dict = enchant.Dict(dict_name) - - if self.config.spelling_store_unknown_words: - self.unknown_words = set() - - # Prepare regex for stripping punctuation signs from text. - # ' and _ are treated in a special way. - puncts = string.punctuation.replace("'", "").replace("_", "") - self.punctuation_regex = re.compile('[%s]' % re.escape(puncts)) - self.initialized = True - - def close(self): - if self.private_dict_file: - self.private_dict_file.close() - - def _check_spelling(self, msgid, line, line_num): - line2 = line.strip() - # Replace ['afadf with afadf (but preserve don't) - line2 = re.sub("'([^a-zA-Z]|$)", " ", line2) - # Replace afadf'] with afadf (but preserve don't) - line2 = re.sub("([^a-zA-Z]|^)'", " ", line2) - # Replace punctuation signs with space e.g. and/or -> and or - line2 = self.punctuation_regex.sub(' ', line2) - - words = [] - for word in line2.split(): - # Skip words with digits. - if len(re.findall(r"\d", word)) > 0: - continue - - # Skip words with mixed big and small letters, - # they are probaly class names. - if (len(re.findall("[A-Z]", word)) > 0 and - len(re.findall("[a-z]", word)) > 0 and - len(word) > 2): - continue - - # Skip words with _ - they are probably function parameter names. - if word.count('_') > 0: - continue - - words.append(word) - - # Go through words and check them. - for word in words: - # Skip words from ignore list. - if word in self.ignore_list: - continue - - orig_word = word - word = word.lower() - - # Strip starting u' from unicode literals and r' from raw strings. - if (word.startswith("u'") or - word.startswith('u"') or - word.startswith("r'") or - word.startswith('r"')) and len(word) > 2: - word = word[2:] - - # If it is a known word, then continue. - try: - if self.spelling_dict.check(word): - continue - except enchant.errors.Error: - # this can only happen in docstrings, not comments - self.add_message('invalid-characters-in-docstring', - line=line_num, args=(word,)) - continue - - # Store word to private dict or raise a message. - if self.config.spelling_store_unknown_words: - if word not in self.unknown_words: - self.private_dict_file.write("%s\n" % word) - self.unknown_words.add(word) - else: - # Present up to 4 suggestions. - # TODO: add support for customising this. - suggestions = self.spelling_dict.suggest(word)[:4] - - m = re.search(r"(\W|^)(%s)(\W|$)" % word, line.lower()) - if m: - # Start position of second group in regex. - col = m.regs[2][0] - else: - col = line.lower().index(word) - indicator = (" " * col) + ("^" * len(word)) - - self.add_message(msgid, line=line_num, - args=(orig_word, line, - indicator, - "' or '".join(suggestions))) - - def process_tokens(self, tokens): - if not self.initialized: - return - - # Process tokens and look for comments. - for (tok_type, token, (start_row, _), _, _) in tokens: - if tok_type == tokenize.COMMENT: - self._check_spelling('wrong-spelling-in-comment', - token, start_row) - - @check_messages('wrong-spelling-in-docstring') - def visit_module(self, node): - if not self.initialized: - return - self._check_docstring(node) - - @check_messages('wrong-spelling-in-docstring') - def visit_class(self, node): - if not self.initialized: - return - self._check_docstring(node) - - @check_messages('wrong-spelling-in-docstring') - def visit_function(self, node): - if not self.initialized: - return - self._check_docstring(node) - - def _check_docstring(self, node): - """check the node has any spelling errors""" - docstring = node.doc - if not docstring: - return - - start_line = node.lineno + 1 - - # Go through lines of docstring - for idx, line in enumerate(docstring.splitlines()): - self._check_spelling('wrong-spelling-in-docstring', - line, start_line + idx) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(SpellingChecker(linter)) diff --git a/checkers/stdlib.py b/checkers/stdlib.py deleted file mode 100644 index b6b8026..0000000 --- a/checkers/stdlib.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright 2012 Google Inc. -# -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""Checkers for various standard library functions.""" - -import re -import six -import sys - -import astroid -from astroid.bases import Instance - -from pylint.interfaces import IAstroidChecker -from pylint.checkers import BaseChecker -from pylint.checkers import utils - - -if sys.version_info >= (3, 0): - OPEN_MODULE = '_io' -else: - OPEN_MODULE = '__builtin__' - - -def _check_mode_str(mode): - # check type - if not isinstance(mode, six.string_types): - return False - # check syntax - modes = set(mode) - _mode = "rwatb+U" - creating = False - if six.PY3: - _mode += "x" - creating = "x" in modes - if modes - set(_mode) or len(mode) > len(modes): - return False - # check logic - reading = "r" in modes - writing = "w" in modes - appending = "a" in modes - updating = "+" in modes - text = "t" in modes - binary = "b" in modes - if "U" in modes: - if writing or appending or creating and six.PY3: - return False - reading = True - if not six.PY3: - binary = True - if text and binary: - return False - total = reading + writing + appending + (creating if six.PY3 else 0) - if total > 1: - return False - if not (reading or writing or appending or creating and six.PY3): - return False - # other 2.x constraints - if not six.PY3: - if "U" in mode: - mode = mode.replace("U", "") - if "r" not in mode: - mode = "r" + mode - return mode[0] in ("r", "w", "a", "U") - return True - - -class StdlibChecker(BaseChecker): - __implements__ = (IAstroidChecker,) - name = 'stdlib' - - msgs = { - 'W1501': ('"%s" is not a valid mode for open.', - 'bad-open-mode', - 'Python supports: r, w, a[, x] modes with b, +, ' - 'and U (only with r) options. ' - 'See http://docs.python.org/2/library/functions.html#open'), - 'W1502': ('Using datetime.time in a boolean context.', - 'boolean-datetime', - 'Using datetetime.time in a boolean context can hide ' - 'subtle bugs when the time they represent matches ' - 'midnight UTC. This behaviour was fixed in Python 3.5. ' - 'See http://bugs.python.org/issue13936 for reference.', - {'maxversion': (3, 5)}), - 'W1503': ('Redundant use of %s with constant ' - 'value %r', - 'redundant-unittest-assert', - 'The first argument of assertTrue and assertFalse is' - 'a condition. If a constant is passed as parameter, that' - 'condition will be always true. In this case a warning ' - 'should be emitted.') - } - - @utils.check_messages('bad-open-mode', 'redundant-unittest-assert') - def visit_callfunc(self, node): - """Visit a CallFunc node.""" - if hasattr(node, 'func'): - infer = utils.safe_infer(node.func) - if infer: - if infer.root().name == OPEN_MODULE: - if getattr(node.func, 'name', None) in ('open', 'file'): - self._check_open_mode(node) - if infer.root().name == 'unittest.case': - self._check_redundant_assert(node, infer) - - @utils.check_messages('boolean-datetime') - def visit_unaryop(self, node): - if node.op == 'not': - self._check_datetime(node.operand) - - @utils.check_messages('boolean-datetime') - def visit_if(self, node): - self._check_datetime(node.test) - - @utils.check_messages('boolean-datetime') - def visit_ifexp(self, node): - self._check_datetime(node.test) - - @utils.check_messages('boolean-datetime') - def visit_boolop(self, node): - for value in node.values: - self._check_datetime(value) - - def _check_redundant_assert(self, node, infer): - if (isinstance(infer, astroid.BoundMethod) and - node.args and isinstance(node.args[0], astroid.Const) and - infer.name in ['assertTrue', 'assertFalse']): - self.add_message('redundant-unittest-assert', - args=(infer.name, node.args[0].value, ), - node=node) - - def _check_datetime(self, node): - """ Check that a datetime was infered. - If so, emit boolean-datetime warning. - """ - try: - infered = next(node.infer()) - except astroid.InferenceError: - return - if (isinstance(infered, Instance) and - infered.qname() == 'datetime.time'): - self.add_message('boolean-datetime', node=node) - - - def _check_open_mode(self, node): - """Check that the mode argument of an open or file call is valid.""" - try: - mode_arg = utils.get_argument_from_call(node, position=1, - keyword='mode') - except utils.NoSuchArgumentError: - return - if mode_arg: - mode_arg = utils.safe_infer(mode_arg) - if (isinstance(mode_arg, astroid.Const) - and not _check_mode_str(mode_arg.value)): - self.add_message('bad-open-mode', node=node, - args=mode_arg.value) - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(StdlibChecker(linter)) diff --git a/checkers/strings.py b/checkers/strings.py deleted file mode 100644 index 8892c2c..0000000 --- a/checkers/strings.py +++ /dev/null @@ -1,615 +0,0 @@ -# Copyright (c) 2009-2010 Arista Networks, Inc. - James Lingard -# Copyright (c) 2004-2013 LOGILAB S.A. (Paris, FRANCE). -# Copyright 2012 Google Inc. -# -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""Checker for string formatting operations. -""" - -import sys -import tokenize -import string -import numbers - -import astroid - -from pylint.interfaces import ITokenChecker, IAstroidChecker, IRawChecker -from pylint.checkers import BaseChecker, BaseTokenChecker -from pylint.checkers import utils -from pylint.checkers.utils import check_messages - -import six - - -_PY3K = sys.version_info[:2] >= (3, 0) -_PY27 = sys.version_info[:2] == (2, 7) - -MSGS = { - 'E1300': ("Unsupported format character %r (%#02x) at index %d", - "bad-format-character", - "Used when a unsupported format character is used in a format\ - string."), - 'E1301': ("Format string ends in middle of conversion specifier", - "truncated-format-string", - "Used when a format string terminates before the end of a \ - conversion specifier."), - 'E1302': ("Mixing named and unnamed conversion specifiers in format string", - "mixed-format-string", - "Used when a format string contains both named (e.g. '%(foo)d') \ - and unnamed (e.g. '%d') conversion specifiers. This is also \ - used when a named conversion specifier contains * for the \ - minimum field width and/or precision."), - 'E1303': ("Expected mapping for format string, not %s", - "format-needs-mapping", - "Used when a format string that uses named conversion specifiers \ - is used with an argument that is not a mapping."), - 'W1300': ("Format string dictionary key should be a string, not %s", - "bad-format-string-key", - "Used when a format string that uses named conversion specifiers \ - is used with a dictionary whose keys are not all strings."), - 'W1301': ("Unused key %r in format string dictionary", - "unused-format-string-key", - "Used when a format string that uses named conversion specifiers \ - is used with a dictionary that conWtains keys not required by the \ - format string."), - 'E1304': ("Missing key %r in format string dictionary", - "missing-format-string-key", - "Used when a format string that uses named conversion specifiers \ - is used with a dictionary that doesn't contain all the keys \ - required by the format string."), - 'E1305': ("Too many arguments for format string", - "too-many-format-args", - "Used when a format string that uses unnamed conversion \ - specifiers is given too many arguments."), - 'E1306': ("Not enough arguments for format string", - "too-few-format-args", - "Used when a format string that uses unnamed conversion \ - specifiers is given too few arguments"), - - 'W1302': ("Invalid format string", - "bad-format-string", - "Used when a PEP 3101 format string is invalid.", - {'minversion': (2, 7)}), - 'W1303': ("Missing keyword argument %r for format string", - "missing-format-argument-key", - "Used when a PEP 3101 format string that uses named fields " - "doesn't receive one or more required keywords.", - {'minversion': (2, 7)}), - 'W1304': ("Unused format argument %r", - "unused-format-string-argument", - "Used when a PEP 3101 format string that uses named " - "fields is used with an argument that " - "is not required by the format string.", - {'minversion': (2, 7)}), - 'W1305': ("Format string contains both automatic field numbering " - "and manual field specification", - "format-combined-specification", - "Usen when a PEP 3101 format string contains both automatic " - "field numbering (e.g. '{}') and manual field " - "specification (e.g. '{0}').", - {'minversion': (2, 7)}), - 'W1306': ("Missing format attribute %r in format specifier %r", - "missing-format-attribute", - "Used when a PEP 3101 format string uses an " - "attribute specifier ({0.length}), but the argument " - "passed for formatting doesn't have that attribute.", - {'minversion': (2, 7)}), - 'W1307': ("Using invalid lookup key %r in format specifier %r", - "invalid-format-index", - "Used when a PEP 3101 format string uses a lookup specifier " - "({a[1]}), but the argument passed for formatting " - "doesn't contain or doesn't have that key as an attribute.", - {'minversion': (2, 7)}) - } - -OTHER_NODES = (astroid.Const, astroid.List, astroid.Backquote, - astroid.Lambda, astroid.Function, - astroid.ListComp, astroid.SetComp, astroid.GenExpr) - -if _PY3K: - import _string - - def split_format_field_names(format_string): - return _string.formatter_field_name_split(format_string) -else: - def _field_iterator_convertor(iterator): - for is_attr, key in iterator: - if isinstance(key, numbers.Number): - yield is_attr, int(key) - else: - yield is_attr, key - - def split_format_field_names(format_string): - keyname, fielditerator = format_string._formatter_field_name_split() - # it will return longs, instead of ints, which will complicate - # the output - return keyname, _field_iterator_convertor(fielditerator) - - -def collect_string_fields(format_string): - """ Given a format string, return an iterator - of all the valid format fields. It handles nested fields - as well. - """ - - formatter = string.Formatter() - try: - parseiterator = formatter.parse(format_string) - for result in parseiterator: - if all(item is None for item in result[1:]): - # not a replacement format - continue - name = result[1] - nested = result[2] - yield name - if nested: - for field in collect_string_fields(nested): - yield field - except ValueError: - # probably the format string is invalid - # should we check the argument of the ValueError? - raise utils.IncompleteFormatString(format_string) - -def parse_format_method_string(format_string): - """ - Parses a PEP 3101 format string, returning a tuple of - (keys, num_args, manual_pos_arg), - where keys is the set of mapping keys in the format string, num_args - is the number of arguments required by the format string and - manual_pos_arg is the number of arguments passed with the position. - """ - keys = [] - num_args = 0 - manual_pos_arg = set() - for name in collect_string_fields(format_string): - if name and str(name).isdigit(): - manual_pos_arg.add(str(name)) - elif name: - keyname, fielditerator = split_format_field_names(name) - if isinstance(keyname, numbers.Number): - # In Python 2 it will return long which will lead - # to different output between 2 and 3 - manual_pos_arg.add(str(keyname)) - keyname = int(keyname) - keys.append((keyname, list(fielditerator))) - else: - num_args += 1 - return keys, num_args, len(manual_pos_arg) - -def get_args(callfunc): - """ Get the arguments from the given `CallFunc` node. - Return a tuple, where the first element is the - number of positional arguments and the second element - is the keyword arguments in a dict. - """ - positional = 0 - named = {} - - for arg in callfunc.args: - if isinstance(arg, astroid.Keyword): - named[arg.arg] = utils.safe_infer(arg.value) - else: - positional += 1 - return positional, named - -def get_access_path(key, parts): - """ Given a list of format specifiers, returns - the final access path (e.g. a.b.c[0][1]). - """ - path = [] - for is_attribute, specifier in parts: - if is_attribute: - path.append(".{}".format(specifier)) - else: - path.append("[{!r}]".format(specifier)) - return str(key) + "".join(path) - - -class StringFormatChecker(BaseChecker): - """Checks string formatting operations to ensure that the format string - is valid and the arguments match the format string. - """ - - __implements__ = (IAstroidChecker,) - name = 'string' - msgs = MSGS - - @check_messages(*(MSGS.keys())) - def visit_binop(self, node): - if node.op != '%': - return - left = node.left - args = node.right - - if not (isinstance(left, astroid.Const) - and isinstance(left.value, six.string_types)): - return - format_string = left.value - try: - required_keys, required_num_args = \ - utils.parse_format_string(format_string) - except utils.UnsupportedFormatCharacter as e: - c = format_string[e.index] - self.add_message('bad-format-character', - node=node, args=(c, ord(c), e.index)) - return - except utils.IncompleteFormatString: - self.add_message('truncated-format-string', node=node) - return - if required_keys and required_num_args: - # The format string uses both named and unnamed format - # specifiers. - self.add_message('mixed-format-string', node=node) - elif required_keys: - # The format string uses only named format specifiers. - # Check that the RHS of the % operator is a mapping object - # that contains precisely the set of keys required by the - # format string. - if isinstance(args, astroid.Dict): - keys = set() - unknown_keys = False - for k, _ in args.items: - if isinstance(k, astroid.Const): - key = k.value - if isinstance(key, six.string_types): - keys.add(key) - else: - self.add_message('bad-format-string-key', - node=node, args=key) - else: - # One of the keys was something other than a - # constant. Since we can't tell what it is, - # supress checks for missing keys in the - # dictionary. - unknown_keys = True - if not unknown_keys: - for key in required_keys: - if key not in keys: - self.add_message('missing-format-string-key', - node=node, args=key) - for key in keys: - if key not in required_keys: - self.add_message('unused-format-string-key', - node=node, args=key) - elif isinstance(args, OTHER_NODES + (astroid.Tuple,)): - type_name = type(args).__name__ - self.add_message('format-needs-mapping', - node=node, args=type_name) - # else: - # The RHS of the format specifier is a name or - # expression. It may be a mapping object, so - # there's nothing we can check. - else: - # The format string uses only unnamed format specifiers. - # Check that the number of arguments passed to the RHS of - # the % operator matches the number required by the format - # string. - if isinstance(args, astroid.Tuple): - num_args = len(args.elts) - elif isinstance(args, OTHER_NODES + (astroid.Dict, astroid.DictComp)): - num_args = 1 - else: - # The RHS of the format specifier is a name or - # expression. It could be a tuple of unknown size, so - # there's nothing we can check. - num_args = None - if num_args is not None: - if num_args > required_num_args: - self.add_message('too-many-format-args', node=node) - elif num_args < required_num_args: - self.add_message('too-few-format-args', node=node) - - -class StringMethodsChecker(BaseChecker): - __implements__ = (IAstroidChecker,) - name = 'string' - msgs = { - 'E1310': ("Suspicious argument in %s.%s call", - "bad-str-strip-call", - "The argument to a str.{l,r,}strip call contains a" - " duplicate character, "), - } - - @check_messages(*(MSGS.keys())) - def visit_callfunc(self, node): - func = utils.safe_infer(node.func) - if (isinstance(func, astroid.BoundMethod) - and isinstance(func.bound, astroid.Instance) - and func.bound.name in ('str', 'unicode', 'bytes')): - if func.name in ('strip', 'lstrip', 'rstrip') and node.args: - arg = utils.safe_infer(node.args[0]) - if not isinstance(arg, astroid.Const): - return - if len(arg.value) != len(set(arg.value)): - self.add_message('bad-str-strip-call', node=node, - args=(func.bound.name, func.name)) - elif func.name == 'format': - if _PY27 or _PY3K: - self._check_new_format(node, func) - - def _check_new_format(self, node, func): - """ Check the new string formatting. """ - # TODO: skip (for now) format nodes which don't have - # an explicit string on the left side of the format operation. - # We do this because our inference engine can't properly handle - # redefinitions of the original string. - # For more details, see issue 287. - # - # Note that there may not be any left side at all, if the format method - # has been assigned to another variable. See issue 351. For example: - # - # fmt = 'some string {}'.format - # fmt('arg') - if (isinstance(node.func, astroid.Getattr) - and not isinstance(node.func.expr, astroid.Const)): - return - try: - strnode = next(func.bound.infer()) - except astroid.InferenceError: - return - if not isinstance(strnode, astroid.Const): - return - if node.starargs or node.kwargs: - # TODO: Don't complicate the logic, skip these for now. - return - try: - positional, named = get_args(node) - except astroid.InferenceError: - return - try: - fields, num_args, manual_pos = parse_format_method_string(strnode.value) - except utils.IncompleteFormatString: - self.add_message('bad-format-string', node=node) - return - - named_fields = set(field[0] for field in fields - if isinstance(field[0], six.string_types)) - if num_args and manual_pos: - self.add_message('format-combined-specification', - node=node) - return - - check_args = False - # Consider "{[0]} {[1]}" as num_args. - num_args += sum(1 for field in named_fields - if field == '') - if named_fields: - for field in named_fields: - if field not in named and field: - self.add_message('missing-format-argument-key', - node=node, - args=(field, )) - for field in named: - if field not in named_fields: - self.add_message('unused-format-string-argument', - node=node, - args=(field, )) - # num_args can be 0 if manual_pos is not. - num_args = num_args or manual_pos - if positional or num_args: - empty = any(True for field in named_fields - if field == '') - if named or empty: - # Verify the required number of positional arguments - # only if the .format got at least one keyword argument. - # This means that the format strings accepts both - # positional and named fields and we should warn - # when one of the them is missing or is extra. - check_args = True - else: - check_args = True - if check_args: - # num_args can be 0 if manual_pos is not. - num_args = num_args or manual_pos - if positional > num_args: - self.add_message('too-many-format-args', node=node) - elif positional < num_args: - self.add_message('too-few-format-args', node=node) - - self._check_new_format_specifiers(node, fields, named) - - def _check_new_format_specifiers(self, node, fields, named): - """ - Check attribute and index access in the format - string ("{0.a}" and "{0[a]}"). - """ - for key, specifiers in fields: - # Obtain the argument. If it can't be obtained - # or infered, skip this check. - if key == '': - # {[0]} will have an unnamed argument, defaulting - # to 0. It will not be present in `named`, so use the value - # 0 for it. - key = 0 - if isinstance(key, numbers.Number): - try: - argname = utils.get_argument_from_call(node, key) - except utils.NoSuchArgumentError: - continue - else: - if key not in named: - continue - argname = named[key] - if argname in (astroid.YES, None): - continue - try: - argument = next(argname.infer()) - except astroid.InferenceError: - continue - if not specifiers or argument is astroid.YES: - # No need to check this key if it doesn't - # use attribute / item access - continue - if argument.parent and isinstance(argument.parent, astroid.Arguments): - # Ignore any object coming from an argument, - # because we can't infer its value properly. - continue - previous = argument - parsed = [] - for is_attribute, specifier in specifiers: - if previous is astroid.YES: - break - parsed.append((is_attribute, specifier)) - if is_attribute: - try: - previous = previous.getattr(specifier)[0] - except astroid.NotFoundError: - if (hasattr(previous, 'has_dynamic_getattr') and - previous.has_dynamic_getattr()): - # Don't warn if the object has a custom __getattr__ - break - path = get_access_path(key, parsed) - self.add_message('missing-format-attribute', - args=(specifier, path), - node=node) - break - else: - warn_error = False - if hasattr(previous, 'getitem'): - try: - previous = previous.getitem(specifier) - except (IndexError, TypeError): - warn_error = True - else: - try: - # Lookup __getitem__ in the current node, - # but skip further checks, because we can't - # retrieve the looked object - previous.getattr('__getitem__') - break - except astroid.NotFoundError: - warn_error = True - if warn_error: - path = get_access_path(key, parsed) - self.add_message('invalid-format-index', - args=(specifier, path), - node=node) - break - - try: - previous = next(previous.infer()) - except astroid.InferenceError: - # can't check further if we can't infer it - break - - - -class StringConstantChecker(BaseTokenChecker): - """Check string literals""" - __implements__ = (ITokenChecker, IRawChecker) - name = 'string_constant' - msgs = { - 'W1401': ('Anomalous backslash in string: \'%s\'. ' - 'String constant might be missing an r prefix.', - 'anomalous-backslash-in-string', - 'Used when a backslash is in a literal string but not as an ' - 'escape.'), - 'W1402': ('Anomalous Unicode escape in byte string: \'%s\'. ' - 'String constant might be missing an r or u prefix.', - 'anomalous-unicode-escape-in-string', - 'Used when an escape like \\u is encountered in a byte ' - 'string where it has no effect.'), - } - - # Characters that have a special meaning after a backslash in either - # Unicode or byte strings. - ESCAPE_CHARACTERS = 'abfnrtvx\n\r\t\\\'\"01234567' - - # TODO(mbp): Octal characters are quite an edge case today; people may - # prefer a separate warning where they occur. \0 should be allowed. - - # Characters that have a special meaning after a backslash but only in - # Unicode strings. - UNICODE_ESCAPE_CHARACTERS = 'uUN' - - def process_module(self, module): - self._unicode_literals = 'unicode_literals' in module.future_imports - - def process_tokens(self, tokens): - for (tok_type, token, (start_row, _), _, _) in tokens: - if tok_type == tokenize.STRING: - # 'token' is the whole un-parsed token; we can look at the start - # of it to see whether it's a raw or unicode string etc. - self.process_string_token(token, start_row) - - def process_string_token(self, token, start_row): - for i, c in enumerate(token): - if c in '\'\"': - quote_char = c - break - # pylint: disable=undefined-loop-variable - prefix = token[:i].lower() # markers like u, b, r. - after_prefix = token[i:] - if after_prefix[:3] == after_prefix[-3:] == 3 * quote_char: - string_body = after_prefix[3:-3] - else: - string_body = after_prefix[1:-1] # Chop off quotes - # No special checks on raw strings at the moment. - if 'r' not in prefix: - self.process_non_raw_string_token(prefix, string_body, start_row) - - def process_non_raw_string_token(self, prefix, string_body, start_row): - """check for bad escapes in a non-raw string. - - prefix: lowercase string of eg 'ur' string prefix markers. - string_body: the un-parsed body of the string, not including the quote - marks. - start_row: integer line number in the source. - """ - # Walk through the string; if we see a backslash then escape the next - # character, and skip over it. If we see a non-escaped character, - # alert, and continue. - # - # Accept a backslash when it escapes a backslash, or a quote, or - # end-of-line, or one of the letters that introduce a special escape - # sequence - # - # TODO(mbp): Maybe give a separate warning about the rarely-used - # \a \b \v \f? - # - # TODO(mbp): We could give the column of the problem character, but - # add_message doesn't seem to have a way to pass it through at present. - i = 0 - while True: - i = string_body.find('\\', i) - if i == -1: - break - # There must be a next character; having a backslash at the end - # of the string would be a SyntaxError. - next_char = string_body[i+1] - match = string_body[i:i+2] - if next_char in self.UNICODE_ESCAPE_CHARACTERS: - if 'u' in prefix: - pass - elif (_PY3K or self._unicode_literals) and 'b' not in prefix: - pass # unicode by default - else: - self.add_message('anomalous-unicode-escape-in-string', - line=start_row, args=(match, )) - elif next_char not in self.ESCAPE_CHARACTERS: - self.add_message('anomalous-backslash-in-string', - line=start_row, args=(match, )) - # Whether it was a valid escape or not, backslash followed by - # another character can always be consumed whole: the second - # character can never be the start of a new backslash escape. - i += 2 - - - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(StringFormatChecker(linter)) - linter.register_checker(StringMethodsChecker(linter)) - linter.register_checker(StringConstantChecker(linter)) diff --git a/checkers/typecheck.py b/checkers/typecheck.py deleted file mode 100644 index 9f074ae..0000000 --- a/checkers/typecheck.py +++ /dev/null @@ -1,627 +0,0 @@ -# Copyright (c) 2006-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""try to find more bugs in the code using astroid inference capabilities -""" - -import re -import shlex - -import astroid -from astroid import InferenceError, NotFoundError, YES, Instance -from astroid.bases import BUILTINS - -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE -from pylint.checkers import BaseChecker -from pylint.checkers.utils import ( - safe_infer, is_super, - check_messages, decorated_with_property) - -MSGS = { - 'E1101': ('%s %r has no %r member', - 'no-member', - 'Used when a variable is accessed for an unexistent member.', - {'old_names': [('E1103', 'maybe-no-member')]}), - 'E1102': ('%s is not callable', - 'not-callable', - 'Used when an object being called has been inferred to a non \ - callable object'), - 'E1111': ('Assigning to function call which doesn\'t return', - 'assignment-from-no-return', - 'Used when an assignment is done on a function call but the \ - inferred function doesn\'t return anything.'), - 'W1111': ('Assigning to function call which only returns None', - 'assignment-from-none', - 'Used when an assignment is done on a function call but the \ - inferred function returns nothing but None.'), - - 'E1120': ('No value for argument %s in %s call', - 'no-value-for-parameter', - 'Used when a function call passes too few arguments.'), - 'E1121': ('Too many positional arguments for %s call', - 'too-many-function-args', - 'Used when a function call passes too many positional \ - arguments.'), - 'E1123': ('Unexpected keyword argument %r in %s call', - 'unexpected-keyword-arg', - 'Used when a function call passes a keyword argument that \ - doesn\'t correspond to one of the function\'s parameter names.'), - 'E1124': ('Argument %r passed by position and keyword in %s call', - 'redundant-keyword-arg', - 'Used when a function call would result in assigning multiple \ - values to a function parameter, one value from a positional \ - argument and one from a keyword argument.'), - 'E1125': ('Missing mandatory keyword argument %r in %s call', - 'missing-kwoa', - ('Used when a function call does not pass a mandatory' - ' keyword-only argument.'), - {'minversion': (3, 0)}), - 'E1126': ('Sequence index is not an int, slice, or instance with __index__', - 'invalid-sequence-index', - 'Used when a sequence type is indexed with an invalid type. ' - 'Valid types are ints, slices, and objects with an __index__ ' - 'method.'), - 'E1127': ('Slice index is not an int, None, or instance with __index__', - 'invalid-slice-index', - 'Used when a slice index is not an integer, None, or an object \ - with an __index__ method.'), - } - -# builtin sequence types in Python 2 and 3. -SEQUENCE_TYPES = set(['str', 'unicode', 'list', 'tuple', 'bytearray', - 'xrange', 'range', 'bytes', 'memoryview']) - -def _determine_callable(callable_obj): - # Ordering is important, since BoundMethod is a subclass of UnboundMethod, - # and Function inherits Lambda. - if isinstance(callable_obj, astroid.BoundMethod): - # Bound methods have an extra implicit 'self' argument. - return callable_obj, 1, callable_obj.type - elif isinstance(callable_obj, astroid.UnboundMethod): - return callable_obj, 0, 'unbound method' - elif isinstance(callable_obj, astroid.Function): - return callable_obj, 0, callable_obj.type - elif isinstance(callable_obj, astroid.Lambda): - return callable_obj, 0, 'lambda' - elif isinstance(callable_obj, astroid.Class): - # Class instantiation, lookup __new__ instead. - # If we only find object.__new__, we can safely check __init__ - # instead. - try: - # Use the last definition of __new__. - new = callable_obj.local_attr('__new__')[-1] - except astroid.NotFoundError: - new = None - - if not new or new.parent.scope().name == 'object': - try: - # Use the last definition of __init__. - callable_obj = callable_obj.local_attr('__init__')[-1] - except astroid.NotFoundError: - # do nothing, covered by no-init. - raise ValueError - else: - callable_obj = new - - if not isinstance(callable_obj, astroid.Function): - raise ValueError - # both have an extra implicit 'cls'/'self' argument. - return callable_obj, 1, 'constructor' - else: - raise ValueError - -class TypeChecker(BaseChecker): - """try to find bugs in the code using type inference - """ - - __implements__ = (IAstroidChecker,) - - # configuration section name - name = 'typecheck' - # messages - msgs = MSGS - priority = -1 - # configuration options - options = (('ignore-mixin-members', - {'default' : True, 'type' : 'yn', 'metavar': '', - 'help' : 'Tells whether missing members accessed in mixin \ -class should be ignored. A mixin class is detected if its name ends with \ -"mixin" (case insensitive).'} - ), - ('ignored-modules', - {'default': (), - 'type': 'csv', - 'metavar': '', - 'help': 'List of module names for which member attributes \ -should not be checked (useful for modules/projects where namespaces are \ -manipulated during runtime and thus existing member attributes cannot be \ -deduced by static analysis'}, - ), - ('ignored-classes', - {'default' : ('SQLObject',), - 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of classes names for which member attributes \ -should not be checked (useful for classes with attributes dynamically set).'} - ), - - ('zope', - {'default' : False, 'type' : 'yn', 'metavar': '', - 'help' : 'When zope mode is activated, add a predefined set \ -of Zope acquired attributes to generated-members.'} - ), - ('generated-members', - {'default' : ('REQUEST', 'acl_users', 'aq_parent'), - 'type' : 'string', - 'metavar' : '', - 'help' : 'List of members which are set dynamically and \ -missed by pylint inference system, and so shouldn\'t trigger E0201 when \ -accessed. Python regular expressions are accepted.'} - ), - ) - - def open(self): - # do this in open since config not fully initialized in __init__ - self.generated_members = list(self.config.generated_members) - if self.config.zope: - self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent')) - - def visit_assattr(self, node): - if isinstance(node.ass_type(), astroid.AugAssign): - self.visit_getattr(node) - - def visit_delattr(self, node): - self.visit_getattr(node) - - @check_messages('no-member') - def visit_getattr(self, node): - """check that the accessed attribute exists - - to avoid to much false positives for now, we'll consider the code as - correct if a single of the inferred nodes has the accessed attribute. - - function/method, super call and metaclasses are ignored - """ - # generated_members may containt regular expressions - # (surrounded by quote `"` and followed by a comma `,`) - # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' => - # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}') - if isinstance(self.config.generated_members, str): - gen = shlex.shlex(self.config.generated_members) - gen.whitespace += ',' - gen.wordchars += '[]-+' - self.config.generated_members = tuple(tok.strip('"') for tok in gen) - for pattern in self.config.generated_members: - # attribute is marked as generated, stop here - if re.match(pattern, node.attrname): - return - try: - infered = list(node.expr.infer()) - except InferenceError: - return - # list of (node, nodename) which are missing the attribute - missingattr = set() - ignoremim = self.config.ignore_mixin_members - inference_failure = False - for owner in infered: - # skip yes object - if owner is YES: - inference_failure = True - continue - # skip None anyway - if isinstance(owner, astroid.Const) and owner.value is None: - continue - # XXX "super" / metaclass call - if is_super(owner) or getattr(owner, 'type', None) == 'metaclass': - continue - name = getattr(owner, 'name', 'None') - if name in self.config.ignored_classes: - continue - if ignoremim and name[-5:].lower() == 'mixin': - continue - try: - if not [n for n in owner.getattr(node.attrname) - if not isinstance(n.statement(), astroid.AugAssign)]: - missingattr.add((owner, name)) - continue - except AttributeError: - # XXX method / function - continue - except NotFoundError: - if isinstance(owner, astroid.Function) and owner.decorators: - continue - if isinstance(owner, Instance) and owner.has_dynamic_getattr(): - continue - # explicit skipping of module member access - if owner.root().name in self.config.ignored_modules: - continue - if isinstance(owner, astroid.Class): - # Look up in the metaclass only if the owner is itself - # a class. - # TODO: getattr doesn't return by default members - # from the metaclass, because handling various cases - # of methods accessible from the metaclass itself - # and/or subclasses only is too complicated for little to - # no benefit. - metaclass = owner.metaclass() - try: - if metaclass and metaclass.getattr(node.attrname): - continue - except NotFoundError: - pass - missingattr.add((owner, name)) - continue - # stop on the first found - break - else: - # we have not found any node with the attributes, display the - # message for infered nodes - done = set() - for owner, name in missingattr: - if isinstance(owner, Instance): - actual = owner._proxied - else: - actual = owner - if actual in done: - continue - done.add(actual) - confidence = INFERENCE if not inference_failure else INFERENCE_FAILURE - self.add_message('no-member', node=node, - args=(owner.display_type(), name, - node.attrname), - confidence=confidence) - - @check_messages('assignment-from-no-return', 'assignment-from-none') - def visit_assign(self, node): - """check that if assigning to a function call, the function is - possibly returning something valuable - """ - if not isinstance(node.value, astroid.CallFunc): - return - function_node = safe_infer(node.value.func) - # skip class, generator and incomplete function definition - if not (isinstance(function_node, astroid.Function) and - function_node.root().fully_defined()): - return - if function_node.is_generator() \ - or function_node.is_abstract(pass_is_abstract=False): - return - returns = list(function_node.nodes_of_class(astroid.Return, - skip_klass=astroid.Function)) - if len(returns) == 0: - self.add_message('assignment-from-no-return', node=node) - else: - for rnode in returns: - if not (isinstance(rnode.value, astroid.Const) - and rnode.value.value is None - or rnode.value is None): - break - else: - self.add_message('assignment-from-none', node=node) - - def _check_uninferable_callfunc(self, node): - """ - Check that the given uninferable CallFunc node does not - call an actual function. - """ - if not isinstance(node.func, astroid.Getattr): - return - - # Look for properties. First, obtain - # the lhs of the Getattr node and search the attribute - # there. If that attribute is a property or a subclass of properties, - # then most likely it's not callable. - - # TODO: since astroid doesn't understand descriptors very well - # we will not handle them here, right now. - - expr = node.func.expr - klass = safe_infer(expr) - if (klass is None or klass is astroid.YES or - not isinstance(klass, astroid.Instance)): - return - - try: - attrs = klass._proxied.getattr(node.func.attrname) - except astroid.NotFoundError: - return - - for attr in attrs: - if attr is astroid.YES: - continue - if not isinstance(attr, astroid.Function): - continue - - # Decorated, see if it is decorated with a property. - # Also, check the returns and see if they are callable. - if decorated_with_property(attr): - if all(return_node.callable() - for return_node in attr.infer_call_result(node)): - continue - else: - self.add_message('not-callable', node=node, - args=node.func.as_string()) - break - - @check_messages(*(list(MSGS.keys()))) - def visit_callfunc(self, node): - """check that called functions/methods are inferred to callable objects, - and that the arguments passed to the function match the parameters in - the inferred function's definition - """ - # Build the set of keyword arguments, checking for duplicate keywords, - # and count the positional arguments. - keyword_args = set() - num_positional_args = 0 - for arg in node.args: - if isinstance(arg, astroid.Keyword): - keyword_args.add(arg.arg) - else: - num_positional_args += 1 - - called = safe_infer(node.func) - # only function, generator and object defining __call__ are allowed - if called is not None and not called.callable(): - self.add_message('not-callable', node=node, - args=node.func.as_string()) - - self._check_uninferable_callfunc(node) - - try: - called, implicit_args, callable_name = _determine_callable(called) - except ValueError: - # Any error occurred during determining the function type, most of - # those errors are handled by different warnings. - return - num_positional_args += implicit_args - if called.args.args is None: - # Built-in functions have no argument information. - return - - if len(called.argnames()) != len(set(called.argnames())): - # Duplicate parameter name (see E9801). We can't really make sense - # of the function call in this case, so just return. - return - - # Analyze the list of formal parameters. - num_mandatory_parameters = len(called.args.args) - len(called.args.defaults) - parameters = [] - parameter_name_to_index = {} - for i, arg in enumerate(called.args.args): - if isinstance(arg, astroid.Tuple): - name = None - # Don't store any parameter names within the tuple, since those - # are not assignable from keyword arguments. - else: - if isinstance(arg, astroid.Keyword): - name = arg.arg - else: - assert isinstance(arg, astroid.AssName) - # This occurs with: - # def f( (a), (b) ): pass - name = arg.name - parameter_name_to_index[name] = i - if i >= num_mandatory_parameters: - defval = called.args.defaults[i - num_mandatory_parameters] - else: - defval = None - parameters.append([(name, defval), False]) - - kwparams = {} - for i, arg in enumerate(called.args.kwonlyargs): - if isinstance(arg, astroid.Keyword): - name = arg.arg - else: - assert isinstance(arg, astroid.AssName) - name = arg.name - kwparams[name] = [called.args.kw_defaults[i], False] - - # Match the supplied arguments against the function parameters. - - # 1. Match the positional arguments. - for i in range(num_positional_args): - if i < len(parameters): - parameters[i][1] = True - elif called.args.vararg is not None: - # The remaining positional arguments get assigned to the *args - # parameter. - break - else: - # Too many positional arguments. - self.add_message('too-many-function-args', - node=node, args=(callable_name,)) - break - - # 2. Match the keyword arguments. - for keyword in keyword_args: - if keyword in parameter_name_to_index: - i = parameter_name_to_index[keyword] - if parameters[i][1]: - # Duplicate definition of function parameter. - self.add_message('redundant-keyword-arg', - node=node, args=(keyword, callable_name)) - else: - parameters[i][1] = True - elif keyword in kwparams: - if kwparams[keyword][1]: # XXX is that even possible? - # Duplicate definition of function parameter. - self.add_message('redundant-keyword-arg', node=node, - args=(keyword, callable_name)) - else: - kwparams[keyword][1] = True - elif called.args.kwarg is not None: - # The keyword argument gets assigned to the **kwargs parameter. - pass - else: - # Unexpected keyword argument. - self.add_message('unexpected-keyword-arg', node=node, - args=(keyword, callable_name)) - - # 3. Match the *args, if any. Note that Python actually processes - # *args _before_ any keyword arguments, but we wait until after - # looking at the keyword arguments so as to make a more conservative - # guess at how many values are in the *args sequence. - if node.starargs is not None: - for i in range(num_positional_args, len(parameters)): - [(name, defval), assigned] = parameters[i] - # Assume that *args provides just enough values for all - # non-default parameters after the last parameter assigned by - # the positional arguments but before the first parameter - # assigned by the keyword arguments. This is the best we can - # get without generating any false positives. - if (defval is not None) or assigned: - break - parameters[i][1] = True - - # 4. Match the **kwargs, if any. - if node.kwargs is not None: - for i, [(name, defval), assigned] in enumerate(parameters): - # Assume that *kwargs provides values for all remaining - # unassigned named parameters. - if name is not None: - parameters[i][1] = True - else: - # **kwargs can't assign to tuples. - pass - - # Check that any parameters without a default have been assigned - # values. - for [(name, defval), assigned] in parameters: - if (defval is None) and not assigned: - if name is None: - display_name = '' - else: - display_name = repr(name) - self.add_message('no-value-for-parameter', node=node, - args=(display_name, callable_name)) - - for name in kwparams: - defval, assigned = kwparams[name] - if defval is None and not assigned: - self.add_message('missing-kwoa', node=node, - args=(name, callable_name)) - - @check_messages('invalid-sequence-index') - def visit_extslice(self, node): - # Check extended slice objects as if they were used as a sequence - # index to check if the object being sliced can support them - return self.visit_index(node) - - @check_messages('invalid-sequence-index') - def visit_index(self, node): - if not node.parent or not hasattr(node.parent, "value"): - return - - # Look for index operations where the parent is a sequence type. - # If the types can be determined, only allow indices to be int, - # slice or instances with __index__. - - parent_type = safe_infer(node.parent.value) - if not isinstance(parent_type, (astroid.Class, astroid.Instance)): - return - - # Determine what method on the parent this index will use - # The parent of this node will be a Subscript, and the parent of that - # node determines if the Subscript is a get, set, or delete operation. - operation = node.parent.parent - if isinstance(operation, astroid.Assign): - methodname = '__setitem__' - elif isinstance(operation, astroid.Delete): - methodname = '__delitem__' - else: - methodname = '__getitem__' - - # Check if this instance's __getitem__, __setitem__, or __delitem__, as - # appropriate to the statement, is implemented in a builtin sequence - # type. This way we catch subclasses of sequence types but skip classes - # that override __getitem__ and which may allow non-integer indices. - try: - methods = parent_type.getattr(methodname) - if methods is astroid.YES: - return - itemmethod = methods[0] - except (astroid.NotFoundError, IndexError): - return - - if not isinstance(itemmethod, astroid.Function): - return - if itemmethod.root().name != BUILTINS: - return - if not itemmethod.parent: - return - if itemmethod.parent.name not in SEQUENCE_TYPES: - return - - # For ExtSlice objects coming from visit_extslice, no further - # inference is necessary, since if we got this far the ExtSlice - # is an error. - if isinstance(node, astroid.ExtSlice): - index_type = node - else: - index_type = safe_infer(node) - if index_type is None or index_type is astroid.YES: - return - - # Constants must be of type int - if isinstance(index_type, astroid.Const): - if isinstance(index_type.value, int): - return - # Instance values must be int, slice, or have an __index__ method - elif isinstance(index_type, astroid.Instance): - if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'): - return - try: - index_type.getattr('__index__') - return - except astroid.NotFoundError: - pass - - # Anything else is an error - self.add_message('invalid-sequence-index', node=node) - - @check_messages('invalid-slice-index') - def visit_slice(self, node): - # Check the type of each part of the slice - for index in (node.lower, node.upper, node.step): - if index is None: - continue - - index_type = safe_infer(index) - if index_type is None or index_type is astroid.YES: - continue - - # Constants must of type int or None - if isinstance(index_type, astroid.Const): - if isinstance(index_type.value, (int, type(None))): - continue - # Instance values must be of type int, None or an object - # with __index__ - elif isinstance(index_type, astroid.Instance): - if index_type.pytype() in (BUILTINS + '.int', - BUILTINS + '.NoneType'): - continue - - try: - index_type.getattr('__index__') - return - except astroid.NotFoundError: - pass - - # Anything else is an error - self.add_message('invalid-slice-index', node=node) - -def register(linter): - """required method to auto register this checker """ - linter.register_checker(TypeChecker(linter)) diff --git a/checkers/utils.py b/checkers/utils.py deleted file mode 100644 index 2cb01d5..0000000 --- a/checkers/utils.py +++ /dev/null @@ -1,564 +0,0 @@ -# pylint: disable=W0611 -# -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""some functions that may be useful for various checkers -""" - -import re -import sys -import string - -import astroid -from astroid import scoped_nodes -from logilab.common.compat import builtins - -BUILTINS_NAME = builtins.__name__ -COMP_NODE_TYPES = astroid.ListComp, astroid.SetComp, astroid.DictComp, astroid.GenExpr -PY3K = sys.version_info[0] == 3 - -if not PY3K: - EXCEPTIONS_MODULE = "exceptions" -else: - EXCEPTIONS_MODULE = "builtins" -ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod', - 'abc.abstractclassmethod', 'abc.abstractstaticmethod')) - - -class NoSuchArgumentError(Exception): - pass - -def is_inside_except(node): - """Returns true if node is inside the name of an except handler.""" - current = node - while current and not isinstance(current.parent, astroid.ExceptHandler): - current = current.parent - - return current and current is current.parent.name - - -def get_all_elements(node): - """Recursively returns all atoms in nested lists and tuples.""" - if isinstance(node, (astroid.Tuple, astroid.List)): - for child in node.elts: - for e in get_all_elements(child): - yield e - else: - yield node - - -def clobber_in_except(node): - """Checks if an assignment node in an except handler clobbers an existing - variable. - - Returns (True, args for W0623) if assignment clobbers an existing variable, - (False, None) otherwise. - """ - if isinstance(node, astroid.AssAttr): - return (True, (node.attrname, 'object %r' % (node.expr.as_string(),))) - elif isinstance(node, astroid.AssName): - name = node.name - if is_builtin(name): - return (True, (name, 'builtins')) - else: - stmts = node.lookup(name)[1] - if (stmts and not isinstance(stmts[0].ass_type(), - (astroid.Assign, astroid.AugAssign, - astroid.ExceptHandler))): - return (True, (name, 'outer scope (line %s)' % stmts[0].fromlineno)) - return (False, None) - - -def safe_infer(node): - """return the inferred value for the given node. - Return None if inference failed or if there is some ambiguity (more than - one node has been inferred) - """ - try: - inferit = node.infer() - value = next(inferit) - except astroid.InferenceError: - return - try: - next(inferit) - return # None if there is ambiguity on the inferred node - except astroid.InferenceError: - return # there is some kind of ambiguity - except StopIteration: - return value - -def is_super(node): - """return True if the node is referencing the "super" builtin function - """ - if getattr(node, 'name', None) == 'super' and \ - node.root().name == BUILTINS_NAME: - return True - return False - -def is_error(node): - """return true if the function does nothing but raising an exception""" - for child_node in node.get_children(): - if isinstance(child_node, astroid.Raise): - return True - return False - -def is_raising(body): - """return true if the given statement node raise an exception""" - for node in body: - if isinstance(node, astroid.Raise): - return True - return False - -def is_empty(body): - """return true if the given node does nothing but 'pass'""" - return len(body) == 1 and isinstance(body[0], astroid.Pass) - -builtins = builtins.__dict__.copy() -SPECIAL_BUILTINS = ('__builtins__',) # '__path__', '__file__') - -def is_builtin_object(node): - """Returns True if the given node is an object from the __builtin__ module.""" - return node and node.root().name == BUILTINS_NAME - -def is_builtin(name): # was is_native_builtin - """return true if could be considered as a builtin defined by python - """ - if name in builtins: - return True - if name in SPECIAL_BUILTINS: - return True - return False - -def is_defined_before(var_node): - """return True if the variable node is defined by a parent node (list, - set, dict, or generator comprehension, lambda) or in a previous sibling - node on the same line (statement_defining ; statement_using) - """ - varname = var_node.name - _node = var_node.parent - while _node: - if isinstance(_node, COMP_NODE_TYPES): - for ass_node in _node.nodes_of_class(astroid.AssName): - if ass_node.name == varname: - return True - elif isinstance(_node, astroid.For): - for ass_node in _node.target.nodes_of_class(astroid.AssName): - if ass_node.name == varname: - return True - elif isinstance(_node, astroid.With): - for expr, ids in _node.items: - if expr.parent_of(var_node): - break - if (ids and - isinstance(ids, astroid.AssName) and - ids.name == varname): - return True - elif isinstance(_node, (astroid.Lambda, astroid.Function)): - if _node.args.is_argument(varname): - return True - if getattr(_node, 'name', None) == varname: - return True - break - elif isinstance(_node, astroid.ExceptHandler): - if isinstance(_node.name, astroid.AssName): - ass_node = _node.name - if ass_node.name == varname: - return True - _node = _node.parent - # possibly multiple statements on the same line using semi colon separator - stmt = var_node.statement() - _node = stmt.previous_sibling() - lineno = stmt.fromlineno - while _node and _node.fromlineno == lineno: - for ass_node in _node.nodes_of_class(astroid.AssName): - if ass_node.name == varname: - return True - for imp_node in _node.nodes_of_class((astroid.From, astroid.Import)): - if varname in [name[1] or name[0] for name in imp_node.names]: - return True - _node = _node.previous_sibling() - return False - -def is_func_default(node): - """return true if the given Name node is used in function default argument's - value - """ - parent = node.scope() - if isinstance(parent, astroid.Function): - for default_node in parent.args.defaults: - for default_name_node in default_node.nodes_of_class(astroid.Name): - if default_name_node is node: - return True - return False - -def is_func_decorator(node): - """return true if the name is used in function decorator""" - parent = node.parent - while parent is not None: - if isinstance(parent, astroid.Decorators): - return True - if (parent.is_statement or - isinstance(parent, astroid.Lambda) or - isinstance(parent, (scoped_nodes.ComprehensionScope, - scoped_nodes.ListComp))): - break - parent = parent.parent - return False - -def is_ancestor_name(frame, node): - """return True if `frame` is a astroid.Class node with `node` in the - subtree of its bases attribute - """ - try: - bases = frame.bases - except AttributeError: - return False - for base in bases: - if node in base.nodes_of_class(astroid.Name): - return True - return False - -def assign_parent(node): - """return the higher parent which is not an AssName, Tuple or List node - """ - while node and isinstance(node, (astroid.AssName, - astroid.Tuple, - astroid.List)): - node = node.parent - return node - -def overrides_an_abstract_method(class_node, name): - """return True if pnode is a parent of node""" - for ancestor in class_node.ancestors(): - if name in ancestor and isinstance(ancestor[name], astroid.Function) and \ - ancestor[name].is_abstract(pass_is_abstract=False): - return True - return False - -def overrides_a_method(class_node, name): - """return True if is a method overridden from an ancestor""" - for ancestor in class_node.ancestors(): - if name in ancestor and isinstance(ancestor[name], astroid.Function): - return True - return False - -PYMETHODS = set(('__new__', '__init__', '__del__', '__hash__', - '__str__', '__repr__', - '__len__', '__iter__', - '__delete__', '__get__', '__set__', - '__getitem__', '__setitem__', '__delitem__', '__contains__', - '__getattribute__', '__getattr__', '__setattr__', '__delattr__', - '__call__', - '__enter__', '__exit__', - '__cmp__', '__ge__', '__gt__', '__le__', '__lt__', '__eq__', - '__nonzero__', '__neg__', '__invert__', - '__mul__', '__imul__', '__rmul__', - '__div__', '__idiv__', '__rdiv__', - '__add__', '__iadd__', '__radd__', - '__sub__', '__isub__', '__rsub__', - '__pow__', '__ipow__', '__rpow__', - '__mod__', '__imod__', '__rmod__', - '__and__', '__iand__', '__rand__', - '__or__', '__ior__', '__ror__', - '__xor__', '__ixor__', '__rxor__', - # XXX To be continued - )) - -def check_messages(*messages): - """decorator to store messages that are handled by a checker method""" - - def store_messages(func): - func.checks_msgs = messages - return func - return store_messages - -class IncompleteFormatString(Exception): - """A format string ended in the middle of a format specifier.""" - pass - -class UnsupportedFormatCharacter(Exception): - """A format character in a format string is not one of the supported - format characters.""" - def __init__(self, index): - Exception.__init__(self, index) - self.index = index - -def parse_format_string(format_string): - """Parses a format string, returning a tuple of (keys, num_args), where keys - is the set of mapping keys in the format string, and num_args is the number - of arguments required by the format string. Raises - IncompleteFormatString or UnsupportedFormatCharacter if a - parse error occurs.""" - keys = set() - num_args = 0 - def next_char(i): - i += 1 - if i == len(format_string): - raise IncompleteFormatString - return (i, format_string[i]) - i = 0 - while i < len(format_string): - char = format_string[i] - if char == '%': - i, char = next_char(i) - # Parse the mapping key (optional). - key = None - if char == '(': - depth = 1 - i, char = next_char(i) - key_start = i - while depth != 0: - if char == '(': - depth += 1 - elif char == ')': - depth -= 1 - i, char = next_char(i) - key_end = i - 1 - key = format_string[key_start:key_end] - - # Parse the conversion flags (optional). - while char in '#0- +': - i, char = next_char(i) - # Parse the minimum field width (optional). - if char == '*': - num_args += 1 - i, char = next_char(i) - else: - while char in string.digits: - i, char = next_char(i) - # Parse the precision (optional). - if char == '.': - i, char = next_char(i) - if char == '*': - num_args += 1 - i, char = next_char(i) - else: - while char in string.digits: - i, char = next_char(i) - # Parse the length modifier (optional). - if char in 'hlL': - i, char = next_char(i) - # Parse the conversion type (mandatory). - if PY3K: - flags = 'diouxXeEfFgGcrs%a' - else: - flags = 'diouxXeEfFgGcrs%' - if char not in flags: - raise UnsupportedFormatCharacter(i) - if key: - keys.add(key) - elif char != '%': - num_args += 1 - i += 1 - return keys, num_args - - -def is_attr_protected(attrname): - """return True if attribute name is protected (start with _ and some other - details), False otherwise. - """ - return attrname[0] == '_' and not attrname == '_' and not ( - attrname.startswith('__') and attrname.endswith('__')) - -def node_frame_class(node): - """return klass node for a method node (or a staticmethod or a - classmethod), return null otherwise - """ - klass = node.frame() - - while klass is not None and not isinstance(klass, astroid.Class): - if klass.parent is None: - klass = None - else: - klass = klass.parent.frame() - - return klass - -def is_super_call(expr): - """return True if expression node is a function call and if function name - is super. Check before that you're in a method. - """ - return (isinstance(expr, astroid.CallFunc) and - isinstance(expr.func, astroid.Name) and - expr.func.name == 'super') - -def is_attr_private(attrname): - """Check that attribute name is private (at least two leading underscores, - at most one trailing underscore) - """ - regex = re.compile('^_{2,}.*[^_]+_?$') - return regex.match(attrname) - -def get_argument_from_call(callfunc_node, position=None, keyword=None): - """Returns the specified argument from a function call. - - :param callfunc_node: Node representing a function call to check. - :param int position: position of the argument. - :param str keyword: the keyword of the argument. - - :returns: The node representing the argument, None if the argument is not found. - :raises ValueError: if both position and keyword are None. - :raises NoSuchArgumentError: if no argument at the provided position or with - the provided keyword. - """ - if position is None and keyword is None: - raise ValueError('Must specify at least one of: position or keyword.') - try: - if position is not None and not isinstance(callfunc_node.args[position], astroid.Keyword): - return callfunc_node.args[position] - except IndexError as error: - raise NoSuchArgumentError(error) - if keyword: - for arg in callfunc_node.args: - if isinstance(arg, astroid.Keyword) and arg.arg == keyword: - return arg.value - raise NoSuchArgumentError - -def inherit_from_std_ex(node): - """ - Return true if the given class node is subclass of - exceptions.Exception. - """ - if node.name in ('Exception', 'BaseException') \ - and node.root().name == EXCEPTIONS_MODULE: - return True - return any(inherit_from_std_ex(parent) - for parent in node.ancestors(recurs=False)) - -def is_import_error(handler): - """ - Check if the given exception handler catches - ImportError. - - :param handler: A node, representing an ExceptHandler node. - :returns: True if the handler catches ImportError, False otherwise. - """ - names = None - if isinstance(handler.type, astroid.Tuple): - names = [name for name in handler.type.elts - if isinstance(name, astroid.Name)] - elif isinstance(handler.type, astroid.Name): - names = [handler.type] - else: - # Don't try to infer that. - return - for name in names: - try: - for infered in name.infer(): - if (isinstance(infered, astroid.Class) and - inherit_from_std_ex(infered) and - infered.name == 'ImportError'): - return True - except astroid.InferenceError: - continue - -def has_known_bases(klass): - """Returns true if all base classes of a class could be inferred.""" - try: - return klass._all_bases_known - except AttributeError: - pass - for base in klass.bases: - result = safe_infer(base) - # TODO: check for A->B->A->B pattern in class structure too? - if (not isinstance(result, astroid.Class) or - result is klass or - not has_known_bases(result)): - klass._all_bases_known = False - return False - klass._all_bases_known = True - return True - -def decorated_with_property(node): - """ Detect if the given function node is decorated with a property. """ - if not node.decorators: - return False - for decorator in node.decorators.nodes: - if not isinstance(decorator, astroid.Name): - continue - try: - for infered in decorator.infer(): - if isinstance(infered, astroid.Class): - if (infered.root().name == BUILTINS_NAME and - infered.name == 'property'): - return True - for ancestor in infered.ancestors(): - if (ancestor.name == 'property' and - ancestor.root().name == BUILTINS_NAME): - return True - except astroid.InferenceError: - pass - - -def decorated_with_abc(func): - """Determine if the `func` node is decorated with `abc` decorators.""" - if func.decorators: - for node in func.decorators.nodes: - try: - infered = next(node.infer()) - except astroid.InferenceError: - continue - if infered and infered.qname() in ABC_METHODS: - return True - - -def unimplemented_abstract_methods(node, is_abstract_cb=decorated_with_abc): - """ - Get the unimplemented abstract methods for the given *node*. - - A method can be considered abstract if the callback *is_abstract_cb* - returns a ``True`` value. The check defaults to verifying that - a method is decorated with abstract methods. - The function will work only for new-style classes. For old-style - classes, it will simply return an empty dictionary. - For the rest of them, it will return a dictionary of abstract method - names and their inferred objects. - """ - visited = {} - try: - mro = reversed(node.mro()) - except NotImplementedError: - # Old style class, it will not have a mro. - return {} - except astroid.ResolveError: - # Probably inconsistent hierarchy, don'try - # to figure this out here. - return {} - for ancestor in mro: - for obj in ancestor.values(): - infered = obj - if isinstance(obj, astroid.AssName): - infered = safe_infer(obj) - if not infered: - continue - if not isinstance(infered, astroid.Function): - if obj.name in visited: - del visited[obj.name] - if isinstance(infered, astroid.Function): - # It's critical to use the original name, - # since after inferring, an object can be something - # else than expected, as in the case of the - # following assignment. - # - # class A: - # def keys(self): pass - # __iter__ = keys - abstract = is_abstract_cb(infered) - if abstract: - visited[obj.name] = infered - elif not abstract and obj.name in visited: - del visited[obj.name] - return visited diff --git a/checkers/variables.py b/checkers/variables.py deleted file mode 100644 index 8f6f957..0000000 --- a/checkers/variables.py +++ /dev/null @@ -1,1069 +0,0 @@ -# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""variables checkers for Python code -""" -import os -import sys -import re -from copy import copy - -import astroid -from astroid import are_exclusive, builtin_lookup -from astroid import modutils - -from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH -from pylint.utils import get_global_option -from pylint.checkers import BaseChecker -from pylint.checkers.utils import ( - PYMETHODS, is_ancestor_name, is_builtin, - is_defined_before, is_error, is_func_default, is_func_decorator, - assign_parent, check_messages, is_inside_except, clobber_in_except, - get_all_elements, has_known_bases) -import six - -SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$") - -PY3K = sys.version_info >= (3, 0) - -def in_for_else_branch(parent, stmt): - """Returns True if stmt in inside the else branch for a parent For stmt.""" - return (isinstance(parent, astroid.For) and - any(else_stmt.parent_of(stmt) for else_stmt in parent.orelse)) - -def overridden_method(klass, name): - """get overridden method if any""" - try: - parent = next(klass.local_attr_ancestors(name)) - except (StopIteration, KeyError): - return None - try: - meth_node = parent[name] - except KeyError: - # We have found an ancestor defining but it's not in the local - # dictionary. This may happen with astroid built from living objects. - return None - if isinstance(meth_node, astroid.Function): - return meth_node - return None - -def _get_unpacking_extra_info(node, infered): - """return extra information to add to the message for unpacking-non-sequence - and unbalanced-tuple-unpacking errors - """ - more = '' - infered_module = infered.root().name - if node.root().name == infered_module: - if node.lineno == infered.lineno: - more = ' %s' % infered.as_string() - elif infered.lineno: - more = ' defined at line %s' % infered.lineno - elif infered.lineno: - more = ' defined at line %s of %s' % (infered.lineno, infered_module) - return more - -def _detect_global_scope(node, frame, defframe): - """ Detect that the given frames shares a global - scope. - - Two frames shares a global scope when neither - of them are hidden under a function scope, as well - as any of parent scope of them, until the root scope. - In this case, depending from something defined later on - will not work, because it is still undefined. - - Example: - class A: - # B has the same global scope as `C`, leading to a NameError. - class B(C): ... - class C: ... - - """ - def_scope = scope = None - if frame and frame.parent: - scope = frame.parent.scope() - if defframe and defframe.parent: - def_scope = defframe.parent.scope() - if isinstance(frame, astroid.Function): - # If the parent of the current node is a - # function, then it can be under its scope - # (defined in, which doesn't concern us) or - # the `->` part of annotations. The same goes - # for annotations of function arguments, they'll have - # their parent the Arguments node. - if not isinstance(node.parent, - (astroid.Function, astroid.Arguments)): - return False - elif any(not isinstance(f, (astroid.Class, astroid.Module)) - for f in (frame, defframe)): - # Not interested in other frames, since they are already - # not in a global scope. - return False - - break_scopes = [] - for s in (scope, def_scope): - # Look for parent scopes. If there is anything different - # than a module or a class scope, then they frames don't - # share a global scope. - parent_scope = s - while parent_scope: - if not isinstance(parent_scope, (astroid.Class, astroid.Module)): - break_scopes.append(parent_scope) - break - if parent_scope.parent: - parent_scope = parent_scope.parent.scope() - else: - break - if break_scopes and len(set(break_scopes)) != 1: - # Store different scopes than expected. - # If the stored scopes are, in fact, the very same, then it means - # that the two frames (frame and defframe) shares the same scope, - # and we could apply our lineno analysis over them. - # For instance, this works when they are inside a function, the node - # that uses a definition and the definition itself. - return False - # At this point, we are certain that frame and defframe shares a scope - # and the definition of the first depends on the second. - return frame.lineno < defframe.lineno - -def _fix_dot_imports(not_consumed): - """ Try to fix imports with multiple dots, by returning a dictionary - with the import names expanded. The function unflattens root imports, - like 'xml' (when we have both 'xml.etree' and 'xml.sax'), to 'xml.etree' - and 'xml.sax' respectively. - """ - # TODO: this should be improved in issue astroid #46 - names = {} - for name, stmts in six.iteritems(not_consumed): - if any(isinstance(stmt, astroid.AssName) - and isinstance(stmt.ass_type(), astroid.AugAssign) - for stmt in stmts): - continue - for stmt in stmts: - if not isinstance(stmt, (astroid.From, astroid.Import)): - continue - for imports in stmt.names: - second_name = None - if imports[0] == "*": - # In case of wildcard imports, - # pick the name from inside the imported module. - second_name = name - else: - if imports[0].find(".") > -1 or name in imports: - # Most likely something like 'xml.etree', - # which will appear in the .locals as 'xml'. - # Only pick the name if it wasn't consumed. - second_name = imports[0] - if second_name and second_name not in names: - names[second_name] = stmt - return sorted(names.items(), key=lambda a: a[1].fromlineno) - -def _find_frame_imports(name, frame): - """ - Detect imports in the frame, with the required - *name*. Such imports can be considered assignments. - Returns True if an import for the given name was found. - """ - imports = frame.nodes_of_class((astroid.Import, astroid.From)) - for import_node in imports: - for import_name, import_alias in import_node.names: - # If the import uses an alias, check only that. - # Otherwise, check only the import name. - if import_alias: - if import_alias == name: - return True - elif import_name and import_name == name: - return True - - -MSGS = { - 'E0601': ('Using variable %r before assignment', - 'used-before-assignment', - 'Used when a local variable is accessed before it\'s \ - assignment.'), - 'E0602': ('Undefined variable %r', - 'undefined-variable', - 'Used when an undefined variable is accessed.'), - 'E0603': ('Undefined variable name %r in __all__', - 'undefined-all-variable', - 'Used when an undefined variable name is referenced in __all__.'), - 'E0604': ('Invalid object %r in __all__, must contain only strings', - 'invalid-all-object', - 'Used when an invalid (non-string) object occurs in __all__.'), - 'E0611': ('No name %r in module %r', - 'no-name-in-module', - 'Used when a name cannot be found in a module.'), - - 'W0601': ('Global variable %r undefined at the module level', - 'global-variable-undefined', - 'Used when a variable is defined through the "global" statement \ - but the variable is not defined in the module scope.'), - 'W0602': ('Using global for %r but no assignment is done', - 'global-variable-not-assigned', - 'Used when a variable is defined through the "global" statement \ - but no assignment to this variable is done.'), - 'W0603': ('Using the global statement', # W0121 - 'global-statement', - 'Used when you use the "global" statement to update a global \ - variable. Pylint just try to discourage this \ - usage. That doesn\'t mean you can not use it !'), - 'W0604': ('Using the global statement at the module level', # W0103 - 'global-at-module-level', - 'Used when you use the "global" statement at the module level \ - since it has no effect'), - 'W0611': ('Unused %s', - 'unused-import', - 'Used when an imported module or variable is not used.'), - 'W0612': ('Unused variable %r', - 'unused-variable', - 'Used when a variable is defined but not used.'), - 'W0613': ('Unused argument %r', - 'unused-argument', - 'Used when a function or method argument is not used.'), - 'W0614': ('Unused import %s from wildcard import', - 'unused-wildcard-import', - 'Used when an imported module or variable is not used from a \ - \'from X import *\' style import.'), - - 'W0621': ('Redefining name %r from outer scope (line %s)', - 'redefined-outer-name', - 'Used when a variable\'s name hide a name defined in the outer \ - scope.'), - 'W0622': ('Redefining built-in %r', - 'redefined-builtin', - 'Used when a variable or function override a built-in.'), - 'W0623': ('Redefining name %r from %s in exception handler', - 'redefine-in-handler', - 'Used when an exception handler assigns the exception \ - to an existing name'), - - 'W0631': ('Using possibly undefined loop variable %r', - 'undefined-loop-variable', - 'Used when an loop variable (i.e. defined by a for loop or \ - a list comprehension or a generator expression) is used outside \ - the loop.'), - - 'W0632': ('Possible unbalanced tuple unpacking with ' - 'sequence%s: ' - 'left side has %d label(s), right side has %d value(s)', - 'unbalanced-tuple-unpacking', - 'Used when there is an unbalanced tuple unpacking in assignment'), - - 'W0633': ('Attempting to unpack a non-sequence%s', - 'unpacking-non-sequence', - 'Used when something which is not ' - 'a sequence is used in an unpack assignment'), - - 'W0640': ('Cell variable %s defined in loop', - 'cell-var-from-loop', - 'A variable used in a closure is defined in a loop. ' - 'This will result in all closures using the same value for ' - 'the closed-over variable.'), - - } - -class VariablesChecker(BaseChecker): - """checks for - * unused variables / imports - * undefined variables - * redefinition of variable from builtins or from an outer scope - * use of variable before assignment - * __all__ consistency - """ - - __implements__ = IAstroidChecker - - name = 'variables' - msgs = MSGS - priority = -1 - options = (("init-import", - {'default': 0, 'type' : 'yn', 'metavar' : '', - 'help' : 'Tells whether we should check for unused import in \ -__init__ files.'}), - ("dummy-variables-rgx", - {'default': ('_$|dummy'), - 'type' :'regexp', 'metavar' : '', - 'help' : 'A regular expression matching the name of dummy \ -variables (i.e. expectedly not used).'}), - ("additional-builtins", - {'default': (), 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of additional names supposed to be defined in \ -builtins. Remember that you should avoid to define new builtins when possible.' - }), - ("callbacks", - {'default' : ('cb_', '_cb'), 'type' : 'csv', - 'metavar' : '', - 'help' : 'List of strings which can identify a callback ' - 'function by name. A callback name must start or ' - 'end with one of those strings.'} - ) - ) - def __init__(self, linter=None): - BaseChecker.__init__(self, linter) - self._to_consume = None - self._checking_mod_attr = None - - def visit_module(self, node): - """visit module : update consumption analysis variable - checks globals doesn't overrides builtins - """ - self._to_consume = [(copy(node.locals), {}, 'module')] - for name, stmts in six.iteritems(node.locals): - if is_builtin(name) and not is_inside_except(stmts[0]): - # do not print Redefining builtin for additional builtins - self.add_message('redefined-builtin', args=name, node=stmts[0]) - - @check_messages('unused-import', 'unused-wildcard-import', - 'redefined-builtin', 'undefined-all-variable', - 'invalid-all-object') - def leave_module(self, node): - """leave module: check globals - """ - assert len(self._to_consume) == 1 - not_consumed = self._to_consume.pop()[0] - # attempt to check for __all__ if defined - if '__all__' in node.locals: - assigned = next(node.igetattr('__all__')) - if assigned is not astroid.YES: - for elt in getattr(assigned, 'elts', ()): - try: - elt_name = next(elt.infer()) - except astroid.InferenceError: - continue - - if not isinstance(elt_name, astroid.Const) \ - or not isinstance(elt_name.value, six.string_types): - self.add_message('invalid-all-object', - args=elt.as_string(), node=elt) - continue - elt_name = elt_name.value - # If elt is in not_consumed, remove it from not_consumed - if elt_name in not_consumed: - del not_consumed[elt_name] - continue - if elt_name not in node.locals: - if not node.package: - self.add_message('undefined-all-variable', - args=elt_name, - node=elt) - else: - basename = os.path.splitext(node.file)[0] - if os.path.basename(basename) == '__init__': - name = node.name + "." + elt_name - try: - modutils.file_from_modpath(name.split(".")) - except ImportError: - self.add_message('undefined-all-variable', - args=elt_name, - node=elt) - except SyntaxError: - # don't yield an syntax-error warning, - # because it will be later yielded - # when the file will be checked - pass - # don't check unused imports in __init__ files - if not self.config.init_import and node.package: - return - - self._check_imports(not_consumed) - - def _check_imports(self, not_consumed): - local_names = _fix_dot_imports(not_consumed) - checked = set() - for name, stmt in local_names: - for imports in stmt.names: - real_name = imported_name = imports[0] - if imported_name == "*": - real_name = name - as_name = imports[1] - if real_name in checked: - continue - if name not in (real_name, as_name): - continue - checked.add(real_name) - - if (isinstance(stmt, astroid.Import) or - (isinstance(stmt, astroid.From) and - not stmt.modname)): - if (isinstance(stmt, astroid.From) and - SPECIAL_OBJ.search(imported_name)): - # Filter special objects (__doc__, __all__) etc., - # because they can be imported for exporting. - continue - if as_name is None: - msg = "import %s" % imported_name - else: - msg = "%s imported as %s" % (imported_name, as_name) - self.add_message('unused-import', args=msg, node=stmt) - elif isinstance(stmt, astroid.From) and stmt.modname != '__future__': - if SPECIAL_OBJ.search(imported_name): - # Filter special objects (__doc__, __all__) etc., - # because they can be imported for exporting. - continue - if imported_name == '*': - self.add_message('unused-wildcard-import', - args=name, node=stmt) - else: - if as_name is None: - msg = "%s imported from %s" % (imported_name, stmt.modname) - else: - fields = (imported_name, stmt.modname, as_name) - msg = "%s imported from %s as %s" % fields - self.add_message('unused-import', args=msg, node=stmt) - del self._to_consume - - def visit_class(self, node): - """visit class: update consumption analysis variable - """ - self._to_consume.append((copy(node.locals), {}, 'class')) - - def leave_class(self, _): - """leave class: update consumption analysis variable - """ - # do not check for not used locals here (no sense) - self._to_consume.pop() - - def visit_lambda(self, node): - """visit lambda: update consumption analysis variable - """ - self._to_consume.append((copy(node.locals), {}, 'lambda')) - - def leave_lambda(self, _): - """leave lambda: update consumption analysis variable - """ - # do not check for not used locals here - self._to_consume.pop() - - def visit_genexpr(self, node): - """visit genexpr: update consumption analysis variable - """ - self._to_consume.append((copy(node.locals), {}, 'comprehension')) - - def leave_genexpr(self, _): - """leave genexpr: update consumption analysis variable - """ - # do not check for not used locals here - self._to_consume.pop() - - def visit_dictcomp(self, node): - """visit dictcomp: update consumption analysis variable - """ - self._to_consume.append((copy(node.locals), {}, 'comprehension')) - - def leave_dictcomp(self, _): - """leave dictcomp: update consumption analysis variable - """ - # do not check for not used locals here - self._to_consume.pop() - - def visit_setcomp(self, node): - """visit setcomp: update consumption analysis variable - """ - self._to_consume.append((copy(node.locals), {}, 'comprehension')) - - def leave_setcomp(self, _): - """leave setcomp: update consumption analysis variable - """ - # do not check for not used locals here - self._to_consume.pop() - - def visit_function(self, node): - """visit function: update consumption analysis variable and check locals - """ - self._to_consume.append((copy(node.locals), {}, 'function')) - if not (self.linter.is_message_enabled('redefined-outer-name') or - self.linter.is_message_enabled('redefined-builtin')): - return - globs = node.root().globals - for name, stmt in node.items(): - if is_inside_except(stmt): - continue - if name in globs and not isinstance(stmt, astroid.Global): - line = globs[name][0].fromlineno - dummy_rgx = self.config.dummy_variables_rgx - if not dummy_rgx.match(name): - self.add_message('redefined-outer-name', args=(name, line), node=stmt) - elif is_builtin(name): - # do not print Redefining builtin for additional builtins - self.add_message('redefined-builtin', args=name, node=stmt) - - def leave_function(self, node): - """leave function: check function's locals are consumed""" - not_consumed = self._to_consume.pop()[0] - if not (self.linter.is_message_enabled('unused-variable') or - self.linter.is_message_enabled('unused-argument')): - return - # don't check arguments of function which are only raising an exception - if is_error(node): - return - # don't check arguments of abstract methods or within an interface - is_method = node.is_method() - klass = node.parent.frame() - if is_method and (klass.type == 'interface' or node.is_abstract()): - return - if is_method and isinstance(klass, astroid.Class): - confidence = INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE - else: - confidence = HIGH - authorized_rgx = self.config.dummy_variables_rgx - called_overridden = False - argnames = node.argnames() - global_names = set() - nonlocal_names = set() - for global_stmt in node.nodes_of_class(astroid.Global): - global_names.update(set(global_stmt.names)) - for nonlocal_stmt in node.nodes_of_class(astroid.Nonlocal): - nonlocal_names.update(set(nonlocal_stmt.names)) - - for name, stmts in six.iteritems(not_consumed): - # ignore some special names specified by user configuration - if authorized_rgx.match(name): - continue - # ignore names imported by the global statement - # FIXME: should only ignore them if it's assigned latter - stmt = stmts[0] - if isinstance(stmt, astroid.Global): - continue - if isinstance(stmt, (astroid.Import, astroid.From)): - # Detect imports, assigned to global statements. - if global_names: - skip = False - for import_name, import_alias in stmt.names: - # If the import uses an alias, check only that. - # Otherwise, check only the import name. - if import_alias: - if import_alias in global_names: - skip = True - break - elif import_name in global_names: - skip = True - break - if skip: - continue - - # care about functions with unknown argument (builtins) - if name in argnames: - if is_method: - # don't warn for the first argument of a (non static) method - if node.type != 'staticmethod' and name == argnames[0]: - continue - # don't warn for argument of an overridden method - if not called_overridden: - overridden = overridden_method(klass, node.name) - called_overridden = True - if overridden is not None and name in overridden.argnames(): - continue - if node.name in PYMETHODS and node.name not in ('__init__', '__new__'): - continue - # don't check callback arguments - if any(node.name.startswith(cb) or node.name.endswith(cb) - for cb in self.config.callbacks): - continue - self.add_message('unused-argument', args=name, node=stmt, - confidence=confidence) - else: - if stmt.parent and isinstance(stmt.parent, astroid.Assign): - if name in nonlocal_names: - continue - self.add_message('unused-variable', args=name, node=stmt) - - @check_messages('global-variable-undefined', 'global-variable-not-assigned', 'global-statement', - 'global-at-module-level', 'redefined-builtin') - def visit_global(self, node): - """check names imported exists in the global scope""" - frame = node.frame() - if isinstance(frame, astroid.Module): - self.add_message('global-at-module-level', node=node) - return - module = frame.root() - default_message = True - for name in node.names: - try: - assign_nodes = module.getattr(name) - except astroid.NotFoundError: - # unassigned global, skip - assign_nodes = [] - for anode in assign_nodes: - if anode.parent is None: - # node returned for builtin attribute such as __file__, - # __doc__, etc... - continue - if anode.frame() is frame: - # same scope level assignment - break - else: - if not _find_frame_imports(name, frame): - self.add_message('global-variable-not-assigned', - args=name, node=node) - default_message = False - if not assign_nodes: - continue - for anode in assign_nodes: - if anode.parent is None: - self.add_message('redefined-builtin', args=name, node=node) - break - if anode.frame() is module: - # module level assignment - break - else: - # global undefined at the module scope - self.add_message('global-variable-undefined', args=name, node=node) - default_message = False - if default_message: - self.add_message('global-statement', node=node) - - def _check_late_binding_closure(self, node, assignment_node): - def _is_direct_lambda_call(): - return (isinstance(node_scope.parent, astroid.CallFunc) - and node_scope.parent.func is node_scope) - - node_scope = node.scope() - if not isinstance(node_scope, (astroid.Lambda, astroid.Function)): - return - if isinstance(node.parent, astroid.Arguments): - return - - if isinstance(assignment_node, astroid.Comprehension): - if assignment_node.parent.parent_of(node.scope()): - self.add_message('cell-var-from-loop', node=node, args=node.name) - else: - assign_scope = assignment_node.scope() - maybe_for = assignment_node - while not isinstance(maybe_for, astroid.For): - if maybe_for is assign_scope: - break - maybe_for = maybe_for.parent - else: - if (maybe_for.parent_of(node_scope) - and not _is_direct_lambda_call() - and not isinstance(node_scope.statement(), astroid.Return)): - self.add_message('cell-var-from-loop', node=node, args=node.name) - - def _loopvar_name(self, node, name): - # filter variables according to node's scope - # XXX used to filter parents but don't remember why, and removing this - # fixes a W0631 false positive reported by Paul Hachmann on 2008/12 on - # python-projects (added to func_use_for_or_listcomp_var test) - #astmts = [stmt for stmt in node.lookup(name)[1] - # if hasattr(stmt, 'ass_type')] and - # not stmt.statement().parent_of(node)] - if not self.linter.is_message_enabled('undefined-loop-variable'): - return - astmts = [stmt for stmt in node.lookup(name)[1] - if hasattr(stmt, 'ass_type')] - # filter variables according their respective scope test is_statement - # and parent to avoid #74747. This is not a total fix, which would - # introduce a mechanism similar to special attribute lookup in - # modules. Also, in order to get correct inference in this case, the - # scope lookup rules would need to be changed to return the initial - # assignment (which does not exist in code per se) as well as any later - # modifications. - if not astmts or (astmts[0].is_statement or astmts[0].parent) \ - and astmts[0].statement().parent_of(node): - _astmts = [] - else: - _astmts = astmts[:1] - for i, stmt in enumerate(astmts[1:]): - if (astmts[i].statement().parent_of(stmt) - and not in_for_else_branch(astmts[i].statement(), stmt)): - continue - _astmts.append(stmt) - astmts = _astmts - if len(astmts) == 1: - ass = astmts[0].ass_type() - if isinstance(ass, (astroid.For, astroid.Comprehension, astroid.GenExpr)) \ - and not ass.statement() is node.statement(): - self.add_message('undefined-loop-variable', args=name, node=node) - - @check_messages('redefine-in-handler') - def visit_excepthandler(self, node): - for name in get_all_elements(node.name): - clobbering, args = clobber_in_except(name) - if clobbering: - self.add_message('redefine-in-handler', args=args, node=name) - - def visit_assname(self, node): - if isinstance(node.ass_type(), astroid.AugAssign): - self.visit_name(node) - - def visit_delname(self, node): - self.visit_name(node) - - @check_messages(*(MSGS.keys())) - def visit_name(self, node): - """check that a name is defined if the current scope and doesn't - redefine a built-in - """ - stmt = node.statement() - if stmt.fromlineno is None: - # name node from a astroid built from live code, skip - assert not stmt.root().file.endswith('.py') - return - name = node.name - frame = stmt.scope() - # if the name node is used as a function default argument's value or as - # a decorator, then start from the parent frame of the function instead - # of the function frame - and thus open an inner class scope - if (is_func_default(node) or is_func_decorator(node) - or is_ancestor_name(frame, node)): - start_index = len(self._to_consume) - 2 - else: - start_index = len(self._to_consume) - 1 - # iterates through parent scopes, from the inner to the outer - base_scope_type = self._to_consume[start_index][-1] - for i in range(start_index, -1, -1): - to_consume, consumed, scope_type = self._to_consume[i] - # if the current scope is a class scope but it's not the inner - # scope, ignore it. This prevents to access this scope instead of - # the globals one in function members when there are some common - # names. The only exception is when the starting scope is a - # comprehension and its direct outer scope is a class - if scope_type == 'class' and i != start_index and not ( - base_scope_type == 'comprehension' and i == start_index-1): - # Detect if we are in a local class scope, as an assignment. - # For example, the following is fair game. - # - # class A: - # b = 1 - # c = lambda b=b: b * b - # - # class B: - # tp = 1 - # def func(self, arg: tp): - # ... - - in_annotation = ( - PY3K and isinstance(frame, astroid.Function) - and node.statement() is frame and - (node in frame.args.annotations - or node is frame.args.varargannotation - or node is frame.args.kwargannotation)) - if in_annotation: - frame_locals = frame.parent.scope().locals - else: - frame_locals = frame.locals - if not ((isinstance(frame, astroid.Class) or in_annotation) - and name in frame_locals): - continue - # the name has already been consumed, only check it's not a loop - # variable used outside the loop - if name in consumed: - defnode = assign_parent(consumed[name][0]) - self._check_late_binding_closure(node, defnode) - self._loopvar_name(node, name) - break - # mark the name as consumed if it's defined in this scope - # (i.e. no KeyError is raised by "to_consume[name]") - try: - consumed[name] = to_consume[name] - except KeyError: - continue - # checks for use before assignment - defnode = assign_parent(to_consume[name][0]) - if defnode is not None: - self._check_late_binding_closure(node, defnode) - defstmt = defnode.statement() - defframe = defstmt.frame() - maybee0601 = True - if not frame is defframe: - maybee0601 = _detect_global_scope(node, frame, defframe) - elif defframe.parent is None: - # we are at the module level, check the name is not - # defined in builtins - if name in defframe.scope_attrs or builtin_lookup(name)[1]: - maybee0601 = False - else: - # we are in a local scope, check the name is not - # defined in global or builtin scope - if defframe.root().lookup(name)[1]: - maybee0601 = False - else: - # check if we have a nonlocal - if name in defframe.locals: - maybee0601 = not any(isinstance(child, astroid.Nonlocal) - and name in child.names - for child in defframe.get_children()) - - # Handle a couple of class scoping issues. - annotation_return = False - # The class reuses itself in the class scope. - recursive_klass = (frame is defframe and - defframe.parent_of(node) and - isinstance(defframe, astroid.Class) and - node.name == defframe.name) - if (self._to_consume[-1][-1] == 'lambda' and - isinstance(frame, astroid.Class) - and name in frame.locals): - maybee0601 = True - elif (isinstance(defframe, astroid.Class) and - isinstance(frame, astroid.Function)): - # Special rule for function return annotations, - # which uses the same name as the class where - # the function lives. - if (PY3K and node is frame.returns and - defframe.parent_of(frame.returns)): - maybee0601 = annotation_return = True - - if (maybee0601 and defframe.name in defframe.locals and - defframe.locals[name][0].lineno < frame.lineno): - # Detect class assignments with the same - # name as the class. In this case, no warning - # should be raised. - maybee0601 = False - elif recursive_klass: - maybee0601 = True - else: - maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno - - if (maybee0601 - and not is_defined_before(node) - and not are_exclusive(stmt, defstmt, ('NameError', - 'Exception', - 'BaseException'))): - if recursive_klass or (defstmt is stmt and - isinstance(node, (astroid.DelName, - astroid.AssName))): - self.add_message('undefined-variable', args=name, node=node) - elif annotation_return: - self.add_message('undefined-variable', args=name, node=node) - elif self._to_consume[-1][-1] != 'lambda': - # E0601 may *not* occurs in lambda scope. - self.add_message('used-before-assignment', args=name, node=node) - elif self._to_consume[-1][-1] == 'lambda': - # E0601 can occur in class-level scope in lambdas, as in - # the following example: - # class A: - # x = lambda attr: f + attr - # f = 42 - if isinstance(frame, astroid.Class) and name in frame.locals: - if isinstance(node.parent, astroid.Arguments): - # Doing the following is fine: - # class A: - # x = 42 - # y = lambda attr=x: attr - if stmt.fromlineno <= defstmt.fromlineno: - self.add_message('used-before-assignment', - args=name, node=node) - else: - self.add_message('undefined-variable', - args=name, node=node) - - if isinstance(node, astroid.AssName): # Aug AssName - del consumed[name] - else: - del to_consume[name] - # check it's not a loop variable used outside the loop - self._loopvar_name(node, name) - break - else: - # we have not found the name, if it isn't a builtin, that's an - # undefined name ! - if not (name in astroid.Module.scope_attrs or is_builtin(name) - or name in self.config.additional_builtins): - self.add_message('undefined-variable', args=name, node=node) - - @check_messages('no-name-in-module') - def visit_import(self, node): - """check modules attribute accesses""" - for name, _ in node.names: - parts = name.split('.') - try: - module = next(node.infer_name_module(parts[0])) - except astroid.ResolveError: - continue - self._check_module_attrs(node, module, parts[1:]) - - @check_messages('no-name-in-module') - def visit_from(self, node): - """check modules attribute accesses""" - name_parts = node.modname.split('.') - level = getattr(node, 'level', None) - try: - module = node.root().import_module(name_parts[0], level=level) - except Exception: # pylint: disable=broad-except - return - module = self._check_module_attrs(node, module, name_parts[1:]) - if not module: - return - for name, _ in node.names: - if name == '*': - continue - self._check_module_attrs(node, module, name.split('.')) - - @check_messages('unbalanced-tuple-unpacking', 'unpacking-non-sequence') - def visit_assign(self, node): - """Check unbalanced tuple unpacking for assignments - and unpacking non-sequences. - """ - if not isinstance(node.targets[0], (astroid.Tuple, astroid.List)): - return - - targets = node.targets[0].itered() - try: - for infered in node.value.infer(): - self._check_unpacking(infered, node, targets) - except astroid.InferenceError: - return - - def _check_unpacking(self, infered, node, targets): - """ Check for unbalanced tuple unpacking - and unpacking non sequences. - """ - if infered is astroid.YES: - return - if (isinstance(infered.parent, astroid.Arguments) and - isinstance(node.value, astroid.Name) and - node.value.name == infered.parent.vararg): - # Variable-length argument, we can't determine the length. - return - if isinstance(infered, (astroid.Tuple, astroid.List)): - # attempt to check unpacking is properly balanced - values = infered.itered() - if len(targets) != len(values): - # Check if we have starred nodes. - if any(isinstance(target, astroid.Starred) - for target in targets): - return - self.add_message('unbalanced-tuple-unpacking', node=node, - args=(_get_unpacking_extra_info(node, infered), - len(targets), - len(values))) - # attempt to check unpacking may be possible (ie RHS is iterable) - elif isinstance(infered, astroid.Instance): - for meth in ('__iter__', '__getitem__'): - try: - infered.getattr(meth) - break - except astroid.NotFoundError: - continue - else: - self.add_message('unpacking-non-sequence', node=node, - args=(_get_unpacking_extra_info(node, infered),)) - else: - self.add_message('unpacking-non-sequence', node=node, - args=(_get_unpacking_extra_info(node, infered),)) - - - def _check_module_attrs(self, node, module, module_names): - """check that module_names (list of string) are accessible through the - given module - if the latest access name corresponds to a module, return it - """ - assert isinstance(module, astroid.Module), module - ignored_modules = get_global_option(self, 'ignored-modules', - default=[]) - while module_names: - name = module_names.pop(0) - if name == '__dict__': - module = None - break - try: - module = next(module.getattr(name)[0].infer()) - if module is astroid.YES: - return None - except astroid.NotFoundError: - if module.name in ignored_modules: - return None - self.add_message('no-name-in-module', - args=(name, module.name), node=node) - return None - except astroid.InferenceError: - return None - if module_names: - # FIXME: other message if name is not the latest part of - # module_names ? - modname = module and module.name or '__dict__' - self.add_message('no-name-in-module', node=node, - args=('.'.join(module_names), modname)) - return None - if isinstance(module, astroid.Module): - return module - return None - - -class VariablesChecker3k(VariablesChecker): - '''Modified variables checker for 3k''' - # listcomp have now also their scope - - def visit_listcomp(self, node): - """visit dictcomp: update consumption analysis variable - """ - self._to_consume.append((copy(node.locals), {}, 'comprehension')) - - def leave_listcomp(self, _): - """leave dictcomp: update consumption analysis variable - """ - # do not check for not used locals here - self._to_consume.pop() - - def leave_module(self, node): - """ Update consumption analysis variable - for metaclasses. - """ - module_locals = self._to_consume[0][0] - module_imports = self._to_consume[0][1] - consumed = {} - - for klass in node.nodes_of_class(astroid.Class): - found = metaclass = name = None - if not klass._metaclass: - # Skip if this class doesn't use - # explictly a metaclass, but inherits it from ancestors - continue - - metaclass = klass.metaclass() - - # Look the name in the already found locals. - # If it's not found there, look in the module locals - # and in the imported modules. - if isinstance(klass._metaclass, astroid.Name): - name = klass._metaclass.name - elif metaclass: - # if it uses a `metaclass=module.Class` - name = metaclass.root().name - - if name: - found = consumed.setdefault( - name, module_locals.get(name, module_imports.get(name))) - - if found is None and not metaclass: - name = None - if isinstance(klass._metaclass, astroid.Name): - name = klass._metaclass.name - elif isinstance(klass._metaclass, astroid.Getattr): - name = klass._metaclass.as_string() - - if name is not None: - if not (name in astroid.Module.scope_attrs or - is_builtin(name) or - name in self.config.additional_builtins or - name in node.locals): - self.add_message('undefined-variable', - node=klass, - args=(name, )) - # Pop the consumed items, in order to - # avoid having unused-import false positives - for name in consumed: - module_locals.pop(name, None) - super(VariablesChecker3k, self).leave_module(node) - -if sys.version_info >= (3, 0): - VariablesChecker = VariablesChecker3k - - -def register(linter): - """required method to auto register this checker""" - linter.register_checker(VariablesChecker(linter)) diff --git a/config.py b/config.py deleted file mode 100644 index ebfe578..0000000 --- a/config.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""utilities for Pylint configuration : - -* pylintrc -* pylint.d (PYLINTHOME) -""" -from __future__ import with_statement -from __future__ import print_function - -import pickle -import os -import sys -from os.path import exists, isfile, join, expanduser, abspath, dirname - -# pylint home is used to save old runs results ################################ - -USER_HOME = expanduser('~') -if 'PYLINTHOME' in os.environ: - PYLINT_HOME = os.environ['PYLINTHOME'] - if USER_HOME == '~': - USER_HOME = dirname(PYLINT_HOME) -elif USER_HOME == '~': - PYLINT_HOME = ".pylint.d" -else: - PYLINT_HOME = join(USER_HOME, '.pylint.d') - -def get_pdata_path(base_name, recurs): - """return the path of the file which should contain old search data for the - given base_name with the given options values - """ - base_name = base_name.replace(os.sep, '_') - return join(PYLINT_HOME, "%s%s%s"%(base_name, recurs, '.stats')) - -def load_results(base): - """try to unpickle and return data from file if it exists and is not - corrupted - - return an empty dictionary if it doesn't exists - """ - data_file = get_pdata_path(base, 1) - try: - with open(data_file, _PICK_LOAD) as stream: - return pickle.load(stream) - except Exception: # pylint: disable=broad-except - return {} - -if sys.version_info < (3, 0): - _PICK_DUMP, _PICK_LOAD = 'w', 'r' -else: - _PICK_DUMP, _PICK_LOAD = 'wb', 'rb' - -def save_results(results, base): - """pickle results""" - if not exists(PYLINT_HOME): - try: - os.mkdir(PYLINT_HOME) - except OSError: - print('Unable to create directory %s' % PYLINT_HOME, file=sys.stderr) - data_file = get_pdata_path(base, 1) - try: - with open(data_file, _PICK_DUMP) as stream: - pickle.dump(results, stream) - except (IOError, OSError) as ex: - print('Unable to create file %s: %s' % (data_file, ex), file=sys.stderr) - -# location of the configuration file ########################################## - - -def find_pylintrc(): - """search the pylint rc file and return its path if it find it, else None - """ - # is there a pylint rc file in the current directory ? - if exists('pylintrc'): - return abspath('pylintrc') - if isfile('__init__.py'): - curdir = abspath(os.getcwd()) - while isfile(join(curdir, '__init__.py')): - curdir = abspath(join(curdir, '..')) - if isfile(join(curdir, 'pylintrc')): - return join(curdir, 'pylintrc') - if 'PYLINTRC' in os.environ and exists(os.environ['PYLINTRC']): - pylintrc = os.environ['PYLINTRC'] - else: - user_home = expanduser('~') - if user_home == '~' or user_home == '/root': - pylintrc = ".pylintrc" - else: - pylintrc = join(user_home, '.pylintrc') - if not isfile(pylintrc): - pylintrc = join(user_home, '.config', 'pylintrc') - if not isfile(pylintrc): - if isfile('/etc/pylintrc'): - pylintrc = '/etc/pylintrc' - else: - pylintrc = None - return pylintrc - -PYLINTRC = find_pylintrc() - -ENV_HELP = ''' -The following environment variables are used: - * PYLINTHOME - Path to the directory where the persistent for the run will be stored. If -not found, it defaults to ~/.pylint.d/ or .pylint.d (in the current working -directory). - * PYLINTRC - Path to the configuration file. See the documentation for the method used -to search for configuration file. -''' % globals() - -# evaluation messages ######################################################### - -def get_note_message(note): - """return a message according to note - note is a float < 10 (10 is the highest note) - """ - assert note <= 10, "Note is %.2f. Either you cheated, or pylint's \ -broken!" % note - if note < 0: - msg = 'You have to do something quick !' - elif note < 1: - msg = 'Hey! This is really dreadful. Or maybe pylint is buggy?' - elif note < 2: - msg = "Come on! You can't be proud of this code" - elif note < 3: - msg = 'Hum... Needs work.' - elif note < 4: - msg = 'Wouldn\'t you be a bit lazy?' - elif note < 5: - msg = 'A little more work would make it acceptable.' - elif note < 6: - msg = 'Just the bare minimum. Give it a bit more polish. ' - elif note < 7: - msg = 'This is okay-ish, but I\'m sure you can do better.' - elif note < 8: - msg = 'If you commit now, people should not be making nasty \ -comments about you on c.l.py' - elif note < 9: - msg = 'That\'s pretty good. Good work mate.' - elif note < 10: - msg = 'So close to being perfect...' - else: - msg = 'Wow ! Now this deserves our uttermost respect.\nPlease send \ -your code to python-projects@logilab.org' - return msg diff --git a/epylint.py b/epylint.py deleted file mode 100755 index 4fd683e..0000000 --- a/epylint.py +++ /dev/null @@ -1,176 +0,0 @@ -# -*- coding: utf-8; mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=python:et:sw=4:ts=4:sts=4 -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""Emacs and Flymake compatible Pylint. - -This script is for integration with emacs and is compatible with flymake mode. - -epylint walks out of python packages before invoking pylint. This avoids -reporting import errors that occur when a module within a package uses the -absolute import path to get another module within this package. - -For example: - - Suppose a package is structured as - - a/__init__.py - a/b/x.py - a/c/y.py - - - Then if y.py imports x as "from a.b import x" the following produces pylint - errors - - cd a/c; pylint y.py - - - The following obviously doesn't - - pylint a/c/y.py - - - As this script will be invoked by emacs within the directory of the file - we are checking we need to go out of it to avoid these false positives. - - -You may also use py_run to run pylint with desired options and get back (or not) -its output. -""" -from __future__ import print_function - -import sys, os -import os.path as osp -from subprocess import Popen, PIPE - -def _get_env(): - '''Extracts the environment PYTHONPATH and appends the current sys.path to - those.''' - env = dict(os.environ) - env['PYTHONPATH'] = os.pathsep.join(sys.path) - return env - -def lint(filename, options=None): - """Pylint the given file. - - When run from emacs we will be in the directory of a file, and passed its - filename. If this file is part of a package and is trying to import other - modules from within its own package or another package rooted in a directory - below it, pylint will classify it as a failed import. - - To get around this, we traverse down the directory tree to find the root of - the package this module is in. We then invoke pylint from this directory. - - Finally, we must correct the filenames in the output generated by pylint so - Emacs doesn't become confused (it will expect just the original filename, - while pylint may extend it with extra directories if we've traversed down - the tree) - """ - # traverse downwards until we are out of a python package - full_path = osp.abspath(filename) - parent_path = osp.dirname(full_path) - child_path = osp.basename(full_path) - - while parent_path != "/" and osp.exists(osp.join(parent_path, '__init__.py')): - child_path = osp.join(osp.basename(parent_path), child_path) - parent_path = osp.dirname(parent_path) - - # Start pylint - # Ensure we use the python and pylint associated with the running epylint - from pylint import lint as lint_mod - lint_path = lint_mod.__file__ - options = options or ['--disable=C,R,I'] - cmd = [sys.executable, lint_path] + options + [ - '--msg-template', '{path}:{line}: {category} ({msg_id}, {symbol}, {obj}) {msg}', - '-r', 'n', child_path] - process = Popen(cmd, stdout=PIPE, cwd=parent_path, env=_get_env(), - universal_newlines=True) - - for line in process.stdout: - # remove pylintrc warning - if line.startswith("No config file found"): - continue - - # modify the file name thats output to reverse the path traversal we made - parts = line.split(":") - if parts and parts[0] == child_path: - line = ":".join([filename] + parts[1:]) - print(line, end=' ') - - process.wait() - return process.returncode - - -def py_run(command_options='', return_std=False, stdout=None, stderr=None, - script='epylint'): - """Run pylint from python - - ``command_options`` is a string containing ``pylint`` command line options; - ``return_std`` (boolean) indicates return of created standard output - and error (see below); - ``stdout`` and ``stderr`` are 'file-like' objects in which standard output - could be written. - - Calling agent is responsible for stdout/err management (creation, close). - Default standard output and error are those from sys, - or standalone ones (``subprocess.PIPE``) are used - if they are not set and ``return_std``. - - If ``return_std`` is set to ``True``, this function returns a 2-uple - containing standard output and error related to created process, - as follows: ``(stdout, stderr)``. - - A trivial usage could be as follows: - >>> py_run( '--version') - No config file found, using default configuration - pylint 0.18.1, - ... - - To silently run Pylint on a module, and get its standard output and error: - >>> (pylint_stdout, pylint_stderr) = py_run( 'module_name.py', True) - """ - # Create command line to call pylint - if os.name == 'nt': - script += '.bat' - command_line = script + ' ' + command_options - # Providing standard output and/or error if not set - if stdout is None: - if return_std: - stdout = PIPE - else: - stdout = sys.stdout - if stderr is None: - if return_std: - stderr = PIPE - else: - stderr = sys.stderr - # Call pylint in a subprocess - p = Popen(command_line, shell=True, stdout=stdout, stderr=stderr, - env=_get_env(), universal_newlines=True) - p.wait() - # Return standard output and error - if return_std: - return (p.stdout, p.stderr) - - -def Run(): - if len(sys.argv) == 1: - print("Usage: %s [options]" % sys.argv[0]) - sys.exit(1) - elif not osp.exists(sys.argv[1]): - print("%s does not exist" % sys.argv[1]) - sys.exit(1) - else: - sys.exit(lint(sys.argv[1], sys.argv[2:])) - - -if __name__ == '__main__': - Run() diff --git a/gui.py b/gui.py deleted file mode 100644 index 8327e0e..0000000 --- a/gui.py +++ /dev/null @@ -1,531 +0,0 @@ -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""Tkinker gui for pylint""" -from __future__ import print_function - -import os -import sys -import re -from threading import Thread - -import six - -from six.moves.tkinter import ( - Tk, Frame, Listbox, Entry, Label, Button, Scrollbar, - Checkbutton, Radiobutton, IntVar, StringVar, PanedWindow, - TOP, LEFT, RIGHT, BOTTOM, END, X, Y, BOTH, SUNKEN, W, - HORIZONTAL, DISABLED, NORMAL, W, -) -from six.moves.tkinter_tkfiledialog import ( - askopenfilename, askdirectory, -) - -import pylint.lint -from pylint.reporters.guireporter import GUIReporter - -HOME = os.path.expanduser('~/') -HISTORY = '.pylint-gui-history' -COLORS = {'(I)':'green', - '(C)':'blue', '(R)':'darkblue', - '(W)':'black', '(E)':'darkred', - '(F)':'red'} - - -def convert_to_string(msg): - """make a string representation of a message""" - module_object = msg.module - if msg.obj: - module_object += ".%s" % msg.obj - return "(%s) %s [%d]: %s" % (msg.C, module_object, msg.line, msg.msg) - -class BasicStream(object): - ''' - used in gui reporter instead of writing to stdout, it is written to - this stream and saved in contents - ''' - def __init__(self, gui): - """init""" - self.curline = "" - self.gui = gui - self.contents = [] - self.outdict = {} - self.currout = None - self.next_title = None - - def write(self, text): - """write text to the stream""" - if re.match('^--+$', text.strip()) or re.match('^==+$', text.strip()): - if self.currout: - self.outdict[self.currout].remove(self.next_title) - self.outdict[self.currout].pop() - self.currout = self.next_title - self.outdict[self.currout] = [''] - - if text.strip(): - self.next_title = text.strip() - - if text.startswith(os.linesep): - self.contents.append('') - if self.currout: - self.outdict[self.currout].append('') - self.contents[-1] += text.strip(os.linesep) - if self.currout: - self.outdict[self.currout][-1] += text.strip(os.linesep) - if text.endswith(os.linesep) and text.strip(): - self.contents.append('') - if self.currout: - self.outdict[self.currout].append('') - - def fix_contents(self): - """finalize what the contents of the dict should look like before output""" - for item in self.outdict: - num_empty = self.outdict[item].count('') - for _ in range(num_empty): - self.outdict[item].remove('') - if self.outdict[item]: - self.outdict[item].pop(0) - - def output_contents(self): - """output contents of dict to the gui, and set the rating""" - self.fix_contents() - self.gui.tabs = self.outdict - try: - self.gui.rating.set(self.outdict['Global evaluation'][0]) - except KeyError: - self.gui.rating.set('Error') - self.gui.refresh_results_window() - - #reset stream variables for next run - self.contents = [] - self.outdict = {} - self.currout = None - self.next_title = None - - -class LintGui(object): - """Build and control a window to interact with pylint""" - - def __init__(self, root=None): - """init""" - self.root = root or Tk() - self.root.title('Pylint') - #reporter - self.reporter = None - #message queue for output from reporter - self.msg_queue = six.moves.queue.Queue() - self.msgs = [] - self.visible_msgs = [] - self.filenames = [] - self.rating = StringVar() - self.tabs = {} - self.report_stream = BasicStream(self) - #gui objects - self.lb_messages = None - self.showhistory = None - self.results = None - self.btnRun = None - self.information_box = None - self.convention_box = None - self.refactor_box = None - self.warning_box = None - self.error_box = None - self.fatal_box = None - self.txtModule = None - self.status = None - self.msg_type_dict = None - self.init_gui() - - def init_gui(self): - """init helper""" - - window = PanedWindow(self.root, orient="vertical") - window.pack(side=TOP, fill=BOTH, expand=True) - - top_pane = Frame(window) - window.add(top_pane) - mid_pane = Frame(window) - window.add(mid_pane) - bottom_pane = Frame(window) - window.add(bottom_pane) - - #setting up frames - top_frame = Frame(top_pane) - mid_frame = Frame(top_pane) - history_frame = Frame(top_pane) - radio_frame = Frame(mid_pane) - rating_frame = Frame(mid_pane) - res_frame = Frame(mid_pane) - check_frame = Frame(bottom_pane) - msg_frame = Frame(bottom_pane) - btn_frame = Frame(bottom_pane) - top_frame.pack(side=TOP, fill=X) - mid_frame.pack(side=TOP, fill=X) - history_frame.pack(side=TOP, fill=BOTH, expand=True) - radio_frame.pack(side=TOP, fill=X) - rating_frame.pack(side=TOP, fill=X) - res_frame.pack(side=TOP, fill=BOTH, expand=True) - check_frame.pack(side=TOP, fill=X) - msg_frame.pack(side=TOP, fill=BOTH, expand=True) - btn_frame.pack(side=TOP, fill=X) - - # Binding F5 application-wide to run lint - self.root.bind('', self.run_lint) - - #Message ListBox - rightscrollbar = Scrollbar(msg_frame) - rightscrollbar.pack(side=RIGHT, fill=Y) - bottomscrollbar = Scrollbar(msg_frame, orient=HORIZONTAL) - bottomscrollbar.pack(side=BOTTOM, fill=X) - self.lb_messages = Listbox( - msg_frame, - yscrollcommand=rightscrollbar.set, - xscrollcommand=bottomscrollbar.set, - bg="white") - self.lb_messages.bind("", self.show_sourcefile) - self.lb_messages.pack(expand=True, fill=BOTH) - rightscrollbar.config(command=self.lb_messages.yview) - bottomscrollbar.config(command=self.lb_messages.xview) - - #History ListBoxes - rightscrollbar2 = Scrollbar(history_frame) - rightscrollbar2.pack(side=RIGHT, fill=Y) - bottomscrollbar2 = Scrollbar(history_frame, orient=HORIZONTAL) - bottomscrollbar2.pack(side=BOTTOM, fill=X) - self.showhistory = Listbox( - history_frame, - yscrollcommand=rightscrollbar2.set, - xscrollcommand=bottomscrollbar2.set, - bg="white") - self.showhistory.pack(expand=True, fill=BOTH) - rightscrollbar2.config(command=self.showhistory.yview) - bottomscrollbar2.config(command=self.showhistory.xview) - self.showhistory.bind('', self.select_recent_file) - self.set_history_window() - - #status bar - self.status = Label(self.root, text="", bd=1, relief=SUNKEN, anchor=W) - self.status.pack(side=BOTTOM, fill=X) - - #labelbl_ratingls - lbl_rating_label = Label(rating_frame, text='Rating:') - lbl_rating_label.pack(side=LEFT) - lbl_rating = Label(rating_frame, textvariable=self.rating) - lbl_rating.pack(side=LEFT) - Label(mid_frame, text='Recently Used:').pack(side=LEFT) - Label(top_frame, text='Module or package').pack(side=LEFT) - - #file textbox - self.txt_module = Entry(top_frame, background='white') - self.txt_module.bind('', self.run_lint) - self.txt_module.pack(side=LEFT, expand=True, fill=X) - - #results box - rightscrollbar = Scrollbar(res_frame) - rightscrollbar.pack(side=RIGHT, fill=Y) - bottomscrollbar = Scrollbar(res_frame, orient=HORIZONTAL) - bottomscrollbar.pack(side=BOTTOM, fill=X) - self.results = Listbox( - res_frame, - yscrollcommand=rightscrollbar.set, - xscrollcommand=bottomscrollbar.set, - bg="white", font="Courier") - self.results.pack(expand=True, fill=BOTH, side=BOTTOM) - rightscrollbar.config(command=self.results.yview) - bottomscrollbar.config(command=self.results.xview) - - #buttons - Button(top_frame, text='Open', command=self.file_open).pack(side=LEFT) - Button(top_frame, text='Open Package', - command=(lambda: self.file_open(package=True))).pack(side=LEFT) - - self.btnRun = Button(top_frame, text='Run', command=self.run_lint) - self.btnRun.pack(side=LEFT) - Button(btn_frame, text='Quit', command=self.quit).pack(side=BOTTOM) - - #radio buttons - self.information_box = IntVar() - self.convention_box = IntVar() - self.refactor_box = IntVar() - self.warning_box = IntVar() - self.error_box = IntVar() - self.fatal_box = IntVar() - i = Checkbutton(check_frame, text="Information", fg=COLORS['(I)'], - variable=self.information_box, command=self.refresh_msg_window) - c = Checkbutton(check_frame, text="Convention", fg=COLORS['(C)'], - variable=self.convention_box, command=self.refresh_msg_window) - r = Checkbutton(check_frame, text="Refactor", fg=COLORS['(R)'], - variable=self.refactor_box, command=self.refresh_msg_window) - w = Checkbutton(check_frame, text="Warning", fg=COLORS['(W)'], - variable=self.warning_box, command=self.refresh_msg_window) - e = Checkbutton(check_frame, text="Error", fg=COLORS['(E)'], - variable=self.error_box, command=self.refresh_msg_window) - f = Checkbutton(check_frame, text="Fatal", fg=COLORS['(F)'], - variable=self.fatal_box, command=self.refresh_msg_window) - i.select() - c.select() - r.select() - w.select() - e.select() - f.select() - i.pack(side=LEFT) - c.pack(side=LEFT) - r.pack(side=LEFT) - w.pack(side=LEFT) - e.pack(side=LEFT) - f.pack(side=LEFT) - - #check boxes - self.box = StringVar() - # XXX should be generated - report = Radiobutton( - radio_frame, text="Report", variable=self.box, - value="Report", command=self.refresh_results_window) - raw_met = Radiobutton( - radio_frame, text="Raw metrics", variable=self.box, - value="Raw metrics", command=self.refresh_results_window) - dup = Radiobutton( - radio_frame, text="Duplication", variable=self.box, - value="Duplication", command=self.refresh_results_window) - ext = Radiobutton( - radio_frame, text="External dependencies", - variable=self.box, value="External dependencies", - command=self.refresh_results_window) - stat = Radiobutton( - radio_frame, text="Statistics by type", - variable=self.box, value="Statistics by type", - command=self.refresh_results_window) - msg_cat = Radiobutton( - radio_frame, text="Messages by category", - variable=self.box, value="Messages by category", - command=self.refresh_results_window) - msg = Radiobutton( - radio_frame, text="Messages", variable=self.box, - value="Messages", command=self.refresh_results_window) - source_file = Radiobutton( - radio_frame, text="Source File", variable=self.box, - value="Source File", command=self.refresh_results_window) - report.select() - report.grid(column=0, row=0, sticky=W) - raw_met.grid(column=1, row=0, sticky=W) - dup.grid(column=2, row=0, sticky=W) - msg.grid(column=3, row=0, sticky=W) - stat.grid(column=0, row=1, sticky=W) - msg_cat.grid(column=1, row=1, sticky=W) - ext.grid(column=2, row=1, sticky=W) - source_file.grid(column=3, row=1, sticky=W) - - #dictionary for check boxes and associated error term - self.msg_type_dict = { - 'I': lambda: self.information_box.get() == 1, - 'C': lambda: self.convention_box.get() == 1, - 'R': lambda: self.refactor_box.get() == 1, - 'E': lambda: self.error_box.get() == 1, - 'W': lambda: self.warning_box.get() == 1, - 'F': lambda: self.fatal_box.get() == 1 - } - self.txt_module.focus_set() - - - def select_recent_file(self, event): # pylint: disable=unused-argument - """adds the selected file in the history listbox to the Module box""" - if not self.showhistory.size(): - return - - selected = self.showhistory.curselection() - item = self.showhistory.get(selected) - #update module - self.txt_module.delete(0, END) - self.txt_module.insert(0, item) - - def refresh_msg_window(self): - """refresh the message window with current output""" - #clear the window - self.lb_messages.delete(0, END) - self.visible_msgs = [] - for msg in self.msgs: - if self.msg_type_dict.get(msg.C)(): - self.visible_msgs.append(msg) - msg_str = convert_to_string(msg) - self.lb_messages.insert(END, msg_str) - fg_color = COLORS.get(msg_str[:3], 'black') - self.lb_messages.itemconfigure(END, fg=fg_color) - - def refresh_results_window(self): - """refresh the results window with current output""" - #clear the window - self.results.delete(0, END) - try: - for res in self.tabs[self.box.get()]: - self.results.insert(END, res) - except KeyError: - pass - - def process_incoming(self): - """process the incoming messages from running pylint""" - while self.msg_queue.qsize(): - try: - msg = self.msg_queue.get(0) - if msg == "DONE": - self.report_stream.output_contents() - return False - - #adding message to list of msgs - self.msgs.append(msg) - - #displaying msg if message type is selected in check box - if self.msg_type_dict.get(msg.C)(): - self.visible_msgs.append(msg) - msg_str = convert_to_string(msg) - self.lb_messages.insert(END, msg_str) - fg_color = COLORS.get(msg_str[:3], 'black') - self.lb_messages.itemconfigure(END, fg=fg_color) - - except six.moves.queue.Empty: - pass - return True - - def periodic_call(self): - """determine when to unlock the run button""" - if self.process_incoming(): - self.root.after(100, self.periodic_call) - else: - #enabling button so it can be run again - self.btnRun.config(state=NORMAL) - - def mainloop(self): - """launch the mainloop of the application""" - self.root.mainloop() - - def quit(self, _=None): - """quit the application""" - self.root.quit() - - def halt(self): # pylint: disable=no-self-use - """program halt placeholder""" - return - - def file_open(self, package=False, _=None): - """launch a file browser""" - if not package: - filename = askopenfilename(parent=self.root, - filetypes=[('pythonfiles', '*.py'), - ('allfiles', '*')], - title='Select Module') - else: - filename = askdirectory(title="Select A Folder", mustexist=1) - - if filename == (): - return - - self.txt_module.delete(0, END) - self.txt_module.insert(0, filename) - - def update_filenames(self): - """update the list of recent filenames""" - filename = self.txt_module.get() - if not filename: - filename = os.getcwd() - if filename+'\n' in self.filenames: - index = self.filenames.index(filename+'\n') - self.filenames.pop(index) - - #ensure only 10 most recent are stored - if len(self.filenames) == 10: - self.filenames.pop() - self.filenames.insert(0, filename+'\n') - - def set_history_window(self): - """update the history window with info from the history file""" - #clear the window - self.showhistory.delete(0, END) - # keep the last 10 most recent files - try: - view_history = open(HOME+HISTORY, 'r') - for hist in view_history.readlines(): - if not hist in self.filenames: - self.filenames.append(hist) - self.showhistory.insert(END, hist.split('\n')[0]) - view_history.close() - except IOError: - # do nothing since history file will be created later - return - - def run_lint(self, _=None): - """launches pylint""" - self.update_filenames() - self.root.configure(cursor='watch') - self.reporter = GUIReporter(self, output=self.report_stream) - module = self.txt_module.get() - if not module: - module = os.getcwd() - - #cleaning up msgs and windows - self.msgs = [] - self.visible_msgs = [] - self.lb_messages.delete(0, END) - self.tabs = {} - self.results.delete(0, END) - self.btnRun.config(state=DISABLED) - - #setting up a worker thread to run pylint - worker = Thread(target=lint_thread, args=(module, self.reporter, self,)) - self.periodic_call() - worker.start() - - # Overwrite the .pylint-gui-history file with all the new recently added files - # in order from filenames but only save last 10 files - write_history = open(HOME+HISTORY, 'w') - write_history.writelines(self.filenames) - write_history.close() - self.set_history_window() - - self.root.configure(cursor='') - - def show_sourcefile(self, event=None): # pylint: disable=unused-argument - selected = self.lb_messages.curselection() - if not selected: - return - - msg = self.visible_msgs[int(selected[0])] - scroll = msg.line - 3 - if scroll < 0: - scroll = 0 - - self.tabs["Source File"] = open(msg.path, "r").readlines() - self.box.set("Source File") - self.refresh_results_window() - self.results.yview(scroll) - self.results.select_set(msg.line - 1) - - -def lint_thread(module, reporter, gui): - """thread for pylint""" - gui.status.text = "processing module(s)" - pylint.lint.Run(args=[module], reporter=reporter, exit=False) - gui.msg_queue.put("DONE") - - -def Run(args): - """launch pylint gui from args""" - if args: - print('USAGE: pylint-gui\n launch a simple pylint gui using Tk') - sys.exit(1) - gui = LintGui() - gui.mainloop() - sys.exit(0) - -if __name__ == '__main__': - Run(sys.argv[1:]) diff --git a/interfaces.py b/interfaces.py deleted file mode 100644 index 64f5a95..0000000 --- a/interfaces.py +++ /dev/null @@ -1,84 +0,0 @@ -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""Interfaces for Pylint objects""" -from collections import namedtuple - -from logilab.common.interface import Interface - -Confidence = namedtuple('Confidence', ['name', 'description']) -# Warning Certainties -HIGH = Confidence('HIGH', 'No false positive possible.') -INFERENCE = Confidence('INFERENCE', 'Warning based on inference result.') -INFERENCE_FAILURE = Confidence('INFERENCE_FAILURE', - 'Warning based on inference with failures.') -UNDEFINED = Confidence('UNDEFINED', - 'Warning without any associated confidence level.') - -CONFIDENCE_LEVELS = [HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED] - - -class IChecker(Interface): - """This is an base interface, not designed to be used elsewhere than for - sub interfaces definition. - """ - - def open(self): - """called before visiting project (i.e set of modules)""" - - def close(self): - """called after visiting project (i.e set of modules)""" - - -class IRawChecker(IChecker): - """interface for checker which need to parse the raw file - """ - - def process_module(self, astroid): - """ process a module - - the module's content is accessible via astroid.stream - """ - - -class ITokenChecker(IChecker): - """Interface for checkers that need access to the token list.""" - def process_tokens(self, tokens): - """Process a module. - - tokens is a list of all source code tokens in the file. - """ - - -class IAstroidChecker(IChecker): - """ interface for checker which prefers receive events according to - statement type - """ - - -class IReporter(Interface): - """ reporter collect messages and display results encapsulated in a layout - """ - def add_message(self, msg_id, location, msg): - """add a message of a given type - - msg_id is a message identifier - location is a 3-uple (module, object, line) - msg is the actual message - """ - - def display_results(self, layout): - """display results encapsulated in the layout tree - """ - - -__all__ = ('IRawChecker', 'IAstroidChecker', 'ITokenChecker', 'IReporter') diff --git a/lint.py b/lint.py deleted file mode 100644 index e10ae56..0000000 --- a/lint.py +++ /dev/null @@ -1,1332 +0,0 @@ -# Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details -# -# You should have received a copy of the GNU General Public License along with -# this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -""" %prog [options] module_or_package - - Check that a module satisfies a coding standard (and more !). - - %prog --help - - Display this help message and exit. - - %prog --help-msg [,] - - Display help messages about given message identifiers and exit. -""" -from __future__ import print_function - -import collections -import contextlib -import itertools -import operator -import os -try: - import multiprocessing -except ImportError: - multiprocessing = None -import sys -import tokenize -import warnings - -import astroid -from astroid.__pkginfo__ import version as astroid_version -from astroid import modutils -from logilab.common import configuration -from logilab.common import optik_ext -from logilab.common import interface -from logilab.common import textutils -from logilab.common import ureports -from logilab.common.__pkginfo__ import version as common_version -import six - -from pylint import checkers -from pylint import interfaces -from pylint import reporters -from pylint import utils -from pylint import config -from pylint.__pkginfo__ import version - - -MANAGER = astroid.MANAGER - -def _get_new_args(message): - location = ( - message.abspath, - message.path, - message.module, - message.obj, - message.line, - message.column, - ) - return ( - message.msg_id, - message.symbol, - location, - message.msg, - message.confidence, - ) - -def _get_python_path(filepath): - dirname = os.path.realpath(os.path.expanduser(filepath)) - if not os.path.isdir(dirname): - dirname = os.path.dirname(dirname) - while True: - if not os.path.exists(os.path.join(dirname, "__init__.py")): - return dirname - old_dirname = dirname - dirname = os.path.dirname(dirname) - if old_dirname == dirname: - return os.getcwd() - - -def _merge_stats(stats): - merged = {} - for stat in stats: - for key, item in six.iteritems(stat): - if key not in merged: - merged[key] = item - else: - if isinstance(item, dict): - merged[key].update(item) - else: - merged[key] = merged[key] + item - return merged - - -# Python Linter class ######################################################### - -MSGS = { - 'F0001': ('%s', - 'fatal', - 'Used when an error occurred preventing the analysis of a \ - module (unable to find it for instance).'), - 'F0002': ('%s: %s', - 'astroid-error', - 'Used when an unexpected error occurred while building the ' - 'Astroid representation. This is usually accompanied by a ' - 'traceback. Please report such errors !'), - 'F0003': ('ignored builtin module %s', - 'ignored-builtin-module', - 'Used to indicate that the user asked to analyze a builtin ' - 'module which has been skipped.'), - 'F0010': ('error while code parsing: %s', - 'parse-error', - 'Used when an exception occured while building the Astroid ' - 'representation which could be handled by astroid.'), - - 'I0001': ('Unable to run raw checkers on built-in module %s', - 'raw-checker-failed', - 'Used to inform that a built-in module has not been checked ' - 'using the raw checkers.'), - - 'I0010': ('Unable to consider inline option %r', - 'bad-inline-option', - 'Used when an inline option is either badly formatted or can\'t ' - 'be used inside modules.'), - - 'I0011': ('Locally disabling %s (%s)', - 'locally-disabled', - 'Used when an inline option disables a message or a messages ' - 'category.'), - 'I0012': ('Locally enabling %s (%s)', - 'locally-enabled', - 'Used when an inline option enables a message or a messages ' - 'category.'), - 'I0013': ('Ignoring entire file', - 'file-ignored', - 'Used to inform that the file will not be checked'), - 'I0020': ('Suppressed %s (from line %d)', - 'suppressed-message', - 'A message was triggered on a line, but suppressed explicitly ' - 'by a disable= comment in the file. This message is not ' - 'generated for messages that are ignored due to configuration ' - 'settings.'), - 'I0021': ('Useless suppression of %s', - 'useless-suppression', - 'Reported when a message is explicitly disabled for a line or ' - 'a block of code, but never triggered.'), - 'I0022': ('Pragma "%s" is deprecated, use "%s" instead', - 'deprecated-pragma', - 'Some inline pylint options have been renamed or reworked, ' - 'only the most recent form should be used. ' - 'NOTE:skip-all is only available with pylint >= 0.26', - {'old_names': [('I0014', 'deprecated-disable-all')]}), - - 'E0001': ('%s', - 'syntax-error', - 'Used when a syntax error is raised for a module.'), - - 'E0011': ('Unrecognized file option %r', - 'unrecognized-inline-option', - 'Used when an unknown inline option is encountered.'), - 'E0012': ('Bad option value %r', - 'bad-option-value', - 'Used when a bad value for an inline option is encountered.'), - } - - -def _deprecated_option(shortname, opt_type): - def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument - sys.stderr.write('Warning: option %s is deprecated and ignored.\n' % (optname,)) - return {'short': shortname, 'help': 'DEPRECATED', 'hide': True, - 'type': opt_type, 'action': 'callback', 'callback': _warn_deprecated} - - -if multiprocessing is not None: - class ChildLinter(multiprocessing.Process): # pylint: disable=no-member - def run(self): - tasks_queue, results_queue, self._config = self._args # pylint: disable=no-member - - self._config["jobs"] = 1 # Child does not parallelize any further. - - # Run linter for received files/modules. - for file_or_module in iter(tasks_queue.get, 'STOP'): - result = self._run_linter(file_or_module[0]) - try: - results_queue.put(result) - except Exception as ex: - print("internal error with sending report for module %s" % file_or_module, file=sys.stderr) - print(ex, file=sys.stderr) - results_queue.put({}) - - def _run_linter(self, file_or_module): - linter = PyLinter() - - # Register standard checkers. - linter.load_default_plugins() - # Load command line plugins. - # TODO linter.load_plugin_modules(self._plugins) - - linter.load_configuration(**self._config) - linter.set_reporter(reporters.CollectingReporter()) - - # Run the checks. - linter.check(file_or_module) - - msgs = [_get_new_args(m) for m in linter.reporter.messages] - return (file_or_module, linter.file_state.base_name, linter.current_name, - msgs, linter.stats, linter.msg_status) - - -class PyLinter(configuration.OptionsManagerMixIn, - utils.MessagesHandlerMixIn, - utils.ReportsHandlerMixIn, - checkers.BaseTokenChecker): - """lint Python modules using external checkers. - - This is the main checker controlling the other ones and the reports - generation. It is itself both a raw checker and an astroid checker in order - to: - * handle message activation / deactivation at the module level - * handle some basic but necessary stats'data (number of classes, methods...) - - IDE plugins developpers: you may have to call - `astroid.builder.MANAGER.astroid_cache.clear()` accross run if you want - to ensure the latest code version is actually checked. - """ - - __implements__ = (interfaces.ITokenChecker, ) - - name = 'master' - priority = 0 - level = 0 - msgs = MSGS - - @staticmethod - def make_options(): - return (('ignore', - {'type' : 'csv', 'metavar' : '[,...]', - 'dest' : 'black_list', 'default' : ('CVS',), - 'help' : 'Add files or directories to the blacklist. ' - 'They should be base names, not paths.'}), - ('persistent', - {'default': True, 'type' : 'yn', 'metavar' : '', - 'level': 1, - 'help' : 'Pickle collected data for later comparisons.'}), - - ('load-plugins', - {'type' : 'csv', 'metavar' : '', 'default' : (), - 'level': 1, - 'help' : 'List of plugins (as comma separated values of ' - 'python modules names) to load, usually to register ' - 'additional checkers.'}), - - ('output-format', - {'default': 'text', 'type': 'string', 'metavar' : '', - 'short': 'f', - 'group': 'Reports', - 'help' : 'Set the output format. Available formats are text,' - ' parseable, colorized, msvs (visual studio) and html. You ' - 'can also give a reporter class, eg mypackage.mymodule.' - 'MyReporterClass.'}), - - ('files-output', - {'default': 0, 'type' : 'yn', 'metavar' : '', - 'group': 'Reports', 'level': 1, - 'help' : 'Put messages in a separate file for each module / ' - 'package specified on the command line instead of printing ' - 'them on stdout. Reports (if any) will be written in a file ' - 'name "pylint_global.[txt|html]".'}), - - ('reports', - {'default': 1, 'type' : 'yn', 'metavar' : '', - 'short': 'r', - 'group': 'Reports', - 'help' : 'Tells whether to display a full report or only the ' - 'messages'}), - - ('evaluation', - {'type' : 'string', 'metavar' : '', - 'group': 'Reports', 'level': 1, - 'default': '10.0 - ((float(5 * error + warning + refactor + ' - 'convention) / statement) * 10)', - 'help' : 'Python expression which should return a note less ' - 'than 10 (10 is the highest note). You have access ' - 'to the variables errors warning, statement which ' - 'respectively contain the number of errors / ' - 'warnings messages and the total number of ' - 'statements analyzed. This is used by the global ' - 'evaluation report (RP0004).'}), - - ('comment', - {'default': 0, 'type' : 'yn', 'metavar' : '', - 'group': 'Reports', 'level': 1, - 'help' : 'Add a comment according to your evaluation note. ' - 'This is used by the global evaluation report (RP0004).'}), - - ('confidence', - {'type' : 'multiple_choice', 'metavar': '', - 'default': '', - 'choices': [c.name for c in interfaces.CONFIDENCE_LEVELS], - 'group': 'Messages control', - 'help' : 'Only show warnings with the listed confidence levels.' - ' Leave empty to show all. Valid levels: %s' % ( - ', '.join(c.name for c in interfaces.CONFIDENCE_LEVELS),)}), - - ('enable', - {'type' : 'csv', 'metavar': '', - 'short': 'e', - 'group': 'Messages control', - 'help' : 'Enable the message, report, category or checker with the ' - 'given id(s). You can either give multiple identifier ' - 'separated by comma (,) or put this option multiple time. ' - 'See also the "--disable" option for examples. '}), - - ('disable', - {'type' : 'csv', 'metavar': '', - 'short': 'd', - 'group': 'Messages control', - 'help' : 'Disable the message, report, category or checker ' - 'with the given id(s). You can either give multiple identifiers' - ' separated by comma (,) or put this option multiple times ' - '(only on the command line, not in the configuration file ' - 'where it should appear only once).' - 'You can also use "--disable=all" to disable everything first ' - 'and then reenable specific checks. For example, if you want ' - 'to run only the similarities checker, you can use ' - '"--disable=all --enable=similarities". ' - 'If you want to run only the classes checker, but have no ' - 'Warning level messages displayed, use' - '"--disable=all --enable=classes --disable=W"'}), - - ('msg-template', - {'type' : 'string', 'metavar': '